From aaf2905a9dfacbcdc96013a4646ce628ac4ca638 Mon Sep 17 00:00:00 2001 From: xialixin Date: Sun, 25 Mar 2018 17:47:37 +0800 Subject: [PATCH 01/33] add: hls configuration adds [duration] variable (#1083) --- trunk/conf/full.conf | 1 + trunk/src/app/srs_app_fragment.cpp | 10 ++++++++-- trunk/src/app/srs_app_hls.cpp | 9 ++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 5d1925fd8..9d78a8516 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -1046,6 +1046,7 @@ vhost hls.srs.com { # [999], replace this const to current millisecond. # [timestamp],replace this const to current UNIX timestamp in ms. # [seq], the sequence number of ts. + # [duration], replace this const to current ts duration. # @see https://github.com/ossrs/srs/wiki/v2_CN_DVR#custom-path # @see https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#hls-config # default: [app]/[stream]-[seq].ts diff --git a/trunk/src/app/srs_app_fragment.cpp b/trunk/src/app/srs_app_fragment.cpp index 1c73307ab..f158078fd 100644 --- a/trunk/src/app/srs_app_fragment.cpp +++ b/trunk/src/app/srs_app_fragment.cpp @@ -28,6 +28,7 @@ #include #include +#include using namespace std; SrsFragment::SrsFragment() @@ -126,11 +127,16 @@ srs_error_t SrsFragment::rename() string full_path = fullpath(); string tmp_file = tmppath(); - + int tempdur = (int)duration(); + if (true) { + std::stringstream ss; + ss << tempdur; + full_path = srs_string_replace(full_path, "[duration]", ss.str()); + } if (::rename(tmp_file.c_str(), full_path.c_str()) < 0) { return srs_error_new(ERROR_SYSTEM_FRAGMENT_RENAME, "rename %s to %s", tmp_file.c_str(), full_path.c_str()); } - + filepath = full_path; return err; } diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index f6dacc659..b9484705e 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -688,7 +688,14 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file) ss << "#EXTINF:" << segment->duration() / 1000.0 << ", no desc" << SRS_CONSTS_LF; // {file name}\n - ss << segment->uri << SRS_CONSTS_LF; + std::string seg_uri = segment->uri; + if (true) { + std::stringstream stemp; + stemp << (int)(segment->duration()); + seg_uri = srs_string_replace(seg_uri, "[duration]", stemp.str()); + } + //ss << segment->uri << SRS_CONSTS_LF; + ss << seg_uri << SRS_CONSTS_LF; } // write m3u8 to writer. From bb4e16ae3c9d39e0de8ea8e7c6195171e1799071 Mon Sep 17 00:00:00 2001 From: xialixin Date: Sun, 25 Mar 2018 17:48:07 +0800 Subject: [PATCH 02/33] fix create dir success result check (#1080) --- trunk/src/kernel/srs_kernel_utility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 94f6375d3..274bcef8f 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -546,7 +546,7 @@ int srs_do_create_dir_recursively(string dir) { int ret = srs_do_create_dir_recursively(dir); - if (ret == ERROR_SYSTEM_DIR_EXISTS) { + if (ret == ERROR_SYSTEM_DIR_EXISTS || ret == ERROR_SUCCESS) { return srs_success; } From 01ed485cf373ffc2424644203ec50e90f019cc6c Mon Sep 17 00:00:00 2001 From: "Hui Zhang (huzhang2)" Date: Mon, 16 Jul 2018 16:27:41 +0800 Subject: [PATCH 03/33] fix #1172 #1173 #1174 #1175 security scan report issue --- trunk/src/app/srs_app_server.cpp | 1 + trunk/src/app/srs_app_utility.cpp | 3 +++ trunk/src/kernel/srs_kernel_codec.cpp | 2 +- trunk/src/kernel/srs_kernel_utility.cpp | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index e56d2042a..9efdba41d 100755 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -690,6 +690,7 @@ int SrsServer::acquire_pid_file() if(errno == EACCES || errno == EAGAIN) { ret = ERROR_SYSTEM_PID_ALREADY_RUNNING; srs_error("srs is already running! ret=%#x", ret); + ::close(fd); return ret; } diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 66107cc6c..d6ac34701 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -71,6 +71,7 @@ int srs_socket_connect(string server, int port, int64_t timeout, st_netfd_t* pst if(stfd == NULL){ ret = ERROR_ST_OPEN_SOCKET; srs_error("st_netfd_open_socket failed. ret=%d", ret); + ::close(sock); return ret; } @@ -94,12 +95,14 @@ int srs_socket_connect(string server, int port, int64_t timeout, st_netfd_t* pst srs_info("connect ok. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); *pstfd = stfd; + ::close(sock); return ret; failed: if (stfd) { srs_close_stfd(stfd); } + ::close(sock); return ret; } diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index 42d577204..75001d241 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -169,7 +169,7 @@ string srs_codec_avc_level2str(SrsAvcLevel level) // 1 = 11 kHz = 11025 Hz // 2 = 22 kHz = 22050 Hz // 3 = 44 kHz = 44100 Hz -int flv_sample_rates[] = {5512, 11025, 22050, 44100}; +int flv_sample_rates[] = {5512, 11025, 22050, 44100, 0}; // the sample rates in the codec, // in the sequence header. diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 276c32123..60eef5ddb 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -652,7 +652,7 @@ int srs_av_base64_decode(u_int8_t* out, const char* in_str, int out_size) // no sign extension const u_int8_t *in = (const u_int8_t*)in_str; unsigned bits = 0xff; - unsigned v; + unsigned v = 0; while (end - dst > 3) { BASE64_DEC_STEP(0); From 2d49b9d1a734a348f9f646ab4bf69831740bf69c Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 17 Jul 2018 13:44:01 +0800 Subject: [PATCH 04/33] Merge #1176, fix scaned issues. 2.0.248 --- README.md | 2 ++ trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e5d78fbf..99599ae13 100755 --- a/README.md +++ b/README.md @@ -332,6 +332,7 @@ Remark: ## History +* v2.0, 2018-07-17, Merge [#1176][bug #1176], fix scaned issues. 2.0.248 * v2.0, 2018-02-28, Merge [#1077][bug #1077], fix crash for edge HLS. 2.0.247 * v2.0, 2018-02-13, Fix [#1059][bug #1059], support vhost in stream parameters. 2.0.246 * v2.0, 2018-01-07, Merge [#1045][bug #1045], fix [#1044][bug #1044], TCP connection alive detection. 2.0.245 @@ -1307,6 +1308,7 @@ Winlin [bug #1045]: https://github.com/ossrs/srs/issues/1045 [bug #1059]: https://github.com/ossrs/srs/issues/1059 [bug #1077]: https://github.com/ossrs/srs/issues/1077 +[bug #1176]: https://github.com/ossrs/srs/issues/1176 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index cd0ab889a..ad068719a 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 247 +#define VERSION_REVISION 248 // generated by configure, only macros. #include From d27db215df883d050f964b8d2990416bd36d04fb Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 18 Jul 2018 19:21:29 +0800 Subject: [PATCH 05/33] For #1176, should not close fd when success. --- trunk/src/app/srs_app_utility.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index d6ac34701..a64587fa3 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -95,7 +95,6 @@ int srs_socket_connect(string server, int port, int64_t timeout, st_netfd_t* pst srs_info("connect ok. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); *pstfd = stfd; - ::close(sock); return ret; failed: From 595267168916222d4eb28bb420bf98bff6de6b73 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 18 Jul 2018 20:27:54 +0800 Subject: [PATCH 06/33] Release 2.0r3, SRS/2.0.248 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 99599ae13..15d643d28 100755 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Remark: ## Releases +* 2018-07-18, [Release v2.0-r3][r2.0r3], 2.0 release2, 2.0.248, 86775 lines. * 2017-06-10, [Release v2.0-r2][r2.0r2], 2.0 release2, 2.0.243, 86670 lines. * 2017-04-18, [Release v2.0-r1][r2.0r1], 2.0 release1, 2.0.239, 86515 lines. * 2017-03-03, [Release v2.0-r0][r2.0r0], 2.0 release0, 2.0.234, 86373 lines. @@ -332,6 +333,7 @@ Remark: ## History +* v2.0, 2018-07-18, [2.0 release3(2.0.248)][r2.0r3] released. 86775 lines. * v2.0, 2018-07-17, Merge [#1176][bug #1176], fix scaned issues. 2.0.248 * v2.0, 2018-02-28, Merge [#1077][bug #1077], fix crash for edge HLS. 2.0.247 * v2.0, 2018-02-13, Fix [#1059][bug #1059], support vhost in stream parameters. 2.0.246 @@ -1313,6 +1315,7 @@ Winlin [exo #828]: https://github.com/google/ExoPlayer/pull/828 +[r2.0r3]: https://github.com/ossrs/srs/releases/tag/v2.0-r3 [r2.0r2]: https://github.com/ossrs/srs/releases/tag/v2.0-r2 [r2.0r1]: https://github.com/ossrs/srs/releases/tag/v2.0-r1 [r2.0r0]: https://github.com/ossrs/srs/releases/tag/v2.0-r0 From 551863a7ed115654097e425df792cd8768954a50 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 21 Jul 2018 17:34:31 +0800 Subject: [PATCH 07/33] Refine code --- trunk/modules/mp4-parser/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/modules/mp4-parser/config b/trunk/modules/mp4-parser/config index 04275c39a..7f9adfd66 100644 --- a/trunk/modules/mp4-parser/config +++ b/trunk/modules/mp4-parser/config @@ -1,5 +1,5 @@ -# The module to ingest hls to replace ffmpeg with better behavior. +# The module to parse mp4 file. SRS_MODULE_NAME=("srs_mp4_parser") SRS_MODULE_MAIN=("srs_main_mp4_parser") SRS_MODULE_APP=() From 6d7c7418a8a21fdfa41a0c023a1bae29ce08b95b Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 21 Jul 2018 17:49:46 +0800 Subject: [PATCH 08/33] Merge #1119, fix memory leak. 2.0.249 --- README.md | 2 ++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_ts.cpp | 3 +++ trunk/src/main/srs_main_ingest_hls.cpp | 5 ++++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 15d643d28..334308ca7 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-07-21, Merge [#1119][bug #1119], fix memory leak. 2.0.249 * v2.0, 2018-07-18, [2.0 release3(2.0.248)][r2.0r3] released. 86775 lines. * v2.0, 2018-07-17, Merge [#1176][bug #1176], fix scaned issues. 2.0.248 * v2.0, 2018-02-28, Merge [#1077][bug #1077], fix crash for edge HLS. 2.0.247 @@ -1311,6 +1312,7 @@ Winlin [bug #1059]: https://github.com/ossrs/srs/issues/1059 [bug #1077]: https://github.com/ossrs/srs/issues/1077 [bug #1176]: https://github.com/ossrs/srs/issues/1176 +[bug #1119]: https://github.com/ossrs/srs/issues/1119 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ad068719a..2da22b8f7 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 248 +#define VERSION_REVISION 249 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index fad50ea2b..eebc44a2d 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -184,8 +184,11 @@ SrsTsMessage* SrsTsMessage::detach() cp->sid = sid; cp->PES_packet_length = PES_packet_length; cp->continuity_counter = continuity_counter; + + srs_freep(cp->payload); cp->payload = payload; payload = NULL; + return cp; } diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp index 7480fb0db..5b2435c95 100644 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ b/trunk/src/main/srs_main_ingest_hls.cpp @@ -898,10 +898,12 @@ int SrsIngestSrsOutput::parse_message_queue() std::multimap::iterator it = queue.begin(); SrsTsMessage* msg = it->second; + SrsAutoFree(SrsTsMessage, msg); + queue.erase(it); + if (msg->channel->stream == SrsTsStreamVideoH264) { nb_videos--; } - queue.erase(it); // parse the stream. SrsStream avs; @@ -935,6 +937,7 @@ int SrsIngestSrsOutput::flush_message_queue() std::multimap::iterator it = queue.begin(); SrsTsMessage* msg = it->second; + SrsAutoFree(SrsTsMessage, msg); queue.erase(it); // parse the stream. From dea5e5bb48ace08c56503bede194cb731ca4ecb9 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 21 Jul 2018 19:09:32 +0800 Subject: [PATCH 09/33] Add utest or CRC32 IEEE, refine TABs of code --- trunk/src/kernel/srs_kernel_utility.cpp | 1152 +++++++++++------------ trunk/src/utest/srs_utest_kernel.cpp | 19 + 2 files changed, 595 insertions(+), 576 deletions(-) diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 13bbc68aa..80bb062ca 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -108,6 +108,7 @@ int64_t srs_get_system_time_ms() return _srs_system_time_us_cache / 1000; } + int64_t srs_get_system_startup_time_ms() { if (_srs_system_time_startup_time <= 0) { @@ -116,6 +117,7 @@ int64_t srs_get_system_startup_time_ms() return _srs_system_time_startup_time / 1000; } + int64_t srs_update_system_time_ms() { timeval now; @@ -505,346 +507,346 @@ int srs_do_create_dir_recursively(string dir) mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH; if (::mkdir(dir.c_str(), mode) < 0) { #else - if (::mkdir(dir.c_str()) < 0) { + if (::mkdir(dir.c_str()) < 0) { #endif - if (errno == EEXIST) { - return ERROR_SYSTEM_DIR_EXISTS; - } - - ret = ERROR_SYSTEM_CREATE_DIR; - srs_error("create dir %s failed. ret=%d", dir.c_str(), ret); - return ret; + if (errno == EEXIST) { + return ERROR_SYSTEM_DIR_EXISTS; } - srs_info("create dir %s success.", dir.c_str()); + ret = ERROR_SYSTEM_CREATE_DIR; + srs_error("create dir %s failed. ret=%d", dir.c_str(), ret); return ret; } - bool srs_bytes_equals(void* pa, void* pb, int size) - { - uint8_t* a = (uint8_t*)pa; - uint8_t* b = (uint8_t*)pb; - - if (!a && !b) { - return true; - } - - if (!a || !b) { - return false; - } - - for(int i = 0; i < size; i++){ - if(a[i] != b[i]){ - return false; - } - } - + srs_info("create dir %s success.", dir.c_str()); + + return ret; +} + +bool srs_bytes_equals(void* pa, void* pb, int size) +{ + uint8_t* a = (uint8_t*)pa; + uint8_t* b = (uint8_t*)pb; + + if (!a && !b) { return true; } - srs_error_t srs_create_dir_recursively(string dir) - { - int ret = srs_do_create_dir_recursively(dir); - - if (ret == ERROR_SYSTEM_DIR_EXISTS || ret == ERROR_SUCCESS) { - return srs_success; - } - - return srs_error_new(ret, "create dir %s", dir.c_str()); + if (!a || !b) { + return false; } - bool srs_path_exists(std::string path) - { - struct stat st; - - // stat current dir, if exists, return error. - if (stat(path.c_str(), &st) == 0) { - return true; + for(int i = 0; i < size; i++){ + if(a[i] != b[i]){ + return false; } - - return false; } - string srs_path_dirname(string path) - { - std::string dirname = path; - size_t pos = string::npos; - - if ((pos = dirname.rfind("/")) != string::npos) { - if (pos == 0) { - return "/"; - } - dirname = dirname.substr(0, pos); - } - - return dirname; + return true; +} + +srs_error_t srs_create_dir_recursively(string dir) +{ + int ret = srs_do_create_dir_recursively(dir); + + if (ret == ERROR_SYSTEM_DIR_EXISTS || ret == ERROR_SUCCESS) { + return srs_success; } - string srs_path_basename(string path) - { - std::string dirname = path; - size_t pos = string::npos; - - if ((pos = dirname.rfind("/")) != string::npos) { - // the basename("/") is "/" - if (dirname.length() == 1) { - return dirname; - } - dirname = dirname.substr(pos + 1); - } - - return dirname; + return srs_error_new(ret, "create dir %s", dir.c_str()); +} + +bool srs_path_exists(std::string path) +{ + struct stat st; + + // stat current dir, if exists, return error. + if (stat(path.c_str(), &st) == 0) { + return true; } - string srs_path_filename(string path) - { - std::string filename = path; - size_t pos = string::npos; - - if ((pos = filename.rfind(".")) != string::npos) { - return filename.substr(0, pos); + return false; +} + +string srs_path_dirname(string path) +{ + std::string dirname = path; + size_t pos = string::npos; + + if ((pos = dirname.rfind("/")) != string::npos) { + if (pos == 0) { + return "/"; } - - return filename; + dirname = dirname.substr(0, pos); } - string srs_path_filext(string path) - { - size_t pos = string::npos; - - if ((pos = path.rfind(".")) != string::npos) { - return path.substr(pos); + return dirname; +} + +string srs_path_basename(string path) +{ + std::string dirname = path; + size_t pos = string::npos; + + if ((pos = dirname.rfind("/")) != string::npos) { + // the basename("/") is "/" + if (dirname.length() == 1) { + return dirname; } - - return ""; + dirname = dirname.substr(pos + 1); } - bool srs_avc_startswith_annexb(SrsBuffer* stream, int* pnb_start_code) - { - char* bytes = stream->data() + stream->pos(); - char* p = bytes; - - for (;;) { - if (!stream->require((int)(p - bytes + 3))) { - return false; - } - - // not match - if (p[0] != (char)0x00 || p[1] != (char)0x00) { - return false; - } - - // match N[00] 00 00 01, where N>=0 - if (p[2] == (char)0x01) { - if (pnb_start_code) { - *pnb_start_code = (int)(p - bytes) + 3; - } - return true; - } - - p++; - } - - return false; + return dirname; +} + +string srs_path_filename(string path) +{ + std::string filename = path; + size_t pos = string::npos; + + if ((pos = filename.rfind(".")) != string::npos) { + return filename.substr(0, pos); } - bool srs_aac_startswith_adts(SrsBuffer* stream) - { - char* bytes = stream->data() + stream->pos(); - char* p = bytes; - - if (!stream->require((int)(p - bytes) + 2)) { + return filename; +} + +string srs_path_filext(string path) +{ + size_t pos = string::npos; + + if ((pos = path.rfind(".")) != string::npos) { + return path.substr(pos); + } + + return ""; +} + +bool srs_avc_startswith_annexb(SrsBuffer* stream, int* pnb_start_code) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; + + for (;;) { + if (!stream->require((int)(p - bytes + 3))) { return false; } - // matched 12bits 0xFFF, - // @remark, we must cast the 0xff to char to compare. - if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) { + // not match + if (p[0] != (char)0x00 || p[1] != (char)0x00) { return false; } - return true; - } - - // @see http://www.stmc.edu.hk/~vincent/ffmpeg_0.4.9-pre1/libavformat/mpegtsenc.c - unsigned int __mpegts_crc32(const uint8_t *data, int len) - { - /* - * MPEG2 transport stream (aka DVB) mux - * Copyright (c) 2003 Fabrice Bellard. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - static const uint32_t table[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 - }; - - uint32_t crc = 0xffffffff; - - for (int i=0; i> 24) ^ *data++) & 0xff]; + // match N[00] 00 00 01, where N>=0 + if (p[2] == (char)0x01) { + if (pnb_start_code) { + *pnb_start_code = (int)(p - bytes) + 3; + } + return true; } - return crc; + p++; } - // @see https://github.com/ETrun/crc32/blob/master/crc32.c - uint32_t __crc32_ieee(uint32_t init, const uint8_t* buf, size_t nb_buf) - { - /*----------------------------------------------------------------------------*\ - * CRC-32 version 2.0.0 by Craig Bruce, 2006-04-29. - * - * This program generates the CRC-32 values for the files named in the - * command-line arguments. These are the same CRC-32 values used by GZIP, - * PKZIP, and ZMODEM. The Crc32_ComputeBuf() can also be detached and - * used independently. - * - * THIS PROGRAM IS PUBLIC-DOMAIN SOFTWARE. - * - * Based on the byte-oriented implementation "File Verification Using CRC" - * by Mark R. Nelson in Dr. Dobb's Journal, May 1992, pp. 64-67. - * - * v1.0.0: original release. - * v1.0.1: fixed printf formats. - * v1.0.2: fixed something else. - * v1.0.3: replaced CRC constant table by generator function. - * v1.0.4: reformatted code, made ANSI C. 1994-12-05. - * v2.0.0: rewrote to use memory buffer & static table, 2006-04-29. - * v2.1.0: modified by Nico, 2013-04-20 - \*----------------------------------------------------------------------------*/ - static const uint32_t table[256] = { - 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535, - 0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD, - 0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D, - 0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC, - 0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4, - 0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C, - 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC, - 0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, - 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB, - 0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F, - 0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB, - 0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E, - 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA, - 0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE, - 0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A, - 0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409, - 0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81, - 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739, - 0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8, - 0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268, - 0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0, - 0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8, - 0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, - 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF, - 0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703, - 0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7, - 0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A, - 0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE, - 0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242, - 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6, - 0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D, - 0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5, - 0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605, - 0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94, - 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D - }; - - uint32_t crc = init ^ 0xFFFFFFFF; - - for (size_t i = 0; i < nb_buf; i++) { - crc = table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); - } - - return crc^0xFFFFFFFF; - } + return false; +} + +bool srs_aac_startswith_adts(SrsBuffer* stream) +{ + char* bytes = stream->data() + stream->pos(); + char* p = bytes; - uint32_t srs_crc32_mpegts(const void* buf, int size) - { - return __mpegts_crc32((const uint8_t*)buf, size); + if (!stream->require((int)(p - bytes) + 2)) { + return false; } - uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous) - { - return __crc32_ieee(previous, (const uint8_t*)buf, size); + // matched 12bits 0xFFF, + // @remark, we must cast the 0xff to char to compare. + if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) { + return false; } + return true; +} + +// @see http://www.stmc.edu.hk/~vincent/ffmpeg_0.4.9-pre1/libavformat/mpegtsenc.c +unsigned int __mpegts_crc32(const uint8_t *data, int len) +{ /* - * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) - * - * This file is part of FFmpeg. + * MPEG2 transport stream (aka DVB) mux + * Copyright (c) 2003 Fabrice Bellard. * - * FFmpeg is free software; you can redistribute it and/or + * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * version 2 of the License, or (at your option) any later version. * - * FFmpeg is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + static const uint32_t table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + uint32_t crc = 0xffffffff; + + for (int i=0; i> 24) ^ *data++) & 0xff]; + } + + return crc; +} + +// @see https://github.com/ETrun/crc32/blob/master/crc32.c + uint32_t __crc32_ieee(uint32_t init, const uint8_t* buf, size_t nb_buf) +{ + /*----------------------------------------------------------------------------*\ + * CRC-32 version 2.0.0 by Craig Bruce, 2006-04-29. + * + * This program generates the CRC-32 values for the files named in the + * command-line arguments. These are the same CRC-32 values used by GZIP, + * PKZIP, and ZMODEM. The Crc32_ComputeBuf() can also be detached and + * used independently. + * + * THIS PROGRAM IS PUBLIC-DOMAIN SOFTWARE. + * + * Based on the byte-oriented implementation "File Verification Using CRC" + * by Mark R. Nelson in Dr. Dobb's Journal, May 1992, pp. 64-67. + * + * v1.0.0: original release. + * v1.0.1: fixed printf formats. + * v1.0.2: fixed something else. + * v1.0.3: replaced CRC constant table by generator function. + * v1.0.4: reformatted code, made ANSI C. 1994-12-05. + * v2.0.0: rewrote to use memory buffer & static table, 2006-04-29. + * v2.1.0: modified by Nico, 2013-04-20 + \*----------------------------------------------------------------------------*/ + static const uint32_t table[256] = { + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535, + 0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD, + 0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D, + 0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC, + 0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4, + 0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C, + 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC, + 0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, + 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB, + 0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F, + 0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB, + 0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E, + 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA, + 0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE, + 0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A, + 0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409, + 0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81, + 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739, + 0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8, + 0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268, + 0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0, + 0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8, + 0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, + 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF, + 0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703, + 0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7, + 0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A, + 0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE, + 0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242, + 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6, + 0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D, + 0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5, + 0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605, + 0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94, + 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D + }; + + uint32_t crc = init ^ 0xFFFFFFFF; + + for (size_t i = 0; i < nb_buf; i++) { + crc = table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); + } + return crc^0xFFFFFFFF; +} + +uint32_t srs_crc32_mpegts(const void* buf, int size) +{ + return __mpegts_crc32((const uint8_t*)buf, size); +} + +uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous) +{ + return __crc32_ieee(previous, (const uint8_t*)buf, size); +} + +/* + * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef UINT_MAX #define UINT_MAX 0xffffffff #endif @@ -883,59 +885,58 @@ int srs_do_create_dir_recursively(string dir) #define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16)) #ifndef av_bswap32 - static const uint32_t av_bswap32(uint32_t x) - { - return AV_BSWAP32C(x); - } +static const uint32_t av_bswap32(uint32_t x) +{ + return AV_BSWAP32C(x); +} #endif #define av_be2ne32(x) av_bswap32(x) - - /** - * @file - * @brief Base64 encode/decode - * @author Ryan Martell (with lots of Michael) - */ - - /* ---------------- private code */ - static const uint8_t map2[256] = - { - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, - - 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, - - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }; +/** + * @file + * @brief Base64 encode/decode + * @author Ryan Martell (with lots of Michael) + */ + +/* ---------------- private code */ +static const uint8_t map2[256] = +{ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + + 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, + 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; #define BASE64_DEC_STEP(i) do { \ bits = map2[in[i]]; \ @@ -944,261 +945,260 @@ int srs_do_create_dir_recursively(string dir) v = i ? (v << 6) + bits : bits; \ } while(0) - int srs_av_base64_decode(uint8_t* out, const char* in_str, int out_size) - { - uint8_t *dst = out; - uint8_t *end = out + out_size; - // no sign extension - const uint8_t *in = (const uint8_t*)in_str; - unsigned bits = 0xff; - unsigned v = 0; - - while (end - dst > 3) { - BASE64_DEC_STEP(0); - BASE64_DEC_STEP(1); - BASE64_DEC_STEP(2); - BASE64_DEC_STEP(3); - // Using AV_WB32 directly confuses compiler - v = av_be2ne32(v << 8); - AV_WN32(dst, v); - dst += 3; - in += 4; - } - if (end - dst) { - BASE64_DEC_STEP(0); - BASE64_DEC_STEP(1); - BASE64_DEC_STEP(2); - BASE64_DEC_STEP(3); - *dst++ = v >> 16; - if (end - dst) - *dst++ = v >> 8; - if (end - dst) - *dst++ = v; - in += 4; - } - while (1) { - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; - } - - out3: - *dst++ = v >> 10; - v <<= 2; - out2: - *dst++ = v >> 4; - out1: - out0: - return bits & 1 ? -1 : (int)(dst - out); +int srs_av_base64_decode(uint8_t* out, const char* in_str, int out_size) +{ + uint8_t *dst = out; + uint8_t *end = out + out_size; + // no sign extension + const uint8_t *in = (const uint8_t*)in_str; + unsigned bits = 0xff; + unsigned v = 0; + + while (end - dst > 3) { + BASE64_DEC_STEP(0); + BASE64_DEC_STEP(1); + BASE64_DEC_STEP(2); + BASE64_DEC_STEP(3); + // Using AV_WB32 directly confuses compiler + v = av_be2ne32(v << 8); + AV_WN32(dst, v); + dst += 3; + in += 4; + } + if (end - dst) { + BASE64_DEC_STEP(0); + BASE64_DEC_STEP(1); + BASE64_DEC_STEP(2); + BASE64_DEC_STEP(3); + *dst++ = v >> 16; + if (end - dst) + *dst++ = v >> 8; + if (end - dst) + *dst++ = v; + in += 4; + } + while (1) { + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; } - /***************************************************************************** - * b64_encode: Stolen from VLC's http.c. - * Simplified by Michael. - * Fixed edge cases and made it work from data (vs. strings) by Ryan. - *****************************************************************************/ - - char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, int in_size) - { - static const char b64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - char *ret, *dst; - unsigned i_bits = 0; - int i_shift = 0; - int bytes_remaining = in_size; - - if (in_size >= (int)(UINT_MAX / 4) || - out_size < SRS_AV_BASE64_SIZE(in_size)) - return NULL; - ret = dst = out; - while (bytes_remaining > 3) { - i_bits = AV_RB32(in); - in += 3; bytes_remaining -= 3; - *dst++ = b64[ i_bits>>26 ]; - *dst++ = b64[(i_bits>>20) & 0x3F]; - *dst++ = b64[(i_bits>>14) & 0x3F]; - *dst++ = b64[(i_bits>>8 ) & 0x3F]; - } - i_bits = 0; - while (bytes_remaining) { - i_bits = (i_bits << 8) + *in++; - bytes_remaining--; - i_shift += 8; - } - while (i_shift > 0) { - *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; - i_shift -= 6; - } - while ((dst - ret) & 3) - *dst++ = '='; - *dst = '\0'; - - return ret; +out3: + *dst++ = v >> 10; + v <<= 2; +out2: + *dst++ = v >> 4; +out1: +out0: + return bits & 1 ? -1 : (int)(dst - out); +} + +/***************************************************************************** + * b64_encode: Stolen from VLC's http.c. + * Simplified by Michael. + * Fixed edge cases and made it work from data (vs. strings) by Ryan. + *****************************************************************************/ +char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, int in_size) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *ret, *dst; + unsigned i_bits = 0; + int i_shift = 0; + int bytes_remaining = in_size; + + if (in_size >= (int)(UINT_MAX / 4) || + out_size < SRS_AV_BASE64_SIZE(in_size)) + return NULL; + ret = dst = out; + while (bytes_remaining > 3) { + i_bits = AV_RB32(in); + in += 3; bytes_remaining -= 3; + *dst++ = b64[ i_bits>>26 ]; + *dst++ = b64[(i_bits>>20) & 0x3F]; + *dst++ = b64[(i_bits>>14) & 0x3F]; + *dst++ = b64[(i_bits>>8 ) & 0x3F]; + } + i_bits = 0; + while (bytes_remaining) { + i_bits = (i_bits << 8) + *in++; + bytes_remaining--; + i_shift += 8; } + while (i_shift > 0) { + *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; + i_shift -= 6; + } + while ((dst - ret) & 3) + *dst++ = '='; + *dst = '\0'; + return ret; +} + #define SPACE_CHARS " \t\r\n" + +int av_toupper(int c) +{ + if (c >= 'a' && c <= 'z') { + c ^= 0x20; + } + return c; +} + +int ff_hex_to_data(uint8_t* data, const char* p) +{ + int c, len, v; - int av_toupper(int c) - { - if (c >= 'a' && c <= 'z') { - c ^= 0x20; + len = 0; + v = 1; + for (;;) { + p += strspn(p, SPACE_CHARS); + if (*p == '\0') + break; + c = av_toupper((unsigned char) *p++); + if (c >= '0' && c <= '9') + c = c - '0'; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else + break; + v = (v << 4) | c; + if (v & 0x100) { + if (data) + data[len] = v; + len++; + v = 1; } - return c; } + return len; +} + +int srs_chunk_header_c0(int perfer_cid, uint32_t timestamp, int32_t payload_length, int8_t message_type, int32_t stream_id, char* cache, int nb_cache) +{ + // to directly set the field. + char* pp = NULL; - int ff_hex_to_data(uint8_t* data, const char* p) - { - int c, len, v; - - len = 0; - v = 1; - for (;;) { - p += strspn(p, SPACE_CHARS); - if (*p == '\0') - break; - c = av_toupper((unsigned char) *p++); - if (c >= '0' && c <= '9') - c = c - '0'; - else if (c >= 'A' && c <= 'F') - c = c - 'A' + 10; - else - break; - v = (v << 4) | c; - if (v & 0x100) { - if (data) - data[len] = v; - len++; - v = 1; - } - } - return len; + // generate the header. + char* p = cache; + + // no header. + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { + return 0; } - int srs_chunk_header_c0(int perfer_cid, uint32_t timestamp, int32_t payload_length, int8_t message_type, int32_t stream_id, char* cache, int nb_cache) - { - // to directly set the field. - char* pp = NULL; - - // generate the header. - char* p = cache; - - // no header. - if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) { - return 0; - } - - // write new chunk stream header, fmt is 0 - *p++ = 0x00 | (perfer_cid & 0x3F); - - // chunk message header, 11 bytes - // timestamp, 3bytes, big-endian - if (timestamp < RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } else { - *p++ = 0xFF; - *p++ = 0xFF; - *p++ = 0xFF; - } - - // message_length, 3bytes, big-endian - pp = (char*)&payload_length; + // write new chunk stream header, fmt is 0 + *p++ = 0x00 | (perfer_cid & 0x3F); + + // chunk message header, 11 bytes + // timestamp, 3bytes, big-endian + if (timestamp < RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; *p++ = pp[2]; *p++ = pp[1]; *p++ = pp[0]; - - // message_type, 1bytes - *p++ = message_type; - - // stream_id, 4bytes, little-endian - pp = (char*)&stream_id; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; + } else { + *p++ = 0xFF; + *p++ = 0xFF; + *p++ = 0xFF; + } + + // message_length, 3bytes, big-endian + pp = (char*)&payload_length; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // message_type, 1bytes + *p++ = message_type; + + // stream_id, 4bytes, little-endian + pp = (char*)&stream_id; + *p++ = pp[0]; + *p++ = pp[1]; + *p++ = pp[2]; + *p++ = pp[3]; + + // for c0 + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // + // for c3: + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + // TODO: FIXME: extract to outer. + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; *p++ = pp[3]; - - // for c0 - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // - // for c3: - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // 6.1.3. Extended Timestamp - // This field is transmitted only when the normal time stamp in the - // chunk message header is set to 0x00ffffff. If normal time stamp is - // set to any value less than 0x00ffffff, this field MUST NOT be - // present. This field MUST NOT be present if the timestamp field is not - // present. Type 3 chunks MUST NOT have this field. - // adobe changed for Type3 chunk: - // FMLE always sendout the extended-timestamp, - // must send the extended-timestamp to FMS, - // must send the extended-timestamp to flash-player. - // @see: ngx_rtmp_prepare_message - // @see: http://blog.csdn.net/win_lin/article/details/13363699 - // TODO: FIXME: extract to outer. - if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } - - // always has header - return (int)(p - cache); + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; } - int srs_chunk_header_c3(int perfer_cid, uint32_t timestamp, char* cache, int nb_cache) - { - // to directly set the field. - char* pp = NULL; - - // generate the header. - char* p = cache; - - // no header. - if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) { - return 0; - } - - // write no message header chunk stream, fmt is 3 - // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, - // SRS will rollback to 1B chunk header. - *p++ = 0xC0 | (perfer_cid & 0x3F); - - // for c0 - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // - // for c3: - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // 6.1.3. Extended Timestamp - // This field is transmitted only when the normal time stamp in the - // chunk message header is set to 0x00ffffff. If normal time stamp is - // set to any value less than 0x00ffffff, this field MUST NOT be - // present. This field MUST NOT be present if the timestamp field is not - // present. Type 3 chunks MUST NOT have this field. - // adobe changed for Type3 chunk: - // FMLE always sendout the extended-timestamp, - // must send the extended-timestamp to FMS, - // must send the extended-timestamp to flash-player. - // @see: ngx_rtmp_prepare_message - // @see: http://blog.csdn.net/win_lin/article/details/13363699 - // TODO: FIXME: extract to outer. - if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { - pp = (char*)×tamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - } - - // always has header - return (int)(p - cache); + // always has header + return (int)(p - cache); +} + +int srs_chunk_header_c3(int perfer_cid, uint32_t timestamp, char* cache, int nb_cache) +{ + // to directly set the field. + char* pp = NULL; + + // generate the header. + char* p = cache; + + // no header. + if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) { + return 0; + } + + // write no message header chunk stream, fmt is 3 + // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header, + // SRS will rollback to 1B chunk header. + *p++ = 0xC0 | (perfer_cid & 0x3F); + + // for c0 + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // + // for c3: + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + // TODO: FIXME: extract to outer. + if (timestamp >= RTMP_EXTENDED_TIMESTAMP) { + pp = (char*)×tamp; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; } + // always has header + return (int)(p - cache); +} + diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 1354dd3f8..194ff4ccb 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -1523,5 +1523,24 @@ VOID TEST(KernelUtility, AvcUev) } } +VOID TEST(KernelUtility, CRC32IEEE) +{ + string datas[] = { + "123456789", "srs", "ossrs.net", + "SRS's a simplest, conceptual integrated, industrial-strength live streaming origin cluster." + }; + + uint32_t checksums[] = { + 0xcbf43926, 0x7df334e9, 0x2f52242b, + 0x7e8677bd + }; + + for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { + string data = datas[i]; + uint32_t checksum = checksums[i]; + EXPECT_EQ(checksum, srs_crc32_ieee(data.data(), data.length(), 0)); + } +} + #endif From e3958789165663eb74fd69e27e302c81d61821bb Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 21 Jul 2018 20:58:20 +0800 Subject: [PATCH 10/33] Replace crc32 IEEE by golang to match MIT license. 3.0.30 --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_utility.cpp | 127 ++++++++++-------------- trunk/src/utest/srs_utest_kernel.cpp | 7 ++ 4 files changed, 64 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index bb4b44987..deabc0b74 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-07-21, Replace crc32 IEEE by golang to match MIT license. 3.0.30 * v3.0, 2018-02-16, Fix [#464][bug #464], support RTMP origin cluster. 3.0.29 * v3.0, 2018-02-13, Fix [#1057][bug #1057], switch to simple handshake. 3.0.28 * v3.0, 2018-02-13, Fix [#1059][bug #1059], merge from 2.0, supports url with vhost in stream. 3.0.27 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 4346114c9..2db884d2b 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 29 +#define VERSION_REVISION 30 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 80bb062ca..7c901eb2d 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -745,87 +745,70 @@ unsigned int __mpegts_crc32(const uint8_t *data, int len) return crc; } -// @see https://github.com/ETrun/crc32/blob/master/crc32.c - uint32_t __crc32_ieee(uint32_t init, const uint8_t* buf, size_t nb_buf) +uint32_t srs_crc32_mpegts(const void* buf, int size) { - /*----------------------------------------------------------------------------*\ - * CRC-32 version 2.0.0 by Craig Bruce, 2006-04-29. - * - * This program generates the CRC-32 values for the files named in the - * command-line arguments. These are the same CRC-32 values used by GZIP, - * PKZIP, and ZMODEM. The Crc32_ComputeBuf() can also be detached and - * used independently. - * - * THIS PROGRAM IS PUBLIC-DOMAIN SOFTWARE. - * - * Based on the byte-oriented implementation "File Verification Using CRC" - * by Mark R. Nelson in Dr. Dobb's Journal, May 1992, pp. 64-67. - * - * v1.0.0: original release. - * v1.0.1: fixed printf formats. - * v1.0.2: fixed something else. - * v1.0.3: replaced CRC constant table by generator function. - * v1.0.4: reformatted code, made ANSI C. 1994-12-05. - * v2.0.0: rewrote to use memory buffer & static table, 2006-04-29. - * v2.1.0: modified by Nico, 2013-04-20 - \*----------------------------------------------------------------------------*/ - static const uint32_t table[256] = { - 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535, - 0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD, - 0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D, - 0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC, - 0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4, - 0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C, - 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC, - 0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, - 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB, - 0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F, - 0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB, - 0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E, - 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA, - 0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE, - 0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A, - 0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409, - 0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81, - 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739, - 0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8, - 0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268, - 0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0, - 0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8, - 0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, - 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF, - 0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703, - 0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7, - 0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A, - 0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE, - 0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242, - 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6, - 0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D, - 0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5, - 0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605, - 0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94, - 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D - }; + return __mpegts_crc32((const uint8_t*)buf, size); +} + +// @see golang IEEE of hash/crc32/crc32.go +// IEEE is by far and away the most common CRC-32 polynomial. +// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... +static uint32_t __crc32_IEEE = 0xedb88320; + +// @see golang IEEE of hash/crc32/crc32.go +// Castagnoli's polynomial, used in iSCSI. +// Has better error detection characteristics than IEEE. +// http://dx.doi.org/10.1109/26.231911 +//static uint32_t __crc32_Castagnoli = 0x82f63b78; + +// @see golang IEEE of hash/crc32/crc32.go +// Koopman's polynomial. +// Also has better error detection characteristics than IEEE. +// http://dx.doi.org/10.1109/DSN.2002.1028931 +//static uint32_t __crc32_Koopman = 0xeb31d82e; + +// @see golang makeTable of hash/crc32/crc32.go +void __crc32_make_table(uint32_t t[256], uint32_t poly) +{ + for (int i = 0; i < 256; i++) { + uint32_t crc = uint32_t(i); + + for (int j = 0; j < 8; j++) { + if ((crc&1) == 1) { + crc = (crc >> 1) ^ poly; + } else { + crc = crc >> 1; + } + } + + t[i] = crc; + } +} - uint32_t crc = init ^ 0xFFFFFFFF; +// @see golang update of hash/crc32/crc32.go +uint32_t __crc32_update(uint32_t crc, uint32_t t[256], const uint8_t* buf, int size) { + crc = crc ^ 0xffffffff; - for (size_t i = 0; i < nb_buf; i++) { - crc = table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); + for (int i = 0; i < size; i++) { + uint8_t v = (uint8_t)buf[i]; + crc = t[uint8_t(crc) ^ v] ^ (crc >> 8); } - return crc^0xFFFFFFFF; -} - -uint32_t srs_crc32_mpegts(const void* buf, int size) -{ - return __mpegts_crc32((const uint8_t*)buf, size); + return crc ^ 0xffffffff; } + +// @see golang IEEE of hash/crc32/crc32.go +// IEEETable is the table for the IEEE polynomial. +static uint32_t __crc32_IEEE_table[256]; +static bool __crc32_IEEE_table_initialized = false; uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous) { - return __crc32_ieee(previous, (const uint8_t*)buf, size); + if (!__crc32_IEEE_table_initialized) { + __crc32_make_table(__crc32_IEEE_table, __crc32_IEEE); + __crc32_IEEE_table_initialized = true; + } + return __crc32_update(previous, __crc32_IEEE_table, (uint8_t*)buf, size); } /* diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 194ff4ccb..6fd379695 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -1540,6 +1540,13 @@ VOID TEST(KernelUtility, CRC32IEEE) uint32_t checksum = checksums[i]; EXPECT_EQ(checksum, srs_crc32_ieee(data.data(), data.length(), 0)); } + + uint32_t previous = 0; + for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { + string data = datas[i]; + previous = srs_crc32_ieee(data.data(), data.length(), previous); + } + EXPECT_EQ((uint32_t)0x431b8785, previous); } #endif From 0a63448b86bfa2998f14055402896406a33de109 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 22 Jul 2018 15:21:05 +0800 Subject: [PATCH 11/33] Replace crc32 MPEG and IEEE by pycrc to match MIT license. 3.0.31 --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_utility.cpp | 247 +++++++++++++----------- trunk/src/utest/srs_utest_kernel.cpp | 145 +++++++++++++- 4 files changed, 270 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index deabc0b74..7b7137216 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-07-22, Replace crc32 IEEE and MPEG by pycrc to match MIT license. 3.0.31 * v3.0, 2018-07-21, Replace crc32 IEEE by golang to match MIT license. 3.0.30 * v3.0, 2018-02-16, Fix [#464][bug #464], support RTMP origin cluster. 3.0.29 * v3.0, 2018-02-13, Fix [#1057][bug #1057], switch to simple handshake. 3.0.28 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 2db884d2b..48f3a7e30 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 30 +#define VERSION_REVISION 31 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 7c901eb2d..009c51bca 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -668,147 +668,166 @@ bool srs_aac_startswith_adts(SrsBuffer* stream) return true; } - -// @see http://www.stmc.edu.hk/~vincent/ffmpeg_0.4.9-pre1/libavformat/mpegtsenc.c -unsigned int __mpegts_crc32(const uint8_t *data, int len) + +// @see pycrc reflect at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L107 +uint64_t __crc32_reflect(uint64_t data, int width) { - /* - * MPEG2 transport stream (aka DVB) mux - * Copyright (c) 2003 Fabrice Bellard. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - static const uint32_t table[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 - }; - - uint32_t crc = 0xffffffff; - - for (int i=0; i> 24) ^ *data++) & 0xff]; + uint64_t res = data & 0x01; + + for (int i = 0; i < width - 1; i++) { + data >>= 1; + res = (res << 1) | (data & 0x01); } - return crc; + return res; } - -uint32_t srs_crc32_mpegts(const void* buf, int size) -{ - return __mpegts_crc32((const uint8_t*)buf, size); -} - -// @see golang IEEE of hash/crc32/crc32.go -// IEEE is by far and away the most common CRC-32 polynomial. -// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... -static uint32_t __crc32_IEEE = 0xedb88320; - -// @see golang IEEE of hash/crc32/crc32.go -// Castagnoli's polynomial, used in iSCSI. -// Has better error detection characteristics than IEEE. -// http://dx.doi.org/10.1109/26.231911 -//static uint32_t __crc32_Castagnoli = 0x82f63b78; - -// @see golang IEEE of hash/crc32/crc32.go -// Koopman's polynomial. -// Also has better error detection characteristics than IEEE. -// http://dx.doi.org/10.1109/DSN.2002.1028931 -//static uint32_t __crc32_Koopman = 0xeb31d82e; - -// @see golang makeTable of hash/crc32/crc32.go -void __crc32_make_table(uint32_t t[256], uint32_t poly) + +// @see pycrc gen_table at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L178 +void __crc32_make_table(uint32_t t[256], uint32_t poly, bool reflect_in) { - for (int i = 0; i < 256; i++) { - uint32_t crc = uint32_t(i); + int width = 32; // 32bits checksum. + uint64_t msb_mask = (uint32_t)(0x01 << (width - 1)); + uint64_t mask = (uint32_t)(((msb_mask - 1) << 1) | 1); + + int tbl_idx_width = 8; // table index size. + int tbl_width = 0x01 << tbl_idx_width; // table size: 256 + + for (int i = 0; i < tbl_width; i++) { + uint64_t reg = uint64_t(i); - for (int j = 0; j < 8; j++) { - if ((crc&1) == 1) { - crc = (crc >> 1) ^ poly; + if (reflect_in) { + reg = __crc32_reflect(reg, tbl_idx_width); + } + + reg = reg << (width - tbl_idx_width); + for (int j = 0; j < tbl_idx_width; j++) { + if ((reg&msb_mask) != 0) { + reg = (reg << 1) ^ poly; } else { - crc = crc >> 1; + reg = reg << 1; } } - t[i] = crc; + if (reflect_in) { + reg = __crc32_reflect(reg, width); + } + + t[i] = (uint32_t)(reg & mask); } } + +// @see pycrc table_driven at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L207 +uint32_t __crc32_table_driven(uint32_t* t, const void* buf, int size, uint32_t previous, bool reflect_in, uint32_t xor_in, bool reflect_out, uint32_t xor_out) +{ + int width = 32; // 32bits checksum. + uint64_t msb_mask = (uint32_t)(0x01 << (width - 1)); + uint64_t mask = (uint32_t)(((msb_mask - 1) << 1) | 1); -// @see golang update of hash/crc32/crc32.go -uint32_t __crc32_update(uint32_t crc, uint32_t t[256], const uint8_t* buf, int size) { - crc = crc ^ 0xffffffff; + int tbl_idx_width = 8; // table index size. - for (int i = 0; i < size; i++) { - uint8_t v = (uint8_t)buf[i]; - crc = t[uint8_t(crc) ^ v] ^ (crc >> 8); + uint8_t* p = (uint8_t*)buf; + uint64_t reg = 0; + + if (!reflect_in) { + reg = xor_in; + + for (int i = 0; i < size; i++) { + uint8_t tblidx = (uint8_t)((reg >> (width - tbl_idx_width)) ^ p[i]); + reg = t[tblidx] ^ (reg << tbl_idx_width); + } + } else { + reg = previous ^ __crc32_reflect(xor_in, width); + + for (int i = 0; i < size; i++) { + uint8_t tblidx = (uint8_t)(reg ^ p[i]); + reg = t[tblidx] ^ (reg >> tbl_idx_width); + } + + reg = __crc32_reflect(reg, width); + } + + if (reflect_out) { + reg = __crc32_reflect(reg, width); } - return crc ^ 0xffffffff; + reg ^= xor_out; + return (uint32_t)(reg & mask); } -// @see golang IEEE of hash/crc32/crc32.go +// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L207 // IEEETable is the table for the IEEE polynomial. static uint32_t __crc32_IEEE_table[256]; static bool __crc32_IEEE_table_initialized = false; +// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/models.py#L220 +// crc32('123456789') = 0xcbf43926 +// where it's defined as model: +// 'name': 'crc-32', +// 'width': 32, +// 'poly': 0x4c11db7, +// 'reflect_in': True, +// 'xor_in': 0xffffffff, +// 'reflect_out': True, +// 'xor_out': 0xffffffff, +// 'check': 0xcbf43926, uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous) { + // @see golang IEEE of hash/crc32/crc32.go + // IEEE is by far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... + // @remark The poly of CRC32 IEEE is 0x04C11DB7, its reverse is 0xEDB88320, + // please read https://en.wikipedia.org/wiki/Cyclic_redundancy_check + uint32_t poly = 0x04C11DB7; + + bool reflect_in = true; + uint32_t xor_in = 0xffffffff; + bool reflect_out = true; + uint32_t xor_out = 0xffffffff; + if (!__crc32_IEEE_table_initialized) { - __crc32_make_table(__crc32_IEEE_table, __crc32_IEEE); + __crc32_make_table(__crc32_IEEE_table, poly, reflect_in); __crc32_IEEE_table_initialized = true; } - return __crc32_update(previous, __crc32_IEEE_table, (uint8_t*)buf, size); + + return __crc32_table_driven(__crc32_IEEE_table, buf, size, previous, reflect_in, xor_in, reflect_out, xor_out); +} + +// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L238 +// IEEETable is the table for the MPEG polynomial. +static uint32_t __crc32_MPEG_table[256]; +static bool __crc32_MPEG_table_initialized = false; + +// @see pycrc https://github.com/winlinvip/pycrc/blob/master/pycrc/models.py#L238 +// crc32('123456789') = 0x0376e6e7 +// where it's defined as model: +// 'name': 'crc-32', +// 'width': 32, +// 'poly': 0x4c11db7, +// 'reflect_in': False, +// 'xor_in': 0xffffffff, +// 'reflect_out': False, +// 'xor_out': 0x0, +// 'check': 0x0376e6e7, +uint32_t srs_crc32_mpegts(const void* buf, int size) +{ + // @see golang IEEE of hash/crc32/crc32.go + // IEEE is by far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... + // @remark The poly of CRC32 IEEE is 0x04C11DB7, its reverse is 0xEDB88320, + // please read https://en.wikipedia.org/wiki/Cyclic_redundancy_check + uint32_t poly = 0x04C11DB7; + + bool reflect_in = false; + uint32_t xor_in = 0xffffffff; + bool reflect_out = false; + uint32_t xor_out = 0x0; + + if (!__crc32_MPEG_table_initialized) { + __crc32_make_table(__crc32_MPEG_table, poly, reflect_in); + __crc32_MPEG_table_initialized = true; + } + + return __crc32_table_driven(__crc32_MPEG_table, buf, size, 0x00, reflect_in, xor_in, reflect_out, xor_out); } /* diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 6fd379695..5fafd417f 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -1523,7 +1523,139 @@ VOID TEST(KernelUtility, AvcUev) } } +extern void __crc32_make_table(uint32_t t[256], uint32_t poly, bool reflect_in); + +VOID TEST(KernelUtility, CRC32MakeTable) +{ + uint32_t t[256]; + + // IEEE, @see https://github.com/ossrs/srs/blob/608c88b8f2b352cdbce3b89b9042026ea907e2d3/trunk/src/kernel/srs_kernel_utility.cpp#L770 + __crc32_make_table(t, 0x4c11db7, true); + + EXPECT_EQ((uint32_t)0x00000000, t[0]); + EXPECT_EQ((uint32_t)0x77073096, t[1]); + EXPECT_EQ((uint32_t)0xEE0E612C, t[2]); + EXPECT_EQ((uint32_t)0x990951BA, t[3]); + EXPECT_EQ((uint32_t)0x076DC419, t[4]); + EXPECT_EQ((uint32_t)0x706AF48F, t[5]); + EXPECT_EQ((uint32_t)0xE963A535, t[6]); + EXPECT_EQ((uint32_t)0x9E6495A3, t[7]); + + EXPECT_EQ((uint32_t)0xB3667A2E, t[248]); + EXPECT_EQ((uint32_t)0xC4614AB8, t[249]); + EXPECT_EQ((uint32_t)0x5D681B02, t[250]); + EXPECT_EQ((uint32_t)0x2A6F2B94, t[251]); + EXPECT_EQ((uint32_t)0xB40BBE37, t[252]); + EXPECT_EQ((uint32_t)0xC30C8EA1, t[253]); + EXPECT_EQ((uint32_t)0x5A05DF1B, t[254]); + EXPECT_EQ((uint32_t)0x2D02EF8D, t[255]); + + // IEEE, @see https://github.com/ossrs/srs/blob/608c88b8f2b352cdbce3b89b9042026ea907e2d3/trunk/src/kernel/srs_kernel_utility.cpp#L770 + __crc32_make_table(t, 0x4c11db7, true); + + EXPECT_EQ((uint32_t)0x00000000, t[0]); + EXPECT_EQ((uint32_t)0x77073096, t[1]); + EXPECT_EQ((uint32_t)0xEE0E612C, t[2]); + EXPECT_EQ((uint32_t)0x990951BA, t[3]); + EXPECT_EQ((uint32_t)0x076DC419, t[4]); + EXPECT_EQ((uint32_t)0x706AF48F, t[5]); + EXPECT_EQ((uint32_t)0xE963A535, t[6]); + EXPECT_EQ((uint32_t)0x9E6495A3, t[7]); + + EXPECT_EQ((uint32_t)0xB3667A2E, t[248]); + EXPECT_EQ((uint32_t)0xC4614AB8, t[249]); + EXPECT_EQ((uint32_t)0x5D681B02, t[250]); + EXPECT_EQ((uint32_t)0x2A6F2B94, t[251]); + EXPECT_EQ((uint32_t)0xB40BBE37, t[252]); + EXPECT_EQ((uint32_t)0xC30C8EA1, t[253]); + EXPECT_EQ((uint32_t)0x5A05DF1B, t[254]); + EXPECT_EQ((uint32_t)0x2D02EF8D, t[255]); + + // MPEG, @see https://github.com/ossrs/srs/blob/608c88b8f2b352cdbce3b89b9042026ea907e2d3/trunk/src/kernel/srs_kernel_utility.cpp#L691 + __crc32_make_table(t, 0x4c11db7, false); + + EXPECT_EQ((uint32_t)0x00000000, t[0]); + EXPECT_EQ((uint32_t)0x04c11db7, t[1]); + EXPECT_EQ((uint32_t)0x09823b6e, t[2]); + EXPECT_EQ((uint32_t)0x0d4326d9, t[3]); + EXPECT_EQ((uint32_t)0x130476dc, t[4]); + EXPECT_EQ((uint32_t)0x17c56b6b, t[5]); + EXPECT_EQ((uint32_t)0x1a864db2, t[6]); + EXPECT_EQ((uint32_t)0x1e475005, t[7]); + + EXPECT_EQ((uint32_t)0xafb010b1, t[248]); + EXPECT_EQ((uint32_t)0xab710d06, t[249]); + EXPECT_EQ((uint32_t)0xa6322bdf, t[250]); + EXPECT_EQ((uint32_t)0xa2f33668, t[251]); + EXPECT_EQ((uint32_t)0xbcb4666d, t[252]); + EXPECT_EQ((uint32_t)0xb8757bda, t[253]); + EXPECT_EQ((uint32_t)0xb5365d03, t[254]); + EXPECT_EQ((uint32_t)0xb1f740b4, t[255]); + + // MPEG, @see https://github.com/ossrs/srs/blob/608c88b8f2b352cdbce3b89b9042026ea907e2d3/trunk/src/kernel/srs_kernel_utility.cpp#L691 + __crc32_make_table(t, 0x4c11db7, false); + + EXPECT_EQ((uint32_t)0x00000000, t[0]); + EXPECT_EQ((uint32_t)0x04c11db7, t[1]); + EXPECT_EQ((uint32_t)0x09823b6e, t[2]); + EXPECT_EQ((uint32_t)0x0d4326d9, t[3]); + EXPECT_EQ((uint32_t)0x130476dc, t[4]); + EXPECT_EQ((uint32_t)0x17c56b6b, t[5]); + EXPECT_EQ((uint32_t)0x1a864db2, t[6]); + EXPECT_EQ((uint32_t)0x1e475005, t[7]); + + EXPECT_EQ((uint32_t)0xafb010b1, t[248]); + EXPECT_EQ((uint32_t)0xab710d06, t[249]); + EXPECT_EQ((uint32_t)0xa6322bdf, t[250]); + EXPECT_EQ((uint32_t)0xa2f33668, t[251]); + EXPECT_EQ((uint32_t)0xbcb4666d, t[252]); + EXPECT_EQ((uint32_t)0xb8757bda, t[253]); + EXPECT_EQ((uint32_t)0xb5365d03, t[254]); + EXPECT_EQ((uint32_t)0xb1f740b4, t[255]); +} + VOID TEST(KernelUtility, CRC32IEEE) +{ + if (true) { + string datas[] = { + "123456789", "srs", "ossrs.net", + "SRS's a simplest, conceptual integrated, industrial-strength live streaming origin cluster." + }; + + uint32_t checksums[] = { + 0xcbf43926, 0x7df334e9, 0x2f52242b, + 0x7e8677bd, + }; + + for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { + string data = datas[i]; + uint32_t checksum = checksums[i]; + EXPECT_EQ(checksum, srs_crc32_ieee(data.data(), data.length(), 0)); + } + + uint32_t previous = 0; + for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { + string data = datas[i]; + previous = srs_crc32_ieee(data.data(), data.length(), previous); + } + EXPECT_EQ((uint32_t)0x431b8785, previous); + } + + if (true) { + string data = "123456789srs"; + EXPECT_EQ((uint32_t)0xf567b5cf, srs_crc32_ieee(data.data(), data.length(), 0)); + } + + if (true) { + string data = "123456789"; + EXPECT_EQ((uint32_t)0xcbf43926, srs_crc32_ieee(data.data(), data.length(), 0)); + + data = "srs"; + EXPECT_EQ((uint32_t)0xf567b5cf, srs_crc32_ieee(data.data(), data.length(), 0xcbf43926)); + } +} + +VOID TEST(KernelUtility, CRC32MPEGTS) { string datas[] = { "123456789", "srs", "ossrs.net", @@ -1531,22 +1663,15 @@ VOID TEST(KernelUtility, CRC32IEEE) }; uint32_t checksums[] = { - 0xcbf43926, 0x7df334e9, 0x2f52242b, - 0x7e8677bd + 0x0376e6e7, 0xd9089591, 0xbd17933f, + 0x9f389f7d }; for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { string data = datas[i]; uint32_t checksum = checksums[i]; - EXPECT_EQ(checksum, srs_crc32_ieee(data.data(), data.length(), 0)); - } - - uint32_t previous = 0; - for (int i = 0; i < (int)(sizeof(datas)/sizeof(string)); i++) { - string data = datas[i]; - previous = srs_crc32_ieee(data.data(), data.length(), previous); + EXPECT_EQ(checksum, (uint32_t)srs_crc32_mpegts(data.data(), data.length())); } - EXPECT_EQ((uint32_t)0x431b8785, previous); } #endif From 84f81983aa609d2027e290c808280428a4e69f0e Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 22 Jul 2018 17:56:38 +0800 Subject: [PATCH 12/33] Replace base64 to match MIT license. 3.0.32 --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/kernel/srs_kernel_utility.cpp | 300 ++++++++---------------- trunk/src/kernel/srs_kernel_utility.hpp | 21 +- trunk/src/protocol/srs_rtsp_stack.cpp | 17 +- trunk/src/utest/srs_utest_kernel.cpp | 10 + 7 files changed, 117 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index 7b7137216..f2d2d52c9 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-07-22, Replace base64 to match MIT license. 3.0.32 * v3.0, 2018-07-22, Replace crc32 IEEE and MPEG by pycrc to match MIT license. 3.0.31 * v3.0, 2018-07-21, Replace crc32 IEEE by golang to match MIT license. 3.0.30 * v3.0, 2018-02-16, Fix [#464][bug #464], support RTMP origin cluster. 3.0.29 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 48f3a7e30..b62795c9f 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 31 +#define VERSION_REVISION 32 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index a80dd8272..9ffc1c557 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -318,6 +318,7 @@ #define ERROR_KAFKA_CODEC_MESSAGE 4036 #define ERROR_KAFKA_CODEC_PRODUCER 4037 #define ERROR_HTTP_302_INVALID 4038 +#define ERROR_BASE64_DECODE 4039 /////////////////////////////////////////////////////// // HTTP API error. diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 009c51bca..a247bf27d 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -830,217 +830,113 @@ uint32_t srs_crc32_mpegts(const void* buf, int size) return __crc32_table_driven(__crc32_MPEG_table, buf, size, 0x00, reflect_in, xor_in, reflect_out, xor_out); } -/* - * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef UINT_MAX -#define UINT_MAX 0xffffffff -#endif - -#ifndef AV_RB32 -# define AV_RB32(x) \ - (((uint32_t)((const uint8_t*)(x))[0] << 24) | \ - (((const uint8_t*)(x))[1] << 16) | \ - (((const uint8_t*)(x))[2] << 8) | \ - ((const uint8_t*)(x))[3]) -#endif - -#ifndef AV_WL32 -# define AV_WL32(p, darg) do { \ - unsigned d = (darg); \ - ((uint8_t*)(p))[0] = (d); \ - ((uint8_t*)(p))[1] = (d)>>8; \ - ((uint8_t*)(p))[2] = (d)>>16; \ - ((uint8_t*)(p))[3] = (d)>>24; \ -} while(0) -#endif +// @see golang encoding/base64/base64.go +srs_error_t srs_av_base64_decode(string cipher, string& plaintext) +{ + srs_error_t err = srs_success; -# define AV_WN(s, p, v) AV_WL##s(p, v) + // We use the standard encoding: + // var StdEncoding = NewEncoding(encodeStd) + // StdEncoding is the standard base64 encoding, as defined in RFC 4648. + char padding = '='; + string encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -# if defined(AV_WN32) && !defined(AV_WL32) -# define AV_WL32(p, v) AV_WN32(p, v) -# elif !defined(AV_WN32) && defined(AV_WL32) -# define AV_WN32(p, v) AV_WL32(p, v) -# endif + uint8_t decodeMap[256]; + memset(decodeMap, 0xff, sizeof(decodeMap)); -#ifndef AV_WN32 - # define AV_WN32(p, v) AV_WN(32, p, v) -#endif - -#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff)) -#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16)) + for (int i = 0; i < encoder.length(); i++) { + decodeMap[(uint8_t)encoder.at(i)] = uint8_t(i); + } -#ifndef av_bswap32 -static const uint32_t av_bswap32(uint32_t x) -{ - return AV_BSWAP32C(x); -} -#endif + // decode is like Decode but returns an additional 'end' value, which + // indicates if end-of-message padding or a partial quantum was encountered + // and thus any additional data is an error. + int si = 0; -#define av_be2ne32(x) av_bswap32(x) -/** - * @file - * @brief Base64 encode/decode - * @author Ryan Martell (with lots of Michael) - */ - -/* ---------------- private code */ -static const uint8_t map2[256] = -{ - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, - - 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, - - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -#define BASE64_DEC_STEP(i) do { \ - bits = map2[in[i]]; \ - if (bits & 0x80) \ - goto out ## i; \ - v = i ? (v << 6) + bits : bits; \ -} while(0) - -int srs_av_base64_decode(uint8_t* out, const char* in_str, int out_size) -{ - uint8_t *dst = out; - uint8_t *end = out + out_size; - // no sign extension - const uint8_t *in = (const uint8_t*)in_str; - unsigned bits = 0xff; - unsigned v = 0; - - while (end - dst > 3) { - BASE64_DEC_STEP(0); - BASE64_DEC_STEP(1); - BASE64_DEC_STEP(2); - BASE64_DEC_STEP(3); - // Using AV_WB32 directly confuses compiler - v = av_be2ne32(v << 8); - AV_WN32(dst, v); - dst += 3; - in += 4; - } - if (end - dst) { - BASE64_DEC_STEP(0); - BASE64_DEC_STEP(1); - BASE64_DEC_STEP(2); - BASE64_DEC_STEP(3); - *dst++ = v >> 16; - if (end - dst) - *dst++ = v >> 8; - if (end - dst) - *dst++ = v; - in += 4; - } - while (1) { - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; - BASE64_DEC_STEP(0); - in++; + // skip over newlines + for (; si < cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) { } -out3: - *dst++ = v >> 10; - v <<= 2; -out2: - *dst++ = v >> 4; -out1: -out0: - return bits & 1 ? -1 : (int)(dst - out); -} - -/***************************************************************************** - * b64_encode: Stolen from VLC's http.c. - * Simplified by Michael. - * Fixed edge cases and made it work from data (vs. strings) by Ryan. - *****************************************************************************/ -char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, int in_size) -{ - static const char b64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - char *ret, *dst; - unsigned i_bits = 0; - int i_shift = 0; - int bytes_remaining = in_size; - - if (in_size >= (int)(UINT_MAX / 4) || - out_size < SRS_AV_BASE64_SIZE(in_size)) - return NULL; - ret = dst = out; - while (bytes_remaining > 3) { - i_bits = AV_RB32(in); - in += 3; bytes_remaining -= 3; - *dst++ = b64[ i_bits>>26 ]; - *dst++ = b64[(i_bits>>20) & 0x3F]; - *dst++ = b64[(i_bits>>14) & 0x3F]; - *dst++ = b64[(i_bits>>8 ) & 0x3F]; - } - i_bits = 0; - while (bytes_remaining) { - i_bits = (i_bits << 8) + *in++; - bytes_remaining--; - i_shift += 8; - } - while (i_shift > 0) { - *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; - i_shift -= 6; + for (bool end = false; si < cipher.length() && !end;) { + // Decode quantum using the base64 alphabet + uint8_t dbuf[4]; + memset(dbuf, 0x00, sizeof(dbuf)); + + int dinc = 3; + int dlen = 4; + + for (int j = 0; j < sizeof(dbuf); j++) { + if (si == cipher.length()) { + if (padding != -1 || j < 2) { + return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + } + + dinc = j - 1; + dlen = j; + end = true; + break; + } + + char in = cipher.at(si); + + si++; + // skip over newlines + for (; si < cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) { + } + + if (in == padding) { + // We've reached the end and there's padding + switch (j) { + case 0: + case 1: + // incorrect padding + return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + case 2: + // "==" is expected, the first "=" is already consumed. + if (si == cipher.length()) { + return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + } + if (cipher.at(si) != padding) { + // incorrect padding + return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + } + + si++; + // skip over newlines + for (; si < cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) { + } + } + + if (si < cipher.length()) { + // trailing garbage + err = srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + } + dinc = 3; + dlen = j; + end = true; + break; + } + + dbuf[j] = decodeMap[(uint8_t)in]; + if (dbuf[j] == 0xff) { + return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si); + } + } + + // Convert 4x 6bit source bytes into 3 bytes + uint32_t val = uint32_t(dbuf[0])<<18 | uint32_t(dbuf[1])<<12 | uint32_t(dbuf[2])<<6 | uint32_t(dbuf[3]); + if (dlen >= 2) { + plaintext.append(1, char(val >> 16)); + } + if (dlen >= 3) { + plaintext.append(1, char(val >> 8)); + } + if (dlen >= 4) { + plaintext.append(1, char(val)); + } } - while ((dst - ret) & 3) - *dst++ = '='; - *dst = '\0'; - return ret; + return err; } #define SPACE_CHARS " \t\r\n" diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index b0c4665dc..34b63eaca 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -147,27 +147,8 @@ extern uint32_t srs_crc32_ieee(const void* buf, int size, uint32_t previous = 0) /** * Decode a base64-encoded string. - * - * @param out buffer for decoded data - * @param in null-terminated input string - * @param out_size size in bytes of the out buffer, must be at - * least 3/4 of the length of in - * @return number of bytes written, or a negative value in case of - * invalid input - */ -extern int srs_av_base64_decode(uint8_t* out, const char* in, int out_size); - -/** - * Encode data to base64 and null-terminate. - * - * @param out buffer for encoded data - * @param out_size size in bytes of the out buffer (including the - * null terminator), must be at least AV_BASE64_SIZE(in_size) - * @param in input buffer containing the data to encode - * @param in_size size in bytes of the in buffer - * @return out or NULL in case of error */ -extern char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, int in_size); +extern srs_error_t srs_av_base64_decode(std::string cipher, std::string& plaintext); /** * Calculate the output size needed to base64-encode x bytes to a diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index f20daa014..55a306c7f 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -583,23 +583,16 @@ srs_error_t SrsRtspSdp::parse_control_attribute(string attr) return err; } -string SrsRtspSdp::base64_decode(string value) +string SrsRtspSdp::base64_decode(string cipher) { - if (value.empty()) { + if (cipher.empty()) { return ""; } - int nb_output = (int)(value.length() * 2); - uint8_t* output = new uint8_t[nb_output]; - SrsAutoFreeA(uint8_t, output); + string plaintext; + srs_error_t err = srs_av_base64_decode(cipher, plaintext); + srs_freep(err); - int ret = srs_av_base64_decode(output, (char*)value.c_str(), nb_output); - if (ret <= 0) { - return ""; - } - - std::string plaintext; - plaintext.append((char*)output, ret); return plaintext; } diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 5fafd417f..4b6364e1f 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -1674,5 +1674,15 @@ VOID TEST(KernelUtility, CRC32MPEGTS) } } +VOID TEST(KernelUtility, Base64Decode) +{ + string cipher = "dXNlcjpwYXNzd29yZA=="; + string expect = "user:password"; + + string plaintext; + EXPECT_TRUE(srs_success == srs_av_base64_decode(cipher, plaintext)); + EXPECT_TRUE(expect == plaintext); +} + #endif From 41c6e833b99829be4929f5bc90f83a237ccf7c33 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 22 Jul 2018 18:47:38 +0800 Subject: [PATCH 13/33] Replace hex to string to match MIT license. 3.0.33 --- README.md | 1 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_utility.cpp | 55 +++++++++++++++---------- trunk/src/kernel/srs_kernel_utility.hpp | 2 +- trunk/src/protocol/srs_rtsp_stack.cpp | 8 +++- trunk/src/utest/srs_utest_kernel.cpp | 23 +++++++++++ 6 files changed, 65 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f2d2d52c9..a791194dd 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-07-22, Replace hex to string to match MIT license. 3.0.33 * v3.0, 2018-07-22, Replace base64 to match MIT license. 3.0.32 * v3.0, 2018-07-22, Replace crc32 IEEE and MPEG by pycrc to match MIT license. 3.0.31 * v3.0, 2018-07-21, Replace crc32 IEEE by golang to match MIT license. 3.0.30 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b62795c9f..8e76a8d70 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 32 +#define VERSION_REVISION 33 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index a247bf27d..eba746293 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -948,33 +948,44 @@ int av_toupper(int c) } return c; } + +// fromHexChar converts a hex character into its value and a success flag. +uint8_t srs_from_hex_char(uint8_t c) +{ + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + + return -1; +} -int ff_hex_to_data(uint8_t* data, const char* p) +int srs_hex_to_data(uint8_t* data, const char* p, int size) { - int c, len, v; + if (size <= 0 || (size%2) == 1) { + return -1; + } - len = 0; - v = 1; - for (;;) { - p += strspn(p, SPACE_CHARS); - if (*p == '\0') - break; - c = av_toupper((unsigned char) *p++); - if (c >= '0' && c <= '9') - c = c - '0'; - else if (c >= 'A' && c <= 'F') - c = c - 'A' + 10; - else - break; - v = (v << 4) | c; - if (v & 0x100) { - if (data) - data[len] = v; - len++; - v = 1; + for (int i = 0; i < size / 2; i++) { + uint8_t a = srs_from_hex_char(p[i*2]); + if (a == (uint8_t)-1) { + return -1; } + + uint8_t b = srs_from_hex_char(p[i*2 + 1]); + if (b == (uint8_t)-1) { + return -1; + } + + data[i] = (a << 4) | b; } - return len; + + return size / 2; } int srs_chunk_header_c0(int perfer_cid, uint32_t timestamp, int32_t payload_length, int8_t message_type, int32_t stream_id, char* cache, int nb_cache) diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 34b63eaca..19677d776 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -161,7 +161,7 @@ extern srs_error_t srs_av_base64_decode(std::string cipher, std::string& plainte * for example, p=config='139056E5A0' * output hex to data={0x13, 0x90, 0x56, 0xe5, 0xa0} */ -extern int ff_hex_to_data(uint8_t* data, const char* p); +extern int srs_hex_to_data(uint8_t* data, const char* p, int size); /** * generate the c0 chunk header for msg. diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index 55a306c7f..ac0083d82 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -537,8 +537,12 @@ srs_error_t SrsRtspSdp::parse_fmtp_attribute(string attr) char* tmp_sh = new char[item_value.length()]; SrsAutoFreeA(char, tmp_sh); - int nb_tmp_sh = ff_hex_to_data((uint8_t*)tmp_sh, item_value.c_str()); - srs_assert(nb_tmp_sh > 0); + + int nb_tmp_sh = srs_hex_to_data((uint8_t*)tmp_sh, item_value.c_str(), item_value.length()); + if (nb_tmp_sh <= 0) { + return srs_error_new(ERROR_RTSP_AUDIO_CONFIG, "audio config"); + } + audio_sh.append(tmp_sh, nb_tmp_sh); } } diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 4b6364e1f..e2e66f794 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -1684,5 +1684,28 @@ VOID TEST(KernelUtility, Base64Decode) EXPECT_TRUE(expect == plaintext); } +VOID TEST(KernelUtility, StringToHex) +{ + if (true) { + uint8_t h[16]; + EXPECT_EQ(-1, srs_hex_to_data(h, NULL, 0)); + EXPECT_EQ(-1, srs_hex_to_data(h, "0", 1)); + EXPECT_EQ(-1, srs_hex_to_data(h, "0g", 2)); + } + + if (true) { + string s = "139056E5A0"; + uint8_t h[16]; + + int n = srs_hex_to_data(h, s.data(), s.length()); + EXPECT_EQ(n, 5); + EXPECT_EQ(0x13, h[0]); + EXPECT_EQ(0x90, h[1]); + EXPECT_EQ(0x56, h[2]); + EXPECT_EQ(0xe5, h[3]); + EXPECT_EQ(0xa0, h[4]); + } +} + #endif From 586899a01016bffcef16977f202eed8ad27643a0 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 2 Aug 2018 06:59:57 +0800 Subject: [PATCH 14/33] For #1031, SRS edge support douyu.com. 2.0.250 --- README.md | 2 ++ trunk/src/app/srs_app_edge.cpp | 15 ++++++++++++--- trunk/src/core/srs_core.hpp | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 334308ca7..2969ff97a 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 2.0.250 * v2.0, 2018-07-21, Merge [#1119][bug #1119], fix memory leak. 2.0.249 * v2.0, 2018-07-18, [2.0 release3(2.0.248)][r2.0r3] released. 86775 lines. * v2.0, 2018-07-17, Merge [#1176][bug #1176], fix scaned issues. 2.0.248 @@ -1313,6 +1314,7 @@ Winlin [bug #1077]: https://github.com/ossrs/srs/issues/1077 [bug #1176]: https://github.com/ossrs/srs/issues/1176 [bug #1119]: https://github.com/ossrs/srs/issues/1119 +[bug #1031]: https://github.com/ossrs/srs/issues/1031 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 754f9cc0a..25c8850cb 100755 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -462,11 +462,20 @@ int SrsEdgeForwarder::start() return ret; } - if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { - srs_error("publish failed, stream=%s, stream_id=%d. ret=%d", - req->stream.c_str(), stream_id, ret); + string stream = req->stream; + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + if (!req->param.empty()) { + if (req->param.find("?") != 0) { + stream += "?"; + } + stream += req->param; + } + + if ((ret = client->publish(stream, stream_id)) != ERROR_SUCCESS) { + srs_error("publish failed, stream=%s, stream_id=%d. ret=%d", stream.c_str(), stream_id, ret); return ret; } + srs_trace("publish stream %s", stream.c_str()); return pthread->start(); } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 2da22b8f7..62352b69e 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 249 +#define VERSION_REVISION 250 // generated by configure, only macros. #include From 09dbda5b186a6fd7e99faf05426c4274e975dc85 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 2 Aug 2018 07:24:08 +0800 Subject: [PATCH 15/33] For #1031, SRS forwarder support douyu.com. --- trunk/src/app/srs_app_forward.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 7698cb5bc..47fe6e809 100755 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -248,9 +248,17 @@ int SrsForwarder::cycle() return ret; } - if ((ret = client->publish(_req->stream, stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", - _req->stream.c_str(), stream_id, ret); + string stream = _req->stream; + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + if (!_req->param.empty()) { + if (_req->param.find("?") != 0) { + stream += "?"; + } + stream += _req->param; + } + + if ((ret = client->publish(stream, stream_id)) != ERROR_SUCCESS) { + srs_error("connect with server failed, stream_name=%s, stream_id=%d. ret=%d", stream.c_str(), stream_id, ret); return ret; } From 68a1656e0780a504e0e9bc1ce97d0c33fa22738f Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 2 Aug 2018 09:17:49 +0800 Subject: [PATCH 16/33] Fix #1031, Always use vhost in stream query, the unify uri. 3.0.35 --- README.md | 1 + trunk/src/app/srs_app_caster_flv.cpp | 2 +- trunk/src/app/srs_app_edge.cpp | 16 ++--- trunk/src/app/srs_app_forward.cpp | 40 +---------- trunk/src/app/srs_app_mpegts_udp.cpp | 2 +- trunk/src/app/srs_app_ng_exec.cpp | 2 +- trunk/src/app/srs_app_rtsp.cpp | 2 +- trunk/src/app/srs_app_source.cpp | 4 +- trunk/src/core/srs_core.hpp | 2 +- trunk/src/libs/srs_librtmp.cpp | 18 ++--- trunk/src/main/srs_main_ingest_hls.cpp | 2 +- trunk/src/protocol/srs_protocol_utility.cpp | 78 +++++++++++---------- trunk/src/protocol/srs_protocol_utility.hpp | 32 ++++----- trunk/src/protocol/srs_rtmp_stack.cpp | 18 ++--- trunk/src/protocol/srs_rtmp_stack.hpp | 4 +- trunk/src/service/srs_service_rtmp_conn.cpp | 28 ++------ trunk/src/service/srs_service_rtmp_conn.hpp | 4 +- trunk/src/utest/srs_utest_protocol.cpp | 6 +- 18 files changed, 102 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index 2af9b3438..55bfb91cd 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-08-02, Always use vhost in stream query, the unify uri. 3.0.35 * v3.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 3.0.34 * v3.0, 2018-07-22, Replace hex to string to match MIT license. 3.0.33 * v3.0, 2018-07-22, Replace base64 to match MIT license. 3.0.32 diff --git a/trunk/src/app/srs_app_caster_flv.cpp b/trunk/src/app/srs_app_caster_flv.cpp index 639eeea20..98a7c8520 100644 --- a/trunk/src/app/srs_app_caster_flv.cpp +++ b/trunk/src/app/srs_app_caster_flv.cpp @@ -202,7 +202,7 @@ srs_error_t SrsDynamicHttpConn::do_proxy(ISrsHttpResponseReader* rr, SrsFlvDecod return srs_error_wrap(err, "connect %s failed, cto=%" PRId64 ", sto=%" PRId64, output.c_str(), cto, sto); } - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { return srs_error_wrap(err, "publish"); } diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index 490472fc3..fe4102524 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -110,7 +110,7 @@ srs_error_t SrsEdgeRtmpUpstream::connect(SrsRequest* r, SrsLbRoundRobin* lb) std::string vhost = _srs_config->get_vhost_edge_transform_vhost(req->vhost); vhost = srs_string_replace(vhost, "[vhost]", req->vhost); - url = srs_generate_rtmp_url(server, port, vhost, req->app, req->stream); + url = srs_generate_rtmp_url(server, port, req->host, vhost, req->app, req->stream, req->param); } srs_freep(sdk); @@ -122,7 +122,7 @@ srs_error_t SrsEdgeRtmpUpstream::connect(SrsRequest* r, SrsLbRoundRobin* lb) return srs_error_wrap(err, "edge pull %s failed, cto=%" PRId64 ", sto=%" PRId64, url.c_str(), cto, sto); } - if ((err = sdk->play()) != srs_success) { + if ((err = sdk->play(_srs_config->get_chunk_size(req->vhost))) != srs_success) { return srs_error_wrap(err, "edge pull %s stream failed", url.c_str()); } @@ -469,15 +469,7 @@ srs_error_t SrsEdgeForwarder::start() std::string vhost = _srs_config->get_vhost_edge_transform_vhost(req->vhost); vhost = srs_string_replace(vhost, "[vhost]", req->vhost); - url = srs_generate_rtmp_url(server, port, vhost, req->app, req->stream); - } - - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - if (!req->param.empty()) { - if (req->param.find("?") != 0) { - url += "?"; - } - url += req->param; + url = srs_generate_rtmp_url(server, port, req->host, vhost, req->app, req->stream, req->param); } // open socket. @@ -490,7 +482,7 @@ srs_error_t SrsEdgeForwarder::start() return srs_error_wrap(err, "sdk connect %s failed, cto=%" PRId64 ", sto=%" PRId64, url.c_str(), cto, sto); } - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost))) != srs_success) { return srs_error_wrap(err, "sdk publish"); } diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index 1f542fc27..a39ecb881 100755 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -94,42 +94,6 @@ srs_error_t SrsForwarder::on_publish() { srs_error_t err = srs_success; - // discovery the server port and tcUrl from req and ep_forward. - std::string server; - std::string tcUrl; - int port = SRS_CONSTS_RTMP_DEFAULT_PORT; - if (true) { - // parse host:port from hostport. - srs_parse_hostport(ep_forward, server, port); - - // generate tcUrl - tcUrl = srs_generate_tc_url(server, req->vhost, req->app, port, req->param); - } - - // dead loop check - std::string source_ep = "rtmp://"; - source_ep += req->host; - source_ep += ":"; - source_ep += req->port; - source_ep += "?vhost="; - source_ep += req->vhost; - - std::string dest_ep = "rtmp://"; - if (ep_forward == SRS_CONSTS_LOCALHOST) { - dest_ep += req->host; - } else { - dest_ep += server; - } - dest_ep += ":"; - dest_ep += port; - dest_ep += "?vhost="; - dest_ep += req->vhost; - - if (source_ep == dest_ep) { - return srs_error_new(ERROR_SYSTEM_FORWARD_LOOP, "forward loop detected. src=%s, dest=%s", source_ep.c_str(), dest_ep.c_str()); - } - srs_trace("start forward %s to %s, tcUrl=%s, stream=%s", source_ep.c_str(), dest_ep.c_str(), tcUrl.c_str(), req->stream.c_str()); - srs_freep(trd); trd = new SrsSTCoroutine("forward", this); if ((err = trd->start()) != srs_success) { @@ -245,7 +209,7 @@ srs_error_t SrsForwarder::do_cycle() srs_parse_hostport(ep_forward, server, port); // generate url - url = srs_generate_rtmp_url(server, port, req->vhost, req->app, req->stream); + url = srs_generate_rtmp_url(server, port, req->host, req->vhost, req->app, req->stream, req->param); } srs_freep(sdk); @@ -257,7 +221,7 @@ srs_error_t SrsForwarder::do_cycle() return srs_error_wrap(err, "sdk connect url=%s, cto=%" PRId64 ", sto=%" PRId64, url.c_str(), cto, sto); } - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(_srs_config->get_chunk_size(req->vhost))) != srs_success) { return srs_error_wrap(err, "sdk publish"); } diff --git a/trunk/src/app/srs_app_mpegts_udp.cpp b/trunk/src/app/srs_app_mpegts_udp.cpp index cf37c82dd..cdc86f476 100644 --- a/trunk/src/app/srs_app_mpegts_udp.cpp +++ b/trunk/src/app/srs_app_mpegts_udp.cpp @@ -626,7 +626,7 @@ srs_error_t SrsMpegtsOverUdp::connect() return srs_error_wrap(err, "connect %s failed, cto=%" PRId64 ", sto=%" PRId64, output.c_str(), cto, sto); } - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { close(); return srs_error_wrap(err, "publish"); } diff --git a/trunk/src/app/srs_app_ng_exec.cpp b/trunk/src/app/srs_app_ng_exec.cpp index c5f94f2bb..545644518 100644 --- a/trunk/src/app/srs_app_ng_exec.cpp +++ b/trunk/src/app/srs_app_ng_exec.cpp @@ -221,7 +221,7 @@ string SrsNgExec::parse(SrsRequest* req, string tmpl) output = srs_string_replace(output, "[pageUrl]", req->pageUrl); if (output.find("[url]") != string::npos) { - string url = srs_generate_rtmp_url(req->host, req->port, req->vhost, req->app, req->stream); + string url = srs_generate_rtmp_url(req->host, req->port, req->host, req->vhost, req->app, req->stream, req->param); output = srs_string_replace(output, "[url]", url); } diff --git a/trunk/src/app/srs_app_rtsp.cpp b/trunk/src/app/srs_app_rtsp.cpp index 3c46ab9e6..bcbf1f916 100644 --- a/trunk/src/app/srs_app_rtsp.cpp +++ b/trunk/src/app/srs_app_rtsp.cpp @@ -661,7 +661,7 @@ srs_error_t SrsRtspConn::connect() } // publish. - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { close(); return srs_error_wrap(err, "publish %s failed", url.c_str()); } diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index 3219c4fdb..fb47c6d61 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -1482,8 +1482,8 @@ srs_error_t SrsOriginHub::create_forwarders() } // TODO: FIXME: support queue size. - //double queue_size = _srs_config->get_queue_length(req->vhost); - //forwarder->set_queue_size(queue_size); + double queue_size = _srs_config->get_queue_length(req->vhost); + forwarder->set_queue_size(queue_size); if ((err = forwarder->on_publish()) != srs_success) { return srs_error_wrap(err, "start forwarder failed, vhost=%s, app=%s, stream=%s, forward-to=%s", diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 8104f6907..e4dcce25f 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 34 +#define VERSION_REVISION 35 // generated by configure, only macros. #include diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 9bb0f3f95..824610d1c 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -750,16 +750,12 @@ extern "C"{ string tcUrl; switch(context->schema) { + // For SRS3, only use one format url. case srs_url_schema_normal: - tcUrl=srs_generate_normal_tc_url(context->ip, context->vhost, context->app, context->port, context->param); - break; case srs_url_schema_via: - tcUrl=srs_generate_via_tc_url(context->ip, context->vhost, context->app, context->port, context->param); - break; case srs_url_schema_vis: case srs_url_schema_vis2: - tcUrl=srs_generate_vis_tc_url(context->ip, context->vhost, context->app, context->port, context->param); - break; + tcUrl = srs_generate_tc_url(context->ip, context->vhost, context->app, context->port); default: break; } @@ -823,7 +819,10 @@ extern "C"{ return ret; } - if ((err = context->rtmp->play(context->stream, context->stream_id)) != srs_success) { + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->play(stream, context->stream_id, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; @@ -840,7 +839,10 @@ extern "C"{ srs_assert(rtmp != NULL); Context* context = (Context*)rtmp; - if ((err = context->rtmp->fmle_publish(context->stream, context->stream_id)) != srs_success) { + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->fmle_publish(stream, context->stream_id)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp index a17c6946b..65a321111 100644 --- a/trunk/src/main/srs_main_ingest_hls.cpp +++ b/trunk/src/main/srs_main_ingest_hls.cpp @@ -1289,7 +1289,7 @@ int SrsIngestHlsOutput::connect() } // publish. - if ((err = sdk->publish()) != srs_success) { + if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { // TODO: FIXME: Use error ret = srs_error_code(err); srs_freep(err); diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 64592d697..11ee6486c 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -151,43 +151,59 @@ void srs_random_generate(char* bytes, int size) } } -string srs_generate_tc_url(string ip, string vhost, string app, int port, string param) +string srs_generate_tc_url(string host, string vhost, string app, int port) { string tcUrl = "rtmp://"; if (vhost == SRS_CONSTS_RTMP_DEFAULT_VHOST) { - tcUrl += ip; + tcUrl += host; } else { tcUrl += vhost; } if (port != SRS_CONSTS_RTMP_DEFAULT_PORT) { - tcUrl += ":"; - tcUrl += srs_int2str(port); + tcUrl += ":" + srs_int2str(port); } - tcUrl += "/"; - tcUrl += app; - if (!param.empty()) { - tcUrl += "?" + param; - } + tcUrl += "/" + app; return tcUrl; } -string srs_generate_normal_tc_url(string ip, string vhost, string app, int port, string param) -{ - return "rtmp://" + vhost + ":" + srs_int2str(port) + "/" + app + (param.empty() ? "" : "?" + param); -} - -string srs_generate_via_tc_url(string ip, string vhost, string app, int port, string param) -{ - return "rtmp://" + ip + ":" + srs_int2str(port) + "/" + vhost + "/" + app + (param.empty() ? "" : "?" + param); -} - -string srs_generate_vis_tc_url(string ip, string vhost, string app, int port, string param) +string srs_generate_stream_with_query(string host, string vhost, string stream, string param) { - return "rtmp://" + ip + ":" + srs_int2str(port) + "/" + app + (param.empty() ? "" : "?" + param); + string url = stream; + string query = param; + + // If no vhost in param, try to append one. + string guessVhost; + if (query.find("vhost=") == string::npos) { + if (vhost != SRS_CONSTS_RTMP_DEFAULT_VHOST) { + guessVhost = vhost; + } else if (!srs_is_ipv4(host)) { + guessVhost = host; + } + } + + // Well, if vhost exists, always append in query string. + if (!guessVhost.empty()) { + query += "&vhost=" + guessVhost; + } + + // Remove the start & when param is empty. + srs_string_trim_start(query, "&"); + + // Prefix query with ?. + if (!srs_string_starts_with(query, "?")) { + url += "?"; + } + + // Append query to url. + if (!query.empty()) { + url += query; + } + + return url; } template @@ -287,22 +303,12 @@ void srs_parse_rtmp_url(string url, string& tcUrl, string& stream) } } -string srs_generate_rtmp_url(string server, int port, string vhost, string app, string stream) +string srs_generate_rtmp_url(string server, int port, string host, string vhost, string app, string stream, string param) { - std::stringstream ss; - - ss << "rtmp://" << server << ":" << std::dec << port << "/" << app; - - // when default or server is vhost, donot specifies the vhost in params. - if (SRS_CONSTS_RTMP_DEFAULT_VHOST != vhost && server != vhost) { - ss << "...vhost..." << vhost; - } - - if (!stream.empty()) { - ss << "/" << stream; - } - - return ss.str(); + string tcUrl = "rtmp://" + server + ":" + srs_int2str(port) + "/" + app; + string streamWithQuery = srs_generate_stream_with_query(host, vhost, stream, param); + string url = tcUrl + "/" + streamWithQuery; + return url; } srs_error_t srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite) diff --git a/trunk/src/protocol/srs_protocol_utility.hpp b/trunk/src/protocol/srs_protocol_utility.hpp index 79a8e2f79..349a16f4f 100644 --- a/trunk/src/protocol/srs_protocol_utility.hpp +++ b/trunk/src/protocol/srs_protocol_utility.hpp @@ -71,28 +71,16 @@ extern void srs_parse_query_string(std::string q, std::map& vs, std::string separator); +// Whether domain is an IPv4 address. +extern bool srs_is_ipv4(std::string domain); + #endif diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index 88c2a7bce..683c72653 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -2053,7 +2053,7 @@ srs_error_t SrsRtmpClient::create_stream(int& stream_id) return err; } -srs_error_t SrsRtmpClient::play(string stream, int stream_id) +srs_error_t SrsRtmpClient::play(string stream, int stream_id, int chunk_size) { srs_error_t err = srs_success; @@ -2081,27 +2081,27 @@ srs_error_t SrsRtmpClient::play(string stream, int stream_id) } // SetChunkSize - if (true) { + if (chunk_size != SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE) { SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; + pkt->chunk_size = chunk_size; if ((err = protocol->send_and_free_packet(pkt, 0)) != srs_success) { - return srs_error_wrap(err, "send set chunk size failed. stream=%s, chunk_size=%d", stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE); + return srs_error_wrap(err, "send set chunk size failed. stream=%s, chunk_size=%d", stream.c_str(), chunk_size); } } return err; } -srs_error_t SrsRtmpClient::publish(string stream, int stream_id) +srs_error_t SrsRtmpClient::publish(string stream, int stream_id, int chunk_size) { srs_error_t err = srs_success; // SetChunkSize - if (true) { + if (chunk_size != SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE) { SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; + pkt->chunk_size = chunk_size; if ((err = protocol->send_and_free_packet(pkt, 0)) != srs_success) { - return srs_error_wrap(err, "send set chunk size failed. stream=%s, chunk_size=%d", stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE); + return srs_error_wrap(err, "send set chunk size failed. stream=%s, chunk_size=%d", stream.c_str(), chunk_size); } } @@ -2407,7 +2407,7 @@ srs_error_t SrsRtmpServer::redirect(SrsRequest* r, string host, int port, bool& srs_error_t err = srs_success; if (true) { - string url = srs_generate_rtmp_url(host, port, r->vhost, r->app, ""); + string url = srs_generate_rtmp_url(host, port, r->host, r->vhost, r->app, r->stream, r->param); SrsAmf0Object* ex = SrsAmf0Any::object(); ex->set("code", SrsAmf0Any::number(302)); diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp index 617d6e300..ed1947457 100644 --- a/trunk/src/protocol/srs_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_rtmp_stack.hpp @@ -729,12 +729,12 @@ public: /** * start play stream. */ - virtual srs_error_t play(std::string stream, int stream_id); + virtual srs_error_t play(std::string stream, int stream_id, int chunk_size); /** * start publish stream. use flash publish workflow: * connect-app => create-stream => flash-publish */ - virtual srs_error_t publish(std::string stream, int stream_id); + virtual srs_error_t publish(std::string stream, int stream_id, int chunk_size); /** * start publish stream. use FMLE publish workflow: * connect-app => FMLE publish diff --git a/trunk/src/service/srs_service_rtmp_conn.cpp b/trunk/src/service/srs_service_rtmp_conn.cpp index d22c2ca1f..266a21c91 100644 --- a/trunk/src/service/srs_service_rtmp_conn.cpp +++ b/trunk/src/service/srs_service_rtmp_conn.cpp @@ -134,7 +134,7 @@ srs_error_t SrsBasicRtmpClient::do_connect_app(string local_ip, bool debug) // generate the tcUrl std::string param = ""; std::string target_vhost = req->vhost; - std::string tc_url = srs_generate_tc_url(req->host, req->vhost, req->app, req->port, param); + std::string tc_url = srs_generate_tc_url(req->host, req->vhost, req->app, req->port); // replace the tcUrl in request, // which will replace the tc_url in client.connect_app(). @@ -150,43 +150,29 @@ srs_error_t SrsBasicRtmpClient::do_connect_app(string local_ip, bool debug) return err; } -srs_error_t SrsBasicRtmpClient::publish() +srs_error_t SrsBasicRtmpClient::publish(int chunk_size) { srs_error_t err = srs_success; - string stream = req->stream; - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - if (!req->param.empty()) { - if (req->param.find("?") != 0) { - stream += "?"; - } - stream += req->param; - } + string stream = srs_generate_stream_with_query(req->host, req->vhost, req->stream, req->param); // publish. - if ((err = client->publish(stream, stream_id)) != srs_success) { + if ((err = client->publish(stream, stream_id, chunk_size)) != srs_success) { return srs_error_wrap(err, "publish failed, stream=%s, stream_id=%d", stream.c_str(), stream_id); } return err; } -srs_error_t SrsBasicRtmpClient::play() +srs_error_t SrsBasicRtmpClient::play(int chunk_size) { srs_error_t err = srs_success; - string stream = req->stream; - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - if (!req->param.empty()) { - if (req->param.find("?") != 0) { - stream += "?"; - } - stream += req->param; - } + string stream = srs_generate_stream_with_query(req->host, req->vhost, req->stream, req->param); - if ((err = client->play(stream, stream_id)) != srs_success) { + if ((err = client->play(stream, stream_id, chunk_size)) != srs_success) { return srs_error_wrap(err, "connect with server failed, stream=%s, stream_id=%d", stream.c_str(), stream_id); } diff --git a/trunk/src/service/srs_service_rtmp_conn.hpp b/trunk/src/service/srs_service_rtmp_conn.hpp index 6f9233df3..a8995814b 100644 --- a/trunk/src/service/srs_service_rtmp_conn.hpp +++ b/trunk/src/service/srs_service_rtmp_conn.hpp @@ -74,8 +74,8 @@ protected: virtual srs_error_t connect_app(); virtual srs_error_t do_connect_app(std::string local_ip, bool debug); public: - virtual srs_error_t publish(); - virtual srs_error_t play(); + virtual srs_error_t publish(int chunk_size); + virtual srs_error_t play(int chunk_size); virtual void kbps_sample(const char* label, int64_t age); virtual void kbps_sample(const char* label, int64_t age, int msgs); virtual int sid(); diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index 3979cd351..ec50d29e9 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -559,15 +559,15 @@ VOID TEST(ProtocolUtilityTest, GenerateTcUrl) string ip; string vhost; string app; int port; string tcUrl; string param; ip = "127.0.0.1"; vhost = "__defaultVhost__"; app = "live"; port = 1935; - tcUrl = srs_generate_tc_url(ip, vhost, app, port, param); + tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://127.0.0.1/live", tcUrl.c_str()); ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 1935; - tcUrl = srs_generate_tc_url(ip, vhost, app, port, param); + tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://demo/live", tcUrl.c_str()); ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 19351; - tcUrl = srs_generate_tc_url(ip, vhost, app, port, param); + tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://demo:19351/live", tcUrl.c_str()); } From 85d3a5001c3db244cabad01da2fe3eebb7936925 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 4 Aug 2018 19:36:42 +0800 Subject: [PATCH 17/33] For #1110, Support params in http callback. 2.0.251 --- README.md | 2 ++ trunk/conf/full.conf | 13 ++++++------ .../srs_xcode.xcodeproj/project.pbxproj | 2 -- trunk/src/app/srs_app_http_hooks.cpp | 21 ++++++++++++++++--- trunk/src/app/srs_app_rtmp_conn.cpp | 8 +++---- trunk/src/core/srs_core.hpp | 2 +- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 2969ff97a..38e281cc9 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 2.0.251 * v2.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 2.0.250 * v2.0, 2018-07-21, Merge [#1119][bug #1119], fix memory leak. 2.0.249 * v2.0, 2018-07-18, [2.0 release3(2.0.248)][r2.0r3] released. 86775 lines. @@ -1315,6 +1316,7 @@ Winlin [bug #1176]: https://github.com/ossrs/srs/issues/1176 [bug #1119]: https://github.com/ossrs/srs/issues/1119 [bug #1031]: https://github.com/ossrs/srs/issues/1031 +[bug #1110]: https://github.com/ossrs/srs/issues/1110 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 4edac7d3d..246d215a0 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -737,7 +737,7 @@ vhost hooks.callback.srs.com { # "action": "on_unpublish", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", "param":"?token=xxx&salt=yyy" # } # if valid, the hook must return HTTP code 200(Stauts OK) and response # an int value specifies the error code(0 corresponding to success): @@ -751,7 +751,7 @@ vhost hooks.callback.srs.com { # "action": "on_play", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "pageUrl": "http://www.test.com/live.html" # } # if valid, the hook must return HTTP code 200(Stauts OK) and response @@ -766,7 +766,7 @@ vhost hooks.callback.srs.com { # "action": "on_stop", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", "param":"?token=xxx&salt=yyy" # } # if valid, the hook must return HTTP code 200(Stauts OK) and response # an int value specifies the error code(0 corresponding to success): @@ -780,7 +780,7 @@ vhost hooks.callback.srs.com { # "action": "on_dvr", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "cwd": "/usr/local/srs", # "file": "./objs/nginx/html/live/livestream.1420254068776.flv" # } @@ -794,7 +794,7 @@ vhost hooks.callback.srs.com { # "action": "on_hls", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "duration": 9.36, // in seconds # "cwd": "/usr/local/srs", # "file": "./objs/nginx/html/live/livestream/2015-04-23/01/476584165.ts", @@ -812,10 +812,11 @@ vhost hooks.callback.srs.com { # so we use HTTP GET and use the variable following: # [app], replace with the app. # [stream], replace with the stream. + # [param], replace with the param. # [ts_url], replace with the ts url. # ignore any return data of server. # @remark random select a url to report, not report all. - on_hls_notify http://127.0.0.1:8085/api/v1/hls/[app]/[stream][ts_url]; + on_hls_notify http://127.0.0.1:8085/api/v1/hls/[app]/[stream]/[ts_url][param]; } } diff --git a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj index b5e5a63e7..4ab868aeb 100644 --- a/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj +++ b/trunk/ide/srs_xcode/srs_xcode.xcodeproj/project.pbxproj @@ -311,7 +311,6 @@ 3C1EE6C91AB1080900576EE9 /* origin.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = origin.conf; path = ../../../conf/origin.conf; sourceTree = ""; }; 3C1EE6CA1AB1080900576EE9 /* push.mpegts.over.udp.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = push.mpegts.over.udp.conf; path = ../../../conf/push.mpegts.over.udp.conf; sourceTree = ""; }; 3C1EE6CB1AB1080900576EE9 /* push.rtsp.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = push.rtsp.conf; path = ../../../conf/push.rtsp.conf; sourceTree = ""; }; - 3C1EE6CC1AB1080900576EE9 /* ram.hls.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ram.hls.conf; path = ../../../conf/ram.hls.conf; sourceTree = ""; }; 3C1EE6CD1AB1080900576EE9 /* realtime.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = realtime.conf; path = ../../../conf/realtime.conf; sourceTree = ""; }; 3C1EE6CE1AB1080900576EE9 /* rtmp.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rtmp.conf; path = ../../../conf/rtmp.conf; sourceTree = ""; }; 3C1EE6CF1AB1080900576EE9 /* security.deny.publish.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = security.deny.publish.conf; path = ../../../conf/security.deny.publish.conf; sourceTree = ""; }; @@ -703,7 +702,6 @@ 3C1EE6C91AB1080900576EE9 /* origin.conf */, 3C1EE6CA1AB1080900576EE9 /* push.mpegts.over.udp.conf */, 3C1EE6CB1AB1080900576EE9 /* push.rtsp.conf */, - 3C1EE6CC1AB1080900576EE9 /* ram.hls.conf */, 3C1EE6CD1AB1080900576EE9 /* realtime.conf */, 3C1EE6CE1AB1080900576EE9 /* rtmp.conf */, 3C1EE6CF1AB1080900576EE9 /* security.deny.publish.conf */, diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index c6d2e9276..d63df4931 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -131,6 +131,15 @@ int SrsHttpHooks::on_publish(string url, SrsRequest* req) int client_id = _srs_context->get_id(); + string stream = req->stream; + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + if (!req->param.empty()) { + if (req->param.find("?") != 0) { + stream += "?"; + } + stream += req->param; + } + std::stringstream ss; ss << SRS_JOBJECT_START << SRS_JFIELD_STR("action", "on_publish") << SRS_JFIELD_CONT @@ -139,7 +148,7 @@ int SrsHttpHooks::on_publish(string url, SrsRequest* req) << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT << SRS_JFIELD_STR("tcUrl", req->tcUrl) << SRS_JFIELD_CONT // Add tcUrl for auth publish rtmp stream client - << SRS_JFIELD_STR("stream", req->stream) + << SRS_JFIELD_STR("stream", stream) << SRS_JOBJECT_END; std::string data = ss.str(); @@ -172,7 +181,8 @@ void SrsHttpHooks::on_unpublish(string url, SrsRequest* req) << SRS_JFIELD_STR("ip", req->ip) << SRS_JFIELD_CONT << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT - << SRS_JFIELD_STR("stream", req->stream) + << SRS_JFIELD_STR("stream", req->stream)<< SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JOBJECT_END; std::string data = ss.str(); @@ -206,6 +216,7 @@ int SrsHttpHooks::on_play(string url, SrsRequest* req) << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JFIELD_CONT << SRS_JFIELD_STR("pageUrl", req->pageUrl) << SRS_JOBJECT_END; @@ -239,7 +250,8 @@ void SrsHttpHooks::on_stop(string url, SrsRequest* req) << SRS_JFIELD_STR("ip", req->ip) << SRS_JFIELD_CONT << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT - << SRS_JFIELD_STR("stream", req->stream) + << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JOBJECT_END; std::string data = ss.str(); @@ -274,6 +286,7 @@ int SrsHttpHooks::on_dvr(int cid, string url, SrsRequest* req, string file) << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JFIELD_CONT << SRS_JFIELD_STR("cwd", cwd) << SRS_JFIELD_CONT << SRS_JFIELD_STR("file", file) << SRS_JOBJECT_END; @@ -316,6 +329,7 @@ int SrsHttpHooks::on_hls(int cid, string url, SrsRequest* req, string file, stri << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JFIELD_CONT << SRS_JFIELD_ORG("duration", duration) << SRS_JFIELD_CONT << SRS_JFIELD_STR("cwd", cwd) << SRS_JFIELD_CONT << SRS_JFIELD_STR("file", file) << SRS_JFIELD_CONT @@ -356,6 +370,7 @@ int SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* req, std:: url = srs_string_replace(url, "[app]", req->app); url = srs_string_replace(url, "[stream]", req->stream); url = srs_string_replace(url, "[ts_url]", ts_url); + url = srs_string_replace(url, "[param]", req->param); int64_t starttime = srs_update_system_time_ms(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index 58781768c..3c8aee8ed 100755 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -438,8 +438,8 @@ int SrsRtmpConn::stream_service_cycle() srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->stream, req->port, req->param); req->strip(); - srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f", - srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration); + srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f, param=%s", + srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration, req->param.c_str()); // discovery vhost, resolve the vhost from config SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost); @@ -460,10 +460,10 @@ int SrsRtmpConn::stream_service_cycle() return ret; } - srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, stream=%s, args=%s", + srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, stream=%s, param=%s, args=%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(), req->stream.c_str(), (req->args? "(obj)":"null")); + req->app.c_str(), req->stream.c_str(), req->param.c_str(), (req->args? "(obj)":"null")); // do token traverse before serve it. // @see https://github.com/ossrs/srs/pull/239 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 62352b69e..c2ceadcf9 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 250 +#define VERSION_REVISION 251 // generated by configure, only macros. #include From 29b74c7220a8e8611eebc6975c1d786b0b2572e5 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 4 Aug 2018 19:51:04 +0800 Subject: [PATCH 18/33] For #1110, Support params in http callback. 3.0.36 --- README.md | 3 +++ trunk/conf/full.conf | 13 +++++++------ trunk/src/app/srs_app_http_hooks.cpp | 7 +++++++ trunk/src/app/srs_app_rtmp_conn.cpp | 15 +++++++-------- trunk/src/core/srs_core.hpp | 2 +- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 55bfb91cd..0e100e4ed 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 3.0.36 * v3.0, 2018-08-02, Always use vhost in stream query, the unify uri. 3.0.35 * v3.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 3.0.34 * v3.0, 2018-07-22, Replace hex to string to match MIT license. 3.0.33 @@ -226,6 +227,7 @@ Please select according to languages: ### V2 changes +* v2.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 2.0.251 * v2.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 2.0.250 * v2.0, 2018-07-21, Merge [#1119][bug #1119], fix memory leak. 2.0.249 * v2.0, 2018-07-18, [2.0 release3(2.0.248)][r2.0r3] released. 86775 lines. @@ -1434,6 +1436,7 @@ Winlin [bug #1176]: https://github.com/ossrs/srs/issues/1176 [bug #1119]: https://github.com/ossrs/srs/issues/1119 [bug #1031]: https://github.com/ossrs/srs/issues/1031 +[bug #1110]: https://github.com/ossrs/srs/issues/1110 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [bug #735]: https://github.com/ossrs/srs/issues/735 diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 9d78a8516..7408566d5 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -851,7 +851,7 @@ vhost hooks.callback.srs.com { # "action": "on_unpublish", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", "param":"?token=xxx&salt=yyy" # } # if valid, the hook must return HTTP code 200(Status OK) and response # an int value specifies the error code(0 corresponding to success): @@ -865,7 +865,7 @@ vhost hooks.callback.srs.com { # "action": "on_play", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "pageUrl": "http://www.test.com/live.html" # } # if valid, the hook must return HTTP code 200(Status OK) and response @@ -880,7 +880,7 @@ vhost hooks.callback.srs.com { # "action": "on_stop", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", "param":"?token=xxx&salt=yyy" # } # if valid, the hook must return HTTP code 200(Status OK) and response # an int value specifies the error code(0 corresponding to success): @@ -894,7 +894,7 @@ vhost hooks.callback.srs.com { # "action": "on_dvr", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "cwd": "/usr/local/srs", # "file": "./objs/nginx/html/live/livestream.1420254068776.flv" # } @@ -908,7 +908,7 @@ vhost hooks.callback.srs.com { # "action": "on_hls", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream", + # "stream": "livestream", "param":"?token=xxx&salt=yyy", # "duration": 9.36, // in seconds # "cwd": "/usr/local/srs", # "file": "./objs/nginx/html/live/livestream/2015-04-23/01/476584165.ts", @@ -926,10 +926,11 @@ vhost hooks.callback.srs.com { # so we use HTTP GET and use the variable following: # [app], replace with the app. # [stream], replace with the stream. + # [param], replace with the param. # [ts_url], replace with the ts url. # ignore any return data of server. # @remark random select a url to report, not report all. - on_hls_notify http://127.0.0.1:8085/api/v1/hls/[app]/[stream][ts_url]; + on_hls_notify http://127.0.0.1:8085/api/v1/hls/[app]/[stream]/[ts_url][param]; } } diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index 3241cde59..08b1e4984 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -141,6 +141,7 @@ srs_error_t SrsHttpHooks::on_publish(string url, SrsRequest* req) obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("tcUrl", SrsJsonAny::str(req->tcUrl.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); std::string data = obj->dumps(); std::string res; @@ -173,6 +174,7 @@ void SrsHttpHooks::on_unpublish(string url, SrsRequest* req) obj->set("vhost", SrsJsonAny::str(req->vhost.c_str())); obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); std::string data = obj->dumps(); std::string res; @@ -208,6 +210,7 @@ srs_error_t SrsHttpHooks::on_play(string url, SrsRequest* req) obj->set("vhost", SrsJsonAny::str(req->vhost.c_str())); obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); obj->set("pageUrl", SrsJsonAny::str(req->pageUrl.c_str())); std::string data = obj->dumps(); @@ -241,6 +244,7 @@ void SrsHttpHooks::on_stop(string url, SrsRequest* req) obj->set("vhost", SrsJsonAny::str(req->vhost.c_str())); obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); std::string data = obj->dumps(); std::string res; @@ -277,6 +281,7 @@ srs_error_t SrsHttpHooks::on_dvr(int cid, string url, SrsRequest* req, string fi obj->set("vhost", SrsJsonAny::str(req->vhost.c_str())); obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); obj->set("cwd", SrsJsonAny::str(cwd.c_str())); obj->set("file", SrsJsonAny::str(file.c_str())); @@ -318,6 +323,7 @@ srs_error_t SrsHttpHooks::on_hls(int cid, string url, SrsRequest* req, string fi obj->set("vhost", SrsJsonAny::str(req->vhost.c_str())); obj->set("app", SrsJsonAny::str(req->app.c_str())); obj->set("stream", SrsJsonAny::str(req->stream.c_str())); + obj->set("param", SrsJsonAny::str(req->param.c_str())); obj->set("duration", SrsJsonAny::number(duration)); obj->set("cwd", SrsJsonAny::str(cwd.c_str())); obj->set("file", SrsJsonAny::str(file.c_str())); @@ -355,6 +361,7 @@ srs_error_t SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* re url = srs_string_replace(url, "[app]", req->app); url = srs_string_replace(url, "[stream]", req->stream); url = srs_string_replace(url, "[ts_url]", ts_url); + url = srs_string_replace(url, "[param]", req->param); int64_t starttime = srs_update_system_time_ms(); diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index cee90deae..db8e67757 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -470,9 +470,9 @@ srs_error_t SrsRtmpConn::stream_service_cycle() srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->stream, req->port, req->param); req->strip(); - srs_trace("client identified, type=%s, vhost=%s, app=%s, stream_name=%s, duration=%.2f", - srs_client_type_string(info->type).c_str(), req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), req->duration); - + srs_trace("client identified, type=%s, vhost=%s, app=%s, stream=%s, param=%s, duration=%.2f", + srs_client_type_string(info->type).c_str(), req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), req->param.c_str(), req->duration); + // discovery vhost, resolve the vhost from config SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost); if (parsed_vhost) { @@ -489,11 +489,10 @@ srs_error_t SrsRtmpConn::stream_service_cycle() return srs_error_wrap(err, "check vhost"); } - srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%d, app=%s, stream=%s, args=%s", - req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), - req->schema.c_str(), req->vhost.c_str(), req->port, - req->app.c_str(), req->stream.c_str(), (req->args? "(obj)":"null")); - + srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%d, app=%s, stream=%s, param=%s, args=%s", + req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port, + req->app.c_str(), req->stream.c_str(), req->param.c_str(), (req->args? "(obj)":"null")); + // do token traverse before serve it. // @see https://github.com/ossrs/srs/pull/239 if (true) { diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index e4dcce25f..a3b148696 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 35 +#define VERSION_REVISION 36 // generated by configure, only macros. #include From 82699ff616a49e878afe6171c6b7466477a215c2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 10:39:52 +0800 Subject: [PATCH 19/33] Refine code for librtmp --- trunk/src/libs/srs_librtmp.cpp | 4040 ++++++++++++++++---------------- trunk/src/libs/srs_librtmp.hpp | 2214 ++++++++--------- 2 files changed, 3127 insertions(+), 3127 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 824610d1c..388604607 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -522,2371 +522,2371 @@ int srs_librtmp_context_connect(Context* context) extern "C"{ #endif - int srs_version_major() - { - return VERSION_MAJOR; - } +int srs_version_major() +{ + return VERSION_MAJOR; +} + +int srs_version_minor() +{ + return VERSION_MINOR; +} + +int srs_version_revision() +{ + return VERSION_REVISION; +} + +srs_rtmp_t srs_rtmp_create(const char* url) +{ + int ret = ERROR_SUCCESS; - int srs_version_minor() - { - return VERSION_MINOR; - } + Context* context = new Context(); + context->url = url; - int srs_version_revision() - { - return VERSION_REVISION; - } + // create socket + srs_freep(context->skt); + context->skt = new SimpleSocketStream(); - srs_rtmp_t srs_rtmp_create(const char* url) - { - int ret = ERROR_SUCCESS; - - Context* context = new Context(); - context->url = url; - - // create socket - srs_freep(context->skt); - context->skt = new SimpleSocketStream(); + if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { + srs_human_error("Create socket failed, ret=%d", ret); - if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { - srs_human_error("Create socket failed, ret=%d", ret); - - // free the context and return NULL - srs_freep(context); - return NULL; - } - - return context; + // free the context and return NULL + srs_freep(context); + return NULL; } - int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms) - { - int ret = ERROR_SUCCESS; - - if (!rtmp) { - return ret; - } - - Context* context = (Context*)rtmp; - - context->stimeout = send_timeout_ms; - context->rtimeout = recv_timeout_ms; - - context->skt->set_recv_timeout(context->rtimeout); - context->skt->set_send_timeout(context->stimeout); - + return context; +} + +int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms) +{ + int ret = ERROR_SUCCESS; + + if (!rtmp) { return ret; } - void srs_rtmp_destroy(srs_rtmp_t rtmp) - { - if (!rtmp) { - return; - } - - Context* context = (Context*)rtmp; - - srs_freep(context); - } + Context* context = (Context*)rtmp; - int srs_rtmp_handshake(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - if ((ret = srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - return ret; - } + context->stimeout = send_timeout_ms; + context->rtimeout = recv_timeout_ms; - int srs_rtmp_dns_resolve(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // parse uri - if ((ret = srs_librtmp_context_parse_uri(context)) != ERROR_SUCCESS) { - return ret; - } - // resolve host - if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) { - return ret; - } - - return ret; + context->skt->set_recv_timeout(context->rtimeout); + context->skt->set_send_timeout(context->stimeout); + + return ret; +} + +void srs_rtmp_destroy(srs_rtmp_t rtmp) +{ + if (!rtmp) { + return; } - int srs_rtmp_connect_server(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // set timeout if user not set. - if (context->stimeout == SRS_CONSTS_NO_TMMS) { - context->stimeout = SRS_SOCKET_DEFAULT_TMMS; - context->skt->set_send_timeout(context->stimeout); - } - if (context->rtimeout == SRS_CONSTS_NO_TMMS) { - context->rtimeout = SRS_SOCKET_DEFAULT_TMMS; - context->skt->set_recv_timeout(context->rtimeout); - } - - if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) { - return ret; - } - + Context* context = (Context*)rtmp; + + srs_freep(context); +} + +int srs_rtmp_handshake(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp) - { -#ifndef SRS_AUTO_SSL - // complex handshake requires ssl - return ERROR_RTMP_HS_SSL_REQUIRE; -#else - int ret = ERROR_SUCCESS; - srs_error_t err = srs_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 ((err = context->rtmp->complex_handshake()) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) { return ret; -#endif } - int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_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 ((err = context->rtmp->simple_handshake()) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { 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_dns_resolve(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // parse uri + if ((ret = srs_librtmp_context_parse_uri(context)) != ERROR_SUCCESS) { return ret; } - - int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - context->schema = schema; - + // resolve host + if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_connect_app(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - string tcUrl; - switch(context->schema) { - // For SRS3, only use one format url. - case srs_url_schema_normal: - case srs_url_schema_via: - case srs_url_schema_vis: - case srs_url_schema_vis2: - tcUrl = srs_generate_tc_url(context->ip, context->vhost, context->app, context->port); - default: - break; - } - - Context* c = context; - if ((err = context->rtmp->connect_app(c->app, tcUrl, c->req, true, &c->si)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; + return ret; +} + +int srs_rtmp_connect_server(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // set timeout if user not set. + if (context->stimeout == SRS_CONSTS_NO_TMMS) { + context->stimeout = SRS_SOCKET_DEFAULT_TMMS; + context->skt->set_send_timeout(context->stimeout); + } + if (context->rtimeout == SRS_CONSTS_NO_TMMS) { + context->rtimeout = SRS_SOCKET_DEFAULT_TMMS; + context->skt->set_recv_timeout(context->rtimeout); } - int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *pid = context->si.pid; - *cid = context->si.cid; - *ip = context->si.ip.empty()? NULL:(char*)context->si.ip.c_str(); - + if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *sig = context->si.sig.empty()? NULL:(char*)context->si.sig.c_str(); - + return ret; +} + +int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp) +{ +#ifndef SRS_AUTO_SSL + // complex handshake requires ssl + return ERROR_RTMP_HS_SSL_REQUIRE; +#else + int ret = ERROR_SUCCESS; + srs_error_t err = srs_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 ((err = context->rtmp->complex_handshake()) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *major = context->si.major; - *minor = context->si.minor; - *revision = context->si.revision; - *build = context->si.build; - + return ret; +#endif +} + +int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_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 ((err = context->rtmp->simple_handshake()) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_play_stream(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - if ((err = context->rtmp->create_stream(context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); - - if ((err = context->rtmp->play(stream, context->stream_id, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - 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; } - int srs_rtmp_publish_stream(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); - - if ((err = context->rtmp->fmle_publish(stream, context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; + return ret; +} + +int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + context->schema = schema; + + return ret; +} + +int srs_rtmp_connect_app(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + string tcUrl; + switch(context->schema) { + // For SRS3, only use one format url. + case srs_url_schema_normal: + case srs_url_schema_via: + case srs_url_schema_vis: + case srs_url_schema_vis2: + tcUrl = srs_generate_tc_url(context->ip, context->vhost, context->app, context->port); + default: + break; } - int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, - int64_t* start_time, int64_t* end_time, - int* play_kbps, int* publish_kbps, - int* play_bytes, int* publish_bytes, - int* play_duration, int* publish_duration - ) { - *start_time = 0; - *end_time = 0; - *play_kbps = 0; - *publish_kbps = 0; - *play_bytes = 0; - *publish_bytes = 0; - *play_duration = 0; - *publish_duration = 0; - - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsBandwidthClient client; - - if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = client.bandwidth_check( - start_time, end_time, play_kbps, publish_kbps, - play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS - ) { - return ret; - } - + Context* c = context; + if ((err = context->rtmp->connect_app(c->app, tcUrl, c->req, true, &c->si)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } + return ret; +} + +int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid) +{ + int ret = ERROR_SUCCESS; - int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg) - { - int ret = ERROR_SUCCESS; - - SrsBuffer* stream = new SrsBuffer(msg->payload, msg->size); - SrsAutoFree(SrsBuffer, stream); - - // the aggregate message always use abs time. - int delta = -1; - - while (!stream->empty()) { - if (!stream->require(1)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message type. ret=%d", ret); - return ret; - } - int8_t type = stream->read_1bytes(); - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message size. ret=%d", ret); - return ret; - } - int32_t data_size = stream->read_3bytes(); - - if (data_size < 0) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message size(negative). ret=%d", ret); - return ret; - } - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message time. ret=%d", ret); - return ret; - } - int32_t timestamp = stream->read_3bytes(); - - if (!stream->require(1)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message time(high). ret=%d", ret); - return ret; - } - int32_t time_h = stream->read_1bytes(); - - timestamp |= time_h<<24; - timestamp &= 0x7FFFFFFF; - - // adjust abs timestamp in aggregate msg. - if (delta < 0) { - delta = (int)msg->header.timestamp - (int)timestamp; - } - timestamp += delta; - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message stream_id. ret=%d", ret); - return ret; - } - int32_t stream_id = stream->read_3bytes(); - - if (data_size > 0 && !stream->require(data_size)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message data. ret=%d", ret); - return ret; - } - - // to common message. - SrsCommonMessage o; - - o.header.message_type = type; - o.header.payload_length = data_size; - o.header.timestamp_delta = timestamp; - o.header.timestamp = timestamp; - o.header.stream_id = stream_id; - o.header.perfer_cid = msg->header.perfer_cid; - - if (data_size > 0) { - o.size = data_size; - o.payload = new char[o.size]; - stream->read_bytes(o.payload, o.size); - } - - if (!stream->require(4)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message previous tag size. ret=%d", ret); - return ret; - } - stream->read_4bytes(); - - // process parsed message - SrsCommonMessage* parsed_msg = new SrsCommonMessage(); - parsed_msg->header = o.header; - parsed_msg->payload = o.payload; - parsed_msg->size = o.size; - o.payload = NULL; - context->msgs.push_back(parsed_msg); - } - + Context* context = (Context*)rtmp; + *pid = context->si.pid; + *cid = context->si.cid; + *ip = context->si.ip.empty()? NULL:(char*)context->si.ip.c_str(); + + return ret; +} + +int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig) +{ + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + *sig = context->si.sig.empty()? NULL:(char*)context->si.sig.c_str(); + + return ret; +} + +int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build) +{ + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + *major = context->si.major; + *minor = context->si.minor; + *revision = context->si.revision; + *build = context->si.build; + + return ret; +} + +int srs_rtmp_play_stream(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + if ((err = context->rtmp->create_stream(context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_go_packet(Context* context, SrsCommonMessage* msg, - char* type, uint32_t* timestamp, char** data, int* size, - bool* got_msg - ) { - int ret = ERROR_SUCCESS; - - // generally we got a message. - *got_msg = true; - - if (msg->header.is_audio()) { - *type = SRS_RTMP_TYPE_AUDIO; - *timestamp = (uint32_t)msg->header.timestamp; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_video()) { - *type = SRS_RTMP_TYPE_VIDEO; - *timestamp = (uint32_t)msg->header.timestamp; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { - *type = SRS_RTMP_TYPE_SCRIPT; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_aggregate()) { - if ((ret = srs_rtmp_on_aggregate(context, msg)) != ERROR_SUCCESS) { - return ret; - } - *got_msg = false; - } else { - *type = msg->header.message_type; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } - + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->play(stream, context->stream_id, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size) - { - *type = 0; - *timestamp = 0; - *data = NULL; - *size = 0; - - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - for (;;) { - SrsCommonMessage* msg = NULL; - - // read from cache first. - if (!context->msgs.empty()) { - std::vector::iterator it = context->msgs.begin(); - msg = *it; - context->msgs.erase(it); - } - - // read from protocol sdk. - if (!msg && (err = context->rtmp->recv_message(&msg)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // no msg, try again. - if (!msg) { - continue; - } - - SrsAutoFree(SrsCommonMessage, msg); - - // process the got packet, if nothing, try again. - bool got_msg; - if ((ret = srs_rtmp_go_packet(context, msg, type, timestamp, data, size, &got_msg)) != ERROR_SUCCESS) { - return ret; - } - - // got expected message. - if (got_msg) { - break; - } - } - + return ret; +} + +int srs_rtmp_publish_stream(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->fmle_publish(stream, context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsSharedPtrMessage* msg = NULL; - - if ((err = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - srs_assert(msg); - - // send out encoded msg. - if ((err = context->rtmp->send_and_free_message(msg, context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + return ret; +} + +int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, + int64_t* start_time, int64_t* end_time, + int* play_kbps, int* publish_kbps, + int* play_bytes, int* publish_bytes, + int* play_duration, int* publish_duration +) { + *start_time = 0; + *end_time = 0; + *play_kbps = 0; + *publish_kbps = 0; + *play_bytes = 0; + *publish_bytes = 0; + *play_duration = 0; + *publish_duration = 0; + + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsBandwidthClient client; + + if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) { return ret; } - void srs_rtmp_free_packet(char* data) - { - srs_freepa(data); + if ((ret = client.bandwidth_check( + start_time, end_time, play_kbps, publish_kbps, + play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS + ) { + return ret; } - srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size) - { - srs_error_t err = srs_success; - - if (type != SRS_RTMP_TYPE_SCRIPT) { - return false; + return ret; +} + + +int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + SrsBuffer* stream = new SrsBuffer(msg->payload, msg->size); + SrsAutoFree(SrsBuffer, stream); + + // the aggregate message always use abs time. + int delta = -1; + + while (!stream->empty()) { + if (!stream->require(1)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message type. ret=%d", ret); + return ret; } + int8_t type = stream->read_1bytes(); - SrsBuffer stream(data, size); - - std::string name; - if ((err = srs_amf0_read_string(&stream, name)) != srs_success) { - srs_freep(err); - return false; + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message size. ret=%d", ret); + return ret; } + int32_t data_size = stream->read_3bytes(); - if (name == SRS_CONSTS_RTMP_ON_METADATA) { - return true; + if (data_size < 0) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message size(negative). ret=%d", ret); + return ret; } - if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { - return true; + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message time. ret=%d", ret); + return ret; } + int32_t timestamp = stream->read_3bytes(); - return false; - } - - /** - * directly write a audio frame. - */ - int srs_write_audio_raw_frame(Context* context, char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t timestamp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - char* data = NULL; - int size = 0; - if ((err = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); + if (!stream->require(1)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message time(high). ret=%d", ret); return ret; } + int32_t time_h = stream->read_1bytes(); - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); - } - - /** - * write aac frame in adts. - */ - int srs_write_aac_adts_frame(Context* context, SrsRawAacStreamCodec* codec, char* frame, int frame_size, uint32_t timestamp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + timestamp |= time_h<<24; + timestamp &= 0x7FFFFFFF; - // send out aac sequence header if not sent. - if (context->aac_specific_config.empty()) { - std::string sh; - if ((err = context->aac_raw.mux_sequence_header(codec, sh)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - context->aac_specific_config = sh; - - codec->aac_packet_type = 0; - - if ((ret = srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) { - return ret; - } + // adjust abs timestamp in aggregate msg. + if (delta < 0) { + delta = (int)msg->header.timestamp - (int)timestamp; } + timestamp += delta; - codec->aac_packet_type = 1; - return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp); - } - - /** - * write aac frames in adts. - */ - int srs_write_aac_adts_frames(Context* context, char sound_format, char sound_rate, - char sound_size, char sound_type, char* frames, int frames_size, uint32_t timestamp - ) { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - SrsBuffer* stream = new SrsBuffer(frames, frames_size); - SrsAutoFree(SrsBuffer, stream); - - while (!stream->empty()) { - char* frame = NULL; - int frame_size = 0; - SrsRawAacStreamCodec codec; - if ((err = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // override by user specified. - codec.sound_format = sound_format; - codec.sound_rate = sound_rate; - codec.sound_size = sound_size; - codec.sound_type = sound_type; - - if ((ret = srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) { - return ret; - } + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message stream_id. ret=%d", ret); + return ret; } + int32_t stream_id = stream->read_3bytes(); - return ret; - } - - /** - * write audio raw frame to SRS. - */ - int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, - char sound_size, char sound_type, char* frame, int frame_size, uint32_t timestamp - ) { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - srs_assert(context); - - if (sound_format == SrsAudioCodecIdAAC) { - // for aac, the frame must be ADTS format. - if (!srs_aac_is_adts(frame, frame_size)) { - return ERROR_AAC_REQUIRED_ADTS; - } - - // for aac, demux the ADTS to RTMP format. - return srs_write_aac_adts_frames(context, sound_format, sound_rate, sound_size, sound_type, frame, frame_size, timestamp); - } else { - // use codec info for aac. - SrsRawAacStreamCodec codec; - codec.sound_format = sound_format; - codec.sound_rate = sound_rate; - codec.sound_size = sound_size; - codec.sound_type = sound_type; - codec.aac_packet_type = 0; - - // for other data, directly write frame. - return srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp); + if (data_size > 0 && !stream->require(data_size)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message data. ret=%d", ret); + return ret; } - return ret; - } - - /** - * whether aac raw data is in adts format, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - */ - srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) - { - SrsBuffer stream(aac_raw_data, ac_raw_size); - return srs_aac_startswith_adts(&stream); - } - - /** - * parse the adts header to get the frame size. - */ - int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) - { - int size = -1; + // to common message. + SrsCommonMessage o; - if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { - return size; - } + o.header.message_type = type; + o.header.payload_length = data_size; + o.header.timestamp_delta = timestamp; + o.header.timestamp = timestamp; + o.header.stream_id = stream_id; + o.header.perfer_cid = msg->header.perfer_cid; - // adts always 7bytes. - if (ac_raw_size <= 7) { - return size; + if (data_size > 0) { + o.size = data_size; + o.payload = new char[o.size]; + stream->read_bytes(o.payload, o.size); } - // last 2bits - int16_t ch3 = aac_raw_data[3]; - // whole 8bits - int16_t ch4 = aac_raw_data[4]; - // first 3bits - int16_t ch5 = aac_raw_data[5]; - - size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); + if (!stream->require(4)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message previous tag size. ret=%d", ret); + return ret; + } + stream->read_4bytes(); - return size; + // process parsed message + SrsCommonMessage* parsed_msg = new SrsCommonMessage(); + parsed_msg->header = o.header; + parsed_msg->payload = o.payload; + parsed_msg->size = o.size; + o.payload = NULL; + context->msgs.push_back(parsed_msg); } - /** - * write h264 IPB-frame. - */ - int srs_write_h264_ipb_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // when sps or pps not sent, ignore the packet. - // @see https://github.com/ossrs/srs/issues/203 - if (!context->h264_sps_pps_sent) { - return ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - // 5bits, 7.3.1 NAL unit syntax, - // ISO_IEC_14496-10-AVC-2003.pdf, page 44. - // 5: I Frame, 1: P/B Frame - // @remark we already group sps/pps to sequence header frame; - // for I/P NALU, we send them in isolate frame, each NALU in a frame; - // for other NALU, for example, AUD/SEI, we just ignore them, because - // AUD used in annexb to split frame, while SEI generally we can ignore it. - // TODO: maybe we should group all NALUs split by AUD to a frame. - SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); - if (nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR) { + return ret; +} + +int srs_rtmp_go_packet(Context* context, SrsCommonMessage* msg, + char* type, uint32_t* timestamp, char** data, int* size, + bool* got_msg +) { + int ret = ERROR_SUCCESS; + + // generally we got a message. + *got_msg = true; + + if (msg->header.is_audio()) { + *type = SRS_RTMP_TYPE_AUDIO; + *timestamp = (uint32_t)msg->header.timestamp; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_video()) { + *type = SRS_RTMP_TYPE_VIDEO; + *timestamp = (uint32_t)msg->header.timestamp; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { + *type = SRS_RTMP_TYPE_SCRIPT; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_aggregate()) { + if ((ret = srs_rtmp_on_aggregate(context, msg)) != ERROR_SUCCESS) { return ret; } + *got_msg = false; + } else { + *type = msg->header.message_type; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } + + return ret; +} + +int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size) +{ + *type = 0; + *timestamp = 0; + *data = NULL; + *size = 0; + + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + for (;;) { + SrsCommonMessage* msg = NULL; - // for IDR frame, the frame is keyframe. - SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; - if (nut == SrsAvcNaluTypeIDR) { - frame_type = SrsVideoAvcFrameTypeKeyFrame; + // read from cache first. + if (!context->msgs.empty()) { + std::vector::iterator it = context->msgs.begin(); + msg = *it; + context->msgs.erase(it); } - std::string ibp; - if ((err = context->avc_raw.mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { + // read from protocol sdk. + if (!msg && (err = context->rtmp->recv_message(&msg)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; - char* flv = NULL; - int nb_flv = 0; - if ((err = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; + // no msg, try again. + if (!msg) { + continue; } - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); - } - - /** - * write the h264 sps/pps in context over RTMP. - */ - int srs_write_h264_sps_pps(Context* context, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + SrsAutoFree(SrsCommonMessage, msg); - // send when sps or pps changed. - if (!context->h264_sps_changed && !context->h264_pps_changed) { + // process the got packet, if nothing, try again. + bool got_msg; + if ((ret = srs_rtmp_go_packet(context, msg, type, timestamp, data, size, &got_msg)) != ERROR_SUCCESS) { return ret; } - // h264 raw to h264 packet. + // got expected message. + if (got_msg) { + break; + } + } + + return ret; +} + +int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsSharedPtrMessage* msg = NULL; + + if ((err = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + srs_assert(msg); + + // send out encoded msg. + if ((err = context->rtmp->send_and_free_message(msg, context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return ret; +} + +void srs_rtmp_free_packet(char* data) +{ + srs_freepa(data); +} + +srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size) +{ + srs_error_t err = srs_success; + + if (type != SRS_RTMP_TYPE_SCRIPT) { + return false; + } + + SrsBuffer stream(data, size); + + std::string name; + if ((err = srs_amf0_read_string(&stream, name)) != srs_success) { + srs_freep(err); + return false; + } + + if (name == SRS_CONSTS_RTMP_ON_METADATA) { + return true; + } + + if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { + return true; + } + + return false; +} + +/** + * directly write a audio frame. + */ +int srs_write_audio_raw_frame(Context* context, char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t timestamp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + char* data = NULL; + int size = 0; + if ((err = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); +} + +/** + * write aac frame in adts. + */ +int srs_write_aac_adts_frame(Context* context, SrsRawAacStreamCodec* codec, char* frame, int frame_size, uint32_t timestamp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // send out aac sequence header if not sent. + if (context->aac_specific_config.empty()) { std::string sh; - if ((err = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != srs_success) { + if ((err = context->aac_raw.mux_sequence_header(codec, sh)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } + context->aac_specific_config = sh; - // h264 packet to flv packet. - int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; - int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; - char* flv = NULL; - int nb_flv = 0; - if ((err = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); + codec->aac_packet_type = 0; + + if ((ret = srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) { return ret; } - - // reset sps and pps. - context->h264_sps_changed = false; - context->h264_pps_changed = false; - context->h264_sps_pps_sent = true; - - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); } - /** - * write h264 raw frame, maybe sps/pps/IPB-frame. - */ - int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // empty frame. - if (frame_size <= 0) { + codec->aac_packet_type = 1; + return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp); +} + +/** + * write aac frames in adts. + */ +int srs_write_aac_adts_frames(Context* context, char sound_format, char sound_rate, + char sound_size, char sound_type, char* frames, int frames_size, uint32_t timestamp +) { + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + SrsBuffer* stream = new SrsBuffer(frames, frames_size); + SrsAutoFree(SrsBuffer, stream); + + while (!stream->empty()) { + char* frame = NULL; + int frame_size = 0; + SrsRawAacStreamCodec codec; + if ((err = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - // for sps - if (context->avc_raw.is_sps(frame, frame_size)) { - std::string sps; - if ((err = context->avc_raw.sps_demux(frame, frame_size, sps)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (context->h264_sps == sps) { - return ERROR_H264_DUPLICATED_SPS; - } - context->h264_sps_changed = true; - context->h264_sps = sps; - - return ret; - } + // override by user specified. + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; - // for pps - if (context->avc_raw.is_pps(frame, frame_size)) { - std::string pps; - if ((err = context->avc_raw.pps_demux(frame, frame_size, pps)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (context->h264_pps == pps) { - return ERROR_H264_DUPLICATED_PPS; - } - context->h264_pps_changed = true; - context->h264_pps = pps; - + if ((ret = srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) { return ret; } - - // ignore others. - // 5bits, 7.3.1 NAL unit syntax, - // ISO_IEC_14496-10-AVC-2003.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD - SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); - if (nut != SrsAvcNaluTypeSPS && nut != SrsAvcNaluTypePPS - && nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR - && nut != SrsAvcNaluTypeAccessUnitDelimiter - ) { - return ret; + } + + return ret; +} + +/** + * write audio raw frame to SRS. + */ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, + char sound_size, char sound_type, char* frame, int frame_size, uint32_t timestamp +) { + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + srs_assert(context); + + if (sound_format == SrsAudioCodecIdAAC) { + // for aac, the frame must be ADTS format. + if (!srs_aac_is_adts(frame, frame_size)) { + return ERROR_AAC_REQUIRED_ADTS; } - // send pps+sps before ipb frames when sps/pps changed. - if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) { - return ret; - } + // for aac, demux the ADTS to RTMP format. + return srs_write_aac_adts_frames(context, sound_format, sound_rate, sound_size, sound_type, frame, frame_size, timestamp); + } else { + // use codec info for aac. + SrsRawAacStreamCodec codec; + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; + codec.aac_packet_type = 0; - // ibp frame. - return srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); + // for other data, directly write frame. + return srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp); } - /** - * write h264 multiple frames, in annexb format. - */ - int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(frames != NULL); - srs_assert(frames_size > 0); - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsBuffer* stream = new SrsBuffer(frames, frames_size); - SrsAutoFree(SrsBuffer, stream); - - // use the last error - // @see https://github.com/ossrs/srs/issues/203 - // @see https://github.com/ossrs/srs/issues/204 - int error_code_return = ret; - - // send each frame. - while (!stream->empty()) { - char* frame = NULL; - int frame_size = 0; - if ((err = context->avc_raw.annexb_demux(stream, &frame, &frame_size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // ignore invalid frame, - // atleast 1bytes for SPS to decode the type - if (frame_size <= 0) { - continue; - } - - // it may be return error, but we must process all packets. - if ((ret = srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) { - error_code_return = ret; - - // ignore known error, process all packets. - if (srs_h264_is_dvbsp_error(ret) - || srs_h264_is_duplicated_sps_error(ret) - || srs_h264_is_duplicated_pps_error(ret) - ) { - continue; - } - - return ret; - } - } - - return error_code_return; + return ret; +} + +/** + * whether aac raw data is in adts format, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + */ +srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) +{ + SrsBuffer stream(aac_raw_data, ac_raw_size); + return srs_aac_startswith_adts(&stream); +} + +/** + * parse the adts header to get the frame size. + */ +int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) +{ + int size = -1; + + if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { + return size; + } + + // adts always 7bytes. + if (ac_raw_size <= 7) { + return size; } - srs_bool srs_h264_is_dvbsp_error(int error_code) - { - return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; + // last 2bits + int16_t ch3 = aac_raw_data[3]; + // whole 8bits + int16_t ch4 = aac_raw_data[4]; + // first 3bits + int16_t ch5 = aac_raw_data[5]; + + size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); + + return size; +} + +/** + * write h264 IPB-frame. + */ +int srs_write_h264_ipb_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // when sps or pps not sent, ignore the packet. + // @see https://github.com/ossrs/srs/issues/203 + if (!context->h264_sps_pps_sent) { + return ERROR_H264_DROP_BEFORE_SPS_PPS; + } + + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 5: I Frame, 1: P/B Frame + // @remark we already group sps/pps to sequence header frame; + // for I/P NALU, we send them in isolate frame, each NALU in a frame; + // for other NALU, for example, AUD/SEI, we just ignore them, because + // AUD used in annexb to split frame, while SEI generally we can ignore it. + // TODO: maybe we should group all NALUs split by AUD to a frame. + SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); + if (nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR) { + return ret; } - srs_bool srs_h264_is_duplicated_sps_error(int error_code) - { - return error_code == ERROR_H264_DUPLICATED_SPS; + // for IDR frame, the frame is keyframe. + SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; + if (nut == SrsAvcNaluTypeIDR) { + frame_type = SrsVideoAvcFrameTypeKeyFrame; } - srs_bool srs_h264_is_duplicated_pps_error(int error_code) - { - return error_code == ERROR_H264_DUPLICATED_PPS; + std::string ibp; + if ((err = context->avc_raw.mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) - { - SrsBuffer stream(h264_raw_data, h264_raw_size); - return srs_avc_startswith_annexb(&stream, pnb_start_code); + int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; + char* flv = NULL; + int nb_flv = 0; + if ((err = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - struct Mp4Context - { - SrsFileReader reader; - SrsMp4Decoder dec; - }; + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); +} + +/** + * write the h264 sps/pps in context over RTMP. + */ +int srs_write_h264_sps_pps(Context* context, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // send when sps or pps changed. + if (!context->h264_sps_changed && !context->h264_pps_changed) { + return ret; + } - srs_mp4_t srs_mp4_open_read(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* mp4 = new Mp4Context(); - - if ((err = mp4->reader.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open MP4 file failed, ret=%d", ret); - - srs_freep(mp4); - return NULL; - } - - return mp4; + // h264 raw to h264 packet. + std::string sh; + if ((err = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - void srs_mp4_close(srs_mp4_t mp4) - { - Mp4Context* context = (Mp4Context*)mp4; - srs_freep(context); + // h264 packet to flv packet. + int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; + int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; + char* flv = NULL; + int nb_flv = 0; + if ((err = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - int srs_mp4_init_demuxer(srs_mp4_t mp4) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* context = (Mp4Context*)mp4; - - if ((err = context->dec.initialize(&context->reader)) != srs_success) { + // reset sps and pps. + context->h264_sps_changed = false; + context->h264_pps_changed = false; + context->h264_sps_pps_sent = true; + + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); +} + +/** + * write h264 raw frame, maybe sps/pps/IPB-frame. + */ +int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // empty frame. + if (frame_size <= 0) { + return ret; + } + + // for sps + if (context->avc_raw.is_sps(frame, frame_size)) { + std::string sps; + if ((err = context->avc_raw.sps_demux(frame, frame_size, sps)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } + if (context->h264_sps == sps) { + return ERROR_H264_DUPLICATED_SPS; + } + context->h264_sps_changed = true; + context->h264_sps = sps; + return ret; } - int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* s) - { - s->sample = NULL; - - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* context = (Mp4Context*)mp4; - SrsMp4Decoder* dec = &context->dec; - - SrsMp4HandlerType ht = SrsMp4HandlerTypeForbidden; - if ((err = dec->read_sample(&ht, &s->frame_type, &s->frame_trait, &s->dts, &s->pts, &s->sample, &s->nb_sample)) != srs_success) { + // for pps + if (context->avc_raw.is_pps(frame, frame_size)) { + std::string pps; + if ((err = context->avc_raw.pps_demux(frame, frame_size, pps)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - if (ht == SrsMp4HandlerTypeForbidden) { - return ERROR_MP4_ILLEGAL_HANDLER; + if (context->h264_pps == pps) { + return ERROR_H264_DUPLICATED_PPS; } - - if (ht == SrsMp4HandlerTypeSOUN) { - s->codec = (uint16_t)dec->acodec; - s->sample_rate = dec->sample_rate; - s->channels = dec->channels; - s->sound_bits = dec->sound_bits; - } else { - s->codec = (uint16_t)dec->vcodec; - } - s->handler_type = (uint32_t)ht; + context->h264_pps_changed = true; + context->h264_pps = pps; return ret; } - void srs_mp4_free_sample(srs_mp4_sample_t* s) - { - srs_freepa(s->sample); + // ignore others. + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD + SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); + if (nut != SrsAvcNaluTypeSPS && nut != SrsAvcNaluTypePPS + && nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR + && nut != SrsAvcNaluTypeAccessUnitDelimiter + ) { + return ret; } - int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* s) - { - if (s->handler_type == SrsMp4HandlerTypeSOUN) { - if (s->codec == (uint16_t)SrsAudioCodecIdAAC) { - return s->nb_sample + 2; - } - return s->nb_sample + 1; - } - - if (s->codec == (uint16_t)SrsVideoCodecIdAVC) { - return s->nb_sample + 5; - } - return s->nb_sample + 1; + // send pps+sps before ipb frames when sps/pps changed. + if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) { + return ret; } - int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* s, char* type, uint32_t* time, char* data, int32_t size) - { - int ret = ERROR_SUCCESS; + // ibp frame. + return srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); +} + +/** + * write h264 multiple frames, in annexb format. + */ +int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(frames != NULL); + srs_assert(frames_size > 0); + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsBuffer* stream = new SrsBuffer(frames, frames_size); + SrsAutoFree(SrsBuffer, stream); + + // use the last error + // @see https://github.com/ossrs/srs/issues/203 + // @see https://github.com/ossrs/srs/issues/204 + int error_code_return = ret; + + // send each frame. + while (!stream->empty()) { + char* frame = NULL; + int frame_size = 0; + if ((err = context->avc_raw.annexb_demux(stream, &frame, &frame_size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } - *time = s->dts; + // ignore invalid frame, + // atleast 1bytes for SPS to decode the type + if (frame_size <= 0) { + continue; + } - SrsBuffer p(data, size); - if (s->handler_type == SrsMp4HandlerTypeSOUN) { - *type = SRS_RTMP_TYPE_AUDIO; + // it may be return error, but we must process all packets. + if ((ret = srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) { + error_code_return = ret; - // E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3 - p.write_1bytes(uint8_t(s->codec << 4) | uint8_t(s->sample_rate << 2) | uint8_t(s->sound_bits << 1) | s->channels); - if (s->codec == SrsAudioCodecIdAAC) { - p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsAudioAacFrameTraitSequenceHeader? 0:1)); + // ignore known error, process all packets. + if (srs_h264_is_dvbsp_error(ret) + || srs_h264_is_duplicated_sps_error(ret) + || srs_h264_is_duplicated_pps_error(ret) + ) { + continue; } - p.write_bytes((char*)s->sample, s->nb_sample); return ret; } + } + + return error_code_return; +} + +srs_bool srs_h264_is_dvbsp_error(int error_code) +{ + return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; +} + +srs_bool srs_h264_is_duplicated_sps_error(int error_code) +{ + return error_code == ERROR_H264_DUPLICATED_SPS; +} + +srs_bool srs_h264_is_duplicated_pps_error(int error_code) +{ + return error_code == ERROR_H264_DUPLICATED_PPS; +} + +srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) +{ + SrsBuffer stream(h264_raw_data, h264_raw_size); + return srs_avc_startswith_annexb(&stream, pnb_start_code); +} + +struct Mp4Context +{ + SrsFileReader reader; + SrsMp4Decoder dec; +}; + +srs_mp4_t srs_mp4_open_read(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* mp4 = new Mp4Context(); + + if ((err = mp4->reader.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open MP4 file failed, ret=%d", ret); - // E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5 - p.write_1bytes(uint8_t(s->frame_type<<4) | uint8_t(s->codec)); - if (s->codec == SrsVideoCodecIdAVC) { - *type = SRS_RTMP_TYPE_VIDEO; - - p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsVideoAvcFrameTraitSequenceHeader? 0:1)); - // cts = pts - dts, where dts = flvheader->timestamp. - uint32_t cts = s->pts - s->dts; - p.write_3bytes(cts); - } - p.write_bytes((char*)s->sample, s->nb_sample); - + srs_freep(mp4); + return NULL; + } + + return mp4; +} + +void srs_mp4_close(srs_mp4_t mp4) +{ + Mp4Context* context = (Mp4Context*)mp4; + srs_freep(context); +} + +int srs_mp4_init_demuxer(srs_mp4_t mp4) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* context = (Mp4Context*)mp4; + + if ((err = context->dec.initialize(&context->reader)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - srs_bool srs_mp4_is_eof(int error_code) - { - return error_code == ERROR_SYSTEM_FILE_EOF; + return ret; +} + +int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* s) +{ + s->sample = NULL; + + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* context = (Mp4Context*)mp4; + SrsMp4Decoder* dec = &context->dec; + + SrsMp4HandlerType ht = SrsMp4HandlerTypeForbidden; + if ((err = dec->read_sample(&ht, &s->frame_type, &s->frame_trait, &s->dts, &s->pts, &s->sample, &s->nb_sample)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - struct FlvContext - { - SrsFileReader reader; - SrsFileWriter writer; - SrsFlvTransmuxer enc; - SrsFlvDecoder dec; - }; + if (ht == SrsMp4HandlerTypeForbidden) { + return ERROR_MP4_ILLEGAL_HANDLER; + } - srs_flv_t srs_flv_open_read(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* flv = new FlvContext(); - - if ((err = flv->reader.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open FLV file failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - if ((err = flv->dec.initialize(&flv->reader)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Initialize FLV demuxer failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - return flv; + if (ht == SrsMp4HandlerTypeSOUN) { + s->codec = (uint16_t)dec->acodec; + s->sample_rate = dec->sample_rate; + s->channels = dec->channels; + s->sound_bits = dec->sound_bits; + } else { + s->codec = (uint16_t)dec->vcodec; } + s->handler_type = (uint32_t)ht; - srs_flv_t srs_flv_open_write(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* flv = new FlvContext(); - - if ((err = flv->writer.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open FLV file failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - if ((err = flv->enc.initialize(&flv->writer)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Initilize FLV muxer failed, ret=%d", ret); - - srs_freep(flv); - return NULL; + return ret; +} + +void srs_mp4_free_sample(srs_mp4_sample_t* s) +{ + srs_freepa(s->sample); +} + +int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* s) +{ + if (s->handler_type == SrsMp4HandlerTypeSOUN) { + if (s->codec == (uint16_t)SrsAudioCodecIdAAC) { + return s->nb_sample + 2; } - - return flv; + return s->nb_sample + 1; } - void srs_flv_close(srs_flv_t flv) - { - FlvContext* context = (FlvContext*)flv; - srs_freep(context); + if (s->codec == (uint16_t)SrsVideoCodecIdAVC) { + return s->nb_sample + 5; } + return s->nb_sample + 1; +} + +int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* s, char* type, uint32_t* time, char* data, int32_t size) +{ + int ret = ERROR_SUCCESS; - int srs_flv_read_header(srs_flv_t flv, char header[9]) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->dec.read_header(header)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } + *time = s->dts; + + SrsBuffer p(data, size); + if (s->handler_type == SrsMp4HandlerTypeSOUN) { + *type = SRS_RTMP_TYPE_AUDIO; - char ts[4]; // tag size - if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; + // E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3 + p.write_1bytes(uint8_t(s->codec << 4) | uint8_t(s->sample_rate << 2) | uint8_t(s->sound_bits << 1) | s->channels); + if (s->codec == SrsAudioCodecIdAAC) { + p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsAudioAacFrameTraitSequenceHeader? 0:1)); } + p.write_bytes((char*)s->sample, s->nb_sample); return ret; } - int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } + // E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5 + p.write_1bytes(uint8_t(s->frame_type<<4) | uint8_t(s->codec)); + if (s->codec == SrsVideoCodecIdAVC) { + *type = SRS_RTMP_TYPE_VIDEO; - if ((err = context->dec.read_tag_header(ptype, pdata_size, ptime)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; + p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsVideoAvcFrameTraitSequenceHeader? 0:1)); + // cts = pts - dts, where dts = flvheader->timestamp. + uint32_t cts = s->pts - s->dts; + p.write_3bytes(cts); } + p.write_bytes((char*)s->sample, s->nb_sample); - int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; + return ret; +} + +srs_bool srs_mp4_is_eof(int error_code) +{ + return error_code == ERROR_SYSTEM_FILE_EOF; +} + +struct FlvContext +{ + SrsFileReader reader; + SrsFileWriter writer; + SrsFlvTransmuxer enc; + SrsFlvDecoder dec; +}; + +srs_flv_t srs_flv_open_read(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* flv = new FlvContext(); + + if ((err = flv->reader.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open FLV file failed, ret=%d", ret); - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } + srs_freep(flv); + return NULL; + } + + if ((err = flv->dec.initialize(&flv->reader)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Initialize FLV demuxer failed, ret=%d", ret); - if ((err = context->dec.read_tag_data(data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } + srs_freep(flv); + return NULL; + } + + return flv; +} + +srs_flv_t srs_flv_open_write(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* flv = new FlvContext(); + + if ((err = flv->writer.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open FLV file failed, ret=%d", ret); - char ts[4]; // tag size - if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } + srs_freep(flv); + return NULL; + } + + if ((err = flv->enc.initialize(&flv->writer)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Initilize FLV muxer failed, ret=%d", ret); + srs_freep(flv); + return NULL; + } + + return flv; +} + +void srs_flv_close(srs_flv_t flv) +{ + FlvContext* context = (FlvContext*)flv; + srs_freep(context); +} + +int srs_flv_read_header(srs_flv_t flv, char header[9]) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_header(header)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_flv_write_header(srs_flv_t flv, char header[9]) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->enc.write_header(header)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + char ts[4]; // tag size + if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if (type == SRS_RTMP_TYPE_AUDIO) { - if ((err = context->enc.write_audio(time, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } else if (type == SRS_RTMP_TYPE_VIDEO) { - if ((err = context->enc.write_video(time, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } else { - if ((err = context->enc.write_metadata(type, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } - + return ret; +} + +int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_tag_header(ptype, pdata_size, ptime)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_flv_size_tag(int data_size) - { - return SrsFlvTransmuxer::size_tag(data_size); + return ret; +} + +int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_tag_data(data, size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - int64_t srs_flv_tellg(srs_flv_t flv) - { - FlvContext* context = (FlvContext*)flv; - return context->reader.tellg(); + char ts[4]; // tag size + if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - void srs_flv_lseek(srs_flv_t flv, int64_t offset) - { - FlvContext* context = (FlvContext*)flv; - int64_t r0 = context->reader.seek2(offset); - srs_assert(r0 != -1); + return ret; +} + +int srs_flv_write_header(srs_flv_t flv, char header[9]) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; } - srs_bool srs_flv_is_eof(int error_code) - { - return error_code == ERROR_SYSTEM_FILE_EOF; + if ((err = context->enc.write_header(header)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - srs_bool srs_flv_is_sequence_header(char* data, int32_t size) - { - return SrsFlvVideo::sh(data, (int)size); - } + return ret; +} + +int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; - srs_bool srs_flv_is_keyframe(char* data, int32_t size) - { - return SrsFlvVideo::keyframe(data, (int)size); + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; } - srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) - { - srs_error_t err = srs_success; - - srs_amf0_t amf0 = NULL; - - SrsBuffer stream(data, size); - - SrsAmf0Any* any = NULL; - if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) { + if (type == SRS_RTMP_TYPE_AUDIO) { + if ((err = context->enc.write_audio(time, data, size)) != srs_success) { + ret = srs_error_code(err); srs_freep(err); - return amf0; + return ret; } - - stream.skip(-1 * stream.pos()); - if ((err = any->read(&stream)) != srs_success) { + } else if (type == SRS_RTMP_TYPE_VIDEO) { + if ((err = context->enc.write_video(time, data, size)) != srs_success) { + ret = srs_error_code(err); srs_freep(err); - srs_freep(any); - return amf0; + return ret; } - - if (nparsed) { - *nparsed = stream.pos(); + } else { + if ((err = context->enc.write_metadata(type, data, size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - amf0 = (srs_amf0_t)any; - - return amf0; } - srs_amf0_t srs_amf0_create_string(const char* value) - { - return SrsAmf0Any::str(value); - } + return ret; +} + +int srs_flv_size_tag(int data_size) +{ + return SrsFlvTransmuxer::size_tag(data_size); +} + +int64_t srs_flv_tellg(srs_flv_t flv) +{ + FlvContext* context = (FlvContext*)flv; + return context->reader.tellg(); +} + +void srs_flv_lseek(srs_flv_t flv, int64_t offset) +{ + FlvContext* context = (FlvContext*)flv; + int64_t r0 = context->reader.seek2(offset); + srs_assert(r0 != -1); +} + +srs_bool srs_flv_is_eof(int error_code) +{ + return error_code == ERROR_SYSTEM_FILE_EOF; +} + +srs_bool srs_flv_is_sequence_header(char* data, int32_t size) +{ + return SrsFlvVideo::sh(data, (int)size); +} + +srs_bool srs_flv_is_keyframe(char* data, int32_t size) +{ + return SrsFlvVideo::keyframe(data, (int)size); +} + +srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) +{ + srs_error_t err = srs_success; - srs_amf0_t srs_amf0_create_number(srs_amf0_number value) - { - return SrsAmf0Any::number(value); - } + srs_amf0_t amf0 = NULL; - srs_amf0_t srs_amf0_create_ecma_array() - { - return SrsAmf0Any::ecma_array(); - } + SrsBuffer stream(data, size); - srs_amf0_t srs_amf0_create_strict_array() - { - return SrsAmf0Any::strict_array(); + SrsAmf0Any* any = NULL; + if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) { + srs_freep(err); + return amf0; } - srs_amf0_t srs_amf0_create_object() - { - return SrsAmf0Any::object(); + stream.skip(-1 * stream.pos()); + if ((err = any->read(&stream)) != srs_success) { + srs_freep(err); + srs_freep(any); + return amf0; } - srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr) - { - srs_assert(srs_amf0_is_ecma_array(ecma_arr)); - - SrsAmf0EcmaArray* arr = (SrsAmf0EcmaArray*)ecma_arr; - SrsAmf0Object* obj = SrsAmf0Any::object(); - - for (int i = 0; i < arr->count(); i++) { - std::string key = arr->key_at(i); - SrsAmf0Any* value = arr->value_at(i); - obj->set(key, value->copy()); - } - - return obj; + if (nparsed) { + *nparsed = stream.pos(); } + amf0 = (srs_amf0_t)any; - void srs_amf0_free(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_freep(any); - } + 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); +} + +srs_amf0_t srs_amf0_create_ecma_array() +{ + return SrsAmf0Any::ecma_array(); +} + +srs_amf0_t srs_amf0_create_strict_array() +{ + return SrsAmf0Any::strict_array(); +} + +srs_amf0_t srs_amf0_create_object() +{ + return SrsAmf0Any::object(); +} + +srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr) +{ + srs_assert(srs_amf0_is_ecma_array(ecma_arr)); - int srs_amf0_size(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->total_size(); + SrsAmf0EcmaArray* arr = (SrsAmf0EcmaArray*)ecma_arr; + SrsAmf0Object* obj = SrsAmf0Any::object(); + + for (int i = 0; i < arr->count(); i++) { + std::string key = arr->key_at(i); + SrsAmf0Any* value = arr->value_at(i); + obj->set(key, value->copy()); } - int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - - SrsBuffer stream(data, size); - - if ((err = any->write(&stream)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + return obj; +} + +void srs_amf0_free(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_freep(any); +} + +int srs_amf0_size(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->total_size(); +} + +int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + + SrsBuffer stream(data, size); + + if ((err = any->write(&stream)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - srs_bool srs_amf0_is_string(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_string(); - } + return ret; +} + +srs_bool srs_amf0_is_string(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_string(); +} + +srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_boolean(); +} + +srs_bool srs_amf0_is_number(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_number(); +} + +srs_bool srs_amf0_is_null(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_null(); +} + +srs_bool srs_amf0_is_object(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_object(); +} + +srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_ecma_array(); +} + +srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_strict_array(); +} + +const char* srs_amf0_to_string(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_str_raw(); +} + +srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_boolean(); +} + +srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_number(); +} + +void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + any->set_number(value); +} + +int srs_amf0_object_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_boolean(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return obj->count(); +} + +const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); + + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return obj->key_raw_at(index); +} + +srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); + + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return (srs_amf0_t)obj->value_at(index); +} + +srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); + + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return (srs_amf0_t)obj->get_property(name); +} + +void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); + + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + any = (SrsAmf0Any*)value; + obj->set(name, any); +} + +void srs_amf0_object_clear(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); + + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + obj->clear(); +} + +int srs_amf0_ecma_array_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); + + SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; + return obj->count(); +} + +const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); + + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return obj->key_raw_at(index); +} + +srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); + + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return (srs_amf0_t)obj->value_at(index); +} + +srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); + + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return (srs_amf0_t)obj->get_property(name); +} + +void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); + + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + any = (SrsAmf0Any*)value; + obj->set(name, any); +} + +int srs_amf0_strict_array_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); + + SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0; + return obj->count(); +} + +srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); + + SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; + return (srs_amf0_t)obj->at(index); +} + +void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); - srs_bool srs_amf0_is_number(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_number(); + SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; + any = (SrsAmf0Any*)value; + obj->append(any); +} + +int64_t srs_utils_time_ms() +{ + return srs_update_system_time_ms(); +} + +int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + if (!context->rtmp) { + return 0; + } + return context->rtmp->get_send_bytes(); +} + +int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + if (!context->rtmp) { + return 0; } + return context->rtmp->get_recv_bytes(); +} + +int srs_utils_parse_timestamp( + uint32_t time, char type, char* data, int size, + uint32_t* ppts + ) { + int ret = ERROR_SUCCESS; - srs_bool srs_amf0_is_null(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_null(); + if (type != SRS_RTMP_TYPE_VIDEO) { + *ppts = time; + return ret; } - srs_bool srs_amf0_is_object(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_object(); + if (!SrsFlvVideo::h264(data, size)) { + return ERROR_FLV_INVALID_VIDEO_TAG; } - srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_ecma_array(); + if (SrsFlvVideo::sh(data, size)) { + *ppts = time; + return ret; } - srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_strict_array(); + // 1bytes, frame type and codec id. + // 1bytes, avc packet type. + // 3bytes, cts, composition time, + // pts = dts + cts, or + // cts = pts - dts. + if (size < 5) { + return ERROR_FLV_INVALID_VIDEO_TAG; } - const char* srs_amf0_to_string(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_str_raw(); + uint32_t cts = 0; + char* p = data + 2; + char* pp = (char*)&cts; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + *ppts = time + cts; + + return ret; +} + +srs_bool srs_utils_flv_tag_is_ok(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT; +} + +srs_bool srs_utils_flv_tag_is_audio(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO; +} + +srs_bool srs_utils_flv_tag_is_video(char type) +{ + return type == SRS_RTMP_TYPE_VIDEO; +} + +srs_bool srs_utils_flv_tag_is_av(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO; +} + +char srs_utils_flv_video_codec_id(char* data, int size) +{ + if (size < 1) { + return 0; } - srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_boolean(); + char codec_id = data[0]; + codec_id = codec_id & 0x0F; + + return codec_id; +} + +char srs_utils_flv_video_avc_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; } - srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_number(); + if (!SrsFlvVideo::h264(data, size)) { + return -1; } - void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - any->set_number(value); + uint8_t avc_packet_type = data[1]; + + if (avc_packet_type > 2) { + return -1; } - int srs_amf0_object_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return obj->count(); + return avc_packet_type; +} + +char srs_utils_flv_video_frame_type(char* data, int size) +{ + if (size < 1) { + return -1; } - const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return obj->key_raw_at(index); + if (!SrsFlvVideo::h264(data, size)) { + return -1; } - srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return (srs_amf0_t)obj->value_at(index); + uint8_t frame_type = data[0]; + frame_type = (frame_type >> 4) & 0x0f; + if (frame_type < 1 || frame_type > 5) { + return -1; } - srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return (srs_amf0_t)obj->get_property(name); + return frame_type; +} + +char srs_utils_flv_audio_sound_format(char* data, int size) +{ + if (size < 1) { + return -1; } - void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - any = (SrsAmf0Any*)value; - obj->set(name, any); + uint8_t sound_format = data[0]; + sound_format = (sound_format >> 4) & 0x0f; + if (sound_format > 15 || sound_format == 12 || sound_format == 13) { + return -1; } - void srs_amf0_object_clear(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - obj->clear(); + return sound_format; +} + +char srs_utils_flv_audio_sound_rate(char* data, int size) +{ + if (size < 1) { + return -1; } - int srs_amf0_ecma_array_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; - return obj->count(); + uint8_t sound_rate = data[0]; + sound_rate = (sound_rate >> 2) & 0x03; + + return sound_rate; +} + +char srs_utils_flv_audio_sound_size(char* data, int size) +{ + if (size < 1) { + return -1; } - const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return obj->key_raw_at(index); + uint8_t sound_size = data[0]; + sound_size = (sound_size >> 1) & 0x01; + + return sound_size; +} + +char srs_utils_flv_audio_sound_type(char* data, int size) +{ + if (size < 1) { + return -1; } - srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return (srs_amf0_t)obj->value_at(index); - } + uint8_t sound_type = data[0]; + sound_type = sound_type & 0x01; - srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return (srs_amf0_t)obj->get_property(name); + return sound_type; +} + +char srs_utils_flv_audio_aac_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; } - void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - any = (SrsAmf0Any*)value; - obj->set(name, any); + if (srs_utils_flv_audio_sound_format(data, size) != 10) { + return -1; } - int srs_amf0_strict_array_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0; - return obj->count(); + uint8_t aac_packet_type = data[1]; + if (aac_packet_type > 1) { + return -1; } - srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; - return (srs_amf0_t)obj->at(index); + return aac_packet_type; +} + +char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) +{ + if (!amf0) { + return NULL; } - void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; - any = (SrsAmf0Any*)value; - obj->append(any); - } + SrsAmf0Any* any = (SrsAmf0Any*)amf0; - int64_t srs_utils_time_ms() - { - return srs_update_system_time_ms(); - } + return any->human_print(pdata, psize); +} + +const char* srs_human_flv_tag_type2string(char type) +{ + static const char* audio = "Audio"; + static const char* video = "Video"; + static const char* data = "Data"; + static const char* unknown = "Unknown"; - int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) - { - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - if (!context->rtmp) { - return 0; - } - return context->rtmp->get_send_bytes(); + switch (type) { + case SRS_RTMP_TYPE_AUDIO: return audio; + case SRS_RTMP_TYPE_VIDEO: return video; + case SRS_RTMP_TYPE_SCRIPT: return data; + default: return unknown; } - int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp) - { - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - if (!context->rtmp) { - return 0; - } - return context->rtmp->get_recv_bytes(); - } + return unknown; +} + +const char* srs_human_flv_video_codec_id2string(char codec_id) +{ + static const char* h263 = "H.263"; + static const char* screen = "Screen"; + static const char* vp6 = "VP6"; + static const char* vp6_alpha = "VP6Alpha"; + static const char* screen2 = "Screen2"; + static const char* h264 = "H.264"; + static const char* unknown = "Unknown"; + + switch (codec_id) { + case 2: return h263; + case 3: return screen; + case 4: return vp6; + case 5: return vp6_alpha; + case 6: return screen2; + case 7: return h264; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) +{ + static const char* sps_pps = "SH"; + static const char* nalu = "Nalu"; + static const char* sps_pps_end = "SpsPpsEnd"; + static const char* unknown = "Unknown"; - int srs_utils_parse_timestamp( - uint32_t time, char type, char* data, int size, - uint32_t* ppts - ) { - int ret = ERROR_SUCCESS; - - if (type != SRS_RTMP_TYPE_VIDEO) { - *ppts = time; - return ret; - } - - if (!SrsFlvVideo::h264(data, size)) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - if (SrsFlvVideo::sh(data, size)) { - *ppts = time; - return ret; - } - - // 1bytes, frame type and codec id. - // 1bytes, avc packet type. - // 3bytes, cts, composition time, - // pts = dts + cts, or - // cts = pts - dts. - if (size < 5) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - uint32_t cts = 0; - char* p = data + 2; - char* pp = (char*)&cts; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - *ppts = time + cts; - - return ret; + switch (avc_packet_type) { + case 0: return sps_pps; + case 1: return nalu; + case 2: return sps_pps_end; + default: return unknown; } - srs_bool srs_utils_flv_tag_is_ok(char type) - { - return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT; - } + return unknown; +} + +const char* srs_human_flv_video_frame_type2string(char frame_type) +{ + static const char* keyframe = "I"; + static const char* interframe = "P/B"; + static const char* disposable_interframe = "DI"; + static const char* generated_keyframe = "GI"; + static const char* video_infoframe = "VI"; + static const char* unknown = "Unknown"; + + switch (frame_type) { + case 1: return keyframe; + case 2: return interframe; + case 3: return disposable_interframe; + case 4: return generated_keyframe; + case 5: return video_infoframe; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_format2string(char sound_format) +{ + static const char* linear_pcm = "LinearPCM"; + static const char* ad_pcm = "ADPCM"; + static const char* mp3 = "MP3"; + static const char* linear_pcm_le = "LinearPCMLe"; + static const char* nellymoser_16khz = "NellymoserKHz16"; + static const char* nellymoser_8khz = "NellymoserKHz8"; + static const char* nellymoser = "Nellymoser"; + static const char* g711_a_pcm = "G711APCM"; + static const char* g711_mu_pcm = "G711MuPCM"; + static const char* reserved = "Reserved"; + static const char* aac = "AAC"; + static const char* speex = "Speex"; + static const char* mp3_8khz = "MP3KHz8"; + static const char* device_specific = "DeviceSpecific"; + static const char* unknown = "Unknown"; + + switch (sound_format) { + case 0: return linear_pcm; + case 1: return ad_pcm; + case 2: return mp3; + case 3: return linear_pcm_le; + case 4: return nellymoser_16khz; + case 5: return nellymoser_8khz; + case 6: return nellymoser; + case 7: return g711_a_pcm; + case 8: return g711_mu_pcm; + case 9: return reserved; + case 10: return aac; + case 11: return speex; + case 14: return mp3_8khz; + case 15: return device_specific; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_rate2string(char sound_rate) +{ + static const char* khz_5_5 = "5.5KHz"; + static const char* khz_11 = "11KHz"; + static const char* khz_22 = "22KHz"; + static const char* khz_44 = "44KHz"; + static const char* unknown = "Unknown"; - srs_bool srs_utils_flv_tag_is_audio(char type) - { - return type == SRS_RTMP_TYPE_AUDIO; + switch (sound_rate) { + case 0: return khz_5_5; + case 1: return khz_11; + case 2: return khz_22; + case 3: return khz_44; + default: return unknown; } - srs_bool srs_utils_flv_tag_is_video(char type) - { - return type == SRS_RTMP_TYPE_VIDEO; - } + return unknown; +} + +const char* srs_human_flv_audio_sound_size2string(char sound_size) +{ + static const char* bit_8 = "8bit"; + static const char* bit_16 = "16bit"; + static const char* unknown = "Unknown"; - srs_bool srs_utils_flv_tag_is_av(char type) - { - return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO; + switch (sound_size) { + case 0: return bit_8; + case 1: return bit_16; + default: return unknown; } - char srs_utils_flv_video_codec_id(char* data, int size) - { - if (size < 1) { - return 0; - } - - char codec_id = data[0]; - codec_id = codec_id & 0x0F; - - return codec_id; - } + return unknown; +} + +const char* srs_human_flv_audio_sound_type2string(char sound_type) +{ + static const char* mono = "Mono"; + static const char* stereo = "Stereo"; + static const char* unknown = "Unknown"; - char srs_utils_flv_video_avc_packet_type(char* data, int size) - { - if (size < 2) { - return -1; - } - - if (!SrsFlvVideo::h264(data, size)) { - return -1; - } - - uint8_t avc_packet_type = data[1]; - - if (avc_packet_type > 2) { - return -1; - } - - return avc_packet_type; + switch (sound_type) { + case 0: return mono; + case 1: return stereo; + default: return unknown; } - char srs_utils_flv_video_frame_type(char* data, int size) - { - if (size < 1) { - return -1; - } - - if (!SrsFlvVideo::h264(data, size)) { - return -1; - } - - uint8_t frame_type = data[0]; - frame_type = (frame_type >> 4) & 0x0f; - if (frame_type < 1 || frame_type > 5) { - return -1; - } - - return frame_type; - } + return unknown; +} + +const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type) +{ + static const char* sps_pps = "SH"; + static const char* raw = "Raw"; + static const char* unknown = "Unknown"; - char srs_utils_flv_audio_sound_format(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_format = data[0]; - sound_format = (sound_format >> 4) & 0x0f; - if (sound_format > 15 || sound_format == 12 || sound_format == 13) { - return -1; - } - - return sound_format; + switch (aac_packet_type) { + case 0: return sps_pps; + case 1: return raw; + default: return unknown; } - char srs_utils_flv_audio_sound_rate(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_rate = data[0]; - sound_rate = (sound_rate >> 2) & 0x03; - - return sound_rate; - } + return unknown; +} + +int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; - char srs_utils_flv_audio_sound_size(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_size = data[0]; - sound_size = (sound_size >> 1) & 0x01; - - return sound_size; - } + // Initialize to empty NULL terminated string. + buffer[0] = 0; - char srs_utils_flv_audio_sound_type(char* data, int size) - { - if (size < 1) { - return -1; + char sbytes[40]; + if (true) { + int nb = srs_min(8, size); + int p = 0; + for (int i = 0; i < nb; i++) { + p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); } - - uint8_t sound_type = data[0]; - sound_type = sound_type & 0x01; - - return sound_type; } - char srs_utils_flv_audio_aac_packet_type(char* data, int size) - { - if (size < 2) { - return -1; - } - - if (srs_utils_flv_audio_sound_format(data, size) != 10) { - return -1; - } - - uint8_t aac_packet_type = data[1]; - if (aac_packet_type > 1) { - return -1; - } - - return aac_packet_type; + uint32_t pts; + if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { + snprintf(buffer, nb_buffer, "Rtmp packet type=%s, dts=%d, size=%d, DecodeError, (%s), ret=%d", + srs_human_flv_tag_type2string(type), timestamp, size, sbytes, ret); + return ret; } - char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) - { - if (!amf0) { - return NULL; + if (type == SRS_RTMP_TYPE_VIDEO) { + snprintf(buffer, nb_buffer, "Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s), (%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + snprintf(buffer, nb_buffer, "Audio packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s,%s,%s), (%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), + srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), + srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), + srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), + srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + int nb = snprintf(buffer, nb_buffer, "Data packet type=%s, time=%d, size=%d, (%s)", + srs_human_flv_tag_type2string(type), timestamp, size, sbytes); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; + srs_freepa(amf0_str); } - - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - - return any->human_print(pdata, psize); + buffer[nb] = 0; + } else { + snprintf(buffer, nb_buffer, "Rtmp packet type=%#x, dts=%d, pts=%d, size=%d, (%s)", + type, timestamp, pts, size, sbytes); } - const char* srs_human_flv_tag_type2string(char type) - { - static const char* audio = "Audio"; - static const char* video = "Video"; - static const char* data = "Data"; - static const char* unknown = "Unknown"; - - switch (type) { - case SRS_RTMP_TYPE_AUDIO: return audio; - case SRS_RTMP_TYPE_VIDEO: return video; - case SRS_RTMP_TYPE_SCRIPT: return data; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_video_codec_id2string(char codec_id) - { - static const char* h263 = "H.263"; - static const char* screen = "Screen"; - static const char* vp6 = "VP6"; - static const char* vp6_alpha = "VP6Alpha"; - static const char* screen2 = "Screen2"; - static const char* h264 = "H.264"; - static const char* unknown = "Unknown"; - - switch (codec_id) { - case 2: return h263; - case 3: return screen; - case 4: return vp6; - case 5: return vp6_alpha; - case 6: return screen2; - case 7: return h264; - default: return unknown; - } - - return unknown; - } + return ret; +} + +int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets) +{ + int ret = ERROR_SUCCESS; - const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) - { - static const char* sps_pps = "SH"; - static const char* nalu = "Nalu"; - static const char* sps_pps_end = "SpsPpsEnd"; - static const char* unknown = "Unknown"; - - switch (avc_packet_type) { - case 0: return sps_pps; - case 1: return nalu; - case 2: return sps_pps_end; - default: return unknown; - } - - return unknown; - } + // Initialize to empty NULL terminated string. + buffer[0] = 0; - const char* srs_human_flv_video_frame_type2string(char frame_type) - { - static const char* keyframe = "I"; - static const char* interframe = "P/B"; - static const char* disposable_interframe = "DI"; - static const char* generated_keyframe = "GI"; - static const char* video_infoframe = "VI"; - static const char* unknown = "Unknown"; - - switch (frame_type) { - case 1: return keyframe; - case 2: return interframe; - case 3: return disposable_interframe; - case 4: return generated_keyframe; - case 5: return video_infoframe; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_sound_format2string(char sound_format) - { - static const char* linear_pcm = "LinearPCM"; - static const char* ad_pcm = "ADPCM"; - static const char* mp3 = "MP3"; - static const char* linear_pcm_le = "LinearPCMLe"; - static const char* nellymoser_16khz = "NellymoserKHz16"; - static const char* nellymoser_8khz = "NellymoserKHz8"; - static const char* nellymoser = "Nellymoser"; - static const char* g711_a_pcm = "G711APCM"; - static const char* g711_mu_pcm = "G711MuPCM"; - static const char* reserved = "Reserved"; - static const char* aac = "AAC"; - static const char* speex = "Speex"; - static const char* mp3_8khz = "MP3KHz8"; - static const char* device_specific = "DeviceSpecific"; - static const char* unknown = "Unknown"; - - switch (sound_format) { - case 0: return linear_pcm; - case 1: return ad_pcm; - case 2: return mp3; - case 3: return linear_pcm_le; - case 4: return nellymoser_16khz; - case 5: return nellymoser_8khz; - case 6: return nellymoser; - case 7: return g711_a_pcm; - case 8: return g711_mu_pcm; - case 9: return reserved; - case 10: return aac; - case 11: return speex; - case 14: return mp3_8khz; - case 15: return device_specific; - default: return unknown; - } - - return unknown; + // packets interval in milliseconds. + double pi = 0; + if (pre_now > starttime && nb_packets > 0) { + pi = (pre_now - starttime) / (double)nb_packets; } - const char* srs_human_flv_audio_sound_rate2string(char sound_rate) - { - static const char* khz_5_5 = "5.5KHz"; - static const char* khz_11 = "11KHz"; - static const char* khz_22 = "22KHz"; - static const char* khz_44 = "44KHz"; - static const char* unknown = "Unknown"; - - switch (sound_rate) { - case 0: return khz_5_5; - case 1: return khz_11; - case 2: return khz_22; - case 3: return khz_44; - default: return unknown; - } - - return unknown; + // global fps(video and audio mixed fps). + double gfps = 0; + if (pi > 0) { + gfps = 1000 / pi; } - const char* srs_human_flv_audio_sound_size2string(char sound_size) - { - static const char* bit_8 = "8bit"; - static const char* bit_16 = "16bit"; - static const char* unknown = "Unknown"; - - switch (sound_size) { - case 0: return bit_8; - case 1: return bit_16; - default: return unknown; - } - - return unknown; + int diff = 0; + if (pre_timestamp > 0) { + diff = (int)timestamp - (int)pre_timestamp; } - const char* srs_human_flv_audio_sound_type2string(char sound_type) - { - static const char* mono = "Mono"; - static const char* stereo = "Stereo"; - static const char* unknown = "Unknown"; - - switch (sound_type) { - case 0: return mono; - case 1: return stereo; - default: return unknown; - } - - return unknown; + int ndiff = 0; + if (pre_now > 0) { + ndiff = (int)(srs_utils_time_ms() - pre_now); } - const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type) - { - static const char* sps_pps = "SH"; - static const char* raw = "Raw"; - static const char* unknown = "Unknown"; - - switch (aac_packet_type) { - case 0: return sps_pps; - case 1: return raw; - default: return unknown; + char sbytes[40]; + if (true) { + int nb = srs_min(8, size); + int p = 0; + for (int i = 0; i < nb; i++) { + p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); } - - return unknown; } - int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size) - { - int ret = ERROR_SUCCESS; - - // Initialize to empty NULL terminated string. - buffer[0] = 0; - - char sbytes[40]; - if (true) { - int nb = srs_min(8, size); - int p = 0; - for (int i = 0; i < nb; i++) { - p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); - } - } - - uint32_t pts; - if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { - snprintf(buffer, nb_buffer, "Rtmp packet type=%s, dts=%d, size=%d, DecodeError, (%s), ret=%d", - srs_human_flv_tag_type2string(type), timestamp, size, sbytes, ret); - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - snprintf(buffer, nb_buffer, "Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s), (%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - snprintf(buffer, nb_buffer, "Audio packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s,%s,%s), (%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), - srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), - srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), - srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), - srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - int nb = snprintf(buffer, nb_buffer, "Data packet type=%s, time=%d, size=%d, (%s)", - srs_human_flv_tag_type2string(type), timestamp, size, sbytes); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; - srs_freepa(amf0_str); - } - buffer[nb] = 0; - } else { - snprintf(buffer, nb_buffer, "Rtmp packet type=%#x, dts=%d, pts=%d, size=%d, (%s)", - type, timestamp, pts, size, sbytes); - } - + uint32_t pts; + if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { + snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, ndiff=%d, diff=%d, size=%d, DecodeError, (%s), ret=%d", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes, ret); return ret; } - int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets) - { - int ret = ERROR_SUCCESS; - - // Initialize to empty NULL terminated string. - buffer[0] = 0; - - // packets interval in milliseconds. - double pi = 0; - if (pre_now > starttime && nb_packets > 0) { - pi = (pre_now - starttime) / (double)nb_packets; - } - - // global fps(video and audio mixed fps). - double gfps = 0; - if (pi > 0) { - gfps = 1000 / pi; - } - - int diff = 0; - if (pre_timestamp > 0) { - diff = (int)timestamp - (int)pre_timestamp; - } - - int ndiff = 0; - if (pre_now > 0) { - ndiff = (int)(srs_utils_time_ms() - pre_now); - } - - char sbytes[40]; - if (true) { - int nb = srs_min(8, size); - int p = 0; - for (int i = 0; i < nb; i++) { - p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); - } - } - - uint32_t pts; - if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { - snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, ndiff=%d, diff=%d, size=%d, DecodeError, (%s), ret=%d", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes, ret); - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - snprintf(buffer, nb_buffer, "Video packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s), (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, - srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - snprintf(buffer, nb_buffer, "Audio packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s,%s,%s), (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, - srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), - srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), - srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), - srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), - srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - int nb = snprintf(buffer, nb_buffer, "Data packet id=%" PRId64 "/%.1f/%.1f, type=%s, time=%d, ndiff=%d, diff=%d, size=%d, (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; - srs_freepa(amf0_str); + if (type == SRS_RTMP_TYPE_VIDEO) { + snprintf(buffer, nb_buffer, "Video packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s), (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, + srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + snprintf(buffer, nb_buffer, "Audio packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s,%s,%s), (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, + srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), + srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), + srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), + srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), + srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + int nb = snprintf(buffer, nb_buffer, "Data packet id=%" PRId64 "/%.1f/%.1f, type=%s, time=%d, ndiff=%d, diff=%d, size=%d, (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; } - buffer[nb] = 0; - } else { - snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%#x, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, (%s)", - nb_packets, pi, gfps, type, timestamp, pts, ndiff, diff, size, sbytes); + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; + srs_freepa(amf0_str); } - - return ret; + buffer[nb] = 0; + } else { + snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%#x, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, (%s)", + nb_packets, pi, gfps, type, timestamp, pts, ndiff, diff, size, sbytes); } - const char* srs_human_format_time() - { - struct timeval tv; - static char buf[24]; - - memset(buf, 0, sizeof(buf)); - - // clock time - if (gettimeofday(&tv, NULL) == -1) { - return buf; - } - - // to calendar time - struct tm* tm; - if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { - return buf; - } - - snprintf(buf, sizeof(buf), - "%d-%02d-%02d %02d:%02d:%02d.%03d", - 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - (int)(tv.tv_usec / 1000)); - - // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 - buf[sizeof(buf) - 1] = 0; - + return ret; +} + +const char* srs_human_format_time() +{ + struct timeval tv; + static char buf[24]; + + memset(buf, 0, sizeof(buf)); + + // clock time + if (gettimeofday(&tv, NULL) == -1) { + return buf; + } + + // to calendar time + struct tm* tm; + if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { return buf; } + snprintf(buf, sizeof(buf), + "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)(tv.tv_usec / 1000)); + + // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 + buf[sizeof(buf) - 1] = 0; + return buf; +} + + #ifdef SRS_HIJACK_IO - srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) - { - if (!rtmp) { - return NULL; - } - - Context* context = (Context*)rtmp; - if (!context->skt) { - return NULL; - } - - return context->skt->hijack_io(); +srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) +{ + if (!rtmp) { + return NULL; } -#endif - srs_rtmp_t srs_rtmp_create2(const char* url) - { - Context* context = new Context(); - - // use url as tcUrl. - context->url = url; - // auto append stream. - context->url += "/livestream"; - - // create socket - srs_freep(context->skt); - context->skt = new SimpleSocketStream(); - - int ret = ERROR_SUCCESS; - if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { - srs_human_error("Create socket failed, ret=%d", ret); - - // free the context and return NULL - srs_freep(context); - return NULL; - } - - return context; - } - - int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128],char srs_server[128], char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid) - { - srs_server_ip[0] = 0; - srs_server[0] = 0; - srs_primary[0] = 0; - srs_authors[0] = 0; - srs_version[0] = 0; - *srs_id = 0; - *srs_pid = 0; - - int ret = ERROR_SUCCESS; - - if ((ret = srs_rtmp_connect_app(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - SrsServerInfo* si = &context->si; - - snprintf(srs_server_ip, 128, "%s", si->ip.c_str()); - snprintf(srs_server, 128, "%s", si->sig.c_str()); - snprintf(srs_version, 32, "%d.%d.%d.%d", si->major, si->minor, si->revision, si->build); - - return ret; + Context* context = (Context*)rtmp; + if (!context->skt) { + return NULL; } - int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size) - { - return srs_human_print_rtmp_packet3(type, timestamp, data, size, 0, 0); - } + return context->skt->hijack_io(); +} +#endif + +srs_rtmp_t srs_rtmp_create2(const char* url) +{ + Context* context = new Context(); - int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp) - { - return srs_human_print_rtmp_packet3(type, timestamp, data, size, pre_timestamp, 0); - } + // use url as tcUrl. + context->url = url; + // auto append stream. + context->url += "/livestream"; + + // create socket + srs_freep(context->skt); + context->skt = new SimpleSocketStream(); - int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now) - { - return srs_human_print_rtmp_packet4(type, timestamp, data, size, pre_timestamp, pre_now, 0, 0); + int ret = ERROR_SUCCESS; + if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { + srs_human_error("Create socket failed, ret=%d", ret); + + // free the context and return NULL + srs_freep(context); + return NULL; } - int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, - int64_t starttime, int64_t nb_packets - ) { - char buffer[1024]; - int ret = srs_human_format_rtmp_packet2(buffer, sizeof(buffer), type, timestamp, data, size, pre_timestamp, pre_now, starttime, nb_packets); - srs_human_trace("%s", buffer); + return context; +} + +int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128],char srs_server[128], char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid) +{ + srs_server_ip[0] = 0; + srs_server[0] = 0; + srs_primary[0] = 0; + srs_authors[0] = 0; + srs_version[0] = 0; + *srs_id = 0; + *srs_pid = 0; + + int ret = ERROR_SUCCESS; + + if ((ret = srs_rtmp_connect_app(rtmp)) != ERROR_SUCCESS) { return ret; } + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + SrsServerInfo* si = &context->si; + + snprintf(srs_server_ip, 128, "%s", si->ip.c_str()); + snprintf(srs_server, 128, "%s", si->sig.c_str()); + snprintf(srs_version, 32, "%d.%d.%d.%d", si->major, si->minor, si->revision, si->build); + + return ret; +} + +int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size) +{ + return srs_human_print_rtmp_packet3(type, timestamp, data, size, 0, 0); +} + +int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp) +{ + return srs_human_print_rtmp_packet3(type, timestamp, data, size, pre_timestamp, 0); +} + +int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now) +{ + return srs_human_print_rtmp_packet4(type, timestamp, data, size, pre_timestamp, pre_now, 0, 0); +} + +int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, + int64_t starttime, int64_t nb_packets +) { + char buffer[1024]; + int ret = srs_human_format_rtmp_packet2(buffer, sizeof(buffer), type, timestamp, data, size, pre_timestamp, pre_now, starttime, nb_packets); + srs_human_trace("%s", buffer); + return ret; +} + #ifdef __cplusplus } #endif diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 416216391..d4191b2b9 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -77,972 +77,972 @@ pid_t getpid(void); extern "C"{ #endif - /** - * The schema of RTMP url, the following are legal urls: - * srs_url_schema_normal: rtmp://vhost:port/app/stream - * srs_url_schema_via : rtmp://ip:port/vhost/app/stream - * srs_url_schema_vis : rtmp://ip:port/app/stream?vhost=xxx - * srs_url_schema_vis2 : rtmp://ip:port/app/stream?domain=xxx - */ - enum srs_url_schema - { - // Forbidden. - srs_url_schema_forbidden = 0, - // Normal RTMP URL, the vhost put in host field, using DNS to resolve the server ip. - // For example, rtmp://vhost:port/app/stream - srs_url_schema_normal, - // VIA(vhost in app), the vhost put in app field. - // For example, rtmp://ip:port/vhost/app/stream - srs_url_schema_via, - // VIS(vhost in stream), the vhost put in query string, keyword use vhost=xxx. - // For example, rtmp://ip:port/app/stream?vhost=xxx - srs_url_schema_vis, - // VIS, keyword use domain=xxx. - // For example, rtmp://ip:port/app/stream?domain=xxx - srs_url_schema_vis2 - }; - - // typedefs - typedef int srs_bool; - - /************************************************************* - ************************************************************** - * srs-librtmp version - ************************************************************** - *************************************************************/ - extern int srs_version_major(); - extern int srs_version_minor(); - extern int srs_version_revision(); - - /************************************************************* - ************************************************************** - * RTMP protocol context - ************************************************************** - *************************************************************/ - // the RTMP handler. - typedef void* srs_rtmp_t; - typedef void* srs_amf0_t; - - /** - * Create a RTMP handler. - * @param url The RTMP url, for example, rtmp://localhost/live/livestream - * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. - * @remark default schema to srs_url_schema_normal, use srs_rtmp_set_schema to change it. - * - * @return a rtmp handler, or NULL if error occured. - */ - extern srs_rtmp_t srs_rtmp_create(const char* url); - /** - * set socket timeout - * @param recv_timeout_ms the timeout for receiving messages in ms. - * @param send_timeout_ms the timeout for sending message in ms. - * @remark user can set timeout once srs_rtmp_create/srs_rtmp_create2, - * or before srs_rtmp_handshake or srs_rtmp_dns_resolve to connect to server. - * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms); - /** - * close and destroy the rtmp stack. - * @remark, user should never use the rtmp again. - */ - extern void srs_rtmp_destroy(srs_rtmp_t rtmp); - - /************************************************************* - ************************************************************** - * RTMP protocol stack - ************************************************************** - *************************************************************/ - /** - * connect and handshake with server - * category: publish/play - * previous: rtmp-create - * next: connect-app - * - * @return 0, success; otherswise, failed. - */ - /** - * simple handshake specifies in rtmp 1.0, - * not depends on ssl. - */ - /** - * srs_rtmp_handshake equals to invoke: - * srs_rtmp_dns_resolve() - * srs_rtmp_connect_server() - * srs_rtmp_do_simple_handshake() - * user can use these functions if needed. - */ - extern int srs_rtmp_handshake(srs_rtmp_t rtmp); - // parse uri, create socket, resolve host - extern int srs_rtmp_dns_resolve(srs_rtmp_t rtmp); - // connect socket to server - 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); - - /** - * Set the schema of URL when connect to tcUrl by srs_rtmp_connect_app. - * @param schema, The schema of URL, @see srs_url_schema. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema); - - /** - * Connect to RTMP tcUrl(Vhost/App), similar to flash AS3 NetConnection.connect(tcUrl). - * @remark When connected to server, user can retrieve informations from RTMP handler, - * for example, use srs_rtmp_get_server_id to get server ip/pid/cid. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); - - /** - * Retrieve server ip from RTMP handler. - * @param ip A NULL terminated string specifies the server ip. - * @param pid An int specifies the PID of server. -1 is no PID information. - * @param cid An int specifies the CID of connection. -1 is no CID information. - * @remark For SRS, ip/pid/cid is the UUID of a client. For other server, these values maybe unknown. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid); - - /** - * Retrieve server signature from RTMP handler. - * @param sig A NULL terminated string specifies the server signature. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig); - - /** - * Retrieve server version from RTMP handler, which in major.minor.revision.build format. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build); - - /** - * play a live/vod stream. - * category: play - * previous: connect-app - * next: destroy - * @return 0, success; otherwise, failed. - */ - extern int srs_rtmp_play_stream(srs_rtmp_t rtmp); - - /** - * publish a live stream. - * category: publish - * previous: connect-app - * next: destroy - * @return 0, success; otherwise, failed. - */ - extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp); - - /** - * do bandwidth check with srs server. - * - * bandwidth info: - * @param start_time, output the start time, in ms. - * @param end_time, output the end time, in ms. - * @param play_kbps, output the play/download kbps. - * @param publish_kbps, output the publish/upload kbps. - * @param play_bytes, output the play/download bytes. - * @param publish_bytes, output the publish/upload bytes. - * @param play_duration, output the play/download test duration, in ms. - * @param publish_duration, output the publish/upload test duration, in ms. - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, - int64_t* start_time, int64_t* end_time, - int* play_kbps, int* publish_kbps, - int* play_bytes, int* publish_bytes, - int* play_duration, int* publish_duration); - - /** - * E.4.1 FLV Tag, page 75 - */ - // 8 = audio +/** + * The schema of RTMP url, the following are legal urls: + * srs_url_schema_normal: rtmp://vhost:port/app/stream + * srs_url_schema_via : rtmp://ip:port/vhost/app/stream + * srs_url_schema_vis : rtmp://ip:port/app/stream?vhost=xxx + * srs_url_schema_vis2 : rtmp://ip:port/app/stream?domain=xxx + */ +enum srs_url_schema +{ + // Forbidden. + srs_url_schema_forbidden = 0, + // Normal RTMP URL, the vhost put in host field, using DNS to resolve the server ip. + // For example, rtmp://vhost:port/app/stream + srs_url_schema_normal, + // VIA(vhost in app), the vhost put in app field. + // For example, rtmp://ip:port/vhost/app/stream + srs_url_schema_via, + // VIS(vhost in stream), the vhost put in query string, keyword use vhost=xxx. + // For example, rtmp://ip:port/app/stream?vhost=xxx + srs_url_schema_vis, + // VIS, keyword use domain=xxx. + // For example, rtmp://ip:port/app/stream?domain=xxx + srs_url_schema_vis2 +}; + +// typedefs +typedef int srs_bool; + +/************************************************************* + ************************************************************** + * srs-librtmp version + ************************************************************** + *************************************************************/ +extern int srs_version_major(); +extern int srs_version_minor(); +extern int srs_version_revision(); + +/************************************************************* + ************************************************************** + * RTMP protocol context + ************************************************************** + *************************************************************/ +// the RTMP handler. +typedef void* srs_rtmp_t; +typedef void* srs_amf0_t; + +/** + * Create a RTMP handler. + * @param url The RTMP url, for example, rtmp://localhost/live/livestream + * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. + * @remark default schema to srs_url_schema_normal, use srs_rtmp_set_schema to change it. + * + * @return a rtmp handler, or NULL if error occured. + */ +extern srs_rtmp_t srs_rtmp_create(const char* url); +/** + * set socket timeout + * @param recv_timeout_ms the timeout for receiving messages in ms. + * @param send_timeout_ms the timeout for sending message in ms. + * @remark user can set timeout once srs_rtmp_create/srs_rtmp_create2, + * or before srs_rtmp_handshake or srs_rtmp_dns_resolve to connect to server. + * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms); +/** + * close and destroy the rtmp stack. + * @remark, user should never use the rtmp again. + */ +extern void srs_rtmp_destroy(srs_rtmp_t rtmp); + +/************************************************************* + ************************************************************** + * RTMP protocol stack + ************************************************************** + *************************************************************/ +/** + * connect and handshake with server + * category: publish/play + * previous: rtmp-create + * next: connect-app + * + * @return 0, success; otherswise, failed. + */ +/** + * simple handshake specifies in rtmp 1.0, + * not depends on ssl. + */ +/** + * srs_rtmp_handshake equals to invoke: + * srs_rtmp_dns_resolve() + * srs_rtmp_connect_server() + * srs_rtmp_do_simple_handshake() + * user can use these functions if needed. + */ +extern int srs_rtmp_handshake(srs_rtmp_t rtmp); +// parse uri, create socket, resolve host +extern int srs_rtmp_dns_resolve(srs_rtmp_t rtmp); +// connect socket to server +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); + +/** + * Set the schema of URL when connect to tcUrl by srs_rtmp_connect_app. + * @param schema, The schema of URL, @see srs_url_schema. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema); + +/** + * Connect to RTMP tcUrl(Vhost/App), similar to flash AS3 NetConnection.connect(tcUrl). + * @remark When connected to server, user can retrieve informations from RTMP handler, + * for example, use srs_rtmp_get_server_id to get server ip/pid/cid. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); + +/** + * Retrieve server ip from RTMP handler. + * @param ip A NULL terminated string specifies the server ip. + * @param pid An int specifies the PID of server. -1 is no PID information. + * @param cid An int specifies the CID of connection. -1 is no CID information. + * @remark For SRS, ip/pid/cid is the UUID of a client. For other server, these values maybe unknown. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid); + +/** + * Retrieve server signature from RTMP handler. + * @param sig A NULL terminated string specifies the server signature. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig); + +/** + * Retrieve server version from RTMP handler, which in major.minor.revision.build format. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build); + +/** + * play a live/vod stream. + * category: play + * previous: connect-app + * next: destroy + * @return 0, success; otherwise, failed. + */ +extern int srs_rtmp_play_stream(srs_rtmp_t rtmp); + +/** + * publish a live stream. + * category: publish + * previous: connect-app + * next: destroy + * @return 0, success; otherwise, failed. + */ +extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp); + +/** + * do bandwidth check with srs server. + * + * bandwidth info: + * @param start_time, output the start time, in ms. + * @param end_time, output the end time, in ms. + * @param play_kbps, output the play/download kbps. + * @param publish_kbps, output the publish/upload kbps. + * @param play_bytes, output the play/download bytes. + * @param publish_bytes, output the publish/upload bytes. + * @param play_duration, output the play/download test duration, in ms. + * @param publish_duration, output the publish/upload test duration, in ms. + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, + int64_t* start_time, int64_t* end_time, + int* play_kbps, int* publish_kbps, + int* play_bytes, int* publish_bytes, + int* play_duration, int* publish_duration); + +/** + * E.4.1 FLV Tag, page 75 + */ +// 8 = audio #define SRS_RTMP_TYPE_AUDIO 8 - // 9 = video +// 9 = video #define SRS_RTMP_TYPE_VIDEO 9 - // 18 = script data +// 18 = script data #define SRS_RTMP_TYPE_SCRIPT 18 - /** - * read a audio/video/script-data packet from rtmp stream. - * @param type, output the packet type, macros: - * SRS_RTMP_TYPE_AUDIO, FlvTagAudio - * SRS_RTMP_TYPE_VIDEO, FlvTagVideo - * SRS_RTMP_TYPE_SCRIPT, FlvTagScript - * otherswise, invalid type. - * @param timestamp, in ms, overflow in 50days - * @param data, the packet data, according to type: - * FlvTagAudio, @see "E.4.2.1 AUDIODATA" - * FlvTagVideo, @see "E.4.3.1 VIDEODATA" - * FlvTagScript, @see "E.4.4.1 SCRIPTDATA" - * User can free the packet by srs_rtmp_free_packet. - * @param size, size of packet. - * @return the error code. 0 for success; otherwise, error. - * - * @remark: for read, user must free the data. - * @remark: for write, user should never free the data, even if error. - * @example /trunk/research/librtmp/srs_play.c - * @example /trunk/research/librtmp/srs_publish.c - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size); - // @param data User should never free it anymore. - extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size); - - /** - * Free the packet allocated by srs_rtmp_read_packet. - */ - extern void srs_rtmp_free_packet(char* data); - - /** - * whether type is script data and the data is onMetaData. - */ - extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); - - /************************************************************* - ************************************************************** - * audio raw codec - ************************************************************** - *************************************************************/ - /** - * write an audio raw frame to srs. - * not similar to h.264 video, the audio never aggregated, always - * encoded one frame by one, so this api is used to write a frame. - * - * @param sound_format Format of SoundData. The following values are defined: - * 0 = Linear PCM, platform endian - * 1 = ADPCM - * 2 = MP3 - * 3 = Linear PCM, little endian - * 4 = Nellymoser 16 kHz mono - * 5 = Nellymoser 8 kHz mono - * 6 = Nellymoser - * 7 = G.711 A-law logarithmic PCM - * 8 = G.711 mu-law logarithmic PCM - * 9 = reserved - * 10 = AAC - * 11 = Speex - * 14 = MP3 8 kHz - * 15 = Device-specific sound - * Formats 7, 8, 14, and 15 are reserved. - * AAC is supported in Flash Player 9,0,115,0 and higher. - * Speex is supported in Flash Player 10 and higher. - * @param sound_rate Sampling rate. The following values are defined: - * 0 = 5.5 kHz - * 1 = 11 kHz - * 2 = 22 kHz - * 3 = 44 kHz - * @param sound_size Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 0 = 8-bit samples - * 1 = 16-bit samples - * @param sound_type Mono or stereo sound - * 0 = Mono sound - * 1 = Stereo sound - * @param timestamp The timestamp of audio. - * - * @example /trunk/research/librtmp/srs_aac_raw_publish.c - * @example /trunk/research/librtmp/srs_audio_raw_publish.c - * - * @remark for aac, the frame must be in ADTS format. - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS - * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 23, 1.5.1.1 Audio object type - * - * @see https://github.com/ossrs/srs/issues/212 - * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf - * - * @return 0, success; otherswise, failed. - */ - extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, - char sound_format, char sound_rate, char sound_size, char sound_type, - char* frame, int frame_size, uint32_t timestamp); - - /** - * whether aac raw data is in adts format, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - * @param aac_raw_data the input aac raw data, a encoded aac frame data. - * @param ac_raw_size the size of aac raw data. - * - * @reamrk used to check whether current frame is in adts format. - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS - * @example /trunk/research/librtmp/srs_aac_raw_publish.c - * - * @return 0 false; otherwise, true. - */ - extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); - - /** - * parse the adts header to get the frame size, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - * @param aac_raw_data the input aac raw data, a encoded aac frame data. - * @param ac_raw_size the size of aac raw data. - * - * @return failed when <=0 failed; otherwise, ok. - */ - extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); - - /************************************************************* - ************************************************************** - * h264 raw codec - ************************************************************** - *************************************************************/ - /** - * write h.264 raw frame over RTMP to rtmp server. - * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. - * frames can be one or more than one frame, - * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, - * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) - * about annexb, @see ISO_IEC_14496-10-AVC-2003.pdf, page 211. - * @param frames_size the size of h264 raw data. - * assert frames_size > 0, at least has 1 bytes header. - * @param dts the dts of h.264 raw data. - * @param pts the pts of h.264 raw data. - * - * @remark, user should free the frames. - * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. - * @remark, cts = pts - dts - * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * @see https://github.com/ossrs/srs/issues/66 - * - * @return 0, success; otherswise, failed. - * for dvbsp error, @see srs_h264_is_dvbsp_error(). - * for duplictated sps error, @see srs_h264_is_duplicated_sps_error(). - * for duplictated pps error, @see srs_h264_is_duplicated_pps_error(). - */ - /** - For the example file: - http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw - The data sequence is: - // SPS - 000000016742802995A014016E40 - // PPS - 0000000168CE3880 - // IFrame - 0000000165B8041014C038008B0D0D3A071..... - // PFrame - 0000000141E02041F8CDDC562BBDEFAD2F..... - User can send the SPS+PPS, then each frame: - // SPS+PPS - srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts) - // IFrame - srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) - // PFrame - srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) - User also can send one by one: - // SPS - srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts) - // PPS - srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts) - // IFrame - srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) - // PFrame - srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) - */ - extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts); - /** - * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. - * - * @see https://github.com/ossrs/srs/issues/203 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * @remark why drop video? - * some encoder, for example, ipcamera, will send sps/pps before each IFrame, - * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), - * this will cause SRS server to disable HLS. - */ - extern srs_bool srs_h264_is_dvbsp_error(int error_code); - /** - * whether error_code is duplicated sps error. - * - * @see https://github.com/ossrs/srs/issues/204 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - */ - extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); - /** - * whether error_code is duplicated pps error. - * - * @see https://github.com/ossrs/srs/issues/204 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - */ - extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); - /** - * whether h264 raw data starts with the annexb, - * which bytes sequence matches N[00] 00 00 01, where N>=0. - * @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. - * @paam h264_raw_size the size of h264 raw data. - * @param pnb_start_code output the size of start code, must >=3. - * NULL to ignore. - * - * @reamrk used to check whether current frame is in annexb format. - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * - * @return 0 false; otherwise, true. - */ - extern srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code); - - /************************************************************* - ************************************************************* - * MP4 muxer and demuxer. - * @example /trunk/research/librtmp/srs_ingest_mp4.c - ************************************************************* - *************************************************************/ - typedef void* srs_mp4_t; - // The sample struct of mp4. - typedef struct { - // The handler type, it's SrsMp4HandlerType. - uint32_t handler_type; - - // The dts in milliseconds. - uint32_t dts; - // The codec id. - // video: SrsVideoCodecId. - // audio: SrsAudioCodecId. - uint16_t codec; - // The frame trait, some characteristic: - // video: SrsVideoAvcFrameTrait. - // audio: SrsAudioAacFrameTrait. - uint16_t frame_trait; - - // The video pts in milliseconds. Ignore for audio. - uint32_t pts; - // The video frame type, it's SrsVideoAvcFrameType. - uint16_t frame_type; - - // The audio sample rate, it's SrsAudioSampleRate. - uint8_t sample_rate; - // The audio sound bits, it's SrsAudioSampleBits. - uint8_t sound_bits; - // The audio sound type, it's SrsAudioChannels. - uint8_t channels; - - // The size of sample payload in bytes. - uint32_t nb_sample; - // The output sample data, user must free it by srs_mp4_free_sample. - uint8_t* sample; - } srs_mp4_sample_t; - /** - * Open mp4 file for muxer(write) or demuxer(read). - * @return A MP4 demuxer, NULL if failed. - */ - extern srs_mp4_t srs_mp4_open_read(const char* file); - /** - * Close the MP4 demuxer. - */ - extern void srs_mp4_close(srs_mp4_t mp4); - /** - * Initialize mp4 demuxer in non-seek mode. - * @remark Only support non-seek mode, that is fmp4 or moov before mdata. - * For the live streaming, we must feed stream frame by frame. - */ - extern int srs_mp4_init_demuxer(srs_mp4_t mp4); - /** - * Read a sample form mp4. - * @remark User can use srs_mp4_sample_to_flv_tag to convert mp4 sampel to flv tag. - * Use the srs_mp4_to_flv_tag_size to calc the flv tag data size to alloc. - */ - extern int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* sample); - /** - * Free the allocated mp4 sample. - */ - extern void srs_mp4_free_sample(srs_mp4_sample_t* sample); - /** - * Calc the size of flv tag, for the mp4 sample to convert to. - */ - extern int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* sample); - /** - * Covert mp4 sample to flv tag. - */ - extern int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* sample, char* type, uint32_t* time, char* data, int32_t size); - /* error code */ - /* whether the error code indicates EOF */ - extern srs_bool srs_mp4_is_eof(int error_code); - - /************************************************************* - ************************************************************** - * flv codec - * @example /trunk/research/librtmp/srs_flv_injecter.c - * @example /trunk/research/librtmp/srs_flv_parser.c - * @example /trunk/research/librtmp/srs_ingest_flv.c - * @example /trunk/research/librtmp/srs_ingest_rtmp.c - ************************************************************** - *************************************************************/ - typedef void* srs_flv_t; - /** - * Open FLV file in demux mode. - * @return A FLV demuxer, NULL if failed. - */ - extern srs_flv_t srs_flv_open_read(const char* file); - /** - * Open FlV file in mux mode. - * @return A FLV muxer, NULL if failed. - */ - extern srs_flv_t srs_flv_open_write(const char* file); - /** - * Close the FLV demuxer or muxer. - */ - extern void srs_flv_close(srs_flv_t flv); - /** - * read the flv header. 9bytes header. - * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. - * 3bytes, signature, "FLV", - * 1bytes, version, 0x01, - * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. - * 4bytes, dataoffset, 0x09, The length of this header in bytes - * - * @return 0, success; otherswise, failed. - * @remark, drop the 4bytes zero previous tag size. - */ - extern int srs_flv_read_header(srs_flv_t flv, char header[9]); - /** - * read the flv tag header, 1bytes tag, 3bytes data_size, - * 4bytes time, 3bytes stream id. - * @param ptype, output the type of tag, macros: - * SRS_RTMP_TYPE_AUDIO, FlvTagAudio - * SRS_RTMP_TYPE_VIDEO, FlvTagVideo - * SRS_RTMP_TYPE_SCRIPT, FlvTagScript - * @param pdata_size, output the size of tag data. - * @param ptime, output the time of tag, the dts in ms. - * - * @return 0, success; otherswise, failed. - * @remark, user must ensure the next is a tag, srs never check it. - */ - extern int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime); - /** - * read the tag data. drop the 4bytes previous tag size - * @param data, the data to read, user alloc and free it. - * @param size, the size of data to read, get by srs_flv_read_tag_header(). - * @remark, srs will ignore and drop the 4bytes previous tag size. - */ - extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); - /** - * write the flv header. 9bytes header. - * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. - * 3bytes, signature, "FLV", - * 1bytes, version, 0x01, - * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. - * 4bytes, dataoffset, 0x09, The length of this header in bytes - * - * @return 0, success; otherswise, failed. - * @remark, auto write the 4bytes zero previous tag size. - */ - extern int srs_flv_write_header(srs_flv_t flv, char header[9]); - /** - * write the flv tag to file. - * - * @return 0, success; otherswise, failed. - * @remark, auto write the 4bytes zero previous tag size. - */ - /* write flv tag to file, auto write the 4bytes previous tag size */ - extern int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); - /** - * get the tag size, for flv injecter to adjust offset, - * size = tag_header(11B) + data_size + previous_tag(4B) - * @return the size of tag. - */ - extern int srs_flv_size_tag(int data_size); - /* file stream */ - /* file stream tellg to get offset */ - extern int64_t srs_flv_tellg(srs_flv_t flv); - /* seek file stream, offset is form the start of file */ - extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); - /* error code */ - /* whether the error code indicates EOF */ - extern srs_bool srs_flv_is_eof(int error_code); - /* media codec */ - /** - * whether the video body is sequence header - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_data(). - */ - extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); - /** - * whether the video body is keyframe - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_data(). - */ - extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); - - /************************************************************* - ************************************************************** - * amf0 codec - * @example /trunk/research/librtmp/srs_ingest_flv.c - * @example /trunk/research/librtmp/srs_ingest_rtmp.c - ************************************************************** - *************************************************************/ - /* the output handler. */ - typedef double srs_amf0_number; - /** - * parse amf0 from data. - * @param nparsed, the parsed size, NULL to ignore. - * @return the parsed amf0 object. NULL for error. - * @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(); - extern srs_amf0_t srs_amf0_create_object(); - extern srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr); - extern void srs_amf0_free(srs_amf0_t amf0); - /* size and to bytes */ - extern int srs_amf0_size(srs_amf0_t amf0); - extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); - /* type detecter */ - extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); - /* value converter */ - extern const char* srs_amf0_to_string(srs_amf0_t amf0); - extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); - extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); - /* value setter */ - extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); - /* object value converter */ - extern int srs_amf0_object_property_count(srs_amf0_t amf0); - extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); - extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); - extern void srs_amf0_object_clear(srs_amf0_t amf0); - /* ecma array value converter */ - extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); - extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); - extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); - /* strict array value converter */ - extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); - extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); - extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); - - /************************************************************* - ************************************************************** - * utilities - ************************************************************** - *************************************************************/ - /** - * get the current system time in ms. - * use gettimeofday() to get system time. - */ - extern int64_t srs_utils_time_ms(); - - /** - * get the send bytes. - */ - extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp); - - /** - * get the recv bytes. - */ - extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp); - - /** - * parse the dts and pts by time in header and data in tag, - * or to parse the RTMP packet by srs_rtmp_read_packet(). - * - * @param time, the timestamp of tag, read by srs_flv_read_tag_header(). - * @param type, the type of tag, read by srs_flv_read_tag_header(). - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_header(). - * @param ppts, output the pts in ms, - * - * @return 0, success; otherswise, failed. - * @remark, the dts always equals to @param time. - * @remark, the pts=dts for audio or data. - * @remark, video only support h.264. - */ - extern int srs_utils_parse_timestamp(uint32_t time, char type, char* data, int size, uint32_t* ppts); - - /** - * whether the flv tag specified by param type is ok. - * @return true when tag is video/audio/script-data; otherwise, false. - */ - extern srs_bool srs_utils_flv_tag_is_ok(char type); - extern srs_bool srs_utils_flv_tag_is_audio(char type); - extern srs_bool srs_utils_flv_tag_is_video(char type); - extern srs_bool srs_utils_flv_tag_is_av(char type); - - /** - * get the CodecID of video tag. - * Codec Identifier. The following values are defined: - * 2 = Sorenson H.263 - * 3 = Screen video - * 4 = On2 VP6 - * 5 = On2 VP6 with alpha channel - * 6 = Screen video version 2 - * 7 = AVC - * @return the code id. 0 for error. - */ - extern char srs_utils_flv_video_codec_id(char* data, int size); - - /** - * get the AVCPacketType of video tag. - * The following values are defined: - * 0 = AVC sequence header - * 1 = AVC NALU - * 2 = AVC end of sequence (lower level NALU sequence ender is - * not required or supported) - * @return the avc packet type. -1(0xff) for error. - */ - extern char srs_utils_flv_video_avc_packet_type(char* data, int size); - - /** - * get the FrameType of video tag. - * Type of video frame. The following values are defined: - * 1 = key frame (for AVC, a seekable frame) - * 2 = inter frame (for AVC, a non-seekable frame) - * 3 = disposable inter frame (H.263 only) - * 4 = generated key frame (reserved for server use only) - * 5 = video info/command frame - * @return the frame type. 0 for error. - */ - extern char srs_utils_flv_video_frame_type(char* data, int size); - - /** - * get the SoundFormat of audio tag. - * Format of SoundData. The following values are defined: - * 0 = Linear PCM, platform endian - * 1 = ADPCM - * 2 = MP3 - * 3 = Linear PCM, little endian - * 4 = Nellymoser 16 kHz mono - * 5 = Nellymoser 8 kHz mono - * 6 = Nellymoser - * 7 = G.711 A-law logarithmic PCM - * 8 = G.711 mu-law logarithmic PCM - * 9 = reserved - * 10 = AAC - * 11 = Speex - * 14 = MP3 8 kHz - * 15 = Device-specific sound - * Formats 7, 8, 14, and 15 are reserved. - * AAC is supported in Flash Player 9,0,115,0 and higher. - * Speex is supported in Flash Player 10 and higher. - * @return the sound format. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_format(char* data, int size); - - /** - * get the SoundRate of audio tag. - * Sampling rate. The following values are defined: - * 0 = 5.5 kHz - * 1 = 11 kHz - * 2 = 22 kHz - * 3 = 44 kHz - * @return the sound rate. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_rate(char* data, int size); - - /** - * get the SoundSize of audio tag. - * Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 0 = 8-bit samples - * 1 = 16-bit samples - * @return the sound size. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_size(char* data, int size); - - /** - * get the SoundType of audio tag. - * Mono or stereo sound - * 0 = Mono sound - * 1 = Stereo sound - * @return the sound type. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_type(char* data, int size); - - /** - * get the AACPacketType of audio tag. - * The following values are defined: - * 0 = AAC sequence header - * 1 = AAC raw - * @return the aac packet type. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_aac_packet_type(char* data, int size); - - /************************************************************* - ************************************************************** - * human readable print. - ************************************************************** - *************************************************************/ - /** - * human readable print - * @param pdata, output the heap data, NULL to ignore. - * user must use srs_amf0_free_bytes to free it. - * @return return the *pdata for print. NULL to ignore. - */ - extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); - /** - * convert the flv tag type to string. - * SRS_RTMP_TYPE_AUDIO to "Audio" - * SRS_RTMP_TYPE_VIDEO to "Video" - * SRS_RTMP_TYPE_SCRIPT to "Data" - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_tag_type2string(char type); - - /** - * get the codec id string. - * H.263 = Sorenson H.263 - * Screen = Screen video - * VP6 = On2 VP6 - * VP6Alpha = On2 VP6 with alpha channel - * Screen2 = Screen video version 2 - * H.264 = AVC - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_codec_id2string(char codec_id); - - /** - * get the avc packet type string. - * SH = AVC sequence header - * Nalu = AVC NALU - * SpsPpsEnd = AVC end of sequence - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); - - /** - * get the frame type string. - * I = key frame (for AVC, a seekable frame) - * P/B = inter frame (for AVC, a non-seekable frame) - * DI = disposable inter frame (H.263 only) - * GI = generated key frame (reserved for server use only) - * VI = video info/command frame - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_frame_type2string(char frame_type); - - /** - * get the SoundFormat string. - * Format of SoundData. The following values are defined: - * LinearPCM = Linear PCM, platform endian - * ADPCM = ADPCM - * MP3 = MP3 - * LinearPCMLe = Linear PCM, little endian - * NellymoserKHz16 = Nellymoser 16 kHz mono - * NellymoserKHz8 = Nellymoser 8 kHz mono - * Nellymoser = Nellymoser - * G711APCM = G.711 A-law logarithmic PCM - * G711MuPCM = G.711 mu-law logarithmic PCM - * Reserved = reserved - * AAC = AAC - * Speex = Speex - * MP3KHz8 = MP3 8 kHz - * DeviceSpecific = Device-specific sound - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_format2string(char sound_format); - - /** - * get the SoundRate of audio tag. - * Sampling rate. The following values are defined: - * 5.5KHz = 5.5 kHz - * 11KHz = 11 kHz - * 22KHz = 22 kHz - * 44KHz = 44 kHz - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate); - - /** - * get the SoundSize of audio tag. - * Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 8bit = 8-bit samples - * 16bit = 16-bit samples - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_size2string(char sound_size); - - /** - * get the SoundType of audio tag. - * Mono or stereo sound - * Mono = Mono sound - * Stereo = Stereo sound - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_type2string(char sound_type); +/** + * read a audio/video/script-data packet from rtmp stream. + * @param type, output the packet type, macros: + * SRS_RTMP_TYPE_AUDIO, FlvTagAudio + * SRS_RTMP_TYPE_VIDEO, FlvTagVideo + * SRS_RTMP_TYPE_SCRIPT, FlvTagScript + * otherswise, invalid type. + * @param timestamp, in ms, overflow in 50days + * @param data, the packet data, according to type: + * FlvTagAudio, @see "E.4.2.1 AUDIODATA" + * FlvTagVideo, @see "E.4.3.1 VIDEODATA" + * FlvTagScript, @see "E.4.4.1 SCRIPTDATA" + * User can free the packet by srs_rtmp_free_packet. + * @param size, size of packet. + * @return the error code. 0 for success; otherwise, error. + * + * @remark: for read, user must free the data. + * @remark: for write, user should never free the data, even if error. + * @example /trunk/research/librtmp/srs_play.c + * @example /trunk/research/librtmp/srs_publish.c + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size); +// @param data User should never free it anymore. +extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size); + +/** + * Free the packet allocated by srs_rtmp_read_packet. + */ +extern void srs_rtmp_free_packet(char* data); + +/** + * whether type is script data and the data is onMetaData. + */ +extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); + +/************************************************************* + ************************************************************** + * audio raw codec + ************************************************************** + *************************************************************/ +/** + * write an audio raw frame to srs. + * not similar to h.264 video, the audio never aggregated, always + * encoded one frame by one, so this api is used to write a frame. + * + * @param sound_format Format of SoundData. The following values are defined: + * 0 = Linear PCM, platform endian + * 1 = ADPCM + * 2 = MP3 + * 3 = Linear PCM, little endian + * 4 = Nellymoser 16 kHz mono + * 5 = Nellymoser 8 kHz mono + * 6 = Nellymoser + * 7 = G.711 A-law logarithmic PCM + * 8 = G.711 mu-law logarithmic PCM + * 9 = reserved + * 10 = AAC + * 11 = Speex + * 14 = MP3 8 kHz + * 15 = Device-specific sound + * Formats 7, 8, 14, and 15 are reserved. + * AAC is supported in Flash Player 9,0,115,0 and higher. + * Speex is supported in Flash Player 10 and higher. + * @param sound_rate Sampling rate. The following values are defined: + * 0 = 5.5 kHz + * 1 = 11 kHz + * 2 = 22 kHz + * 3 = 44 kHz + * @param sound_size Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 0 = 8-bit samples + * 1 = 16-bit samples + * @param sound_type Mono or stereo sound + * 0 = Mono sound + * 1 = Stereo sound + * @param timestamp The timestamp of audio. + * + * @example /trunk/research/librtmp/srs_aac_raw_publish.c + * @example /trunk/research/librtmp/srs_audio_raw_publish.c + * + * @remark for aac, the frame must be in ADTS format. + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS + * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 23, 1.5.1.1 Audio object type + * + * @see https://github.com/ossrs/srs/issues/212 + * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf + * + * @return 0, success; otherswise, failed. + */ +extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char* frame, int frame_size, uint32_t timestamp); + +/** + * whether aac raw data is in adts format, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + * @param aac_raw_data the input aac raw data, a encoded aac frame data. + * @param ac_raw_size the size of aac raw data. + * + * @reamrk used to check whether current frame is in adts format. + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS + * @example /trunk/research/librtmp/srs_aac_raw_publish.c + * + * @return 0 false; otherwise, true. + */ +extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); + +/** + * parse the adts header to get the frame size, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + * @param aac_raw_data the input aac raw data, a encoded aac frame data. + * @param ac_raw_size the size of aac raw data. + * + * @return failed when <=0 failed; otherwise, ok. + */ +extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); + +/************************************************************* + ************************************************************** + * h264 raw codec + ************************************************************** + *************************************************************/ +/** + * write h.264 raw frame over RTMP to rtmp server. + * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. + * frames can be one or more than one frame, + * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, + * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) + * about annexb, @see ISO_IEC_14496-10-AVC-2003.pdf, page 211. + * @param frames_size the size of h264 raw data. + * assert frames_size > 0, at least has 1 bytes header. + * @param dts the dts of h.264 raw data. + * @param pts the pts of h.264 raw data. + * + * @remark, user should free the frames. + * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. + * @remark, cts = pts - dts + * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * @see https://github.com/ossrs/srs/issues/66 + * + * @return 0, success; otherswise, failed. + * for dvbsp error, @see srs_h264_is_dvbsp_error(). + * for duplictated sps error, @see srs_h264_is_duplicated_sps_error(). + * for duplictated pps error, @see srs_h264_is_duplicated_pps_error(). + */ +/** + For the example file: + http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw + The data sequence is: + // SPS + 000000016742802995A014016E40 + // PPS + 0000000168CE3880 + // IFrame + 0000000165B8041014C038008B0D0D3A071..... + // PFrame + 0000000141E02041F8CDDC562BBDEFAD2F..... + User can send the SPS+PPS, then each frame: + // SPS+PPS + srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts) + // IFrame + srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) + User also can send one by one: + // SPS + srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts) + // PPS + srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts) + // IFrame + srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) + */ +extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts); +/** + * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. + * + * @see https://github.com/ossrs/srs/issues/203 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * @remark why drop video? + * some encoder, for example, ipcamera, will send sps/pps before each IFrame, + * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), + * this will cause SRS server to disable HLS. + */ +extern srs_bool srs_h264_is_dvbsp_error(int error_code); +/** + * whether error_code is duplicated sps error. + * + * @see https://github.com/ossrs/srs/issues/204 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + */ +extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); +/** + * whether error_code is duplicated pps error. + * + * @see https://github.com/ossrs/srs/issues/204 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + */ +extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); +/** + * whether h264 raw data starts with the annexb, + * which bytes sequence matches N[00] 00 00 01, where N>=0. + * @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. + * @paam h264_raw_size the size of h264 raw data. + * @param pnb_start_code output the size of start code, must >=3. + * NULL to ignore. + * + * @reamrk used to check whether current frame is in annexb format. + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * + * @return 0 false; otherwise, true. + */ +extern srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code); + +/************************************************************* + ************************************************************* + * MP4 muxer and demuxer. + * @example /trunk/research/librtmp/srs_ingest_mp4.c + ************************************************************* + *************************************************************/ +typedef void* srs_mp4_t; +// The sample struct of mp4. +typedef struct { + // The handler type, it's SrsMp4HandlerType. + uint32_t handler_type; - /** - * get the AACPacketType of audio tag. - * The following values are defined: - * SH = AAC sequence header - * Raw = AAC raw - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type); + // The dts in milliseconds. + uint32_t dts; + // The codec id. + // video: SrsVideoCodecId. + // audio: SrsAudioCodecId. + uint16_t codec; + // The frame trait, some characteristic: + // video: SrsVideoAvcFrameTrait. + // audio: SrsAudioAacFrameTrait. + uint16_t frame_trait; - /** - * Format the RTMP packet to human readable buffer. - * @return Whether parse RTMP packet ok. 0, success; otherwise, failed. - */ - extern int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size); - extern int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, - uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); + // The video pts in milliseconds. Ignore for audio. + uint32_t pts; + // The video frame type, it's SrsVideoAvcFrameType. + uint16_t frame_type; - /** - * Format current time to human readable string. - * @return A NULL terminated string. - */ - extern const char* srs_human_format_time(); + // The audio sample rate, it's SrsAudioSampleRate. + uint8_t sample_rate; + // The audio sound bits, it's SrsAudioSampleBits. + uint8_t sound_bits; + // The audio sound type, it's SrsAudioChannels. + uint8_t channels; + // The size of sample payload in bytes. + uint32_t nb_sample; + // The output sample data, user must free it by srs_mp4_free_sample. + uint8_t* sample; +} srs_mp4_sample_t; +/** + * Open mp4 file for muxer(write) or demuxer(read). + * @return A MP4 demuxer, NULL if failed. + */ +extern srs_mp4_t srs_mp4_open_read(const char* file); +/** + * Close the MP4 demuxer. + */ +extern void srs_mp4_close(srs_mp4_t mp4); +/** + * Initialize mp4 demuxer in non-seek mode. + * @remark Only support non-seek mode, that is fmp4 or moov before mdata. + * For the live streaming, we must feed stream frame by frame. + */ +extern int srs_mp4_init_demuxer(srs_mp4_t mp4); +/** + * Read a sample form mp4. + * @remark User can use srs_mp4_sample_to_flv_tag to convert mp4 sampel to flv tag. + * Use the srs_mp4_to_flv_tag_size to calc the flv tag data size to alloc. + */ +extern int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* sample); +/** + * Free the allocated mp4 sample. + */ +extern void srs_mp4_free_sample(srs_mp4_sample_t* sample); +/** + * Calc the size of flv tag, for the mp4 sample to convert to. + */ +extern int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* sample); +/** + * Covert mp4 sample to flv tag. + */ +extern int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* sample, char* type, uint32_t* time, char* data, int32_t size); +/* error code */ +/* whether the error code indicates EOF */ +extern srs_bool srs_mp4_is_eof(int error_code); + +/************************************************************* + ************************************************************** + * flv codec + * @example /trunk/research/librtmp/srs_flv_injecter.c + * @example /trunk/research/librtmp/srs_flv_parser.c + * @example /trunk/research/librtmp/srs_ingest_flv.c + * @example /trunk/research/librtmp/srs_ingest_rtmp.c + ************************************************************** + *************************************************************/ +typedef void* srs_flv_t; +/** + * Open FLV file in demux mode. + * @return A FLV demuxer, NULL if failed. + */ +extern srs_flv_t srs_flv_open_read(const char* file); +/** + * Open FlV file in mux mode. + * @return A FLV muxer, NULL if failed. + */ +extern srs_flv_t srs_flv_open_write(const char* file); +/** + * Close the FLV demuxer or muxer. + */ +extern void srs_flv_close(srs_flv_t flv); +/** + * read the flv header. 9bytes header. + * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. + * 3bytes, signature, "FLV", + * 1bytes, version, 0x01, + * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. + * 4bytes, dataoffset, 0x09, The length of this header in bytes + * + * @return 0, success; otherswise, failed. + * @remark, drop the 4bytes zero previous tag size. + */ +extern int srs_flv_read_header(srs_flv_t flv, char header[9]); +/** + * read the flv tag header, 1bytes tag, 3bytes data_size, + * 4bytes time, 3bytes stream id. + * @param ptype, output the type of tag, macros: + * SRS_RTMP_TYPE_AUDIO, FlvTagAudio + * SRS_RTMP_TYPE_VIDEO, FlvTagVideo + * SRS_RTMP_TYPE_SCRIPT, FlvTagScript + * @param pdata_size, output the size of tag data. + * @param ptime, output the time of tag, the dts in ms. + * + * @return 0, success; otherswise, failed. + * @remark, user must ensure the next is a tag, srs never check it. + */ +extern int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime); +/** + * read the tag data. drop the 4bytes previous tag size + * @param data, the data to read, user alloc and free it. + * @param size, the size of data to read, get by srs_flv_read_tag_header(). + * @remark, srs will ignore and drop the 4bytes previous tag size. + */ +extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); +/** + * write the flv header. 9bytes header. + * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. + * 3bytes, signature, "FLV", + * 1bytes, version, 0x01, + * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. + * 4bytes, dataoffset, 0x09, The length of this header in bytes + * + * @return 0, success; otherswise, failed. + * @remark, auto write the 4bytes zero previous tag size. + */ +extern int srs_flv_write_header(srs_flv_t flv, char header[9]); +/** + * write the flv tag to file. + * + * @return 0, success; otherswise, failed. + * @remark, auto write the 4bytes zero previous tag size. + */ +/* write flv tag to file, auto write the 4bytes previous tag size */ +extern int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); +/** + * get the tag size, for flv injecter to adjust offset, + * size = tag_header(11B) + data_size + previous_tag(4B) + * @return the size of tag. + */ +extern int srs_flv_size_tag(int data_size); +/* file stream */ +/* file stream tellg to get offset */ +extern int64_t srs_flv_tellg(srs_flv_t flv); +/* seek file stream, offset is form the start of file */ +extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); +/* error code */ +/* whether the error code indicates EOF */ +extern srs_bool srs_flv_is_eof(int error_code); +/* media codec */ +/** + * whether the video body is sequence header + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_data(). + */ +extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); +/** + * whether the video body is keyframe + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_data(). + */ +extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); + +/************************************************************* + ************************************************************** + * amf0 codec + * @example /trunk/research/librtmp/srs_ingest_flv.c + * @example /trunk/research/librtmp/srs_ingest_rtmp.c + ************************************************************** + *************************************************************/ +/* the output handler. */ +typedef double srs_amf0_number; +/** + * parse amf0 from data. + * @param nparsed, the parsed size, NULL to ignore. + * @return the parsed amf0 object. NULL for error. + * @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(); +extern srs_amf0_t srs_amf0_create_object(); +extern srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr); +extern void srs_amf0_free(srs_amf0_t amf0); +/* size and to bytes */ +extern int srs_amf0_size(srs_amf0_t amf0); +extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); +/* type detecter */ +extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +/* value converter */ +extern const char* srs_amf0_to_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); +/* value setter */ +extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); +/* object value converter */ +extern int srs_amf0_object_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +extern void srs_amf0_object_clear(srs_amf0_t amf0); +/* ecma array value converter */ +extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +/* strict array value converter */ +extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); +extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); +extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); + +/************************************************************* + ************************************************************** + * utilities + ************************************************************** + *************************************************************/ +/** + * get the current system time in ms. + * use gettimeofday() to get system time. + */ +extern int64_t srs_utils_time_ms(); + +/** + * get the send bytes. + */ +extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp); + +/** + * get the recv bytes. + */ +extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp); + +/** + * parse the dts and pts by time in header and data in tag, + * or to parse the RTMP packet by srs_rtmp_read_packet(). + * + * @param time, the timestamp of tag, read by srs_flv_read_tag_header(). + * @param type, the type of tag, read by srs_flv_read_tag_header(). + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_header(). + * @param ppts, output the pts in ms, + * + * @return 0, success; otherswise, failed. + * @remark, the dts always equals to @param time. + * @remark, the pts=dts for audio or data. + * @remark, video only support h.264. + */ +extern int srs_utils_parse_timestamp(uint32_t time, char type, char* data, int size, uint32_t* ppts); + +/** + * whether the flv tag specified by param type is ok. + * @return true when tag is video/audio/script-data; otherwise, false. + */ +extern srs_bool srs_utils_flv_tag_is_ok(char type); +extern srs_bool srs_utils_flv_tag_is_audio(char type); +extern srs_bool srs_utils_flv_tag_is_video(char type); +extern srs_bool srs_utils_flv_tag_is_av(char type); + +/** + * get the CodecID of video tag. + * Codec Identifier. The following values are defined: + * 2 = Sorenson H.263 + * 3 = Screen video + * 4 = On2 VP6 + * 5 = On2 VP6 with alpha channel + * 6 = Screen video version 2 + * 7 = AVC + * @return the code id. 0 for error. + */ +extern char srs_utils_flv_video_codec_id(char* data, int size); + +/** + * get the AVCPacketType of video tag. + * The following values are defined: + * 0 = AVC sequence header + * 1 = AVC NALU + * 2 = AVC end of sequence (lower level NALU sequence ender is + * not required or supported) + * @return the avc packet type. -1(0xff) for error. + */ +extern char srs_utils_flv_video_avc_packet_type(char* data, int size); + +/** + * get the FrameType of video tag. + * Type of video frame. The following values are defined: + * 1 = key frame (for AVC, a seekable frame) + * 2 = inter frame (for AVC, a non-seekable frame) + * 3 = disposable inter frame (H.263 only) + * 4 = generated key frame (reserved for server use only) + * 5 = video info/command frame + * @return the frame type. 0 for error. + */ +extern char srs_utils_flv_video_frame_type(char* data, int size); + +/** + * get the SoundFormat of audio tag. + * Format of SoundData. The following values are defined: + * 0 = Linear PCM, platform endian + * 1 = ADPCM + * 2 = MP3 + * 3 = Linear PCM, little endian + * 4 = Nellymoser 16 kHz mono + * 5 = Nellymoser 8 kHz mono + * 6 = Nellymoser + * 7 = G.711 A-law logarithmic PCM + * 8 = G.711 mu-law logarithmic PCM + * 9 = reserved + * 10 = AAC + * 11 = Speex + * 14 = MP3 8 kHz + * 15 = Device-specific sound + * Formats 7, 8, 14, and 15 are reserved. + * AAC is supported in Flash Player 9,0,115,0 and higher. + * Speex is supported in Flash Player 10 and higher. + * @return the sound format. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_format(char* data, int size); + +/** + * get the SoundRate of audio tag. + * Sampling rate. The following values are defined: + * 0 = 5.5 kHz + * 1 = 11 kHz + * 2 = 22 kHz + * 3 = 44 kHz + * @return the sound rate. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_rate(char* data, int size); + +/** + * get the SoundSize of audio tag. + * Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 0 = 8-bit samples + * 1 = 16-bit samples + * @return the sound size. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_size(char* data, int size); + +/** + * get the SoundType of audio tag. + * Mono or stereo sound + * 0 = Mono sound + * 1 = Stereo sound + * @return the sound type. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_type(char* data, int size); + +/** + * get the AACPacketType of audio tag. + * The following values are defined: + * 0 = AAC sequence header + * 1 = AAC raw + * @return the aac packet type. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_aac_packet_type(char* data, int size); + +/************************************************************* + ************************************************************** + * human readable print. + ************************************************************** + *************************************************************/ +/** + * human readable print + * @param pdata, output the heap data, NULL to ignore. + * user must use srs_amf0_free_bytes to free it. + * @return return the *pdata for print. NULL to ignore. + */ +extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); +/** + * convert the flv tag type to string. + * SRS_RTMP_TYPE_AUDIO to "Audio" + * SRS_RTMP_TYPE_VIDEO to "Video" + * SRS_RTMP_TYPE_SCRIPT to "Data" + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_tag_type2string(char type); + +/** + * get the codec id string. + * H.263 = Sorenson H.263 + * Screen = Screen video + * VP6 = On2 VP6 + * VP6Alpha = On2 VP6 with alpha channel + * Screen2 = Screen video version 2 + * H.264 = AVC + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_codec_id2string(char codec_id); + +/** + * get the avc packet type string. + * SH = AVC sequence header + * Nalu = AVC NALU + * SpsPpsEnd = AVC end of sequence + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); + +/** + * get the frame type string. + * I = key frame (for AVC, a seekable frame) + * P/B = inter frame (for AVC, a non-seekable frame) + * DI = disposable inter frame (H.263 only) + * GI = generated key frame (reserved for server use only) + * VI = video info/command frame + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_frame_type2string(char frame_type); + +/** + * get the SoundFormat string. + * Format of SoundData. The following values are defined: + * LinearPCM = Linear PCM, platform endian + * ADPCM = ADPCM + * MP3 = MP3 + * LinearPCMLe = Linear PCM, little endian + * NellymoserKHz16 = Nellymoser 16 kHz mono + * NellymoserKHz8 = Nellymoser 8 kHz mono + * Nellymoser = Nellymoser + * G711APCM = G.711 A-law logarithmic PCM + * G711MuPCM = G.711 mu-law logarithmic PCM + * Reserved = reserved + * AAC = AAC + * Speex = Speex + * MP3KHz8 = MP3 8 kHz + * DeviceSpecific = Device-specific sound + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_format2string(char sound_format); + +/** + * get the SoundRate of audio tag. + * Sampling rate. The following values are defined: + * 5.5KHz = 5.5 kHz + * 11KHz = 11 kHz + * 22KHz = 22 kHz + * 44KHz = 44 kHz + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate); + +/** + * get the SoundSize of audio tag. + * Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 8bit = 8-bit samples + * 16bit = 16-bit samples + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_size2string(char sound_size); + +/** + * get the SoundType of audio tag. + * Mono or stereo sound + * Mono = Mono sound + * Stereo = Stereo sound + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_type2string(char sound_type); + +/** + * get the AACPacketType of audio tag. + * The following values are defined: + * SH = AAC sequence header + * Raw = AAC raw + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type); + +/** + * Format the RTMP packet to human readable buffer. + * @return Whether parse RTMP packet ok. 0, success; otherwise, failed. + */ +extern int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size); +extern int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, + uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); + +/** + * Format current time to human readable string. + * @return A NULL terminated string. + */ +extern const char* srs_human_format_time(); + #ifndef _WIN32 - // for getpid. +// for getpid. #include #endif - // The log function for librtmp. - // User can disable it by define macro SRS_DISABLE_LOG. - // Or user can directly use them, or define the alias by: - // #define trace(msg, ...) srs_human_trace(msg, ##__VA_ARGS__) - // #define warn(msg, ...) srs_human_warn(msg, ##__VA_ARGS__) - // #define error(msg, ...) srs_human_error(msg, ##__VA_ARGS__) +// The log function for librtmp. +// User can disable it by define macro SRS_DISABLE_LOG. +// Or user can directly use them, or define the alias by: +// #define trace(msg, ...) srs_human_trace(msg, ##__VA_ARGS__) +// #define warn(msg, ...) srs_human_warn(msg, ##__VA_ARGS__) +// #define error(msg, ...) srs_human_error(msg, ##__VA_ARGS__) #ifdef SRS_DISABLE_LOG #define srs_human_trace(msg, ...) (void)0 #define srs_human_warn(msg, ...) (void)0 @@ -1066,130 +1066,130 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define srs_human_verbose(msg, ...) (void)0 #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) #endif - - /************************************************************* - ************************************************************** - * IO hijack, use your specified io functions. - ************************************************************** - *************************************************************/ - // the void* will convert to your handler for io hijack. - typedef void* srs_hijack_io_t; + +/************************************************************* + ************************************************************** + * IO hijack, use your specified io functions. + ************************************************************** + *************************************************************/ +// the void* will convert to your handler for io hijack. +typedef void* srs_hijack_io_t; #ifdef SRS_HIJACK_IO #ifndef _WIN32 - // for iovec. +// for iovec. #include #endif - /** - * get the hijack io object in rtmp protocol sdk. - * @remark, user should never provides this method, srs-librtmp provides it. - */ - extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp); +/** + * get the hijack io object in rtmp protocol sdk. + * @remark, user should never provides this method, srs-librtmp provides it. + */ +extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp); #endif - // define the following macro and functions in your module to hijack the io. - // the example @see https://github.com/ossrs/srs-bench - // which use librtmp but use its own io(use st also). +// define the following macro and functions in your module to hijack the io. +// the example @see https://github.com/ossrs/srs-bench +// which use librtmp but use its own io(use st also). #ifdef SRS_HIJACK_IO - /** - * create hijack. - * @return NULL for error; otherwise, ok. - */ - extern srs_hijack_io_t srs_hijack_io_create(); - /** - * destroy the context, user must close the socket. - */ - extern void srs_hijack_io_destroy(srs_hijack_io_t ctx); - /** - * create socket, not connect yet. - * @param owner, the rtmp context which create this socket. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx, srs_rtmp_t owner); - /** - * connect socket at server_ip:port. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port); - /** - * read from socket. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); - /** - * set the socket recv timeout in ms. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * get the socket recv timeout. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx); - /** - * get the socket recv bytes. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx); - /** - * set the socket send timeout in ms. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * get the socket send timeout. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx); - /** - * get the socket send bytes. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx); - /** - * writev of socket. - * @return 0, success; otherswise, failed. - * @remark We assume that the writev always write all data to peer, like what ST or block-socket done. - */ - extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite); - /** - * whether the timeout is never timeout in ms. - * @return 0, with timeout specified; otherwise, never timeout. - */ - extern int srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * read fully, fill the buf exactly size bytes. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); - /** - * write bytes to socket. - * @return 0, success; otherswise, failed. - * @remark We assume that the write always write all data to peer, like what ST or block-socket done. - */ - extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); +/** + * create hijack. + * @return NULL for error; otherwise, ok. + */ +extern srs_hijack_io_t srs_hijack_io_create(); +/** + * destroy the context, user must close the socket. + */ +extern void srs_hijack_io_destroy(srs_hijack_io_t ctx); +/** + * create socket, not connect yet. + * @param owner, the rtmp context which create this socket. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx, srs_rtmp_t owner); +/** + * connect socket at server_ip:port. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port); +/** + * read from socket. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); +/** + * set the socket recv timeout in ms. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * get the socket recv timeout. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx); +/** + * get the socket recv bytes. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx); +/** + * set the socket send timeout in ms. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * get the socket send timeout. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx); +/** + * get the socket send bytes. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx); +/** + * writev of socket. + * @return 0, success; otherswise, failed. + * @remark We assume that the writev always write all data to peer, like what ST or block-socket done. + */ +extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite); +/** + * whether the timeout is never timeout in ms. + * @return 0, with timeout specified; otherwise, never timeout. + */ +extern int srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * read fully, fill the buf exactly size bytes. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); +/** + * write bytes to socket. + * @return 0, success; otherswise, failed. + * @remark We assume that the write always write all data to peer, like what ST or block-socket done. + */ +extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); #endif - - /************************************************************* - ************************************************************** - * Windows SRS-LIBRTMP solution - ************************************************************** - *************************************************************/ - // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 + +/************************************************************* + ************************************************************** + * Windows SRS-LIBRTMP solution + ************************************************************** + *************************************************************/ +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 - // for time. +// for time. #define _CRT_SECURE_NO_WARNINGS #include - int gettimeofday(struct timeval* tv, struct timezone* tz); +int gettimeofday(struct timeval* tv, struct timezone* tz); #define PRId64 "lld" - - // for inet helpers. - typedef int socklen_t; - const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - - // for mkdir(). + +// for inet helpers. +typedef int socklen_t; +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + +// for mkdir(). #include - - // for open(). - typedef int mode_t; + +// for open(). +typedef int mode_t; #define S_IRUSR 0 #define S_IWUSR 0 #define S_IXUSR 0 @@ -1198,8 +1198,8 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define S_IXGRP 0 #define S_IROTH 0 #define S_IXOTH 0 - - // for file seek. + +// for file seek. #include #include #define open _open @@ -1207,45 +1207,45 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define lseek _lseek #define write _write #define read _read - - // for socket. - ssize_t writev(int fd, const struct iovec *iov, int iovcnt); - typedef int64_t useconds_t; - int usleep(useconds_t usec); - int socket_setup(); - int socket_cleanup(); - - // others. + +// for socket. +ssize_t writev(int fd, const struct iovec *iov, int iovcnt); +typedef int64_t useconds_t; +int usleep(useconds_t usec); +int socket_setup(); +int socket_cleanup(); + +// others. #define snprintf _snprintf #endif - - /************************************************************* - ************************************************************* - * Deprecated APIs, maybe removed in future versions. - ************************************************************* - *************************************************************/ - /** - * Deprecated, for bandwidth test check only. - */ - extern srs_rtmp_t srs_rtmp_create2(const char* url); - - /** - * Deprecated, use seperate function to retrieve information from rtmp, - * for example, use srs_rtmp_get_server_ip to get server ip. - */ - extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, - char srs_server_ip[128], char srs_server[128], - char srs_primary[128], char srs_authors[128], - char srs_version[32], int* srs_id, int* srs_pid); - - /** - * Deprecated, use srs_human_format_rtmp_packet instead. - */ - extern int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size); - extern int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp); - extern int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now); - extern int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, - int64_t starttime, int64_t nb_packets); + +/************************************************************* + ************************************************************* + * Deprecated APIs, maybe removed in future versions. + ************************************************************* + *************************************************************/ +/** + * Deprecated, for bandwidth test check only. + */ +extern srs_rtmp_t srs_rtmp_create2(const char* url); + +/** + * Deprecated, use seperate function to retrieve information from rtmp, + * for example, use srs_rtmp_get_server_ip to get server ip. + */ +extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, + char srs_server_ip[128], char srs_server[128], + char srs_primary[128], char srs_authors[128], + char srs_version[32], int* srs_id, int* srs_pid); + +/** + * Deprecated, use srs_human_format_rtmp_packet instead. + */ +extern int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size); +extern int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp); +extern int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now); +extern int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, + int64_t starttime, int64_t nb_packets); #ifdef __cplusplus } From ae99131512be52ab866a393d2c1c6788a873dd15 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 10:41:31 +0800 Subject: [PATCH 20/33] Fix #1087, Ignore iface without address. 3.0.37 --- README.md | 2 ++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/service/srs_service_utility.cpp | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e100e4ed..9d45f47d4 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-08-05, Fix [#1087][bug #1087], Ignore iface without address. 3.0.37 * v3.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 3.0.36 * v3.0, 2018-08-02, Always use vhost in stream query, the unify uri. 3.0.35 * v3.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 3.0.34 @@ -1452,6 +1453,7 @@ Winlin [bug #1057]: https://github.com/ossrs/srs/issues/1057 [bug #105]: https://github.com/ossrs/srs/issues/105 [bug #727]: https://github.com/ossrs/srs/issues/727 +[bug #1087]: https://github.com/ossrs/srs/issues/1087 [bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index a3b148696..237cf0aa8 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 36 +#define VERSION_REVISION 37 // generated by configure, only macros. #include diff --git a/trunk/src/service/srs_service_utility.cpp b/trunk/src/service/srs_service_utility.cpp index e4e62746e..987c2eb1e 100644 --- a/trunk/src/service/srs_service_utility.cpp +++ b/trunk/src/service/srs_service_utility.cpp @@ -150,6 +150,12 @@ void retrieve_local_ips() for (ifaddrs* p = ifap; p ; p = p->ifa_next) { ifaddrs* cur = p; + // Ignore if no address for this interface. + // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115 + if (!cur->ifa_addr) { + continue; + } + // retrieve IP address, ignore the tun0 network device, whose addr is NULL. // @see: https://github.com/ossrs/srs/issues/141 bool ipv4 = (cur->ifa_addr->sa_family == AF_INET); @@ -164,6 +170,12 @@ void retrieve_local_ips() for (ifaddrs* p = ifap; p ; p = p->ifa_next) { ifaddrs* cur = p; + // Ignore if no address for this interface. + // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115 + if (!cur->ifa_addr) { + continue; + } + // retrieve IP address, ignore the tun0 network device, whose addr is NULL. // @see: https://github.com/ossrs/srs/issues/141 bool ipv6 = (cur->ifa_addr->sa_family == AF_INET6); @@ -179,6 +191,12 @@ void retrieve_local_ips() for (ifaddrs* p = ifap; p ; p = p->ifa_next) { ifaddrs* cur = p; + // Ignore if no address for this interface. + // @see https://github.com/ossrs/srs/issues/1087#issuecomment-408847115 + if (!cur->ifa_addr) { + continue; + } + // retrieve IP address, ignore the tun0 network device, whose addr is NULL. // @see: https://github.com/ossrs/srs/issues/141 bool ipv4 = (cur->ifa_addr->sa_family == AF_INET); From ee43fc92ae99eb32c315c81e744c4530717542e1 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 10:47:01 +0800 Subject: [PATCH 21/33] For #1110, Refine conf --- trunk/conf/full.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 246d215a0..a8f12aa10 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -723,7 +723,7 @@ vhost hooks.callback.srs.com { # "action": "on_publish", # "client_id": 1985, # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - # "stream": "livestream" + # "stream": "livestream", "param":"?token=xxx&salt=yyy" # } # if valid, the hook must return HTTP code 200(Stauts OK) and response # an int value specifies the error code(0 corresponding to success): From 31945c244d1f9c0214a251134f1f3cae60c22697 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 11:09:48 +0800 Subject: [PATCH 22/33] For #1110, Refine code. --- trunk/research/api-server/server.py | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/trunk/research/api-server/server.py b/trunk/research/api-server/server.py index 05612d6a9..888569638 100755 --- a/trunk/research/api-server/server.py +++ b/trunk/research/api-server/server.py @@ -176,7 +176,7 @@ class RESTStreams(object): "action": "on_publish", "client_id": 1985, "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream" + "stream": "livestream", "param":"?token=xxx&salt=yyy" } on_unpublish: when client(encoder) stop publish to vhost/app/stream, call the hook, @@ -185,7 +185,7 @@ class RESTStreams(object): "action": "on_unpublish", "client_id": 1985, "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream" + "stream": "livestream", "param":"?token=xxx&salt=yyy" } if valid, the hook must return HTTP code 200(Stauts OK) and response an int value specifies the error code(0 corresponding to success): @@ -223,8 +223,8 @@ class RESTStreams(object): def __on_publish(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"] + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"] )) # TODO: process the on_publish event @@ -234,8 +234,8 @@ class RESTStreams(object): def __on_unpublish(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"] + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"] )) # TODO: process the on_unpublish event @@ -263,7 +263,7 @@ class RESTDvrs(object): "action": "on_dvr", "client_id": 1985, "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream", + "stream": "livestream", "param":"?token=xxx&salt=yyy", "cwd": "/usr/local/srs", "file": "./objs/nginx/html/live/livestream.1420254068776.flv" } @@ -301,8 +301,8 @@ class RESTDvrs(object): def __on_dvr(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s, cwd=%s, file=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"], req["cwd"], req["file"] )) @@ -325,6 +325,7 @@ class RESTProxy(object): so we use HTTP GET and use the variable following: [app], replace with the app. [stream], replace with the stream. + [param], replace with the param. [ts_url], replace with the ts url. ignore any return data of server. ''' @@ -359,6 +360,7 @@ class RESTHls(object): so we use HTTP GET and use the variable following: [app], replace with the app. [stream], replace with the stream. + [param], replace with the param. [ts_url], replace with the ts url. ignore any return data of server. ''' @@ -382,7 +384,7 @@ class RESTHls(object): "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream", + "stream": "livestream", "param":"?token=xxx&salt=yyy", "duration": 9.68, // in seconds "cwd": "/usr/local/srs", "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts", @@ -422,8 +424,8 @@ class RESTHls(object): def __on_hls(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, duration=%s, cwd=%s, file=%s, seq_no=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["duration"], + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s, duration=%s, cwd=%s, file=%s, seq_no=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"], req["duration"], req["cwd"], req["file"], req["seq_no"] )) @@ -452,7 +454,7 @@ class RESTSessions(object): "action": "on_play", "client_id": 1985, "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream", + "stream": "livestream", "param":"?token=xxx&salt=yyy", "pageUrl": "http://www.test.com/live.html" } on_stop: @@ -462,7 +464,7 @@ class RESTSessions(object): "action": "on_stop", "client_id": 1985, "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", - "stream": "livestream" + "stream": "livestream", "param":"?token=xxx&salt=yyy" } if valid, the hook must return HTTP code 200(Stauts OK) and response an int value specifies the error code(0 corresponding to success): @@ -500,8 +502,8 @@ class RESTSessions(object): def __on_play(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, pageUrl=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["pageUrl"] + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s, pageUrl=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"], req["pageUrl"] )) # TODO: process the on_play event @@ -511,8 +513,8 @@ class RESTSessions(object): def __on_stop(self, req): code = Error.success - trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s"%( - req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"] + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, param=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], req["param"] )) # TODO: process the on_stop event From 580c94c8c0f03e05ca5460a2e4d68505fe6e7ba2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 18:58:23 +0800 Subject: [PATCH 23/33] Refine code --- trunk/src/app/srs_app_http_stream.cpp | 2 +- trunk/src/app/srs_app_http_stream.hpp | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 15ec0da06..09a8a358b 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -776,7 +776,7 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r) if ((err = entry->cache->start()) != srs_success) { return srs_error_wrap(err, "http: start stream cache failed"); } - srs_trace("http: mount flv stream for vhost=%s, mount=%s", sid.c_str(), mount.c_str()); + srs_trace("http: mount flv stream for sid=%s, mount=%s", sid.c_str(), mount.c_str()); } else { entry = sflvs[sid]; entry->stream->update(s, r); diff --git a/trunk/src/app/srs_app_http_stream.hpp b/trunk/src/app/srs_app_http_stream.hpp index eb37b7c7f..128bcaaa0 100755 --- a/trunk/src/app/srs_app_http_stream.hpp +++ b/trunk/src/app/srs_app_http_stream.hpp @@ -34,9 +34,7 @@ class SrsFlvTransmuxer; class SrsTsTransmuxer; /** - * for the srs http stream cache, - * for example, the audio stream cache to make android(weixin) happy. - * we start a thread to shrink the queue. + * A cache for HTTP Live Streaming encoder, to make android(weixin) happy. */ class SrsBufferCache : public ISrsCoroutineHandler { @@ -60,7 +58,7 @@ public: }; /** - * the stream encoder in some codec, for example, flv or aac. + * The encoder to transmux RTMP stream. */ class ISrsBufferEncoder { @@ -94,7 +92,7 @@ public: }; /** - * the flv stream encoder, remux rtmp stream to flv stream. + * Transmux RTMP to HTTP Live Streaming. */ class SrsFlvStreamEncoder : public ISrsBufferEncoder { @@ -115,7 +113,7 @@ public: #ifdef SRS_PERF_FAST_FLV_ENCODER /** - * the fast flv stream encoder. + * A Fast HTTP FLV Live Streaming, to write multiple tags by writev. * @see https://github.com/ossrs/srs/issues/405 */ class SrsFastFlvStreamEncoder : public SrsFlvStreamEncoder @@ -132,7 +130,7 @@ public: #endif /** - * the ts stream encoder, remux rtmp stream to ts stream. + * Transmux RTMP to HTTP TS Streaming. */ class SrsTsStreamEncoder : public ISrsBufferEncoder { @@ -152,7 +150,7 @@ public: }; /** - * the aac stream encoder, remux rtmp stream to aac stream. + * Transmux RTMP with AAC stream to HTTP AAC Streaming. */ class SrsAacStreamEncoder : public ISrsBufferEncoder { @@ -173,7 +171,7 @@ public: }; /** - * the mp3 stream encoder, remux rtmp stream to mp3 stream. + * Transmux RTMP with MP3 stream to HTTP MP3 Streaming. */ class SrsMp3StreamEncoder : public ISrsBufferEncoder { @@ -215,8 +213,7 @@ public: }; /** - * the flv live stream supports access rtmp in flv over http. - * srs will remux rtmp to flv streaming. + * HTTP Live Streaming, to transmux RTMP to HTTP FLV or other format. */ class SrsLiveStream : public ISrsHttpHandler { @@ -235,7 +232,7 @@ private: }; /** - * the srs live entry + * The Live Entry, to handle HTTP Live Streaming. */ struct SrsLiveEntry { @@ -264,8 +261,7 @@ public: }; /** - * the http stream server instance, - * serve http stream, for example, flv/ts/mp3/aac live stream. + * The HTTP Live Streaming Server, to serve FLV/TS/MP3/AAC stream. */ // TODO: Support multiple stream. class SrsHttpStreamServer : virtual public ISrsReloadHandler From c45f72ef7bac9c7cf85b9125fc9e3aafd53f396f Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 20:23:17 +0800 Subject: [PATCH 24/33] Refine HTTP-FLV latency, support realtime mode. 2.0.252 --- README.md | 1 + trunk/src/app/srs_app_http_stream.cpp | 17 +++++++++++++---- trunk/src/core/srs_core.hpp | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 38e281cc9..37200fb35 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-05, Refine HTTP-FLV latency, support realtime mode. 2.0.252 * v2.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 2.0.251 * v2.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 2.0.250 * v2.0, 2018-07-21, Merge [#1119][bug #1119], fix memory leak. 2.0.249 diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index f25852d09..9181fb4dd 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -58,6 +58,8 @@ using namespace std; #endif +#include + #ifdef SRS_AUTO_HTTP_SERVER SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) @@ -484,7 +486,12 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) if (srs_string_ends_with(entry->pattern, ".flv")) { w->header()->set_content_type("video/x-flv"); #ifdef SRS_PERF_FAST_FLV_ENCODER - enc = new SrsFastFlvStreamEncoder(); + bool realtime = _srs_config->get_realtime_enabled(req->vhost); + if (realtime) { + enc = new SrsFlvStreamEncoder(); + } else { + enc = new SrsFastFlvStreamEncoder(); + } #else enc = new SrsFlvStreamEncoder(); #endif @@ -542,6 +549,8 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) SrsHttpMessage* hr = dynamic_cast(r); SrsResponseOnlyHttpConn* hc = dynamic_cast(hr->connection()); + int mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); + SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc); SrsAutoFree(SrsHttpRecvThread, trd); @@ -570,15 +579,15 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) if (count <= 0) { srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // directly use sleep, donot use consumer wait. - st_usleep(SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); + st_usleep(mw_sleep); // ignore when nothing got. continue; } if (pprint->can_print()) { - srs_info("-> "SRS_CONSTS_LOG_HTTP_STREAM" http: got %d msgs, age=%d, min=%d, mw=%d", - count, pprint->age(), SRS_PERF_MW_MIN_MSGS, SRS_CONSTS_RTMP_PULSE_TIMEOUT_US / 1000); + srs_trace("-> "SRS_CONSTS_LOG_HTTP_STREAM" http: got %d msgs, age=%d, min=%d, mw=%d", + count, pprint->age(), SRS_PERF_MW_MIN_MSGS, mw_sleep); } // sendout all messages. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index c2ceadcf9..b5444e807 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 251 +#define VERSION_REVISION 252 // generated by configure, only macros. #include From d607c7f90598d3c677eec8620ed793b3fe5d1488 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 20:30:04 +0800 Subject: [PATCH 25/33] Refine HTTP-FLV latency, support realtime mode.3.0.38 --- README.md | 1 + trunk/src/app/srs_app_conn.cpp | 84 +++++++++++++++++++++++++++ trunk/src/app/srs_app_conn.hpp | 4 ++ trunk/src/app/srs_app_http_stream.cpp | 29 +++++++-- trunk/src/app/srs_app_rtmp_conn.cpp | 64 ++------------------ trunk/src/core/srs_core.hpp | 2 +- trunk/src/kernel/srs_kernel_error.hpp | 2 + 7 files changed, 121 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 4258e5bd6..0e6c10606 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-08-05, Refine HTTP-FLV latency, support realtime mode.3.0.38 * v3.0, 2018-08-05, Fix [#1087][bug #1087], Ignore iface without address. 3.0.37 * v3.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 3.0.36 * v3.0, 2018-08-02, Always use vhost in stream query, the unify uri. 3.0.35 diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index 721bd64fe..597a851ac 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -23,6 +23,7 @@ #include +#include using namespace std; #include @@ -95,6 +96,89 @@ srs_error_t SrsConnection::start() return err; } +srs_error_t SrsConnection::set_tcp_nodelay(bool v) +{ + srs_error_t err = srs_success; + + int r0 = 0; + socklen_t nb_v = sizeof(int); + int fd = srs_netfd_fileno(stfd); + + int ov = 0; + if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &ov, &nb_v)) != 0) { + return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0); + } + +#ifndef SRS_PERF_TCP_NODELAY + srs_warn("ignore TCP_NODELAY, fd=%d, ov=%d", fd, ov); + return err; +#endif + + int iv = (v? 1:0); + if ((r0 = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, nb_v)) != 0) { + return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%v", fd, r0); + } + if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, &nb_v)) != 0) { + return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0); + } + + srs_trace("set fd=%d TCP_NODELAY %d=>%d", fd, ov, iv); + + return err; +} + +srs_error_t SrsConnection::set_socket_buffer(int buffer_ms) +{ + srs_error_t err = srs_success; + + int r0 = 0; + int fd = srs_netfd_fileno(stfd); + socklen_t nb_v = sizeof(int); + + int ov = 0; + if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &ov, &nb_v)) != 0) { + return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0); + } + +#ifndef SRS_PERF_MW_SO_SNDBUF + srs_warn("ignore SO_SNDBUF, fd=%d, ov=%d", fd, ov); + return err; +#endif + + // the bytes: + // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, + // 128KB=131072, 256KB=262144, 512KB=524288 + // the buffer should set to sleep*kbps/8, + // for example, your system delivery stream in 1000kbps, + // sleep 800ms for small bytes, the buffer should set to: + // 800*1000/8=100000B(about 128KB). + // other examples: + // 2000*3000/8=750000B(about 732KB). + // 2000*5000/8=1250000B(about 1220KB). + int kbps = 4000; + int iv = buffer_ms * kbps / 8; + + // socket send buffer, system will double it. + iv = iv / 2; + + // override the send buffer by macro. +#ifdef SRS_PERF_SO_SNDBUF_SIZE + iv = SRS_PERF_SO_SNDBUF_SIZE / 2; +#endif + + // set the socket send buffer when required larger buffer + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, nb_v) < 0) { + return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%v", fd, r0); + } + if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, &nb_v)) != 0) { + return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0); + } + + srs_trace("set fd=%d, SO_SNDBUF=%d=>%d, buffer=%dms", fd, ov, iv, buffer_ms); + + return err; +} + srs_error_t SrsConnection::cycle() { srs_error_t err = do_cycle(); diff --git a/trunk/src/app/srs_app_conn.hpp b/trunk/src/app/srs_app_conn.hpp index 16c8bd9b2..58115fede 100644 --- a/trunk/src/app/srs_app_conn.hpp +++ b/trunk/src/app/srs_app_conn.hpp @@ -100,6 +100,10 @@ public: * to remove the client by server->remove(this). */ virtual srs_error_t start(); + // Set socket option TCP_NODELAY. + virtual srs_error_t set_tcp_nodelay(bool v); + // Set socket option SO_SNDBUF in ms. + virtual srs_error_t set_socket_buffer(int buffer_ms); // interface ISrsOneCycleThreadHandler public: /** diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 00c4a9f3f..7c16da63e 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -487,6 +487,7 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage { srs_error_t err = srs_success; + string enc_desc; ISrsBufferEncoder* enc = NULL; srs_assert(entry); @@ -495,21 +496,27 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage #ifdef SRS_PERF_FAST_FLV_ENCODER bool realtime = _srs_config->get_realtime_enabled(req->vhost); if (realtime) { + enc_desc = "FLV"; enc = new SrsFlvStreamEncoder(); } else { + enc_desc = "FastFLV"; enc = new SrsFastFlvStreamEncoder(); } #else + enc_desc = "FLV"; enc = new SrsFlvStreamEncoder(); #endif } else if (srs_string_ends_with(entry->pattern, ".aac")) { w->header()->set_content_type("audio/x-aac"); + enc_desc = "AAC"; enc = new SrsAacStreamEncoder(); } else if (srs_string_ends_with(entry->pattern, ".mp3")) { w->header()->set_content_type("audio/mpeg"); + enc_desc = "MP3"; enc = new SrsMp3StreamEncoder(); } else if (srs_string_ends_with(entry->pattern, ".ts")) { w->header()->set_content_type("video/MP2T"); + enc_desc = "TS"; enc = new SrsTsStreamEncoder(); } else { return srs_error_new(ERROR_HTTP_LIVE_STREAM_EXT, "invalid pattern=%s", entry->pattern.c_str()); @@ -557,7 +564,18 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage SrsHttpMessage* hr = dynamic_cast(r); SrsResponseOnlyHttpConn* hc = dynamic_cast(hr->connection()); + // Set the socket options for transport. + bool tcp_nodelay = _srs_config->get_tcp_nodelay(req->vhost); + if (tcp_nodelay) { + if ((err = hc->set_tcp_nodelay(tcp_nodelay)) != srs_success) { + return srs_error_wrap(err, "set tcp nodelay"); + } + } + int mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); + if ((err = hc->set_socket_buffer(mw_sleep)) != srs_success) { + return srs_error_wrap(err, "set mw_sleep"); + } SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc); SrsAutoFree(SrsHttpRecvThread, trd); @@ -565,6 +583,9 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage if ((err = trd->start()) != srs_success) { return srs_error_wrap(err, "start recv thread"); } + + srs_trace("FLV %s, encoder=%s, nodelay=%d, mw_sleep=%dms, cache=%d, msgs=%d", + entry->pattern.c_str(), enc_desc.c_str(), tcp_nodelay, mw_sleep, enc->has_cache(), msgs.max); // TODO: free and erase the disabled entry after all related connections is closed. while (entry->enabled) { @@ -583,16 +604,14 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage } if (count <= 0) { - srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TMMS); - // directly use sleep, donot use consumer wait. - srs_usleep(mw_sleep); - + // Directly use sleep, donot use consumer wait, because we couldn't awake consumer. + srs_usleep(mw_sleep * 1000); // ignore when nothing got. continue; } if (pprint->can_print()) { - srs_trace("-> "SRS_CONSTS_LOG_HTTP_STREAM" http: got %d msgs, age=%d, min=%d, mw=%d", + srs_trace("-> " SRS_CONSTS_LOG_HTTP_STREAM " http: got %d msgs, age=%d, min=%d, mw=%d", count, pprint->age(), SRS_PERF_MW_MIN_MSGS, mw_sleep); } diff --git a/trunk/src/app/srs_app_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp index db8e67757..cfdc6da76 100644 --- a/trunk/src/app/srs_app_rtmp_conn.cpp +++ b/trunk/src/app/srs_app_rtmp_conn.cpp @@ -1136,48 +1136,7 @@ void SrsRtmpConn::change_mw_sleep(int sleep_ms) return; } - // get the sock buffer size. - int fd = srs_netfd_fileno(stfd); - int onb_sbuf = 0; - socklen_t sock_buf_size = sizeof(int); - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &onb_sbuf, &sock_buf_size); - -#ifdef SRS_PERF_MW_SO_SNDBUF - // the bytes: - // 4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536, - // 128KB=131072, 256KB=262144, 512KB=524288 - // the buffer should set to sleep*kbps/8, - // for example, your system delivery stream in 1000kbps, - // sleep 800ms for small bytes, the buffer should set to: - // 800*1000/8=100000B(about 128KB). - // other examples: - // 2000*3000/8=750000B(about 732KB). - // 2000*5000/8=1250000B(about 1220KB). - int kbps = 5000; - int socket_buffer_size = sleep_ms * kbps / 8; - - // socket send buffer, system will double it. - int nb_sbuf = socket_buffer_size / 2; - - // override the send buffer by macro. -#ifdef SRS_PERF_SO_SNDBUF_SIZE - nb_sbuf = SRS_PERF_SO_SNDBUF_SIZE / 2; -#endif - - // set the socket send buffer when required larger buffer - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, sock_buf_size) < 0) { - srs_warn("set sock SO_SENDBUF=%d failed.", nb_sbuf); - } - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nb_sbuf, &sock_buf_size); - - srs_trace("mw changed sleep %d=>%d, max_msgs=%d, esbuf=%d, sbuf %d=>%d, realtime=%d", - mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, socket_buffer_size, - onb_sbuf, nb_sbuf, realtime); -#else - srs_trace("mw changed sleep %d=>%d, max_msgs=%d, sbuf %d, realtime=%d", - mw_sleep, sleep_ms, SRS_PERF_MW_MSGS, onb_sbuf, realtime); -#endif - + set_socket_buffer(sleep_ms); mw_sleep = sleep_ms; } @@ -1188,25 +1147,12 @@ void SrsRtmpConn::set_sock_options() bool nvalue = _srs_config->get_tcp_nodelay(req->vhost); if (nvalue != tcp_nodelay) { tcp_nodelay = nvalue; -#ifdef SRS_PERF_TCP_NODELAY - int fd = srs_netfd_fileno(stfd); - socklen_t nb_v = sizeof(int); - - int ov = 0; - getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &ov, &nb_v); - - int v = tcp_nodelay; - // set the socket send buffer when required larger buffer - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, nb_v) < 0) { - srs_warn("set sock TCP_NODELAY=%d failed.", v); + srs_error_t err = set_tcp_nodelay(tcp_nodelay); + if (err != srs_success) { + srs_warn("ignore err %s", srs_error_desc(err).c_str()); + srs_freep(err); } - getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, &nb_v); - - srs_trace("set TCP_NODELAY %d=>%d", ov, v); -#else - srs_warn("SRS_PERF_TCP_NODELAY is disabled but tcp_nodelay configed."); -#endif } } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 237cf0aa8..dd60634c7 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 37 +#define VERSION_REVISION 38 // generated by configure, only macros. #include diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 9ffc1c557..3666f096a 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -112,6 +112,8 @@ #define ERROR_ASPROCESS_PPID 1073 #define ERROR_EXCEED_CONNECTIONS 1074 #define ERROR_SOCKET_SETKEEPALIVE 1075 +#define ERROR_SOCKET_NO_NODELAY 1076 +#define ERROR_SOCKET_SNDBUF 1077 /////////////////////////////////////////////////////// // RTMP protocol error. From 33f6e9a21fedacd9af78d4c436103ffa96f6ae3f Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 20:33:45 +0800 Subject: [PATCH 26/33] Update readme for FLV realtime --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 37200fb35..a6db56aa3 100755 --- a/README.md +++ b/README.md @@ -810,6 +810,8 @@ The latency between encoder and player with realtime config([CN][v2_CN_LowLatenc | 2014-12-12 | 2.0.70 |[0.1s][p13]|[0.4s][p14]| 1.0s | 0.9s | | 2014-12-16 | 2.0.72 | 0.1s | 0.4s |[0.8s][p15]|[0.6s][p16]| +> 2018-08-05, [c45f72e](https://github.com/ossrs/srs/commit/c45f72ef7bac9c7cf85b9125fc9e3aafd53f396f), Refine HTTP-FLV latency, support realtime mode. 2.0.252 + We use FMLE as encoder for benchmark. The latency of server is 0.1s+, and the bottleneck is the encoder. For more information, read [bug #257][bug #257-c0]. From 6cea551c64b09ac840c364fec31f25034a0109c2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 11 Aug 2018 10:12:40 +0800 Subject: [PATCH 27/33] For #1110, Refine params in http callback. 2.0.253 --- README.md | 1 + trunk/src/app/srs_app_http_hooks.cpp | 14 +++----------- trunk/src/core/srs_core.hpp | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a6db56aa3..03193b3b8 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-11, For [#1110][bug #1110], Refine params in http callback. 2.0.253 * v2.0, 2018-08-05, Refine HTTP-FLV latency, support realtime mode. 2.0.252 * v2.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 2.0.251 * v2.0, 2018-08-02, For [#1031][bug #1031], SRS edge support douyu.com. 2.0.250 diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index d63df4931..bca1467e7 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -131,15 +131,6 @@ int SrsHttpHooks::on_publish(string url, SrsRequest* req) int client_id = _srs_context->get_id(); - string stream = req->stream; - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - if (!req->param.empty()) { - if (req->param.find("?") != 0) { - stream += "?"; - } - stream += req->param; - } - std::stringstream ss; ss << SRS_JOBJECT_START << SRS_JFIELD_STR("action", "on_publish") << SRS_JFIELD_CONT @@ -148,7 +139,8 @@ int SrsHttpHooks::on_publish(string url, SrsRequest* req) << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT << SRS_JFIELD_STR("tcUrl", req->tcUrl) << SRS_JFIELD_CONT // Add tcUrl for auth publish rtmp stream client - << SRS_JFIELD_STR("stream", stream) + << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT + << SRS_JFIELD_STR("param", req->param) << SRS_JOBJECT_END; std::string data = ss.str(); @@ -181,7 +173,7 @@ void SrsHttpHooks::on_unpublish(string url, SrsRequest* req) << SRS_JFIELD_STR("ip", req->ip) << SRS_JFIELD_CONT << SRS_JFIELD_STR("vhost", req->vhost) << SRS_JFIELD_CONT << SRS_JFIELD_STR("app", req->app) << SRS_JFIELD_CONT - << SRS_JFIELD_STR("stream", req->stream)<< SRS_JFIELD_CONT + << SRS_JFIELD_STR("stream", req->stream) << SRS_JFIELD_CONT << SRS_JFIELD_STR("param", req->param) << SRS_JOBJECT_END; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index b5444e807..7b49613ff 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 252 +#define VERSION_REVISION 253 // generated by configure, only macros. #include From 01d8bba45562d891ab8652844b8f768fcf74a003 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 11 Aug 2018 12:33:03 +0800 Subject: [PATCH 28/33] For #1181, Refine code to write utest --- trunk/src/app/srs_app_http_api.cpp | 9 ++++--- trunk/src/app/srs_app_http_conn.cpp | 16 +++++++---- trunk/src/service/srs_service_http_client.cpp | 4 +-- trunk/src/service/srs_service_http_conn.cpp | 27 +++++++++++-------- trunk/src/service/srs_service_http_conn.hpp | 16 ++++++----- 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 79ae78ca1..9f0389826 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1404,16 +1404,19 @@ srs_error_t SrsHttpApi::do_cycle() ISrsHttpMessage* req = NULL; // get a http message - if ((err = parser->parse_message(skt, this, &req)) != srs_success) { + if ((err = parser->parse_message(skt, &req)) != srs_success) { return srs_error_wrap(err, "parse message"); } // if SUCCESS, always NOT-NULL. - srs_assert(req); - // always free it in this scope. + srs_assert(req); SrsAutoFree(ISrsHttpMessage, req); + // Attach owner connection to message. + SrsHttpMessage* hreq = (SrsHttpMessage*)req; + hreq->set_connection(this); + // ok, handle http request. SrsHttpResponseWriter writer(skt); if ((err = process_request(&writer, req)) != srs_success) { diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 4dad8b0bd..f210df847 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -123,19 +123,21 @@ srs_error_t SrsHttpConn::do_cycle() ISrsHttpMessage* req = NULL; // get a http message - if ((err = parser->parse_message(skt, this, &req)) != srs_success) { + if ((err = parser->parse_message(skt, &req)) != srs_success) { break; } // if SUCCESS, always NOT-NULL. - srs_assert(req); - // always free it in this scope. + srs_assert(req); SrsAutoFree(ISrsHttpMessage, req); + // Attach owner connection to message. + SrsHttpMessage* hreq = (SrsHttpMessage*)req; + hreq->set_connection(this); + // copy request to last request object. srs_freep(last_req); - SrsHttpMessage* hreq = dynamic_cast(req); last_req = hreq->to_request(hreq->host()); // may should discard the body. @@ -218,10 +220,14 @@ srs_error_t SrsResponseOnlyHttpConn::pop_message(ISrsHttpMessage** preq) return srs_error_wrap(err, "init socket"); } - if ((err = parser->parse_message(&skt, this, preq)) != srs_success) { + if ((err = parser->parse_message(&skt, preq)) != srs_success) { return srs_error_wrap(err, "parse message"); } + // Attach owner connection to message. + SrsHttpMessage* hreq = (SrsHttpMessage*)(*preq); + hreq->set_connection(this); + return err; } diff --git a/trunk/src/service/srs_service_http_client.cpp b/trunk/src/service/srs_service_http_client.cpp index 53c29a6a4..759b8b8b8 100644 --- a/trunk/src/service/srs_service_http_client.cpp +++ b/trunk/src/service/srs_service_http_client.cpp @@ -124,7 +124,7 @@ srs_error_t SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg } ISrsHttpMessage* msg = NULL; - if ((err = parser->parse_message(transport, NULL, &msg)) != srs_success) { + if ((err = parser->parse_message(transport, &msg)) != srs_success) { return srs_error_wrap(err, "http: parse response"); } srs_assert(msg); @@ -170,7 +170,7 @@ srs_error_t SrsHttpClient::get(string path, string req, ISrsHttpMessage** ppmsg) } ISrsHttpMessage* msg = NULL; - if ((err = parser->parse_message(transport, NULL, &msg)) != srs_success) { + if ((err = parser->parse_message(transport, &msg)) != srs_success) { return srs_error_wrap(err, "http: parse response"); } srs_assert(msg); diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp index 1f8caba7a..a87e78f5e 100644 --- a/trunk/src/service/srs_service_http_conn.cpp +++ b/trunk/src/service/srs_service_http_conn.cpp @@ -68,7 +68,7 @@ srs_error_t SrsHttpParser::initialize(enum http_parser_type type, bool allow_jso return err; } -srs_error_t SrsHttpParser::parse_message(ISrsProtocolReaderWriter* io, SrsConnection* conn, ISrsHttpMessage** ppmsg) +srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; @@ -85,12 +85,12 @@ srs_error_t SrsHttpParser::parse_message(ISrsProtocolReaderWriter* io, SrsConnec header_parsed = 0; // do parse - if ((err = parse_message_imp(io)) != srs_success) { + if ((err = parse_message_imp(reader)) != srs_success) { return srs_error_wrap(err, "parse message"); } // create msg - SrsHttpMessage* msg = new SrsHttpMessage(io, conn); + SrsHttpMessage* msg = new SrsHttpMessage(reader); // initalize http msg, parse url. if ((err = msg->update(url, jsonp, &header, buffer, headers)) != srs_success) { @@ -104,7 +104,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsProtocolReaderWriter* io, SrsConnec return err; } -srs_error_t SrsHttpParser::parse_message_imp(ISrsProtocolReaderWriter* io) +srs_error_t SrsHttpParser::parse_message_imp(ISrsReader* reader) { srs_error_t err = srs_success; @@ -138,7 +138,7 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsProtocolReaderWriter* io) // when nothing parsed, read more to parse. if (nparsed == 0) { // when requires more, only grow 1bytes, but the buffer will cache more. - if ((err = buffer->grow(io, buffer->size() + 1)) != srs_success) { + if ((err = buffer->grow(reader, buffer->size() + 1)) != srs_success) { return srs_error_wrap(err, "grow buffer"); } } @@ -254,14 +254,14 @@ int SrsHttpParser::on_body(http_parser* parser, const char* at, size_t length) return 0; } -SrsHttpMessage::SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c) : ISrsHttpMessage() +SrsHttpMessage::SrsHttpMessage(ISrsReader* reader) : ISrsHttpMessage() { - conn = c; + owner_conn = NULL; chunked = false; infinite_chunked = false; keep_alive = true; _uri = new SrsHttpUri(); - _body = new SrsHttpResponseReader(this, io); + _body = new SrsHttpResponseReader(this, reader); _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE]; jsonp = false; } @@ -329,7 +329,12 @@ srs_error_t SrsHttpMessage::update(string url, bool allow_jsonp, http_parser* he SrsConnection* SrsHttpMessage::connection() { - return conn; + return owner_conn; +} + +void SrsHttpMessage::set_connection(SrsConnection* conn) +{ + owner_conn = conn; } uint8_t SrsHttpMessage::method() @@ -842,9 +847,9 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size) return skt->write((void*)buf.c_str(), buf.length(), NULL); } -SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsProtocolReaderWriter* io) +SrsHttpResponseReader::SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader) { - skt = io; + skt = reader; owner = msg; is_eof = false; nb_total_read = 0; diff --git a/trunk/src/service/srs_service_http_conn.hpp b/trunk/src/service/srs_service_http_conn.hpp index b4e7de1cc..8b0783db9 100644 --- a/trunk/src/service/srs_service_http_conn.hpp +++ b/trunk/src/service/srs_service_http_conn.hpp @@ -30,10 +30,10 @@ #include -class ISrsProtocolReaderWriter; class SrsConnection; class SrsFastStream; class SrsRequest; +class ISrsReader; class SrsHttpResponseReader; class SrsStSocket; @@ -77,12 +77,12 @@ public: * @remark, if success, *ppmsg always NOT-NULL, *ppmsg always is_complete(). * @remark user must free the ppmsg if not NULL. */ - virtual srs_error_t parse_message(ISrsProtocolReaderWriter* io, SrsConnection* conn, ISrsHttpMessage** ppmsg); + virtual srs_error_t parse_message(ISrsReader* reader, ISrsHttpMessage** ppmsg); private: /** * parse the HTTP message to member field: msg. */ - virtual srs_error_t parse_message_imp(ISrsProtocolReaderWriter* io); + virtual srs_error_t parse_message_imp(ISrsReader* reader); private: static int on_message_begin(http_parser* parser); static int on_headers_complete(http_parser* parser); @@ -149,13 +149,13 @@ private: // the query map std::map _query; // the transport connection, can be NULL. - SrsConnection* conn; + SrsConnection* owner_conn; // whether request is jsonp. bool jsonp; // the method in QueryString will override the HTTP method. std::string jsonp_method; public: - SrsHttpMessage(ISrsProtocolReaderWriter* io, SrsConnection* c); + SrsHttpMessage(ISrsReader* io); virtual ~SrsHttpMessage(); public: /** @@ -163,7 +163,9 @@ public: */ virtual srs_error_t update(std::string url, bool allow_jsonp, http_parser* header, SrsFastStream* body, std::vector& headers); public: + // Get the owner connection, maybe NULL. virtual SrsConnection* connection(); + virtual void set_connection(SrsConnection* conn); public: virtual uint8_t method(); virtual uint16_t status_code(); @@ -295,7 +297,7 @@ public: class SrsHttpResponseReader : virtual public ISrsHttpResponseReader { private: - ISrsProtocolReaderWriter* skt; + ISrsReader* skt; SrsHttpMessage* owner; SrsFastStream* buffer; bool is_eof; @@ -306,7 +308,7 @@ private: // already read total bytes. int64_t nb_total_read; public: - SrsHttpResponseReader(SrsHttpMessage* msg, ISrsProtocolReaderWriter* io); + SrsHttpResponseReader(SrsHttpMessage* msg, ISrsReader* reader); virtual ~SrsHttpResponseReader(); public: /** From 928c6ab091edbe4fafc6e0da4042ab8ac874ac5e Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 11 Aug 2018 13:16:59 +0800 Subject: [PATCH 29/33] For #1181, Add utest --- trunk/src/app/srs_app_http_conn.cpp | 2 +- trunk/src/utest/srs_utest_protocol.cpp | 93 ++++++++++++++++++++++++++ trunk/src/utest/srs_utest_protocol.hpp | 2 + 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index f210df847..ff20f78fc 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -243,8 +243,8 @@ srs_error_t SrsResponseOnlyHttpConn::on_got_http_message(ISrsHttpMessage* msg) } // drop all request body. + char body[4096]; while (!br->eof()) { - char body[4096]; if ((err = br->read(body, 4096, NULL)) != srs_success) { return srs_error_wrap(err, "read response"); } diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp index ec50d29e9..5a32a6ba2 100644 --- a/trunk/src/utest/srs_utest_protocol.cpp +++ b/trunk/src/utest/srs_utest_protocol.cpp @@ -33,6 +33,7 @@ using namespace std; #include #include #include +#include MockEmptyIO::MockEmptyIO() { @@ -105,6 +106,12 @@ MockBufferIO::~MockBufferIO() { } +MockBufferIO* MockBufferIO::append(string data) +{ + in_buffer.append(data.data(), data.length()); + return this; +} + bool MockBufferIO::is_never_timeout(int64_t tm) { return tm == SRS_CONSTS_NO_TMMS; @@ -5523,5 +5530,91 @@ VOID TEST(ProtocolRTMPTest, RTMPHandshakeBytes) EXPECT_TRUE(bytes.s0s1s2 != NULL); } +VOID TEST(ProtocolHTTPTest, ParseHTTPMessage) +{ + if (true) { + MockBufferIO bio; + SrsHttpParser hp; + + bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello"); + EXPECT_TRUE(0 == hp.initialize(HTTP_REQUEST, false)); + + if (true) { + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + ASSERT_TRUE(0 == hp.parse_message(&bio, &req)); + + // We should read body, or next parsing message will fail. + // @see https://github.com/ossrs/srs/issues/1181 + EXPECT_FALSE(req->body_reader()->eof()); + } + + if (true) { + bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello"); + + // Should fail because there is body which not read. + // @see https://github.com/ossrs/srs/issues/1181 + + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + ASSERT_FALSE(0 == hp.parse_message(&bio, &req)); + } + } + + if (true) { + MockBufferIO bio; + SrsHttpParser hp; + + bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello"); + ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false)); + + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + ASSERT_TRUE(0 == hp.parse_message(&bio, &req)); + + char v[64] = {0}; + EXPECT_TRUE(0 == req->body_reader()->read(v, sizeof(v), NULL)); + EXPECT_TRUE(string("Hello") == string(v)); + + EXPECT_TRUE(req->body_reader()->eof()); + } + + if (true) { + MockBufferIO bio; + SrsHttpParser hp; + + bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 0\r\n\r\n"); + ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false)); + + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + EXPECT_TRUE(0 == hp.parse_message(&bio, &req)); + } + + if (true) { + MockBufferIO bio; + SrsHttpParser hp; + + bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n"); + ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false)); + + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + EXPECT_TRUE(0 == hp.parse_message(&bio, &req)); + } + + if (true) { + MockBufferIO bio; + SrsHttpParser hp; + + bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n"); + ASSERT_TRUE(0 == hp.initialize(HTTP_REQUEST, false)); + + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + EXPECT_TRUE(0 == hp.parse_message(&bio, &req)); + } +} + #endif diff --git a/trunk/src/utest/srs_utest_protocol.hpp b/trunk/src/utest/srs_utest_protocol.hpp index 5b3a0b080..6b66826f5 100644 --- a/trunk/src/utest/srs_utest_protocol.hpp +++ b/trunk/src/utest/srs_utest_protocol.hpp @@ -86,6 +86,8 @@ public: public: MockBufferIO(); virtual ~MockBufferIO(); +public: + virtual MockBufferIO* append(std::string data); // for protocol public: virtual bool is_never_timeout(int64_t tm); From 1e7c12a6dd32d0c15609a18a5a4840e563d6892a Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 11 Aug 2018 19:09:56 +0800 Subject: [PATCH 30/33] For #910, Support HTTP FLV with HTTP callback. 2.0.254 --- README.md | 2 + trunk/src/app/srs_app_http_conn.cpp | 6 +- trunk/src/app/srs_app_http_stream.cpp | 94 +++++++++++++++++++++++-- trunk/src/app/srs_app_http_stream.hpp | 3 + trunk/src/core/srs_core.hpp | 2 +- trunk/src/protocol/srs_rtmp_stack.cpp | 6 ++ trunk/src/protocol/srs_rtmp_stack.hpp | 3 + trunk/src/protocol/srs_rtmp_utility.cpp | 4 ++ 8 files changed, 111 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 03193b3b8..a07c25596 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 2.0.254 * v2.0, 2018-08-11, For [#1110][bug #1110], Refine params in http callback. 2.0.253 * v2.0, 2018-08-05, Refine HTTP-FLV latency, support realtime mode. 2.0.252 * v2.0, 2018-08-04, For [#1110][bug #1110], Support params in http callback. 2.0.251 @@ -1321,6 +1322,7 @@ Winlin [bug #1119]: https://github.com/ossrs/srs/issues/1119 [bug #1031]: https://github.com/ossrs/srs/issues/1031 [bug #1110]: https://github.com/ossrs/srs/issues/1110 +[bug #910]: https://github.com/ossrs/srs/issues/910 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index 90845890d..88efeedfd 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -864,10 +864,8 @@ SrsRequest* SrsHttpMessage::to_request(string vhost) req->pageUrl = get_request_header("Referer"); req->objectEncoding = 0; - srs_discovery_tc_url(req->tcUrl, - req->schema, req->host, req->vhost, req->app, req->stream, req->port, - req->param); - req->strip(); + srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->stream, req->port, req->param); + req->as_http(); return req; } diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 9181fb4dd..edf0d8c56 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -55,6 +55,7 @@ using namespace std; #include #include #include +#include #endif @@ -64,7 +65,7 @@ using namespace std; SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) { - req = r->copy(); + req = r->copy()->as_http(); source = s; queue = new SrsMessageQueue(true); pthread = new SrsEndlessThread("http-stream", this); @@ -457,7 +458,7 @@ SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsStreamCache* c) { source = s; cache = c; - req = r->copy(); + req = r->copy()->as_http(); } SrsLiveStream::~SrsLiveStream() @@ -472,7 +473,7 @@ int SrsLiveStream::update(SrsSource* s, SrsRequest* r) srs_freep(req); source = s; req = r->copy(); - + return ret; } @@ -480,6 +481,22 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; + if ((ret = http_hooks_on_play()) != ERROR_SUCCESS) { + srs_error("http hook on_play failed. ret=%d", ret); + return ret; + } + + ret = do_serve_http(w, r); + + http_hooks_on_stop(); + + return ret; +} + +int SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + ISrsStreamEncoder* enc = NULL; srs_assert(entry); @@ -619,6 +636,75 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) return ret; } +int SrsLiveStream::http_hooks_on_play() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_AUTO_HTTP_CALLBACK + if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { + return ret; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_play(req->vhost); + + if (!conf) { + srs_info("ignore the empty http callback: on_play"); + return ret; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + if ((ret = SrsHttpHooks::on_play(url, req)) != ERROR_SUCCESS) { + srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); + return ret; + } + } +#endif + + return ret; +} + +void SrsLiveStream::http_hooks_on_stop() +{ +#ifdef SRS_AUTO_HTTP_CALLBACK + if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { + return; + } + + // the http hooks will cause context switch, + // so we must copy all hooks for the on_connect may freed. + // @see https://github.com/ossrs/srs/issues/475 + vector hooks; + + if (true) { + SrsConfDirective* conf = _srs_config->get_vhost_on_stop(req->vhost); + + if (!conf) { + srs_info("ignore the empty http callback: on_stop"); + return; + } + + hooks = conf->args; + } + + for (int i = 0; i < (int)hooks.size(); i++) { + std::string url = hooks.at(i); + SrsHttpHooks::on_stop(url, req); + } +#endif + + return; +} + int SrsLiveStream::streaming_send_messages(ISrsStreamEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs) { int ret = ERROR_SUCCESS; @@ -844,7 +930,7 @@ int SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r) srs_freep(tmpl->req); tmpl->source = s; - tmpl->req = r->copy(); + tmpl->req = r->copy()->as_http(); sflvs[sid] = entry; diff --git a/trunk/src/app/srs_app_http_stream.hpp b/trunk/src/app/srs_app_http_stream.hpp index 60fc23bc5..18c84a5d0 100755 --- a/trunk/src/app/srs_app_http_stream.hpp +++ b/trunk/src/app/srs_app_http_stream.hpp @@ -232,6 +232,9 @@ public: public: virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); private: + virtual int do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); + virtual int http_hooks_on_play(); + virtual void http_hooks_on_stop(); virtual int streaming_send_messages(ISrsStreamEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs); }; diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 7b49613ff..2c41b8f69 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 253 +#define VERSION_REVISION 254 // generated by configure, only macros. #include diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index 8095092cc..fdf45c224 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -1776,6 +1776,12 @@ void SrsRequest::strip() stream = srs_string_trim_start(stream, "/"); } +SrsRequest* SrsRequest::as_http() +{ + schema = "http"; + return this; +} + SrsResponse::SrsResponse() { stream_id = SRS_DEFAULT_SID; diff --git a/trunk/src/protocol/srs_rtmp_stack.hpp b/trunk/src/protocol/srs_rtmp_stack.hpp index 3c8d3edf9..9900000dc 100644 --- a/trunk/src/protocol/srs_rtmp_stack.hpp +++ b/trunk/src/protocol/srs_rtmp_stack.hpp @@ -606,6 +606,9 @@ public: * strip url, user must strip when update the url. */ virtual void strip(); +public: + // Transform it as HTTP request. + virtual SrsRequest* as_http(); }; /** diff --git a/trunk/src/protocol/srs_rtmp_utility.cpp b/trunk/src/protocol/srs_rtmp_utility.cpp index fdbaddb05..c322a9796 100644 --- a/trunk/src/protocol/srs_rtmp_utility.cpp +++ b/trunk/src/protocol/srs_rtmp_utility.cpp @@ -71,6 +71,10 @@ void srs_discovery_tc_url( vhost = host; srs_vhost_resolve(vhost, app, param); srs_vhost_resolve(vhost, stream, param); + + if (param == "?vhost="SRS_CONSTS_RTMP_DEFAULT_VHOST) { + param = ""; + } } void srs_vhost_resolve(string& vhost, string& app, string& param) From 74a87d54f2cdf9d298a7e1f2b3a67551d56af9de Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Aug 2018 10:44:55 +0800 Subject: [PATCH 31/33] For #1202, Support edge/forward to Aliyun CDN. 2.0.255 --- README.md | 2 ++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/protocol/srs_rtmp_stack.cpp | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a07c25596..c1152598e 100755 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Remark: ## History +* v2.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 2.0.255 * v2.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 2.0.254 * v2.0, 2018-08-11, For [#1110][bug #1110], Refine params in http callback. 2.0.253 * v2.0, 2018-08-05, Refine HTTP-FLV latency, support realtime mode. 2.0.252 @@ -1323,6 +1324,7 @@ Winlin [bug #1031]: https://github.com/ossrs/srs/issues/1031 [bug #1110]: https://github.com/ossrs/srs/issues/1110 [bug #910]: https://github.com/ossrs/srs/issues/910 +[bug #1202]: https://github.com/ossrs/srs/issues/1202 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 2c41b8f69..af8692193 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 254 +#define VERSION_REVISION 255 // generated by configure, only macros. #include diff --git a/trunk/src/protocol/srs_rtmp_stack.cpp b/trunk/src/protocol/srs_rtmp_stack.cpp index fdf45c224..dac4cd2eb 100644 --- a/trunk/src/protocol/srs_rtmp_stack.cpp +++ b/trunk/src/protocol/srs_rtmp_stack.cpp @@ -1743,6 +1743,7 @@ void SrsRequest::update_auth(SrsRequest* req) pageUrl = req->pageUrl; swfUrl = req->swfUrl; tcUrl = req->tcUrl; + param = req->param; if (args) { srs_freep(args); From a9b5d65ca747aa59f1ec9993703e56d9d466db87 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Aug 2018 11:52:18 +0800 Subject: [PATCH 32/33] For #1202, Support auth_key param for srs player --- trunk/research/players/js/srs.page.js | 25 ++++++++++++++++++++++--- trunk/research/players/srs_player.html | 5 +++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/trunk/research/players/js/srs.page.js b/trunk/research/players/js/srs.page.js index 3e458c8d2..6a3132741 100755 --- a/trunk/research/players/js/srs.page.js +++ b/trunk/research/players/js/srs.page.js @@ -44,6 +44,27 @@ function update_nav() { $("#nav_vlc").attr("href", "vlc.html" + window.location.search); } +// Special extra params, such as auth_key. +function user_extra_params(query, params) { + var queries = params || []; + var server = (query.server == undefined)? window.location.hostname:query.server; + var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost; + + for (var key in query.user_query) { + if (key == 'app' || key == 'autostart' || key == 'dir' + || key == 'filename' || key == 'host' || key == 'hostname' + || key == 'http_port' || key == 'pathname' || key == 'port' + || key == 'server' || key == 'stream' || key == 'buffer' + || key == 'schema' || key == 'vhost' + ) { + continue; + } + queries.push(key + '=' + query[key]); + } + + return queries; +} + /** @param server the ip of server. default to window.location.hostname @param vhost the vhost of rtmp. default to window.location.hostname @@ -65,9 +86,7 @@ function build_default_rtmp_url() { if (server != vhost && vhost != "__defaultVhost__") { queries.push("vhost=" + vhost); } - if (query.shp_identify) { - queries.push("shp_identify=" + query.shp_identify); - } + queries = user_extra_params(query, queries); var uri = schema + "://" + server + ":" + port + "/" + app + "/" + stream + "?" + queries.join('&'); while (uri.indexOf("?") == uri.length - 1) { diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index 3598b9c6a..757fc0d7d 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -643,6 +643,11 @@ if (query.buffer) { url += "&buffer=" + query.buffer; } + + var queries = user_extra_params(query); + if (queries && queries.length) { + url += '&' + queries.join('&'); + } $("#player_url").text($("#txt_url").val()).attr("href", url); $("#link_server").text(rtmp.server); From 36cebc4b71a867accb4acf3b446265d2df4964b6 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 12 Aug 2018 12:03:31 +0800 Subject: [PATCH 33/33] Release 2.0r4, 2.0.255 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1152598e..0250519e5 100755 --- a/README.md +++ b/README.md @@ -294,7 +294,8 @@ Remark: ## Releases -* 2018-07-18, [Release v2.0-r3][r2.0r3], 2.0 release2, 2.0.248, 86775 lines. +* 2018-08-12, [Release v2.0-r4][r2.0r4], 2.0 release4, 2.0.255, 86915 lines. +* 2018-07-18, [Release v2.0-r3][r2.0r3], 2.0 release3, 2.0.248, 86775 lines. * 2017-06-10, [Release v2.0-r2][r2.0r2], 2.0 release2, 2.0.243, 86670 lines. * 2017-04-18, [Release v2.0-r1][r2.0r1], 2.0 release1, 2.0.239, 86515 lines. * 2017-03-03, [Release v2.0-r0][r2.0r0], 2.0 release0, 2.0.234, 86373 lines. @@ -333,6 +334,7 @@ Remark: ## History +* v2.0, 2018-08-12, [2.0 release4(2.0.255)][r2.0r4] released. 86915 lines. * v2.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 2.0.255 * v2.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 2.0.254 * v2.0, 2018-08-11, For [#1110][bug #1110], Refine params in http callback. 2.0.253 @@ -1329,6 +1331,7 @@ Winlin [exo #828]: https://github.com/google/ExoPlayer/pull/828 +[r2.0r4]: https://github.com/ossrs/srs/releases/tag/v2.0-r4 [r2.0r3]: https://github.com/ossrs/srs/releases/tag/v2.0-r3 [r2.0r2]: https://github.com/ossrs/srs/releases/tag/v2.0-r2 [r2.0r1]: https://github.com/ossrs/srs/releases/tag/v2.0-r1