diff --git a/README.md b/README.md
index 125eaf56c..a0d513d80 100755
--- a/README.md
+++ b/README.md
@@ -184,6 +184,17 @@ Please select according to languages:
### V3 changes
+* v3.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 3.0.40
+* v3.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 3.0.39
+* 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
+* 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
+* 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
* v3.0, 2018-02-13, Fix [#1059][bug #1059], merge from 2.0, supports url with vhost in stream. 3.0.27
@@ -220,6 +231,16 @@ Please select according to languages:
### V2 changes
+* 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
+* 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
+* 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
* v2.0, 2018-01-07, Merge [#1045][bug #1045], fix [#1044][bug #1044], TCP connection alive detection. 2.0.245
@@ -821,6 +842,8 @@ The latency between encoder and player with realtime config([CN][v3_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 used FMLE as encoder for benchmark. The latency of server was 0.1s+,
and the bottleneck was the encoder. For more information, read
[bug #257][bug #257-c0].
@@ -1421,6 +1444,12 @@ 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 #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 #1202]: https://github.com/ossrs/srs/issues/1202
[bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx
[bug #735]: https://github.com/ossrs/srs/issues/735
@@ -1436,10 +1465,13 @@ 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
+[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
[r2.0r0]: https://github.com/ossrs/srs/releases/tag/v2.0-r0
diff --git a/trunk/720p.h264.264 b/trunk/720p.h264.264
new file mode 100644
index 000000000..571b528c6
Binary files /dev/null and b/trunk/720p.h264.264 differ
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index e517a3b06..847e50c89 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -837,7 +837,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(Status OK) and response
# an int value specifies the error code(0 corresponding to success):
@@ -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];
}
}
@@ -1046,6 +1047,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
@@ -1129,6 +1131,7 @@ vhost hls.srs.com {
# we support the variables to generate the notify url:
# [app], replace with the app.
# [stream], replace with the stream.
+ # [param], replace with the param.
# [ts_url], replace with the ts url.
# for the hls http callback, @see http_hooks.on_hls_notify of vhost hooks.callback.srs.com
# @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#on-hls-notify
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=()
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
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);
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_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_edge.cpp b/trunk/src/app/srs_app_edge.cpp
index 7b378ea99..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,7 +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);
+ url = srs_generate_rtmp_url(server, port, req->host, vhost, req->app, req->stream, req->param);
}
// open socket.
@@ -482,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");
}
@@ -492,6 +492,7 @@ srs_error_t SrsEdgeForwarder::start()
if ((err = trd->start()) != srs_success) {
return srs_error_wrap(err, "coroutine");
}
+ srs_trace("edge-fwr publish url %s", url.c_str());
return err;
}
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_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 b403439bd..ce34d35c7 100644
--- a/trunk/src/app/srs_app_hls.cpp
+++ b/trunk/src/app/srs_app_hls.cpp
@@ -789,7 +789,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.
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..ff20f78fc 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;
}
@@ -237,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/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_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp
index 15ec0da06..865f2e2cf 100755
--- a/trunk/src/app/srs_app_http_stream.cpp
+++ b/trunk/src/app/srs_app_http_stream.cpp
@@ -54,10 +54,11 @@ using namespace std;
#include
#include
#include
+#include
SrsBufferCache::SrsBufferCache(SrsSource* s, SrsRequest* r)
{
- req = r->copy();
+ req = r->copy()->as_http();
source = s;
queue = new SrsMessageQueue(true);
trd = new SrsSTCoroutine("http-stream", this);
@@ -466,7 +467,7 @@ SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsBufferCache* c)
{
source = s;
cache = c;
- req = r->copy();
+ req = r->copy()->as_http();
}
SrsLiveStream::~SrsLiveStream()
@@ -476,9 +477,10 @@ SrsLiveStream::~SrsLiveStream()
srs_error_t SrsLiveStream::update(SrsSource* s, SrsRequest* r)
{
- srs_freep(req);
source = s;
- req = r->copy();
+
+ srs_freep(req);
+ req = r->copy()->as_http();
return srs_success;
}
@@ -487,24 +489,51 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
{
srs_error_t err = srs_success;
+ if ((err = http_hooks_on_play()) != srs_success) {
+ return srs_error_wrap(err, "http hook");
+ }
+
+ err = do_serve_http(w, r);
+
+ http_hooks_on_stop();
+
+ return err;
+}
+
+srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
+{
+ srs_error_t err = srs_success;
+
+ string enc_desc;
ISrsBufferEncoder* enc = NULL;
srs_assert(entry);
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_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());
@@ -552,12 +581,28 @@ 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);
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) {
@@ -576,17 +621,15 @@ 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(SRS_CONSTS_RTMP_PULSE_TMMS * 1000);
-
+ // 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_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_TMMS);
+ 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.
@@ -615,6 +658,69 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
return err;
}
+srs_error_t SrsLiveStream::http_hooks_on_play()
+{
+ srs_error_t err = srs_success;
+
+ if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
+ return err;
+ }
+
+ // 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) {
+ return err;
+ }
+
+ hooks = conf->args;
+ }
+
+ for (int i = 0; i < (int)hooks.size(); i++) {
+ std::string url = hooks.at(i);
+ if ((err = SrsHttpHooks::on_play(url, req)) != srs_success) {
+ return srs_error_wrap(err, "rtmp on_play %s", url.c_str());
+ }
+ }
+
+ return err;
+}
+
+void SrsLiveStream::http_hooks_on_stop()
+{
+ 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);
+ }
+
+ return;
+}
+
srs_error_t SrsLiveStream::streaming_send_messages(ISrsBufferEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs)
{
srs_error_t err = srs_success;
@@ -760,7 +866,7 @@ srs_error_t 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;
@@ -776,7 +882,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..02de95f64 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
{
@@ -231,11 +228,14 @@ public:
public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
+ virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
+ virtual srs_error_t http_hooks_on_play();
+ virtual void http_hooks_on_stop();
virtual srs_error_t streaming_send_messages(ISrsBufferEncoder* enc, SrsSharedPtrMessage** msgs, int nb_msgs);
};
/**
- * the srs live entry
+ * The Live Entry, to handle HTTP Live Streaming.
*/
struct SrsLiveEntry
{
@@ -264,8 +264,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
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_rtmp_conn.cpp b/trunk/src/app/srs_app_rtmp_conn.cpp
index cee90deae..cfdc6da76 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) {
@@ -1137,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;
}
@@ -1189,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/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_server.cpp b/trunk/src/app/srs_app_server.cpp
index 0e0cb18df..1f0a25d91 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -653,6 +653,7 @@ srs_error_t SrsServer::acquire_pid_file()
if (fcntl(fd, F_SETLK, &lock) == -1) {
if(errno == EACCES || errno == EAGAIN) {
+ ::close(fd);
srs_error("srs is already running!");
return srs_error_new(ERROR_SYSTEM_PID_ALREADY_RUNNING, "srs is already running");
}
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 4346114c9..c28ad60d2 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 40
// generated by configure, only macros.
#include
diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp
index 90c5cf360..b8cdb7fac 100644
--- a/trunk/src/kernel/srs_kernel_codec.cpp
+++ b/trunk/src/kernel/srs_kernel_codec.cpp
@@ -203,7 +203,7 @@ bool SrsFlvAudio::aac(char* data, int size)
// 1 = 11 kHz = 11025 Hz
// 2 = 22 kHz = 22050 Hz
// 3 = 44 kHz = 44100 Hz
-int srs_flv_srates[] = {5512, 11025, 22050, 44100};
+int srs_flv_srates[] = {5512, 11025, 22050, 44100, 0};
// the sample rates in the codec,
// in the sequence header.
diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp
index a80dd8272..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.
@@ -318,6 +320,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_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp
index 7f2c5334f..0a054bcec 100644
--- a/trunk/src/kernel/srs_kernel_ts.cpp
+++ b/trunk/src/kernel/srs_kernel_ts.cpp
@@ -182,8 +182,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/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp
index 2f421cce8..0412fa789 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,714 +507,623 @@ 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) {
- return srs_success;
- }
-
- 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;
- }
-
+ if (!a || !b) {
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);
+ for(int i = 0; i < size; i++){
+ if(a[i] != b[i]){
+ return false;
}
-
- 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;
- }
- dirname = dirname.substr(pos + 1);
- }
-
- 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_filename(string path)
- {
- std::string filename = path;
- size_t pos = string::npos;
-
- if ((pos = filename.rfind(".")) != string::npos) {
- return filename.substr(0, pos);
- }
-
- return filename;
+ 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_filext(string path)
- {
- size_t pos = string::npos;
-
- if ((pos = path.rfind(".")) != string::npos) {
- return path.substr(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 "";
+ dirname = dirname.substr(0, pos);
}
- bool srs_avc_startswith_annexb(SrsBuffer* stream, int* pnb_start_code)
- {
- char* bytes = stream->data() + stream->pos();
- char* p = bytes;
+ 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;
+ }
+ dirname = dirname.substr(pos + 1);
+ }
+
+ 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);
+ }
+
+ 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;
+ }
- 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;
}
-
- // not match
- if (p[0] != (char)0x00 || p[1] != (char)0x00) {
- return false;
+ return true;
+ }
+
+ p++;
+ }
+
+ return false;
+}
+
+bool srs_aac_startswith_adts(SrsBuffer* stream)
+{
+ char* bytes = stream->data() + stream->pos();
+ char* p = bytes;
+
+ if (!stream->require((int)(p - bytes) + 2)) {
+ 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) {
+ return false;
+ }
+
+ return true;
+}
+
+// @see pycrc reflect at https://github.com/winlinvip/pycrc/blob/master/pycrc/algorithms.py#L107
+uint64_t __crc32_reflect(uint64_t data, int width)
+{
+ uint64_t res = data & 0x01;
+
+ for (int i = 0; i < width - 1; i++) {
+ data >>= 1;
+ res = (res << 1) | (data & 0x01);
+ }
+
+ return res;
+}
+
+// @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)
+{
+ 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);
+
+ 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 {
+ reg = reg << 1;
}
-
- // 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;
+ }
+
+ 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);
+
+ int tbl_idx_width = 8; // table index size.
+
+ 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);
+ }
+
+ reg ^= xor_out;
+ return (uint32_t)(reg & mask);
+}
+
+// @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, poly, reflect_in);
+ __crc32_IEEE_table_initialized = true;
+ }
+
+ 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);
+}
+
+// @see golang encoding/base64/base64.go
+srs_error_t srs_av_base64_decode(string cipher, string& plaintext)
+{
+ srs_error_t err = srs_success;
+
+ // 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+/";
+
+ uint8_t decodeMap[256];
+ memset(decodeMap, 0xff, sizeof(decodeMap));
+
+ for (int i = 0; i < encoder.length(); i++) {
+ decodeMap[(uint8_t)encoder.at(i)] = uint8_t(i);
+ }
+
+ // 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;
+
+ // skip over newlines
+ for (; si < cipher.length() && (cipher.at(si) == '\n' || cipher.at(si) == '\r'); si++) {
+ }
+
+ 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);
}
- return true;
+
+ dinc = j - 1;
+ dlen = j;
+ end = true;
+ break;
}
- p++;
- }
-
- return false;
- }
-
- bool srs_aac_startswith_adts(SrsBuffer* stream)
- {
- char* bytes = stream->data() + stream->pos();
- char* p = bytes;
-
- if (!stream->require((int)(p - bytes) + 2)) {
- 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) {
- 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];
- }
-
- 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
-
-#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
-
-# define AV_WN(s, p, v) AV_WL##s(p, v)
-
-# 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
-
-#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))
-
-#ifndef av_bswap32
- 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,
- };
-
-#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++;
- }
-
- 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;
- }
-
- char *srs_data_to_hex(char *des,const u_int8_t *src,int len)
- {
- if(src == NULL || len == 0 || des == NULL){
- return NULL;
- }
- const char *hex_table = "0123456789ABCDEF";
- for (int i=0; i> 4];
- des[i * 2 + 1] = hex_table[src[i] & 0x0F];
- }
-
- return des;
- }
-
- 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')
+ 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;
- 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;
+ }
+
+ dbuf[j] = decodeMap[(uint8_t)in];
+ if (dbuf[j] == 0xff) {
+ return srs_error_new(ERROR_BASE64_DECODE, "corrupt input at %d", si);
}
}
- return len;
+
+ // 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));
+ }
}
- 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;
+ return err;
+}
+
+#define SPACE_CHARS " \t\r\n"
+
+int av_toupper(int c)
+{
+ if (c >= 'a' && c <= 'z') {
+ c ^= 0x20;
+ }
+ 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;
+}
+
+char *srs_data_to_hex(char *des,const u_int8_t *src,int len)
+{
+ if(src == NULL || len == 0 || des == NULL){
+ return NULL;
+ }
+
+ const char *hex_table = "0123456789ABCDEF";
+
+ for (int i=0; i> 4];
+ des[i * 2 + 1] = hex_table[src[i] & 0x0F];
+ }
+
+ return des;
+}
+
+int srs_hex_to_data(uint8_t* data, const char* p, int size)
+{
+ if (size <= 0 || (size%2) == 1) {
+ return -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;
}
- // 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;
+ uint8_t b = srs_from_hex_char(p[i*2 + 1]);
+ if (b == (uint8_t)-1) {
+ return -1;
}
- // message_length, 3bytes, big-endian
- pp = (char*)&payload_length;
+ data[i] = (a << 4) | b;
+ }
+
+ 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)
+{
+ // 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];
-
- // 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/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp
index 6ead50e40..301ec7b95 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
@@ -180,11 +161,12 @@ extern char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, in
* 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);
+
/**
* convert data string to hex.
*/
-extern char *srs_data_to_hex(char *buff, const uint8_t *src, int s);
+extern char *srs_data_to_hex(char *des, const uint8_t *src, int len);
/**
* generate the c0 chunk header for msg.
diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp
index 9bb0f3f95..388604607 100644
--- a/trunk/src/libs/srs_librtmp.cpp
+++ b/trunk/src/libs/srs_librtmp.cpp
@@ -522,2368 +522,2370 @@ 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);
-
- // free the context and return NULL
- srs_freep(context);
- return NULL;
- }
-
- 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;
- }
-
- 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 ret;
- }
-
- void srs_rtmp_destroy(srs_rtmp_t rtmp)
- {
- if (!rtmp) {
- return;
- }
-
- Context* context = (Context*)rtmp;
+ 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_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 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;
}
- 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;
- }
-
+ 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 ret;
+}
+
+void srs_rtmp_destroy(srs_rtmp_t rtmp)
+{
+ if (!rtmp) {
+ return;
+ }
+
+ 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_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;
- }
-
+ if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) {
return ret;
}
- int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp)
- {
+ if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ 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;
+ }
+ // resolve host
+ if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) {
+ 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);
+ }
+
+ if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ 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;
+ // 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 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;
+ }
+
+ return ret;
#endif
- }
+}
+
+int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp)
+{
+ int ret = ERROR_SUCCESS;
+ srs_error_t err = srs_success;
- 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;
- }
-
+ 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_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_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_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;
+ }
+
+ 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;
}
- 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_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();
+
+ 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_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) {
- 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;
- 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;
- }
-
+ // 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_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();
-
+ 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_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_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;
}
- 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;
- }
-
- if ((err = context->rtmp->play(context->stream, context->stream_id)) != srs_success) {
- ret = srs_error_code(err);
- srs_freep(err);
- return ret;
- }
-
- 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;
-
- if ((err = context->rtmp->fmle_publish(context->stream, 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
+ if ((ret = client.bandwidth_check(
+ start_time, end_time, play_kbps, publish_kbps,
+ play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS
) {
- *start_time = 0;
- *end_time = 0;
- *play_kbps = 0;
- *publish_kbps = 0;
- *play_bytes = 0;
- *publish_bytes = 0;
- *play_duration = 0;
- *publish_duration = 0;
+ return ret;
+ }
+
+ 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();
- int ret = ERROR_SUCCESS;
+ 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();
- srs_assert(rtmp != NULL);
- Context* context = (Context*)rtmp;
-
- SrsBandwidthClient client;
-
- if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) {
+ if (data_size < 0) {
+ ret = ERROR_RTMP_AGGREGATE;
+ srs_error("invalid aggregate message size(negative). ret=%d", ret);
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
- ) {
+ 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;
}
- return ret;
- }
-
-
- int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg)
- {
- int ret = ERROR_SUCCESS;
+ // to common message.
+ SrsCommonMessage o;
- SrsBuffer* stream = new SrsBuffer(msg->payload, msg->size);
- SrsAutoFree(SrsBuffer, stream);
+ 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;
- // 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);
+ if (data_size > 0) {
+ o.size = data_size;
+ o.payload = new char[o.size];
+ stream->read_bytes(o.payload, o.size);
}
- return ret;
+ 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);
}
- 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;
+ 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;
- // 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;
+ // read from cache first.
+ if (!context->msgs.empty()) {
+ std::vector::iterator it = context->msgs.begin();
+ msg = *it;
+ context->msgs.erase(it);
}
- 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_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) {
+ // read from protocol sdk.
+ if (!msg && (err = context->rtmp->recv_message(&msg)) != srs_success) {
ret = srs_error_code(err);
srs_freep(err);
return ret;
}
- srs_assert(msg);
+ // no msg, try again.
+ if (!msg) {
+ continue;
+ }
- // 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);
+ 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_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;
}
- void srs_rtmp_free_packet(char* data)
- {
- srs_freepa(data);
+ 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;
}
- 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 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;
}
- /**
- * 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;
+ 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->aac_raw.mux_sequence_header(codec, sh)) != srs_success) {
+ ret = srs_error_code(err);
+ srs_freep(err);
+ return ret;
+ }
+ context->aac_specific_config = sh;
- char* data = NULL;
- int size = 0;
- if ((err = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != srs_success) {
+ 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;
+ }
+ }
+
+ 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;
}
- return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size);
+ // 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;
+ }
}
- /**
- * 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->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;
- }
+ 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;
}
- codec->aac_packet_type = 1;
- return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp);
+ // 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);
}
- /**
- * 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;
- }
- }
-
- 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;
- /**
- * 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);
- }
-
- 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;
- }
-
- // 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 (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) {
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;
- }
-
- // for IDR frame, the frame is keyframe.
- SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame;
- if (nut == SrsAvcNaluTypeIDR) {
- frame_type = SrsVideoAvcFrameTypeKeyFrame;
- }
-
- 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;
- }
-
- 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;
- }
-
- // 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);
+ // adts always 7bytes.
+ if (ac_raw_size <= 7) {
+ return size;
}
- /**
- * 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;
- }
-
- // 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;
- }
-
- // 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;
- }
-
- // 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);
+ // 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;
}
- /**
- * 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;
+ // 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;
+ }
+
+ // for IDR frame, the frame is keyframe.
+ SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame;
+ if (nut == SrsAvcNaluTypeIDR) {
+ frame_type = SrsVideoAvcFrameTypeKeyFrame;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
- // empty frame.
+ if (context->h264_sps == sps) {
+ return ERROR_H264_DUPLICATED_SPS;
+ }
+ context->h264_sps_changed = true;
+ context->h264_sps = sps;
+
+ return ret;
+ }
+
+ // 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;
+
+ 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;
+ }
+
+ // send pps+sps before ipb frames when sps/pps changed.
+ if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) {
+ return ret;
+ }
+
+ // 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;
+ }
+
+ // ignore invalid frame,
+ // atleast 1bytes for SPS to decode the type
if (frame_size <= 0) {
- return ret;
+ continue;
}
- // 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;
- }
+ // 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;
- if (context->h264_sps == sps) {
- return ERROR_H264_DUPLICATED_SPS;
- }
- context->h264_sps_changed = true;
- context->h264_sps = sps;
-
- return ret;
- }
-
- // 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;
-
- 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;
- }
-
- // send pps+sps before ipb frames when sps/pps changed.
- if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) {
- return ret;
- }
-
- // 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;
- }
-
- // ignore invalid frame,
- // atleast 1bytes for SPS to decode the type
- if (frame_size <= 0) {
+ // 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;
}
- // 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;
- }
-
- 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);
-
- 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;
}
+ }
+
+ 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);
+ 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;
}
- 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;
- }
-
- if (ht == SrsMp4HandlerTypeForbidden) {
- return ERROR_MP4_ILLEGAL_HANDLER;
- }
-
- 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;
-
+ 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;
}
- void srs_mp4_free_sample(srs_mp4_sample_t* s)
- {
- srs_freepa(s->sample);
+ if (ht == SrsMp4HandlerTypeForbidden) {
+ return ERROR_MP4_ILLEGAL_HANDLER;
}
- 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;
+ 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;
+
+ 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 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;
+ 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;
+
+ *time = s->dts;
+
+ SrsBuffer p(data, size);
+ if (s->handler_type == SrsMp4HandlerTypeSOUN) {
+ *type = SRS_RTMP_TYPE_AUDIO;
- *time = s->dts;
-
- SrsBuffer p(data, size);
- if (s->handler_type == SrsMp4HandlerTypeSOUN) {
- *type = SRS_RTMP_TYPE_AUDIO;
-
- // 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;
+ // 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));
}
- // 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);
-
return ret;
}
- srs_bool srs_mp4_is_eof(int error_code)
- {
- return error_code == ERROR_SYSTEM_FILE_EOF;
+ // 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);
+
+ 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);
+
+ srs_freep(flv);
+ return NULL;
}
- 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;
+ 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);
- 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;
+ srs_freep(flv);
+ return NULL;
}
- srs_flv_t srs_flv_open_write(const char* file)
- {
- int ret = ERROR_SUCCESS;
- srs_error_t err = srs_success;
+ 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);
- 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 flv;
+ srs_freep(flv);
+ return NULL;
}
- void srs_flv_close(srs_flv_t flv)
- {
- FlvContext* context = (FlvContext*)flv;
- srs_freep(context);
+ 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;
}
- 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) {
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
}
-
- char ts[4]; // tag size
- if ((err = context->dec.read_previous_tag_size(ts)) != 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);
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) {
+ } 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_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;
- }
-
- 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;
- }
-
- return ret;
- }
+ 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;
- 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;
- }
-
- return ret;
- }
+ srs_amf0_t amf0 = NULL;
- 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;
- }
+ SrsBuffer stream(data, size);
- 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 amf0 = NULL;
-
- SrsBuffer stream(data, size);
-
- SrsAmf0Any* any = NULL;
- if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) {
- srs_freep(err);
- return amf0;
- }
-
- stream.skip(-1 * stream.pos());
- if ((err = any->read(&stream)) != srs_success) {
- srs_freep(err);
- srs_freep(any);
- return amf0;
- }
-
- if (nparsed) {
- *nparsed = stream.pos();
- }
- amf0 = (srs_amf0_t)any;
-
+ SrsAmf0Any* any = NULL;
+ if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) {
+ srs_freep(err);
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));
-
- 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;
- }
-
- void srs_amf0_free(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
+ stream.skip(-1 * stream.pos());
+ if ((err = any->read(&stream)) != srs_success) {
+ srs_freep(err);
srs_freep(any);
+ return amf0;
}
- int srs_amf0_size(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->total_size();
+ if (nparsed) {
+ *nparsed = stream.pos();
+ }
+ amf0 = (srs_amf0_t)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));
+
+ 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());
- srs_bool srs_amf0_is_number(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->is_number();
- }
+ 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());
- srs_bool srs_amf0_is_null(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->is_null();
- }
+ 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());
- srs_bool srs_amf0_is_object(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return 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());
- srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->is_ecma_array();
- }
+ 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());
- srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->is_strict_array();
- }
+ 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());
- const char* srs_amf0_to_string(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->to_str_raw();
- }
+ 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());
- srs_bool srs_amf0_to_boolean(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->to_boolean();
- }
+ 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());
- srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- return any->to_number();
- }
+ 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());
- void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value)
- {
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
- any->set_number(value);
- }
+ 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());
- 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();
- }
+ 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());
- 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);
- }
+ 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());
- 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);
- }
+ 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_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);
+ 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;
}
-
- 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);
+ 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;
- 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());
-
- 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;
-
- 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;
-
+ if (type != SRS_RTMP_TYPE_VIDEO) {
+ *ppts = time;
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;
+ if (!SrsFlvVideo::h264(data, size)) {
+ return ERROR_FLV_INVALID_VIDEO_TAG;
}
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- char srs_utils_flv_audio_sound_type(char* data, int size)
- {
- if (size < 1) {
- return -1;
- }
-
- 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;
- }
-
- char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize)
- {
- if (!amf0) {
- return NULL;
- }
-
- SrsAmf0Any* any = (SrsAmf0Any*)amf0;
-
- 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";
-
- 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;
- }
-
- 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;
- }
-
- 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";
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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);
- }
-
+ if (SrsFlvVideo::sh(data, size)) {
+ *ppts = time;
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;
+ // 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;
+}
+
+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;
+ }
+
+ 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;
+ }
+
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+char srs_utils_flv_audio_sound_type(char* data, int size)
+{
+ if (size < 1) {
+ return -1;
+ }
+
+ 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;
+}
+
+char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize)
+{
+ if (!amf0) {
+ return NULL;
+ }
+
+ SrsAmf0Any* any = (SrsAmf0Any*)amf0;
+
+ 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";
+
+ 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;
+}
+
+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;
+}
+
+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";
+
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+ }
+
+ 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]);
}
-
- // 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);
- }
- 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);
- }
-
+ }
+
+ 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;
}
- 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;
+ 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);
}
-
- // to calendar time
- struct tm* tm;
- if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) {
- return buf;
+ buffer[nb] = 0;
+ } else {
+ snprintf(buffer, nb_buffer, "Rtmp packet type=%#x, dts=%d, pts=%d, size=%d, (%s)",
+ type, timestamp, pts, size, sbytes);
+ }
+
+ 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]);
}
-
- 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;
-
+ }
+
+ 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);
+ }
+ 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);
+ }
+
+ 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;
}
+
+ Context* context = (Context*)rtmp;
+ if (!context->skt) {
+ return NULL;
+ }
+
+ return context->skt->hijack_io();
+}
#endif
+
+srs_rtmp_t srs_rtmp_create2(const char* url)
+{
+ Context* context = new Context();
- 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);
- // 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;
+ // free the context and return NULL
+ srs_freep(context);
+ return NULL;
}
- 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 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;
}
- 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);
- }
+ srs_assert(rtmp != NULL);
+ Context* context = (Context*)rtmp;
+ SrsServerInfo* si = &context->si;
- 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);
- }
+ 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);
- 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;
- }
+ 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
}
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);
+/**
+ * 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;
- /**
- * Free the packet allocated by srs_rtmp_read_packet.
- */
- extern void srs_rtmp_free_packet(char* data);
+ // 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;
- /**
- * whether type is script data and the data is onMetaData.
- */
- extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size);
+ // The video pts in milliseconds. Ignore for audio.
+ uint32_t pts;
+ // The video frame type, it's SrsVideoAvcFrameType.
+ uint16_t frame_type;
- /*************************************************************
- **************************************************************
- * 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);
-
- /**
- * 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();
+ // 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
}
diff --git a/trunk/src/main/srs_main_ingest_hls.cpp b/trunk/src/main/srs_main_ingest_hls.cpp
index 3caa5968d..65a321111 100644
--- a/trunk/src/main/srs_main_ingest_hls.cpp
+++ b/trunk/src/main/srs_main_ingest_hls.cpp
@@ -906,10 +906,12 @@ int SrsIngestHlsOutput::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.
SrsBuffer avs(msg->payload->bytes(), msg->payload->length());
@@ -939,6 +941,7 @@ int SrsIngestHlsOutput::flush_message_queue()
std::multimap::iterator it = queue.begin();
SrsTsMessage* msg = it->second;
+ SrsAutoFree(SrsTsMessage, msg);
queue.erase(it);
// parse the stream.
@@ -1286,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..402875f35 100644
--- a/trunk/src/protocol/srs_protocol_utility.cpp
+++ b/trunk/src/protocol/srs_protocol_utility.cpp
@@ -113,6 +113,11 @@ void srs_discovery_tc_url(string tcUrl, string& schema, string& host, string& vh
vhost = host;
srs_vhost_resolve(vhost, app, param);
srs_vhost_resolve(vhost, stream, param);
+
+ // Ignore when the param only contains the default vhost.
+ if (param == "?vhost="SRS_CONSTS_RTMP_DEFAULT_VHOST) {
+ param = "";
+ }
}
void srs_parse_query_string(string q, map& query)
@@ -151,43 +156,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)
+string srs_generate_stream_with_query(string host, string vhost, string stream, 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)
-{
- 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 +308,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..bda2af202 100644
--- a/trunk/src/protocol/srs_rtmp_stack.cpp
+++ b/trunk/src/protocol/srs_rtmp_stack.cpp
@@ -1581,6 +1581,7 @@ void SrsRequest::update_auth(SrsRequest* req)
pageUrl = req->pageUrl;
swfUrl = req->swfUrl;
tcUrl = req->tcUrl;
+ param = req->param;
ip = req->ip;
vhost = req->vhost;
@@ -1624,6 +1625,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;
@@ -2053,7 +2060,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 +2088,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 +2414,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..8840b72a5 100644
--- a/trunk/src/protocol/srs_rtmp_stack.hpp
+++ b/trunk/src/protocol/srs_rtmp_stack.hpp
@@ -602,6 +602,9 @@ public:
* strip url, user must strip when update the url.
*/
virtual void strip();
+public:
+ // Transform it as HTTP request.
+ virtual SrsRequest* as_http();
};
/**
@@ -729,12 +732,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/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp
index f20daa014..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);
}
}
@@ -583,23 +587,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/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:
/**
diff --git a/trunk/src/service/srs_service_rtmp_conn.cpp b/trunk/src/service/srs_service_rtmp_conn.cpp
index fa5e103c4..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,24 +150,30 @@ 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;
+ // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733
+ string stream = srs_generate_stream_with_query(req->host, req->vhost, req->stream, req->param);
+
// publish.
- if ((err = client->publish(req->stream, stream_id)) != srs_success) {
- return srs_error_wrap(err, "publish failed, stream=%s, stream_id=%d", req->stream.c_str(), stream_id);
+ 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;
- if ((err = client->play(req->stream, stream_id)) != srs_success) {
- return srs_error_wrap(err, "connect with server failed, stream=%s, stream_id=%d", req->stream.c_str(), stream_id);
+ // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733
+ string stream = srs_generate_stream_with_query(req->host, req->vhost, req->stream, req->param);
+
+ 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);
}
return err;
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/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp
index 9a5eea12e..a30c6d524 100644
--- a/trunk/src/service/srs_service_st.cpp
+++ b/trunk/src/service/srs_service_st.cpp
@@ -136,6 +136,7 @@ srs_error_t srs_socket_connect(string server, int port, int64_t tm, srs_netfd_t*
srs_assert(!stfd);
stfd = st_netfd_open_socket(sock);
if(stfd == NULL){
+ ::close(sock);
return srs_error_new(ERROR_ST_OPEN_SOCKET, "open socket");
}
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);
diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp
index 1354dd3f8..e2e66f794 100644
--- a/trunk/src/utest/srs_utest_kernel.cpp
+++ b/trunk/src/utest/srs_utest_kernel.cpp
@@ -1523,5 +1523,189 @@ 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",
+ "SRS's a simplest, conceptual integrated, industrial-strength live streaming origin cluster."
+ };
+
+ uint32_t checksums[] = {
+ 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, (uint32_t)srs_crc32_mpegts(data.data(), data.length()));
+ }
+}
+
+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);
+}
+
+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
diff --git a/trunk/src/utest/srs_utest_protocol.cpp b/trunk/src/utest/srs_utest_protocol.cpp
index 3979cd351..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;
@@ -559,15 +566,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());
}
@@ -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);