mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 03:41:55 +00:00
Merge branch '3.0release' into develop
This commit is contained in:
commit
94b1cf29c3
29 changed files with 1986 additions and 1840 deletions
43
.github/ISSUE_TEMPLATE
vendored
43
.github/ISSUE_TEMPLATE
vendored
|
@ -1,37 +1,44 @@
|
|||
---
|
||||
name: 提Bug
|
||||
about: 描述一个Bug帮助改进系统
|
||||
name: File bug
|
||||
about: File bug to improve SRS
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**描述**
|
||||
**描述(Description)**
|
||||
|
||||
描述你遇到了什么问题。
|
||||
(Please description your issue here.)
|
||||
|
||||
**环境**
|
||||
1. 操作系统:```...```
|
||||
1. 编码器(工具和版本):```...```
|
||||
1. 播放器(工具和版本):```...```
|
||||
1. SRS版本: ```...```
|
||||
1. SRS的日志如下:
|
||||
**环境(Environment)**
|
||||
|
||||
1. SRS版本(Version): `xxxxxx`
|
||||
1. 操作系统(OS):`xxxxxx`
|
||||
1. 编码器(工具和版本)(Encoder):`xxxxxx`
|
||||
1. 播放器(工具和版本)(Player):`xxxxxx`
|
||||
1. SRS的日志如下(Log):
|
||||
```
|
||||
......
|
||||
xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
**重现**
|
||||
**重现(Replay)**
|
||||
|
||||
重现Bug的步骤如下:
|
||||
1. 启动SRS,运行 ```...```
|
||||
1. 推流,运行 ```...```
|
||||
1. 播放,运行 ```...```
|
||||
1. 操作 ```...```
|
||||
1. 重现了Bug,关键信息如下:
|
||||
(How to replay bug?)
|
||||
|
||||
1. 启动SRS,运行(Start SRS by) `xxxxxx`
|
||||
1. 推流,运行(Publish by) `xxxxxx`
|
||||
1. 播放,运行(Play by) `xxxxxx`
|
||||
1. 操作(Other steps) `xxxxxx`
|
||||
1. 重现了Bug,关键信息如下(Bug replayed, logs):
|
||||
|
||||
```
|
||||
...
|
||||
xxxxxx
|
||||
```
|
||||
|
||||
**期望行为**
|
||||
**期望行为(Expect)**
|
||||
|
||||
描述你期望发生的事情。
|
||||
(Please describe your expectation.)
|
||||
|
|
12
README.md
12
README.md
|
@ -147,6 +147,13 @@ For previous versions, please read:
|
|||
|
||||
## V3 changes
|
||||
|
||||
* <strong>v3.0, 2019-12-19, [3.0 alpha5(3.0.75)][r3.0a5] released. 115362 lines.</strong>
|
||||
* v3.0, 2019-12-19, Refine the RTMP iovs cache increasing to much faster.
|
||||
* v3.0, 2019-12-19, Fix [#1524][bug #1524], memory leak for amf0 strict array. 3.0.75
|
||||
* v3.0, 2019-12-19, Fix random build failed bug for modules.
|
||||
* v3.0, 2019-12-19, Fix [#1520][bug #1520] and [#1223][bug #1223], bug for origin cluster 3+ servers. 3.0.74
|
||||
* v3.0, 2019-12-18, For [#1042][bug #1042], add test for RAW AVC protocol.
|
||||
* v3.0, 2019-12-18, Detect whether flash enabled for srs-player. 3.0.73
|
||||
* v3.0, 2019-12-17, Fix HTTP CORS bug when sending response for OPTIONS. 3.0.72
|
||||
* v3.0, 2019-12-17, Enhance HTTP response write for final_request.
|
||||
* v3.0, 2019-12-17, Refactor HTTP stream to disconnect client when unpublish.
|
||||
|
@ -671,6 +678,7 @@ For previous versions, please read:
|
|||
|
||||
## Releases
|
||||
|
||||
* 2019-12-19, [Release v3.0-a5][r3.0a5], 3.0 alpha5, 3.0.75, 115362 lines.
|
||||
* 2019-12-13, [Release v3.0-a4][r3.0a4], 3.0 alpha4, 3.0.71, 112928 lines.
|
||||
* 2019-11-30, [Release v3.0-a3][r3.0a3], 3.0 alpha3, 3.0.67, 110864 lines.
|
||||
* 2019-11-30, [Release v3.0-a2][r3.0a2], 3.0 alpha2, 3.0.66, 110831 lines.
|
||||
|
@ -1508,6 +1516,7 @@ Winlin
|
|||
[bug #1339]: https://github.com/ossrs/srs/pull/1339
|
||||
[bug #1312]: https://github.com/ossrs/srs/pull/1312
|
||||
[bug #1304]: https://github.com/ossrs/srs/pull/1304
|
||||
[bug #1524]: https://github.com/ossrs/srs/issues/1524
|
||||
[bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx
|
||||
|
||||
[bug #735]: https://github.com/ossrs/srs/issues/735
|
||||
|
@ -1532,6 +1541,8 @@ Winlin
|
|||
[bug #1042]: https://github.com/ossrs/srs/issues/1042
|
||||
[bug #1445]: https://github.com/ossrs/srs/issues/1445
|
||||
[bug #1506]: https://github.com/ossrs/srs/issues/1506
|
||||
[bug #1520]: https://github.com/ossrs/srs/issues/1520
|
||||
[bug #1223]: https://github.com/ossrs/srs/issues/1223
|
||||
[bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
|
||||
|
||||
[bug #1111]: https://github.com/ossrs/srs/issues/1111
|
||||
|
@ -1540,6 +1551,7 @@ Winlin
|
|||
|
||||
[exo #828]: https://github.com/google/ExoPlayer/pull/828
|
||||
|
||||
[r3.0a5]: https://github.com/ossrs/srs/releases/tag/v3.0-a5
|
||||
[r3.0a4]: https://github.com/ossrs/srs/releases/tag/v3.0-a4
|
||||
[r3.0a3]: https://github.com/ossrs/srs/releases/tag/v3.0-a3
|
||||
[r3.0a2]: https://github.com/ossrs/srs/releases/tag/v3.0-a2
|
||||
|
|
|
@ -19,7 +19,7 @@ SRS_HDS=NO
|
|||
SRS_NGINX=NO
|
||||
SRS_FFMPEG_TOOL=NO
|
||||
SRS_LIBRTMP=NO
|
||||
SRS_RESEARCH=YES
|
||||
SRS_RESEARCH=NO
|
||||
SRS_UTEST=YES
|
||||
SRS_GPERF=NO # Performance test: tcmalloc
|
||||
SRS_GPERF_MC=NO # Performance test: gperf memory check
|
||||
|
|
|
@ -665,7 +665,7 @@ vhost refer.anti_suck.com {
|
|||
}
|
||||
}
|
||||
|
||||
# vhost for bandwidth check
|
||||
# vhost for bwt(bandwidth check)
|
||||
# generally, the bandcheck vhost must be: bandcheck.srs.com,
|
||||
# or need to modify the vhost of client.
|
||||
vhost bandcheck.srs.com {
|
||||
|
|
|
@ -15,6 +15,6 @@ vhost __defaultVhost__ {
|
|||
cluster {
|
||||
mode local;
|
||||
origin_cluster on;
|
||||
coworkers 127.0.0.1:9091;
|
||||
coworkers 127.0.0.1:9090 127.0.0.1:9091 127.0.0.1:9092;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ vhost __defaultVhost__ {
|
|||
cluster {
|
||||
mode local;
|
||||
origin_cluster on;
|
||||
coworkers 127.0.0.1:9090;
|
||||
coworkers 127.0.0.1:9090 127.0.0.1:9091 127.0.0.1:9092;
|
||||
}
|
||||
}
|
||||
|
|
20
trunk/conf/origin.cluster.serverC.conf
Normal file
20
trunk/conf/origin.cluster.serverC.conf
Normal file
|
@ -0,0 +1,20 @@
|
|||
# the config for srs origin-origin cluster
|
||||
# @see https://github.com/ossrs/srs/wiki/v3_EN_OriginCluster
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19352;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
pid ./objs/origin.cluster.serverC.pid;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 9092;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode local;
|
||||
origin_cluster on;
|
||||
coworkers 127.0.0.1:9090 127.0.0.1:9091 127.0.0.1:9092;
|
||||
}
|
||||
}
|
12
trunk/configure
vendored
12
trunk/configure
vendored
|
@ -83,6 +83,13 @@ for SRS_MODULE in ${SRS_MODULES[*]}; do
|
|||
fi
|
||||
done
|
||||
|
||||
# generate extra phony for each modules.
|
||||
cat << END > ${SRS_OBJS}/${SRS_MAKEFILE}
|
||||
|
||||
.PHONY: $__mphonys
|
||||
|
||||
END
|
||||
|
||||
#####################################################################################
|
||||
# build tools or compiler args.
|
||||
# enable gdb debug
|
||||
|
@ -112,7 +119,7 @@ if [[ $SRS_GCOV == YES ]]; then
|
|||
CXXFLAGS="${CXXFLAGS} ${SrsGcov}";
|
||||
fi
|
||||
# Start to generate the Makefile.
|
||||
cat << END > ${SRS_OBJS}/${SRS_MAKEFILE}
|
||||
cat << END >> ${SRS_OBJS}/${SRS_MAKEFILE}
|
||||
GCC = gcc
|
||||
CXX = g++
|
||||
AR = ar
|
||||
|
@ -315,7 +322,8 @@ fi
|
|||
# utest, the unit-test cases of srs, base on gtest1.6
|
||||
if [ $SRS_UTEST = YES ]; then
|
||||
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_protocol" "srs_utest_kernel" "srs_utest_core"
|
||||
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_reload" "srs_utest_service" "srs_utest_app")
|
||||
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
|
||||
"srs_utest_service" "srs_utest_app")
|
||||
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
|
||||
ModuleLibFiles=(${LibSTfile} ${LibSSLfile})
|
||||
MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// to query the swf anti cache.
|
||||
function srs_get_version_code() { return "1.31"; }
|
||||
function srs_get_version_code() { return "1.33"; }
|
||||
|
||||
/**
|
||||
* player specified size.
|
||||
|
@ -18,13 +18,6 @@ function srs_get_player_vhost() { return "players"; }
|
|||
function srs_get_api_server_port() { return 8085; }
|
||||
// the srs http server port
|
||||
function srs_get_srs_http_server_port() { return 8080; }
|
||||
// get the stream published to vhost,
|
||||
// generally we need to transcode the stream to support HLS and filters.
|
||||
// for example, src_vhost is "players", we transcode stream to vhost "players_pub".
|
||||
// if not equals to the player vhost, return the orignal vhost.
|
||||
function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); }
|
||||
// for chat, use rtmp only vhost, low latecy, without gop cache.
|
||||
function srs_get_player_chat_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_chat"); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -59,7 +52,10 @@ function user_extra_params(query, params) {
|
|||
) {
|
||||
continue;
|
||||
}
|
||||
queries.push(key + '=' + query[key]);
|
||||
|
||||
if (query[key]) {
|
||||
queries.push(key + '=' + query[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
|
@ -75,12 +71,12 @@ function user_extra_params(query, params) {
|
|||
function build_default_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var schema = (query.schema == undefined)? "rtmp":query.schema;
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? schema=="http"?80:1935:query.port;
|
||||
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "demo":query.stream;
|
||||
var schema = (!query.schema)? "rtmp":query.schema;
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? schema=="http"?80:1935:query.port;
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "livestream":query.stream;
|
||||
|
||||
var queries = [];
|
||||
if (server != vhost && vhost != "__defaultVhost__") {
|
||||
|
@ -99,16 +95,16 @@ function build_default_rtmp_url() {
|
|||
function build_default_publish_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var schema = (query.schema == undefined)? "rtmp":query.schema;
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? schema=="http"?80:1935:query.port;
|
||||
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "demo":query.stream;
|
||||
var schema = (!query.schema)? "rtmp":query.schema;
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? schema=="http"?80:1935:query.port;
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "demo":query.stream;
|
||||
|
||||
var queries = [];
|
||||
if (server != vhost && vhost != "__defaultVhost__") {
|
||||
queries.push("vhost=" + srs_get_player_chat_vhost(vhost));
|
||||
queries.push("vhost=" + vhost);
|
||||
}
|
||||
if (query.shp_identify) {
|
||||
queries.push("shp_identify=" + query.shp_identify);
|
||||
|
@ -125,11 +121,11 @@ function build_default_publish_rtmp_url() {
|
|||
function build_default_bandwidth_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? 1935:query.port;
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? 1935:query.port;
|
||||
var vhost = "bandcheck.srs.com";
|
||||
var app = (query.app == undefined)? "app":query.app;
|
||||
var key = (query.key == undefined)? "35c9b402c12a7246868752e2878f7e0e":query.key;
|
||||
var app = (!query.app)? "app":query.app;
|
||||
var key = (!query.key)? "35c9b402c12a7246868752e2878f7e0e":query.key;
|
||||
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "?key=" + key + "&vhost=" + vhost;
|
||||
}
|
||||
|
@ -153,11 +149,11 @@ function build_default_hls_url() {
|
|||
server = query.hls_vhost;
|
||||
}
|
||||
|
||||
var port = (query.hls_port == undefined)? window.location.port:query.hls_port;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "demo":query.stream;
|
||||
var port = (!query.hls_port)? window.location.port:query.hls_port;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "demo":query.stream;
|
||||
|
||||
if (port == "" || port == null || port == undefined) {
|
||||
if (!port) {
|
||||
port = 8080;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,9 @@ SrsPlayer.prototype.start = function(url) {
|
|||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
if (!callbackObj.success) {
|
||||
console.error('Initialize player failed:'); console.error(callbackObj);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -35,39 +35,58 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>点击“开始测速”即可测带宽,最大可测试带宽由服务器限制</span>
|
||||
<div name="detect_flash">
|
||||
<div id="main_flash_alert" class="alert alert-danger fade in hide">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><p>Usage:</p></strong>
|
||||
<p>
|
||||
请点击下面的图标,启用Flash
|
||||
</p>
|
||||
<p>
|
||||
若没有见到这个图标,Chrome浏览器请打开
|
||||
<span class="text-info">chrome://settings/content/flash</span> 并修改为"Ask first"。
|
||||
</p>
|
||||
</div>
|
||||
<div id="main_flash_hdr" class="hide">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play">开始测速</button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>SRS Bandwidth Check</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row-fluid">
|
||||
<div class="span1"></div>
|
||||
<div class="span10">
|
||||
<div class="progress progress-striped active" id="pb_buffer_bg">
|
||||
<div class="bar" style="width: 0%;" id="progress_bar"></div>
|
||||
|
||||
<div id="main_content" class="hide">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>点击“开始测速”即可测带宽,最大可测试带宽由服务器限制</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play">开始测速</button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>SRS Bandwidth Check</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row-fluid">
|
||||
<div class="span1"></div>
|
||||
<div class="span10">
|
||||
<div class="progress progress-striped active" id="pb_buffer_bg">
|
||||
<div class="bar" style="width: 0%;" id="progress_bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span1"></div>
|
||||
</div>
|
||||
<div class="span1"></div>
|
||||
</div>
|
||||
<div id="check_status">status</div>
|
||||
<div id="check_info">info</div>
|
||||
<div id="check_status">status</div>
|
||||
<div id="check_info">info</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p></p>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
<div class="container">
|
||||
|
@ -86,15 +105,57 @@
|
|||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript" src="srs_bwt/src/srs.bandwidth.js"></script>
|
||||
<script type="text/javascript">
|
||||
var bandwidth = null;
|
||||
var __on_flash_ready = null;
|
||||
|
||||
$(function(){
|
||||
// 探测Flash是否正常启用。
|
||||
$('#main_flash_hdr').html(
|
||||
'\
|
||||
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="100%"> \
|
||||
<param name="movie" value="srs_player/release/srs_player.swf"> \
|
||||
<param name="quality" value="autohigh"> \
|
||||
<param name="swliveconnect" value="true"> \
|
||||
<param name="allowScriptAccess" value="always"> \
|
||||
<param name="bgcolor" value="#0"> \
|
||||
<param name="allowFullScreen" value="true"> \
|
||||
<param name="wmode" value="opaque"> \
|
||||
<param name="FlashVars" value="log=1"> \
|
||||
<param name="flashvars" value="id=1&on_player_ready=__on_flash_ready"> \
|
||||
<embed src="srs_player/release/srs_player.swf" width="100%" height="100%" \
|
||||
quality="autohigh" bgcolor="#0" align="middle" allowfullscreen="true" allowscriptaccess="always" \
|
||||
type="application/x-shockwave-flash" swliveconnect="true" wmode="opaque" \
|
||||
flashvars="id=1&on_player_ready=__on_flash_ready" \
|
||||
pluginspage="http://www.macromedia.com/go/getflashplayer"> \
|
||||
</object> \
|
||||
'
|
||||
);
|
||||
$('#main_flash_hdr').show();
|
||||
|
||||
var showFlashHdr = setTimeout(function(){
|
||||
$('#main_flash_alert').show();
|
||||
}, 300);
|
||||
|
||||
__on_flash_ready = function (id) {
|
||||
clearTimeout(showFlashHdr);
|
||||
|
||||
$('#main_flash_alert').hide();
|
||||
$('#main_flash_hdr').hide();
|
||||
$('#main_content').show();
|
||||
|
||||
autoLoadPage();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var bandwidth = null;
|
||||
|
||||
var autoLoadPage = function() {
|
||||
srs_init_bwt("#txt_url");
|
||||
|
||||
$("#btn_play").click(on_click_play);
|
||||
$("#main_modal").on("show", on_start_bandwidth_test);
|
||||
$("#main_modal").on("hide", on_stop_bandwidth_test);
|
||||
});
|
||||
};
|
||||
|
||||
function on_click_play() {
|
||||
$("#check_status").text("");
|
||||
|
@ -128,8 +189,7 @@
|
|||
}
|
||||
bandwidth.on_srs_info = function(srs_server, srs_primary, srs_authors, srs_id, srs_pid, srs_server_ip) {
|
||||
$("#check_info").text(
|
||||
"server:" + srs_server + ", primary:" + srs_primary + ", authors:" + srs_authors +
|
||||
", srs_id:" + srs_id + ", srs_pid:" + srs_pid + ", ip:" + srs_server_ip
|
||||
"server:" + srs_server + ", srs_id:" + srs_id + ", srs_pid:" + srs_pid + ", ip:" + srs_server_ip
|
||||
);
|
||||
}
|
||||
bandwidth.on_error = function(code) {
|
||||
|
|
|
@ -35,215 +35,13 @@
|
|||
</div>
|
||||
<div class="container">
|
||||
<!-- for the log -->
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<div class="alert alert-danger fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">Usage:</span></strong>
|
||||
<span id="txt_log_msg">输入名字,设点“加入会议”按钮</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
<button class="btn input-small" id="btn_video_settings">摄像头</button>
|
||||
<button class="btn input-small" id="btn_audio_settings">麦克风</button>
|
||||
<input type="text" id="txt_name" class="input-large" placeholder="请输入您的名字..." value=""></input>
|
||||
<button class="btn input-small" id="btn_join">加入会议</button>
|
||||
<input type="text" id="txt_url" class="input-mini hide" value=""></input>
|
||||
</div>
|
||||
</div>
|
||||
<table id="lst_chats" class="table">
|
||||
<tr>
|
||||
<td id="td_0">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>[我的] 本地摄像头</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="local_publisher"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td id="td_1">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>[我的] 远程服务器流</strong>
|
||||
<a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="realtime_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td id="td_2"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="container hide" id="template">
|
||||
<div class="accordion-group" id="collapse_main">
|
||||
<div class="accordion-heading" title="点击展开或收起,收起后停止播放流,展开时从服务器请求流">
|
||||
<span id="headerN" class="accordion-toggle" data-toggle="collapse" href="#collapseN">
|
||||
<strong>[<a href="#"><span id="user_name">XX</span></a>]</strong>
|
||||
<strong>加入时间</strong>[<span id="join_date"></span>]
|
||||
<a id="user_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapseM" class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="chat_player">
|
||||
<div id="chat_player_raw">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table">
|
||||
</table>
|
||||
<div id="video_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>视频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_cameras">
|
||||
摄像头
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_cameras"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_vcodec">
|
||||
Codec
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_vcodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_profile">
|
||||
Profile
|
||||
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_profile"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_level">
|
||||
Level
|
||||
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_level"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_gop">
|
||||
GOP
|
||||
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_gop"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_size">
|
||||
尺寸
|
||||
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_size"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_fps">
|
||||
帧率
|
||||
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_fps"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_bitrate">
|
||||
码率
|
||||
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_bitrate"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="audio_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>音频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_microphones">
|
||||
麦克风
|
||||
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_microphones"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_acodec">
|
||||
编码
|
||||
<a id="sl_acodec_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_acodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
<strong><span id="txt_log_title">Warning:</span></strong>
|
||||
<span id="txt_log_msg">
|
||||
SRS不支持实时聊天,请使用<a href="https://webrtc.org/" target="_blank">WebRTC</a>,
|
||||
SRS会在未来考虑支持(<a href="https://github.com/ossrs/srs/issues/307" target="_blank">#307</a>)。
|
||||
</span>
|
||||
</div>
|
||||
<hr/>
|
||||
<footer>
|
||||
|
@ -253,539 +51,5 @@
|
|||
</body>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript">
|
||||
var srs_publisher = null;
|
||||
var realtime_player = null;
|
||||
var api_server = null;
|
||||
var self_chat = null;
|
||||
var global_chat_user_id = 200;
|
||||
var previous_chats = [];
|
||||
var no_play = false;
|
||||
|
||||
var query = parse_query_string();
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_publish("#txt_url");
|
||||
|
||||
// support 5x3+1 users
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var tr = $("<tr></tr>").hide();
|
||||
$("#lst_chats").append(tr);
|
||||
|
||||
for (var j = 0; j < 3; j++) {
|
||||
tr.append($("<td></td>").attr("id", "td_" + ((i+1) * 8 + j)));
|
||||
}
|
||||
}
|
||||
// remove border of row.
|
||||
$("#lst_chats").find("td").css("border", "none").css("padding", "2px")
|
||||
.css("padding-left", "0px").css("width", "327px");
|
||||
|
||||
if (query.agent == "true") {
|
||||
document.write(navigator.userAgent);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#realtime_player_url").tooltip({
|
||||
title: "右键复制RTMP地址"
|
||||
});
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
if (query.no_play == "true") {
|
||||
no_play = true;
|
||||
}
|
||||
|
||||
$("#btn_video_settings").click(function(){
|
||||
$("#video_modal").modal({show:true});
|
||||
});
|
||||
$("#btn_audio_settings").click(function(){
|
||||
$("#audio_modal").modal({show:true});
|
||||
});
|
||||
$("#btn_join").click(on_user_publish);
|
||||
|
||||
// for publish, we use randome stream name.
|
||||
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
|
||||
|
||||
// start the publisher.
|
||||
srs_publisher = new SrsPublisher("local_publisher", 280, 180);
|
||||
srs_publisher.on_publisher_ready = function(cameras, microphones) {
|
||||
srs_chat_initialize_page(
|
||||
cameras, microphones,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
error(code, desc + "请重试。");
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
};
|
||||
srs_publisher.start();
|
||||
|
||||
update_play_url();
|
||||
|
||||
if (!no_play) {
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 280, 180);
|
||||
realtime_player.on_player_ready = function() {
|
||||
this.set_bt(0.5);
|
||||
};
|
||||
realtime_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
|
||||
info("推流到服务器成功。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。");
|
||||
}
|
||||
realtime_player.start();
|
||||
}
|
||||
|
||||
$("#txt_name").focus();
|
||||
|
||||
api_server = "http://" + query.hostname + ":" + srs_get_api_server_port() + "/api/v1/chats";
|
||||
refresh();
|
||||
});
|
||||
|
||||
function on_publish_stop() {
|
||||
if (!srs_can_republish()) {
|
||||
$("#btn_join").attr("disabled", true);
|
||||
error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试");
|
||||
|
||||
srs_log_disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
|
||||
$("#realtime_player_url").attr("href", url).attr("target", "_blank");
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (!self_chat) {
|
||||
setTimeout(refresh, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : "GET",
|
||||
async : true,
|
||||
url : api_server,
|
||||
contentType: "text/html",
|
||||
data : "",
|
||||
dataType : "json",
|
||||
complete : function() {
|
||||
},
|
||||
error : function(ret) {
|
||||
setTimeout(refresh, 5000);
|
||||
warn(101, "查询会议室失败:" + JSON.stringify(ret));
|
||||
},
|
||||
success : function(ret) {
|
||||
if(0 != ret["code"]) {
|
||||
warn(102, "查询会议室失败: " + JSON.stringify(ret));
|
||||
setTimeout(refresh, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
var chats = ret["data"]["chats"];
|
||||
var server_time = ret["now"];
|
||||
|
||||
on_get_chats(chats);
|
||||
setTimeout(refresh, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function on_get_chats(chats) {
|
||||
if (!self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get self, check self is valid?
|
||||
var _self_chat = null;
|
||||
for (var i = 0; i < chats.length; i++) {
|
||||
var chat = chats[i];
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
_self_chat = chat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// rejoin if invalid.
|
||||
if (!_self_chat) {
|
||||
on_user_exit_chat(function(){
|
||||
on_user_join_chat(function(){
|
||||
info("重新加入会议室成功");
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
render_chat_room(chats);
|
||||
|
||||
if (!self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update self heartbeat.
|
||||
var url = api_server + "/" + self_chat.id;
|
||||
|
||||
var chat = {};
|
||||
chat.localtime = new Date().getTime();
|
||||
|
||||
// publish to api server to requires an id.
|
||||
$.ajax({
|
||||
type : "PUT",
|
||||
async : true,
|
||||
url : url,
|
||||
contentType: "text/html",
|
||||
data : JSON.stringify(chat),
|
||||
dataType : "json",
|
||||
complete : function() {
|
||||
},
|
||||
error : function(ret) {
|
||||
warn(105, "更新会议室信息失败:" + JSON.stringify(ret));
|
||||
},
|
||||
success : function(ret) {
|
||||
if(0 != ret["code"]) {
|
||||
warn(106, "更新会议室信息失败:: " + JSON.stringify(ret));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function render_chat_room(original_chats) {
|
||||
if (!self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
var chats = [];
|
||||
for (var i = 0; i < original_chats.length; i++) {
|
||||
var chat = original_chats[i];
|
||||
|
||||
// ignore the self.
|
||||
if (self_chat && self_chat.id == chat.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chats.push(chat);
|
||||
}
|
||||
|
||||
// new added chat
|
||||
for (var i = 0; i < chats.length; i++) {
|
||||
var chat = chats[i];
|
||||
|
||||
// if previous exists, ignore, only add new here.
|
||||
var previous_chat = get_previous_chat_user(previous_chats, chat.id);
|
||||
if (previous_chat) {
|
||||
// update reference.
|
||||
chat.ui = previous_chat.ui;
|
||||
chat.parent = previous_chat.parent;
|
||||
chat.player = previous_chat.player;
|
||||
if (chat.player) {
|
||||
chat.player.private_object = chat;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// removed chat
|
||||
for (var i = 0; i < previous_chats.length; i++) {
|
||||
var chat = previous_chats[i];
|
||||
|
||||
var new_chat = get_previous_chat_user(chats, chat.id);
|
||||
if (new_chat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chat.player) {
|
||||
chat.player.stop();
|
||||
}
|
||||
$("#div_" + chat.id).remove();
|
||||
}
|
||||
|
||||
// hide empty rows.
|
||||
$("#lst_chats").find("tr").each(function(){
|
||||
var empty = true;
|
||||
|
||||
$(this).find("td").each(function(){
|
||||
if ($(this).html() != "") {
|
||||
empty = false;
|
||||
return false; // abort each
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (empty) {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
// render the chat
|
||||
for (var i = 0; i < chats.length; i++) {
|
||||
var chat = chats[i];
|
||||
|
||||
// if redered, ignore.
|
||||
if (chat.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// generate the ui of chat
|
||||
if (!chat.ui) {
|
||||
global_chat_user_id++;
|
||||
|
||||
// username: a str indicates the user name.
|
||||
// url: a str indicates the url of user stream.
|
||||
// join_date: a str indicates the join timestamp in seconds.
|
||||
// join_date_str: friendly formated time.
|
||||
var obj = $("<div/>").html($("#template").html());
|
||||
if (true) {
|
||||
// save current ui object to the chat.
|
||||
chat.ui = obj;
|
||||
|
||||
$(obj).attr("chat_id", chat.id);
|
||||
$(obj).attr("id", "div_" + chat.id); // for specifed chat: $("#div_" + chat_id)
|
||||
$(obj).attr("class", "div_chat"); // for all chats: $(".div_chat")
|
||||
$(obj).find("#chat_player").attr("id", "rp_" + chat.id); // for specifed player: $("#rp_" + chat_id)
|
||||
$(obj).find("#chat_player_raw").attr("id", "rp_raw_" + chat.id); // for specifed player: $("#rp_raw_" + chat_id)
|
||||
$(obj).find("#user_name").text(chat.username);
|
||||
$(obj).find("#user_player_url").attr("href", chat.url);
|
||||
$(obj).find("#join_date").text(chat.join_date_str.split(" ")[1]);
|
||||
$(obj).find("#collapseM").attr("id", "collapse_" + global_chat_user_id);
|
||||
$(obj).find("#headerN").attr("href", "#collapse_" + global_chat_user_id);
|
||||
}
|
||||
}
|
||||
|
||||
// find a idle td to render the chat.
|
||||
var parent = null;
|
||||
$("#lst_chats").find("td").each(function(){
|
||||
if ($(this).html() != "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = $(this);
|
||||
return false; // abort each
|
||||
});
|
||||
|
||||
if (!parent) {
|
||||
warn("没有可用的位置展示流。");
|
||||
break;
|
||||
}
|
||||
$(parent).append(chat.ui);
|
||||
$(parent).parent().show();
|
||||
|
||||
// when ui elements appent to the page,
|
||||
// create the player, or flash will failed.
|
||||
if (!chat.parent) {
|
||||
chat.parent = $(parent);
|
||||
|
||||
if (!no_play) {
|
||||
// start the realtime player.
|
||||
var _player = new SrsPlayer("rp_raw_" + chat.id, 240, 180, chat);
|
||||
_player.on_player_ready = function() {
|
||||
this.set_bt(0.5);
|
||||
this.play();
|
||||
};
|
||||
_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
_player.start(chat.url);
|
||||
|
||||
chat.player = _player;
|
||||
} else {
|
||||
chat.player = null;
|
||||
}
|
||||
|
||||
$(obj).find("#collapse_main").on('hidden', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.stop();
|
||||
});
|
||||
$(obj).find("#collapse_main").on('shown', function(){
|
||||
var id = $(this).parent().attr("chat_id");
|
||||
var chat = get_previous_chat_user(previous_chats, id);
|
||||
if (!chat || !chat.player) {
|
||||
return;
|
||||
}
|
||||
chat.player.play();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
previous_chats = chats;
|
||||
}
|
||||
function get_previous_chat_user(arr, id) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var chat = arr[i];
|
||||
if (id == chat.id) {
|
||||
return chat;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function on_user_publish() {
|
||||
if ($("#txt_name").val() == "") {
|
||||
$("#txt_name").focus().parent().parent().addClass("error");
|
||||
warn(100, "请输入您的名字");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_name").parent().parent().removeClass("error");
|
||||
|
||||
// join chat.
|
||||
if (!self_chat) {
|
||||
on_user_join_chat();
|
||||
} else {
|
||||
on_user_exit_chat();
|
||||
}
|
||||
}
|
||||
function on_user_exit_chat(complete_pfn) {
|
||||
srs_publisher.stop();
|
||||
$("#btn_join").text("加入会议");
|
||||
|
||||
if (realtime_player) {
|
||||
realtime_player.stop();
|
||||
}
|
||||
|
||||
if (!self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// removed chat
|
||||
for (var i = 0; i < previous_chats.length; i++) {
|
||||
var chat = previous_chats[i];
|
||||
|
||||
if (chat.player) {
|
||||
chat.player.stop();
|
||||
}
|
||||
$("#div_" + chat.id).remove();
|
||||
}
|
||||
previous_chats = [];
|
||||
|
||||
var url = api_server + "/" + self_chat.id;
|
||||
// whatever, cleanup local chat.
|
||||
self_chat = null;
|
||||
|
||||
$("#btn_join").attr("disabled", true);
|
||||
|
||||
// publish to api server to requires an id.
|
||||
$.ajax({
|
||||
type : "DELETE",
|
||||
async : true,
|
||||
url : url,
|
||||
contentType: "text/html",
|
||||
data : "",
|
||||
dataType : "json",
|
||||
complete : function() {
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_join").attr("disabled", false);
|
||||
if (complete_pfn) {
|
||||
complete_pfn();
|
||||
}
|
||||
},
|
||||
error : function(ret) {
|
||||
warn(103, "退出会议室失败");
|
||||
},
|
||||
success : function(ret) {
|
||||
if(0 != ret["code"]) {
|
||||
warn(104, "退出会议室失败");
|
||||
return;
|
||||
}
|
||||
info("退出会议室成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
function on_user_join_chat(complete_pfn) {
|
||||
if (self_chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = $("#txt_url").val();
|
||||
var vcodec = {};
|
||||
var acodec = {};
|
||||
srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
|
||||
var chat = {};
|
||||
chat.id = -1;
|
||||
chat.username = $("#txt_name").val();
|
||||
chat.agent = navigator.userAgent;
|
||||
chat.url = url;
|
||||
chat.vcodec = vcodec;
|
||||
chat.acodec = acodec;
|
||||
|
||||
$("#btn_join").attr("disabled", true);
|
||||
|
||||
// publish to api server to requires an id.
|
||||
$.ajax({
|
||||
type : "POST",
|
||||
async : true,
|
||||
url : api_server,
|
||||
contentType: "text/html",
|
||||
data : JSON.stringify(chat),
|
||||
dataType : "json",
|
||||
complete : function() {
|
||||
$("#btn_join").attr("disabled", false);
|
||||
if (complete_pfn) {
|
||||
complete_pfn();
|
||||
}
|
||||
},
|
||||
error : function(ret) {
|
||||
warn(105, "创建会议室失败:" + JSON.stringify(ret));
|
||||
},
|
||||
success : function(ret) {
|
||||
if(0 != ret["code"]) {
|
||||
warn(106, "创建会议室失败: " + JSON.stringify(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
chat.id = ret["data"];
|
||||
|
||||
// success, start publish.
|
||||
self_chat = chat;
|
||||
|
||||
$("#btn_join").text("退出会议");
|
||||
|
||||
info("开始推流到服务器。请戴耳机聊天,否则音箱的声音会进入麦克风造成回声。");
|
||||
srs_publisher.publish(url, vcodec, acodec);
|
||||
|
||||
if (realtime_player) {
|
||||
// directly play the url for the realtime player.
|
||||
realtime_player.stop();
|
||||
realtime_player.play(url, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -45,370 +45,225 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入RTMP/HTTP地址后点击“播放视频”即可播放视频</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value="">
|
||||
<button class="btn btn-primary" id="btn_play">播放视频</button>
|
||||
<button class="btn" id="btn_generate_link">生成链接</button>
|
||||
</div>
|
||||
<div id="link_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3><a href="https://github.com/ossrs/srs">SRS Link Generator</a></h3>
|
||||
<div name="detect_flash">
|
||||
<div id="main_flash_alert" class="alert alert-danger fade in hide">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><p>Usage:</p></strong>
|
||||
<p>
|
||||
请点击下面的图标,启用Flash
|
||||
</p>
|
||||
<p>
|
||||
若没有见到这个图标,Chrome浏览器请打开
|
||||
<span class="text-info">chrome://settings/content/flash</span> 并修改为"Ask first"。
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_server">服务器地址</label>
|
||||
<div class="controls">
|
||||
<span id="link_server" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_port">服务器端口</label>
|
||||
<div class="controls">
|
||||
<span id="link_port" class="span2 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_vhost">RTMP Vhost</label>
|
||||
<div class="controls">
|
||||
<span id="link_vhost" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_app">RTMP App</label>
|
||||
<div class="controls">
|
||||
<span id="link_app" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_stream">RTMP Stream</label>
|
||||
<div class="controls">
|
||||
<span id="link_stream" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_rtmp">RTMP地址</label>
|
||||
<div class="controls">
|
||||
<span id="link_rtmp" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_url">播放链接地址</label>
|
||||
<div class="controls">
|
||||
<div style="margin-top:5px;"><a href="#" id="link_url" target="_blank">请右键拷贝此链接地址.</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main_flash_hdr" class="hide">
|
||||
</div>
|
||||
<div class="modal-footer"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr/>
|
||||
<span>
|
||||
注意:必须按照<a href="https://github.com/ossrs/srs/wiki/SampleDemo">SRS DEMO</a>
|
||||
启动和设置服务器,下面所有的链接才能观看。
|
||||
</span>
|
||||
<div class="accordion" id="main_accordion">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse1">
|
||||
<strong>[1] SRS示例播放流: 原始流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse1" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish">rtmp://demo.srs.com/live/livestream</a> <br/>
|
||||
<span>用户推送过来的唯一一路流,经过服务器的多种变换和再转发。</span>
|
||||
|
||||
<div id="main_content" class="hide">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入RTMP/HTTP-FLV/HLS地址后点击“播放视频”即可播放视频</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value="">
|
||||
<button class="btn btn-primary" id="btn_play">播放视频</button>
|
||||
<button class="btn" id="btn_generate_link">生成链接</button>
|
||||
</div>
|
||||
<div id="link_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3><a href="https://github.com/ossrs/srs">SRS Link Generator</a></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_server">服务器地址</label>
|
||||
<div class="controls">
|
||||
<span id="link_server" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_port">服务器端口</label>
|
||||
<div class="controls">
|
||||
<span id="link_port" class="span2 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_vhost">RTMP Vhost</label>
|
||||
<div class="controls">
|
||||
<span id="link_vhost" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_app">RTMP App</label>
|
||||
<div class="controls">
|
||||
<span id="link_app" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_stream">RTMP Stream</label>
|
||||
<div class="controls">
|
||||
<span id="link_stream" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_rtmp">RTMP地址</label>
|
||||
<div class="controls">
|
||||
<span id="link_rtmp" class="span4 uneditable-input"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="link_url">播放链接地址</label>
|
||||
<div class="controls">
|
||||
<div style="margin-top:5px;"><a href="#" id="link_url" target="_blank">请右键拷贝此链接地址.</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse10">
|
||||
<strong>[2] SRS示例播放流: 原始流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse10" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_hls" target="_blank">http://demo.srs.com/live/livestream.m3u8</a> <br/>
|
||||
<span>对用户的流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
|
||||
</div>
|
||||
<div class="modal-footer"></div>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3><a href="https://github.com/ossrs/srs">SrsPlayer</a></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="player"></div>
|
||||
<div class="progress progress-striped active" id="pb_buffer_bg">
|
||||
<div class="bar" style="width: 0%;" id="pb_buffer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse2">
|
||||
<strong>[3] SRS示例播放流: 转码配置LD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse2" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_ld">rtmp://demo.srs.com/live/livestream_ld</a> <br/>
|
||||
<span>对原始流加了<a href="http://ffmpeg.org/ffmpeg-filters.html#overlay">FFMPEG图片水印</a></span>
|
||||
<div class="modal-footer" id="my_modal_footer">
|
||||
<div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
全屏比例大小<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_fs_size_screen_100" href="#">屏幕大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_75" href="#">屏幕大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_50" href="#">屏幕大小(50%)</a></li>
|
||||
<li><a id="btn_fs_size_video_100" href="#">视频大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_video_75" href="#">视频大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_video_50" href="#">视频大小(50%)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">显示比例<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_dar_original" href="#">视频原始比例</a></li>
|
||||
<li><a id="btn_dar_21_9" href="#">宽屏影院(21:9)</a></li>
|
||||
<li><a id="btn_dar_16_9" href="#">宽屏电视(16:9)</a></li>
|
||||
<li><a id="btn_dar_4_3" href="#">窄屏(4:3)</a></li>
|
||||
<li><a id="btn_dar_fill" href="#">填充(容器比例)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">缓冲区大小<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_bt_0_1" href="#">0.1秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_2" href="#">0.2秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_3" href="#">0.3秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_5" href="#">0.5秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_8" href="#">0.8秒(会议)</a></li>
|
||||
<li><a id="btn_bt_1_0" href="#">1秒(低延迟)</a></li>
|
||||
<li><a id="btn_bt_2_0" href="#">2秒(较低延时)</a></li>
|
||||
<li><a id="btn_bt_3_0" href="#">3秒(流畅播放)</a></li>
|
||||
<li><a id="btn_bt_4_0" href="#">4秒(流畅播放)</a></li>
|
||||
<li><a id="btn_bt_5_0" href="#">5秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_6_0" href="#">6秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_8_0" href="#">8秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_10_0" href="#">10秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_15_0" href="#">15秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_20_0" href="#">20秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_30_0" href="#">30秒(流畅第一)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">最大缓冲区<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_mbt_0_6" href="#">0.6秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_0_9" href="#">0.9秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_1_2" href="#">1.2秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_1_5" href="#">1.5秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_2_4" href="#">2.4秒(会议)</a></li>
|
||||
<li><a id="btn_mbt_3_0" href="#">3秒(低延迟)</a></li>
|
||||
<li><a id="btn_mbt_6_0" href="#">6秒(较低延时)</a></li>
|
||||
<li><a id="btn_mbt_9_0" href="#">9秒(流畅播放)</a></li>
|
||||
<li><a id="btn_mbt_12_0" href="#">12秒(流畅播放)</a></li>
|
||||
<li><a id="btn_mbt_15_0" href="#">15秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_18_0" href="#">18秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_24_0" href="#">24秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_30_0" href="#">30秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_45_0" href="#">45秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_60_0" href="#">60秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_90_0" href="#">90秒(流畅第一)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<a id="btn_fullscreen" class="btn">全屏</a>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button id="btn_pause" class="btn">暂停播放</button>
|
||||
<button id="btn_resume" class="btn hide">继续播放</button>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">关闭播放器</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse11">
|
||||
<strong>[4] SRS示例播放流: 转码配置LD流HLS</strong>
|
||||
</span>
|
||||
<div class="hide" id="fullscreen_tips">
|
||||
请<font color="red">点击视频</font>进入全屏模式~<br/>
|
||||
由于安全原因,Flash全屏无法使用JS触发
|
||||
</div>
|
||||
<div id="collapse11" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_ld_hls" target="_blank">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/>
|
||||
<span>对转码配置LD流进行HLS切片。</span>
|
||||
<div>
|
||||
<div class="input-prepend div_play_time" title="BufferLength/BufferTime/MaxBufferTime">
|
||||
<span class="add-on">@B</span>
|
||||
<input class="span2" style="width:80px" id="txt_buffer" type="text" placeholder="0/0/0s">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频的播放流畅度">
|
||||
<span class="add-on">@F</span>
|
||||
<input class="span2" style="width:57px" id="txt_fluency" type="text" placeholder="100%">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频总共卡顿次数">
|
||||
<span class="add-on">@E</span>
|
||||
<input class="span2" style="width:45px" id="txt_empty_count" type="text" placeholder="0">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频当前的帧率FPS">
|
||||
<span class="add-on">@F</span>
|
||||
<input class="span2" style="width:55px" id="txt_fps" type="text" placeholder="fps">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频当前的码率(视频+音频),单位:Kbps">
|
||||
<span class="add-on">@B</span>
|
||||
<input class="span2" style="width:55px" id="txt_bitrate" type="text" placeholder="kbps">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="播放时长,格式:天 时:分:秒">
|
||||
<span class="add-on">@T</span>
|
||||
<input class="span2" style="width:85px" id="txt_time" type="text" placeholder="天 时:分:秒">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse3">
|
||||
<strong>[5] SRS示例播放流: 转码配置SD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse3" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_sd">rtmp://demo.srs.com/live/livestream_sd</a> <br/>
|
||||
<span>对原始流应用了<a href="http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction">FFMPEG翻转滤镜</a></span>
|
||||
<div style="margin-top:-12px;">
|
||||
<span id="debug_info"></span>
|
||||
URL: <a href="#" id="player_url"></a>
|
||||
<div class="input-prepend div_play_time" title="当前时间:年-月-日 时:分:秒">
|
||||
<span class="add-on">@N</span>
|
||||
<input class="span2" style="width:135px" id="player_clock" type="text" placeholder="年-月-日 时:分:秒">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse12">
|
||||
<strong>[6] SRS示例播放流: 转码配置SD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse12" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_sd_hls" target="_blank">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/>
|
||||
<span>对转码配置SD流进行HLS切片。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse4">
|
||||
<strong>[7] SRS示例播放流: 转发原始流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse4" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw">rtmp://demo.srs.com:19350/live/livestream</a> <br/>
|
||||
<span>将用户推送的流转发到另外的vhost或服务器,做热备用。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse13">
|
||||
<strong>[8] SRS示例播放流: 转发原始流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse13" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_hls" target="_blank">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/>
|
||||
<span>对转发原始流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse5">
|
||||
<strong>[9] SRS示例播放流: 转发转码配置LD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse5" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_ld">rtmp://demo.srs.com:19350/live/livestream_ld</a> <br/>
|
||||
<span>FFMPEG加水印后的流也会自动转发。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse14">
|
||||
<strong>[10] SRS示例播放流: 转发转码配置LD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse14" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_ld_hls" target="_blank">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/>
|
||||
<span>对转发转码配置LD流进行HLS切片,所有转发的流会自动支持HLS。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse6">
|
||||
<strong>[11] SRS示例播放流: 转发转码配置SD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse6" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_sd">rtmp://demo.srs.com:19350/live/livestream_sd</a> <br/>
|
||||
<span>FFMPEG翻转后的流也会自动转发。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse15">
|
||||
<strong>[12] SRS示例播放流: 转发转码配置SD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse15" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_sd_hls" target="_blank">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/>
|
||||
<span>对转发转码配置SD流进行HLS切片,所有转发的流会自动支持HLS。</span>
|
||||
<div>
|
||||
<div class="input-prepend" title="首播时间,点播放到开始播放的时间,秒">
|
||||
<span class="add-on">@PST</span>
|
||||
<input class="span1" style="width:60px" id="txt_pst" type="text" placeholder="N秒">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3><a href="https://github.com/ossrs/srs">SrsPlayer</a></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="player"></div>
|
||||
<div class="progress progress-striped active" id="pb_buffer_bg">
|
||||
<div class="bar" style="width: 0%;" id="pb_buffer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" id="my_modal_footer">
|
||||
<div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
全屏比例大小<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_fs_size_screen_100" href="#">屏幕大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_75" href="#">屏幕大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_50" href="#">屏幕大小(50%)</a></li>
|
||||
<li><a id="btn_fs_size_video_100" href="#">视频大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_video_75" href="#">视频大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_video_50" href="#">视频大小(50%)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">显示比例<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_dar_original" href="#">视频原始比例</a></li>
|
||||
<li><a id="btn_dar_21_9" href="#">宽屏影院(21:9)</a></li>
|
||||
<li><a id="btn_dar_16_9" href="#">宽屏电视(16:9)</a></li>
|
||||
<li><a id="btn_dar_4_3" href="#">窄屏(4:3)</a></li>
|
||||
<li><a id="btn_dar_fill" href="#">填充(容器比例)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">缓冲区大小<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_bt_0_1" href="#">0.1秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_2" href="#">0.2秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_3" href="#">0.3秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_5" href="#">0.5秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_8" href="#">0.8秒(会议)</a></li>
|
||||
<li><a id="btn_bt_1_0" href="#">1秒(低延迟)</a></li>
|
||||
<li><a id="btn_bt_2_0" href="#">2秒(较低延时)</a></li>
|
||||
<li><a id="btn_bt_3_0" href="#">3秒(流畅播放)</a></li>
|
||||
<li><a id="btn_bt_4_0" href="#">4秒(流畅播放)</a></li>
|
||||
<li><a id="btn_bt_5_0" href="#">5秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_6_0" href="#">6秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_8_0" href="#">8秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_10_0" href="#">10秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_15_0" href="#">15秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_20_0" href="#">20秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_30_0" href="#">30秒(流畅第一)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">最大缓冲区<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_mbt_0_6" href="#">0.6秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_0_9" href="#">0.9秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_1_2" href="#">1.2秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_1_5" href="#">1.5秒(实时)</a></li>
|
||||
<li><a id="btn_mbt_2_4" href="#">2.4秒(会议)</a></li>
|
||||
<li><a id="btn_mbt_3_0" href="#">3秒(低延迟)</a></li>
|
||||
<li><a id="btn_mbt_6_0" href="#">6秒(较低延时)</a></li>
|
||||
<li><a id="btn_mbt_9_0" href="#">9秒(流畅播放)</a></li>
|
||||
<li><a id="btn_mbt_12_0" href="#">12秒(流畅播放)</a></li>
|
||||
<li><a id="btn_mbt_15_0" href="#">15秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_18_0" href="#">18秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_24_0" href="#">24秒(网速较低)</a></li>
|
||||
<li><a id="btn_mbt_30_0" href="#">30秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_45_0" href="#">45秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_60_0" href="#">60秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_mbt_90_0" href="#">90秒(流畅第一)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<a id="btn_fullscreen" class="btn">全屏</a>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button id="btn_pause" class="btn">暂停播放</button>
|
||||
<button id="btn_resume" class="btn hide">继续播放</button>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">关闭播放器</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide" id="fullscreen_tips">
|
||||
请<font color="red">点击视频</font>进入全屏模式~<br/>
|
||||
由于安全原因,Flash全屏无法使用JS触发
|
||||
</div>
|
||||
<div>
|
||||
<div class="input-prepend div_play_time" title="BufferLength/BufferTime/MaxBufferTime">
|
||||
<span class="add-on">@B</span>
|
||||
<input class="span2" style="width:80px" id="txt_buffer" type="text" placeholder="0/0/0s">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频的播放流畅度">
|
||||
<span class="add-on">@F</span>
|
||||
<input class="span2" style="width:57px" id="txt_fluency" type="text" placeholder="100%">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频总共卡顿次数">
|
||||
<span class="add-on">@E</span>
|
||||
<input class="span2" style="width:45px" id="txt_empty_count" type="text" placeholder="0">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频当前的帧率FPS">
|
||||
<span class="add-on">@F</span>
|
||||
<input class="span2" style="width:55px" id="txt_fps" type="text" placeholder="fps">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="视频当前的码率(视频+音频),单位:Kbps">
|
||||
<span class="add-on">@B</span>
|
||||
<input class="span2" style="width:55px" id="txt_bitrate" type="text" placeholder="kbps">
|
||||
</div>
|
||||
<div class="input-prepend div_play_time" title="播放时长,格式:天 时:分:秒">
|
||||
<span class="add-on">@T</span>
|
||||
<input class="span2" style="width:85px" id="txt_time" type="text" placeholder="天 时:分:秒">
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top:-12px;">
|
||||
<span id="debug_info"></span>
|
||||
URL: <a href="#" id="player_url"></a>
|
||||
<div class="input-prepend div_play_time" title="当前时间:年-月-日 时:分:秒">
|
||||
<span class="add-on">@N</span>
|
||||
<input class="span2" style="width:135px" id="player_clock" type="text" placeholder="年-月-日 时:分:秒">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="input-prepend" title="首播时间,点播放到开始播放的时间,秒">
|
||||
<span class="add-on">@PST</span>
|
||||
<input class="span1" style="width:60px" id="txt_pst" type="text" placeholder="N秒">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p></p>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
@ -423,6 +278,48 @@
|
|||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript">
|
||||
var __on_flash_ready = null;
|
||||
|
||||
$(function(){
|
||||
// 探测Flash是否正常启用。
|
||||
$('#main_flash_hdr').html(
|
||||
'\
|
||||
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="100%"> \
|
||||
<param name="movie" value="srs_player/release/srs_player.swf"> \
|
||||
<param name="quality" value="autohigh"> \
|
||||
<param name="swliveconnect" value="true"> \
|
||||
<param name="allowScriptAccess" value="always"> \
|
||||
<param name="bgcolor" value="#0"> \
|
||||
<param name="allowFullScreen" value="true"> \
|
||||
<param name="wmode" value="opaque"> \
|
||||
<param name="FlashVars" value="log=1"> \
|
||||
<param name="flashvars" value="id=1&on_player_ready=__on_flash_ready"> \
|
||||
<embed src="srs_player/release/srs_player.swf" width="100%" height="100%" \
|
||||
quality="autohigh" bgcolor="#0" align="middle" allowfullscreen="true" allowscriptaccess="always" \
|
||||
type="application/x-shockwave-flash" swliveconnect="true" wmode="opaque" \
|
||||
flashvars="id=1&on_player_ready=__on_flash_ready" \
|
||||
pluginspage="http://www.macromedia.com/go/getflashplayer"> \
|
||||
</object> \
|
||||
'
|
||||
);
|
||||
$('#main_flash_hdr').show();
|
||||
|
||||
var showFlashHdr = setTimeout(function(){
|
||||
$('#main_flash_alert').show();
|
||||
}, 300);
|
||||
|
||||
__on_flash_ready = function (id) {
|
||||
clearTimeout(showFlashHdr);
|
||||
|
||||
$('#main_flash_alert').hide();
|
||||
$('#main_flash_hdr').hide();
|
||||
$('#main_content').show();
|
||||
|
||||
autoLoadPage();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var srs_player = null;
|
||||
var url = null;
|
||||
|
@ -504,7 +401,7 @@
|
|||
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream&server=ossrs.net&port=1935&autostart=true&schema=rtmp
|
||||
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream.flv&server=ossrs.net&port=8080&autostart=true&schema=http
|
||||
*/
|
||||
$(function(){
|
||||
var autoLoadPage = function() {
|
||||
var query = parse_query_string();
|
||||
|
||||
// get the vhost and port to set the default url.
|
||||
|
@ -580,9 +477,9 @@
|
|||
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
|
||||
|
||||
$("#pb_buffer_bg").attr("title",
|
||||
"缓冲区:" + buffer_length.toFixed(1) + "秒, 最大缓冲区:"
|
||||
+ this.max_buffer_time.toFixed(1) + "秒, 当前:"
|
||||
+ buffer.toFixed(1) + "%");
|
||||
"缓冲区:" + buffer_length.toFixed(1) + "秒, 最大缓冲区:"
|
||||
+ this.max_buffer_time.toFixed(1) + "秒, 当前:"
|
||||
+ buffer.toFixed(1) + "%");
|
||||
|
||||
var bts = this.buffer_time >= 1? this.buffer_time.toFixed(0) : this.buffer_time.toFixed(1);
|
||||
var mbts = this.buffer_time >= 1? this.max_buffer_time.toFixed(0) : this.max_buffer_time.toFixed(1);
|
||||
|
@ -627,9 +524,9 @@
|
|||
var apply_url_change = function() {
|
||||
var rtmp = parse_rtmp_url($("#txt_url").val());
|
||||
var url = "http://" + query.host + query.pathname + "?"
|
||||
+ "app=" + rtmp.app + "&stream=" + rtmp.stream
|
||||
+ "&server=" + rtmp.server + "&port=" + rtmp.port
|
||||
+ "&autostart=true";
|
||||
+ "app=" + rtmp.app + "&stream=" + rtmp.stream
|
||||
+ "&server=" + rtmp.server + "&port=" + rtmp.port
|
||||
+ "&autostart=true";
|
||||
if (query.shp_identify) {
|
||||
url += "&shp_identify=" + query.shp_identify;
|
||||
}
|
||||
|
@ -806,6 +703,6 @@
|
|||
}
|
||||
|
||||
apply_url_change();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
.accordion-group {
|
||||
width: 310px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/srspublisher'/>
|
||||
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/obs'/>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
|
@ -31,236 +34,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<!-- for the log -->
|
||||
<div class="alert alert-danger fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">Usage:</span></strong>
|
||||
<span id="txt_log_msg">设置编码参数,点“发布视频”,允许Flash访问摄像头即可推流</span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
<button class="btn" id="btn_video_settings">视频编码配置</button>
|
||||
<button class="btn" id="btn_audio_settings">音频编码配置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
发布地址:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_publish">发布视频</button>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="cb_preview">预览
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
播放地址
|
||||
1.<a id="txt_play_realtime" class="input-xxlarge" href="#">RTMP低延时(请发布视频)</a>
|
||||
2.<a id="txt_play_url" class="input-xxlarge" href="#">RTMP已转码(请发布视频)</a>
|
||||
3.<a id="txt_play_hls" class="input-xxlarge" href="#">HLS-m3u8(请发布视频)</a>
|
||||
4.<a id="txt_play_jwplayer" class="input-xxlarge" href="#">HLS-JWPlayer(请发布视频)</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="video_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>视频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_cameras">
|
||||
摄像头
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_cameras"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_vcodec">
|
||||
Codec
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_vcodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_profile">
|
||||
Profile
|
||||
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_profile"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_level">
|
||||
Level
|
||||
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_level"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_gop">
|
||||
GOP
|
||||
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_gop"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_size">
|
||||
尺寸
|
||||
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_size"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_fps">
|
||||
帧率
|
||||
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_fps"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_bitrate">
|
||||
码率
|
||||
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_bitrate"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="audio_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>音频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_microphones">
|
||||
麦克风
|
||||
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_microphones"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_acodec">
|
||||
编码
|
||||
<a id="sl_acodec_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_acodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>本地摄像头</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="local_publisher"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>远程服务器</strong>
|
||||
<a id="remote_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
黑屏<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
<a id="remote_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="remote_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>远程服务器</strong>
|
||||
<a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
低延时<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
<a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="realtime_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
</div>
|
||||
</div>
|
||||
<strong><span id="txt_log_title">Warning:</span></strong>
|
||||
<span id="txt_log_msg">
|
||||
Flash推流已经很少用,建议用<a href="https://obsproject.com/" target="_blank">OBS</a>或<a href="http://ffmpeg.org/" target="_blank">FFMPEG</a>推流,
|
||||
如果一定要使用Flash推流请点<a id="https_publisher" href="srs_publisher2.html">这里</a>。
|
||||
</span>
|
||||
</div>
|
||||
<hr/>
|
||||
<footer>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
|
@ -268,210 +51,18 @@
|
|||
</body>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript">
|
||||
var srs_publisher = null;
|
||||
var remote_player = null;
|
||||
var realtime_player = null;
|
||||
|
||||
var query = parse_query_string();
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", null);
|
||||
|
||||
if (query.agent == "true") {
|
||||
document.write(navigator.userAgent);
|
||||
return;
|
||||
var l = window.location;
|
||||
var url = window.location.href;
|
||||
if (l.hostname !== 'localhost' && l.hostname !== '127.0.0.1' && l.protocol == 'http:') {
|
||||
// For flash publisher, must use HTTPS.
|
||||
url = window.location.href.replace('http:', 'https:');
|
||||
}
|
||||
|
||||
$("#btn_video_settings").click(function(){
|
||||
$("#video_modal").modal({show:true});
|
||||
});
|
||||
$("#btn_audio_settings").click(function(){
|
||||
$("#audio_modal").modal({show:true});
|
||||
});
|
||||
|
||||
$("#remote_tips").tooltip({
|
||||
title: "为了支持HLS输出,FLASH编码器输出的流需要经过转码(VP6=>H264,MP3=>aac),所以会黑屏较长时间,请耐心等待"
|
||||
});
|
||||
$("#low_latecy_tips").tooltip({
|
||||
title: "服务器不转码直接转发FLASH编码器的流,所以延迟比支持HLS的流要低很多"
|
||||
});
|
||||
$("#realtime_player_url").tooltip({
|
||||
title: "右键复制RTMP地址"
|
||||
});
|
||||
$("#remote_player_url").tooltip({
|
||||
title: "右键复制RTMP地址"
|
||||
});
|
||||
|
||||
$("#btn_publish").click(on_user_publish);
|
||||
|
||||
// for publish, we use randome stream name.
|
||||
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
|
||||
|
||||
// start the publisher.
|
||||
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
|
||||
srs_publisher.on_publisher_ready = function(cameras, microphones) {
|
||||
srs_publisher_initialize_page(
|
||||
cameras, microphones,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
error(code, desc + "请重试。");
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
};
|
||||
srs_publisher.start();
|
||||
|
||||
update_play_url();
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
if (query.no_play != "true") {
|
||||
// start the normal player with HLS supported.
|
||||
remote_player = new SrsPlayer("remote_player", 430, 185);
|
||||
remote_player.on_player_ready = function() {
|
||||
this.set_bt(0.8);
|
||||
};
|
||||
remote_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
remote_player.start();
|
||||
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 430, 185);
|
||||
realtime_player.on_player_ready = function() {
|
||||
this.set_bt(0.8);
|
||||
};
|
||||
realtime_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
realtime_player.start();
|
||||
}
|
||||
url = url.substr(0, url.lastIndexOf('/')) + '/srs_publisher2.html';
|
||||
$('#https_publisher').attr('href', url);
|
||||
});
|
||||
|
||||
function on_publish_stop() {
|
||||
if (!srs_can_republish()) {
|
||||
$("#btn_join").attr("disabled", true);
|
||||
error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome/Firefox/Safari/Opera浏览器,支持重试");
|
||||
|
||||
srs_log_disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* we generate the transcoded stream url for flash publish donot support HLS
|
||||
* which requires aac, so the publish vhost maybe players for example, we
|
||||
* use players_pub vhost(transcoded stream to which) for all clients,
|
||||
* both players and players_pub are write HLS to the sample dir,
|
||||
* it's ok for the players vhost disabled the HLS, only the
|
||||
* players_pub enalbed HLS.
|
||||
*/
|
||||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
|
||||
var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "...vhost..." + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream;
|
||||
$("#realtime_player_url").attr("href", url).attr("target", "_blank");
|
||||
$("#remote_player_url").attr("href", remote_url).attr("target", "_blank");
|
||||
|
||||
var srs_player_url = "http://" + query.host + query.dir + "/srs_player.html?";
|
||||
srs_player_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
srs_player_url += "&autostart=true";
|
||||
|
||||
var srs_player_rt_url = "http://" + query.host + query.dir + "/srs_player.html?";
|
||||
srs_player_rt_url += "vhost=" + ret.vhost + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
srs_player_rt_url += "&autostart=true";
|
||||
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?";
|
||||
jwplayer_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
jwplayer_url += "&hls_autostart=true";
|
||||
|
||||
var hls_url = "http://" + ret.server + ":" + query.http_port + "/" + ret.app + "/" + ret.stream + ".m3u8";
|
||||
|
||||
$("#txt_play_realtime").text("RTMP低延时(点击打开)").attr("href", srs_player_rt_url).attr("target", "_blank");
|
||||
$("#txt_play_url").text("RTMP已转码(点击打开)").attr("href", srs_player_url).attr("target", "_blank");
|
||||
$("#txt_play_hls").text("HLS-m3u8(点击打开或右键复制)").attr("href", hls_url).attr("target", "_blank");
|
||||
$("#txt_play_jwplayer").text("HLS-JWPlayer(点击打开)").attr("href", jwplayer_url).attr("target", "_blank");
|
||||
}
|
||||
function on_user_publish() {
|
||||
if ($("#btn_publish").text() == "停止发布") {
|
||||
srs_publisher.stop();
|
||||
$("#btn_publish").text("发布视频");
|
||||
//$("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_realtime").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_url").text("RTMP已转码(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
//$("#remote_player_url").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
//$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_publish").text("停止发布");
|
||||
|
||||
update_play_url();
|
||||
|
||||
var url = $("#txt_url").val();
|
||||
var vcodec = {};
|
||||
var acodec = {};
|
||||
srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
|
||||
info("开始推流到服务器");
|
||||
srs_publisher.publish(url, vcodec, acodec);
|
||||
|
||||
if (!$("#cb_preview").is(":checked")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (realtime_player) {
|
||||
// directly play the url for the realtime player.
|
||||
realtime_player.stop();
|
||||
realtime_player.play(url);
|
||||
}
|
||||
|
||||
if (remote_player) {
|
||||
// the normal player should play the transcoded stream in another vhost.
|
||||
// for example, publish stream to vhost players,
|
||||
// the realtime player play the vhost players, which may donot support HLS,
|
||||
// the normal player play the vhost players_pub, which transcoded to h264/aac with HLS.
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
var pub_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app;
|
||||
pub_url += "?vhost=" + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream;
|
||||
remote_player.stop();
|
||||
remote_player.play(pub_url);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
|
|
450
trunk/research/players/srs_publisher2.html
Normal file
450
trunk/research/players/srs_publisher2.html
Normal file
|
@ -0,0 +1,450 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/srspublisher'/>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a id="srs_index" class="brand" href="#">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
|
||||
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div name="detect_flash">
|
||||
<div id="main_flash_alert" class="alert alert-danger fade in hide">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><p>Usage:</p></strong>
|
||||
<p>
|
||||
请点击下面的图标,启用Flash
|
||||
</p>
|
||||
<p>
|
||||
若没有见到这个图标,Chrome浏览器请打开
|
||||
<span class="text-info">chrome://settings/content/flash</span> 并修改为"Ask first"。
|
||||
</p>
|
||||
</div>
|
||||
<div id="main_flash_hdr" class="hide">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main_content" class="hide">
|
||||
<div class="alert alert-danger fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Remark:</span></strong>
|
||||
<span>Flash推流只支持H.264+Speex,音频不支持AAC,所以无法输出HLS</span>
|
||||
</div>
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">Usage:</span></strong>
|
||||
<span id="txt_log_msg">设置编码参数,点“发布视频”,允许Flash访问摄像头即可推流</span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
<button class="btn" id="btn_video_settings">视频编码配置</button>
|
||||
<button class="btn" id="btn_audio_settings">音频编码配置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
发布地址:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_publish">发布视频</button>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="cb_preview" checked>预览
|
||||
</label>
|
||||
<a id="txt_play_realtime" class="input-xxlarge" href="#">RTMP播放地址(请发布视频)</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="video_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>视频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_cameras">
|
||||
摄像头
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_cameras"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_vcodec">
|
||||
Codec
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_vcodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_profile">
|
||||
Profile
|
||||
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_profile"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_level">
|
||||
Level
|
||||
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_level"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_gop">
|
||||
GOP
|
||||
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_gop"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_size">
|
||||
尺寸
|
||||
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_size"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_fps">
|
||||
帧率
|
||||
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_fps"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_bitrate">
|
||||
码率
|
||||
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_bitrate"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="audio_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>音频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_microphones">
|
||||
麦克风
|
||||
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_microphones"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_acodec">
|
||||
编码
|
||||
<a id="sl_acodec_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_acodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>本地摄像头</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="local_publisher"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle">
|
||||
<strong>远程服务器</strong>
|
||||
<a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
低延时<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
<a id="realtime_player_url" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
播放地址<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="realtime_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p></p>
|
||||
<p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/json2.js"></script>
|
||||
<script type="text/javascript" src="js/srs.page.js"></script>
|
||||
<script type="text/javascript" src="js/srs.log.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<script type="text/javascript" src="js/srs.publisher.js"></script>
|
||||
<script type="text/javascript" src="js/srs.utility.js"></script>
|
||||
<script type="text/javascript" src="js/winlin.utility.js"></script>
|
||||
<script type="text/javascript">
|
||||
var __on_flash_ready = null;
|
||||
|
||||
$(function(){
|
||||
// 探测Flash是否正常启用。
|
||||
$('#main_flash_hdr').html(
|
||||
'\
|
||||
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="100%"> \
|
||||
<param name="movie" value="srs_player/release/srs_player.swf"> \
|
||||
<param name="quality" value="autohigh"> \
|
||||
<param name="swliveconnect" value="true"> \
|
||||
<param name="allowScriptAccess" value="always"> \
|
||||
<param name="bgcolor" value="#0"> \
|
||||
<param name="allowFullScreen" value="true"> \
|
||||
<param name="wmode" value="opaque"> \
|
||||
<param name="FlashVars" value="log=1"> \
|
||||
<param name="flashvars" value="id=1&on_player_ready=__on_flash_ready"> \
|
||||
<embed src="srs_player/release/srs_player.swf" width="100%" height="100%" \
|
||||
quality="autohigh" bgcolor="#0" align="middle" allowfullscreen="true" allowscriptaccess="always" \
|
||||
type="application/x-shockwave-flash" swliveconnect="true" wmode="opaque" \
|
||||
flashvars="id=1&on_player_ready=__on_flash_ready" \
|
||||
pluginspage="http://www.macromedia.com/go/getflashplayer"> \
|
||||
</object> \
|
||||
'
|
||||
);
|
||||
$('#main_flash_hdr').show();
|
||||
|
||||
var showFlashHdr = setTimeout(function(){
|
||||
$('#main_flash_alert').show();
|
||||
}, 300);
|
||||
|
||||
__on_flash_ready = function (id) {
|
||||
clearTimeout(showFlashHdr);
|
||||
|
||||
$('#main_flash_alert').hide();
|
||||
$('#main_flash_hdr').hide();
|
||||
$('#main_content').show();
|
||||
|
||||
autoLoadPage();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var srs_publisher = null;
|
||||
var realtime_player = null;
|
||||
|
||||
var query = parse_query_string();
|
||||
var autoLoadPage = function() {
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init_rtmp("#txt_url", null);
|
||||
|
||||
if (query.agent == "true") {
|
||||
document.write(navigator.userAgent);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_video_settings").click(function(){
|
||||
$("#video_modal").modal({show:true});
|
||||
});
|
||||
$("#btn_audio_settings").click(function(){
|
||||
$("#audio_modal").modal({show:true});
|
||||
});
|
||||
|
||||
$("#low_latecy_tips").tooltip({
|
||||
title: "服务器不转码直接转发FLASH编码器的流,所以延迟比支持HLS的流要低很多"
|
||||
});
|
||||
$("#realtime_player_url").tooltip({
|
||||
title: "右键复制RTMP地址"
|
||||
});
|
||||
|
||||
$("#btn_publish").click(on_user_publish);
|
||||
|
||||
// for publish, we use randome stream name.
|
||||
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
|
||||
|
||||
// start the publisher.
|
||||
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
|
||||
srs_publisher.on_publisher_ready = function(cameras, microphones) {
|
||||
srs_publisher_initialize_page(
|
||||
cameras, microphones,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
error(code, desc + "请重试。");
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
};
|
||||
srs_publisher.start();
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
if (query.no_play != "true") {
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 430, 185);
|
||||
realtime_player.on_player_ready = function() {
|
||||
this.set_bt(0.8);
|
||||
};
|
||||
realtime_player.on_player_metadata = function(metadata) {
|
||||
this.set_dar(0, 0);
|
||||
this.set_fs("screen", 100);
|
||||
}
|
||||
realtime_player.start();
|
||||
}
|
||||
};
|
||||
|
||||
function on_publish_stop() {
|
||||
if (!srs_can_republish()) {
|
||||
$("#btn_join").attr("disabled", true);
|
||||
error(0, "您使用的浏览器很弱,请关闭页面后重新打开页面(刷新也不管用)。<br/>推荐使用Chrome浏览器,支持重试");
|
||||
|
||||
srs_log_disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
|
||||
var remote_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app + "/" + ret.stream + '?vhost=' + ret.vhost;
|
||||
$("#realtime_player_url").attr("href", url).attr("target", "_blank");
|
||||
|
||||
var srs_player_rt_url = "http://" + query.host + query.dir + "/srs_player.html?";
|
||||
srs_player_rt_url += "vhost=" + ret.vhost + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
srs_player_rt_url += "&autostart=true";
|
||||
|
||||
$("#txt_play_realtime").text("RTMP播放地址").attr("href", srs_player_rt_url).attr("target", "_blank");
|
||||
}
|
||||
function on_user_publish() {
|
||||
if ($("#btn_publish").text() == "停止发布") {
|
||||
srs_publisher.stop();
|
||||
$("#btn_publish").text("发布视频");
|
||||
|
||||
if (!on_publish_stop()) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_publish").text("停止发布");
|
||||
|
||||
update_play_url();
|
||||
|
||||
var url = $("#txt_url").val();
|
||||
var vcodec = {};
|
||||
var acodec = {};
|
||||
srs_publiser_get_codec(
|
||||
vcodec, acodec,
|
||||
"#sl_cameras", "#sl_microphones",
|
||||
"#sl_vcodec", "#sl_profile", "#sl_level", "#sl_gop", "#sl_size",
|
||||
"#sl_fps", "#sl_bitrate",
|
||||
"#sl_acodec"
|
||||
);
|
||||
|
||||
info("开始推流到服务器");
|
||||
srs_publisher.publish(url, vcodec, acodec);
|
||||
|
||||
if (!$("#cb_preview").is(":checked")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (realtime_player) {
|
||||
// directly play the url for the realtime player.
|
||||
realtime_player.stop();
|
||||
realtime_player.play(url);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
|
@ -47,24 +47,24 @@ using namespace std;
|
|||
|
||||
SrsBandwidthSample::SrsBandwidthSample()
|
||||
{
|
||||
duration_ms = _SRS_BANDWIDTH_SAMPLE_DURATION;
|
||||
kbps = interval_ms = actual_duration_ms = bytes = 0;
|
||||
duration = _SRS_BANDWIDTH_SAMPLE_DURATION;
|
||||
kbps = interval = actual_duration = bytes = 0;
|
||||
}
|
||||
|
||||
SrsBandwidthSample::~SrsBandwidthSample()
|
||||
{
|
||||
}
|
||||
|
||||
void SrsBandwidthSample::calc_kbps(int _bytes, int _duration)
|
||||
void SrsBandwidthSample::calc_kbps(int _bytes, srs_utime_t _duration)
|
||||
{
|
||||
bytes = _bytes;
|
||||
actual_duration_ms = _duration;
|
||||
actual_duration = _duration;
|
||||
|
||||
if (actual_duration_ms <= 0) {
|
||||
if (actual_duration <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
kbps = bytes * 8 / actual_duration_ms;
|
||||
kbps = bytes * 8 / srsu2ms(actual_duration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,8 +180,8 @@ srs_error_t SrsBandwidth::do_bandwidth_check(SrsKbpsLimit* limit)
|
|||
SrsBandwidthSample publish_sample;
|
||||
|
||||
// timeout for a packet.
|
||||
_rtmp->set_send_timeout(play_sample.duration_ms * 2 * SRS_UTIME_MILLISECONDS);
|
||||
_rtmp->set_recv_timeout(publish_sample.duration_ms * 2 * SRS_UTIME_MILLISECONDS);
|
||||
_rtmp->set_send_timeout(play_sample.duration * 2);
|
||||
_rtmp->set_recv_timeout(publish_sample.duration * 2);
|
||||
|
||||
// start test.
|
||||
srs_utime_t start_time = srs_update_system_time();
|
||||
|
@ -216,9 +216,8 @@ srs_error_t SrsBandwidth::do_bandwidth_check(SrsKbpsLimit* limit)
|
|||
}
|
||||
|
||||
srs_trace("bandwidth ok. duartion=%dms(%d+%d), play=%dkbps, publish=%dkbps",
|
||||
srsu2msi(end_time - start_time), play_sample.actual_duration_ms,
|
||||
publish_sample.actual_duration_ms, play_sample.kbps,
|
||||
publish_sample.kbps);
|
||||
srsu2msi(end_time - start_time), srsu2msi(play_sample.actual_duration), srsu2msi(publish_sample.actual_duration),
|
||||
play_sample.kbps, publish_sample.kbps);
|
||||
|
||||
srs_usleep(_SRS_BANDWIDTH_FINAL_WAIT);
|
||||
|
||||
|
@ -234,8 +233,8 @@ srs_error_t SrsBandwidth::play_start(SrsBandwidthSample* sample, SrsKbpsLimit* l
|
|||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_play();
|
||||
|
||||
pkt->data->set("limit_kbps", SrsAmf0Any::number(limit->limit_kbps()));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(sample->duration_ms));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(sample->interval_ms));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(srsu2msi(sample->duration)));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(srsu2msi(sample->interval)));
|
||||
|
||||
if ((err = _rtmp->send_and_free_packet(pkt, 0)) != srs_success) {
|
||||
return srs_error_wrap(err, "send packet");
|
||||
|
@ -260,8 +259,8 @@ srs_error_t SrsBandwidth::play_checking(SrsBandwidthSample* sample, SrsKbpsLimit
|
|||
|
||||
int data_count = 1;
|
||||
srs_utime_t starttime = srs_update_system_time();
|
||||
while (int64_t(srsu2ms(srs_get_system_time() - starttime)) < sample->duration_ms) {
|
||||
srs_usleep(sample->interval_ms * SRS_UTIME_MILLISECONDS);
|
||||
while (srs_get_system_time() - starttime < sample->duration) {
|
||||
srs_usleep(sample->interval);
|
||||
|
||||
// TODO: FIXME: use shared ptr message.
|
||||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_playing();
|
||||
|
@ -295,9 +294,9 @@ srs_error_t SrsBandwidth::play_stop(SrsBandwidthSample* sample, SrsKbpsLimit* /*
|
|||
// notify client to stop play
|
||||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_play();
|
||||
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(sample->duration_ms));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(sample->interval_ms));
|
||||
pkt->data->set("duration_delta", SrsAmf0Any::number(sample->actual_duration_ms));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(srsu2msi(sample->duration)));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(srsu2msi(sample->interval)));
|
||||
pkt->data->set("duration_delta", SrsAmf0Any::number(srsu2msi(sample->actual_duration)));
|
||||
pkt->data->set("bytes_delta", SrsAmf0Any::number(sample->bytes));
|
||||
|
||||
if ((err = _rtmp->send_and_free_packet(pkt, 0)) != srs_success) {
|
||||
|
@ -321,8 +320,8 @@ srs_error_t SrsBandwidth::publish_start(SrsBandwidthSample* sample, SrsKbpsLimit
|
|||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_start_publish();
|
||||
|
||||
pkt->data->set("limit_kbps", SrsAmf0Any::number(limit->limit_kbps()));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(sample->duration_ms));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(sample->interval_ms));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(srsu2msi(sample->duration)));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(srsu2msi(sample->interval)));
|
||||
|
||||
if ((err = _rtmp->send_and_free_packet(pkt, 0)) != srs_success) {
|
||||
return srs_error_wrap(err, "send packet");
|
||||
|
@ -340,9 +339,9 @@ srs_error_t SrsBandwidth::publish_checking(SrsBandwidthSample* sample, SrsKbpsLi
|
|||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// recv publish msgs until @duration_ms ms
|
||||
// recv publish msgs until @duration ms
|
||||
srs_utime_t starttime = srs_update_system_time();
|
||||
while (int64_t(srsu2ms(srs_get_system_time() - starttime)) < sample->duration_ms) {
|
||||
while (srs_get_system_time() - starttime < sample->duration) {
|
||||
SrsCommonMessage* msg = NULL;
|
||||
SrsBandwidthPacket* pkt = NULL;
|
||||
if ((err = _rtmp->expect_message<SrsBandwidthPacket>(&msg, &pkt)) != srs_success) {
|
||||
|
@ -372,9 +371,9 @@ srs_error_t SrsBandwidth::publish_stop(SrsBandwidthSample* sample, SrsKbpsLimit*
|
|||
if (true) {
|
||||
// notify client to stop publish
|
||||
SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_publish();
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(sample->duration_ms));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(sample->interval_ms));
|
||||
pkt->data->set("duration_delta", SrsAmf0Any::number(sample->actual_duration_ms));
|
||||
pkt->data->set("duration_ms", SrsAmf0Any::number(srsu2msi(sample->duration)));
|
||||
pkt->data->set("interval_ms", SrsAmf0Any::number(srsu2msi(sample->interval)));
|
||||
pkt->data->set("duration_delta", SrsAmf0Any::number(srsu2msi(sample->actual_duration)));
|
||||
pkt->data->set("bytes_delta", SrsAmf0Any::number(sample->bytes));
|
||||
|
||||
if ((err = _rtmp->send_and_free_packet(pkt, 0)) != srs_success) {
|
||||
|
@ -411,8 +410,8 @@ srs_error_t SrsBandwidth::do_final(SrsBandwidthSample& play_sample, SrsBandwidth
|
|||
pkt->data->set("publish_kbps", SrsAmf0Any::number(publish_sample.kbps));
|
||||
pkt->data->set("play_bytes", SrsAmf0Any::number(play_sample.bytes));
|
||||
pkt->data->set("publish_bytes", SrsAmf0Any::number(publish_sample.bytes));
|
||||
pkt->data->set("play_time", SrsAmf0Any::number(play_sample.actual_duration_ms));
|
||||
pkt->data->set("publish_time", SrsAmf0Any::number(publish_sample.actual_duration_ms));
|
||||
pkt->data->set("play_time", SrsAmf0Any::number(srsu2msi(play_sample.actual_duration)));
|
||||
pkt->data->set("publish_time", SrsAmf0Any::number(srsu2msi(publish_sample.actual_duration)));
|
||||
|
||||
if ((err = _rtmp->send_and_free_packet(pkt, 0)) != srs_success) {
|
||||
return srs_error_wrap(err, "send packet");
|
||||
|
|
|
@ -40,14 +40,14 @@ class ISrsProtocolStatistic;
|
|||
class SrsBandwidthSample
|
||||
{
|
||||
public:
|
||||
// The plan, how long to do the test, in ms,
|
||||
// The plan, how long to do the test,
|
||||
// if exceed the duration, abort the test.
|
||||
int duration_ms;
|
||||
// The plan, interval for each check/test packet, in ms
|
||||
int interval_ms;
|
||||
srs_utime_t duration;
|
||||
// The plan, interval for each check/test packet
|
||||
srs_utime_t interval;
|
||||
public:
|
||||
// The actual test duration, in ms.
|
||||
int actual_duration_ms;
|
||||
// The actual test duration.
|
||||
srs_utime_t actual_duration;
|
||||
// The actual test bytes
|
||||
int bytes;
|
||||
// The actual test kbps
|
||||
|
@ -58,8 +58,8 @@ public:
|
|||
public:
|
||||
// Update the bytes and actual duration, then calc the kbps.
|
||||
// @param _bytes update the sample bytes.
|
||||
// @param _duration update the actual duration, in ms.
|
||||
virtual void calc_kbps(int _bytes, int _duration);
|
||||
// @param _duration update the actual duration.
|
||||
virtual void calc_kbps(int _bytes, srs_utime_t _duration);
|
||||
};
|
||||
|
||||
// The bandwidth test agent which provides the interfaces for bandwidth check.
|
||||
|
|
|
@ -607,16 +607,27 @@ srs_error_t SrsRtmpConn::playing(SrsSource* source)
|
|||
if (!info->edge && _srs_config->get_vhost_origin_cluster(req->vhost) && source->inactive()) {
|
||||
vector<string> coworkers = _srs_config->get_vhost_coworkers(req->vhost);
|
||||
for (int i = 0; i < (int)coworkers.size(); i++) {
|
||||
int port;
|
||||
string host;
|
||||
string url = "http://" + coworkers.at(i) + "/api/v1/clusters?"
|
||||
// TODO: FIXME: User may config the server itself as coworker, we must identify and ignore it.
|
||||
string host; int port = 0; string coworker = coworkers.at(i);
|
||||
|
||||
string url = "http://" + coworker + "/api/v1/clusters?"
|
||||
+ "vhost=" + req->vhost + "&ip=" + req->host + "&app=" + req->app + "&stream=" + req->stream
|
||||
+ "&coworker=" + coworkers.at(i);
|
||||
+ "&coworker=" + coworker;
|
||||
if ((err = SrsHttpHooks::discover_co_workers(url, host, port)) != srs_success) {
|
||||
// If failed to discovery stream in this coworker, we should request the next one util the last.
|
||||
// @see https://github.com/ossrs/srs/issues/1223
|
||||
if (i < (int)coworkers.size() - 1) {
|
||||
continue;
|
||||
}
|
||||
return srs_error_wrap(err, "discover coworkers, url=%s", url.c_str());
|
||||
}
|
||||
srs_trace("rtmp: redirect in cluster, from=%s:%d, target=%s:%d, url=%s",
|
||||
req->host.c_str(), req->port, host.c_str(), port, url.c_str());
|
||||
|
||||
// Ignore if host or port is invalid.
|
||||
if (host.empty() || port == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool accepted = false;
|
||||
if ((err = rtmp->redirect(req, host, port, accepted)) != srs_success) {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// The version config.
|
||||
#define VERSION_MAJOR 3
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 72
|
||||
#define VERSION_REVISION 75
|
||||
|
||||
// The macros generated by configure script.
|
||||
#include <srs_auto_headers.hpp>
|
||||
|
|
|
@ -1030,12 +1030,7 @@ SrsAmf0StrictArray::SrsAmf0StrictArray()
|
|||
|
||||
SrsAmf0StrictArray::~SrsAmf0StrictArray()
|
||||
{
|
||||
std::vector<SrsAmf0Any*>::iterator it;
|
||||
for (it = properties.begin(); it != properties.end(); ++it) {
|
||||
SrsAmf0Any* any = *it;
|
||||
srs_freep(any);
|
||||
}
|
||||
properties.clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
int SrsAmf0StrictArray::total_size()
|
||||
|
@ -1147,6 +1142,11 @@ SrsJsonAny* SrsAmf0StrictArray::to_json()
|
|||
|
||||
void SrsAmf0StrictArray::clear()
|
||||
{
|
||||
std::vector<SrsAmf0Any*>::iterator it;
|
||||
for (it = properties.begin(); it != properties.end(); ++it) {
|
||||
SrsAmf0Any* any = *it;
|
||||
srs_freep(any);
|
||||
}
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -356,7 +356,7 @@ public:
|
|||
* convert amf0 to json.
|
||||
*/
|
||||
virtual SrsJsonAny* to_json();
|
||||
// properties iteration
|
||||
// properties iteration
|
||||
public:
|
||||
/**
|
||||
* clear all propergies.
|
||||
|
@ -447,7 +447,7 @@ public:
|
|||
* convert amf0 to json.
|
||||
*/
|
||||
virtual SrsJsonAny* to_json();
|
||||
// properties iteration
|
||||
// properties iteration
|
||||
public:
|
||||
/**
|
||||
* clear all propergies.
|
||||
|
@ -532,7 +532,7 @@ public:
|
|||
* convert amf0 to json.
|
||||
*/
|
||||
virtual SrsJsonAny* to_json();
|
||||
// properties iteration
|
||||
// properties iteration
|
||||
public:
|
||||
/**
|
||||
* clear all elements.
|
||||
|
|
|
@ -76,9 +76,6 @@ void srs_vhost_resolve(string& vhost, string& app, string& param)
|
|||
if (!query.empty()) {
|
||||
vhost = query;
|
||||
}
|
||||
if ((pos = vhost.find("?")) != std::string::npos) {
|
||||
vhost = vhost.substr(0, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +196,7 @@ string srs_generate_stream_with_query(string host, string vhost, string stream,
|
|||
}
|
||||
|
||||
// Remove the start & when param is empty.
|
||||
srs_string_trim_start(query, "&");
|
||||
query = srs_string_trim_start(query, "&");
|
||||
|
||||
// Prefix query with ?.
|
||||
if (!query.empty() && !srs_string_starts_with(query, "?")) {
|
||||
|
|
|
@ -107,34 +107,22 @@ srs_error_t SrsRawH264Stream::sps_demux(char* frame, int nb_frame, string& sps)
|
|||
if (nb_frame < 4) {
|
||||
return err;
|
||||
}
|
||||
|
||||
sps = "";
|
||||
if (nb_frame > 0) {
|
||||
sps.append(frame, nb_frame);
|
||||
}
|
||||
|
||||
// should never be empty.
|
||||
if (sps.empty()) {
|
||||
return srs_error_new(ERROR_STREAM_CASTER_AVC_SPS, "no sps");
|
||||
}
|
||||
|
||||
|
||||
sps = string(frame, nb_frame);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRawH264Stream::pps_demux(char* frame, int nb_frame, string& pps)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
pps = "";
|
||||
if (nb_frame > 0) {
|
||||
pps.append(frame, nb_frame);
|
||||
}
|
||||
|
||||
// should never be empty.
|
||||
if (pps.empty()) {
|
||||
|
||||
if (nb_frame <= 0) {
|
||||
return srs_error_new(ERROR_STREAM_CASTER_AVC_PPS, "no pps");
|
||||
}
|
||||
|
||||
|
||||
pps = string(frame, nb_frame);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -153,9 +141,7 @@ srs_error_t SrsRawH264Stream::mux_sequence_header(string sps, string pps, uint32
|
|||
// numOfPictureParameterSets, pictureParameterSetLength
|
||||
// Nbytes of pps:
|
||||
// pictureParameterSetNALUnit
|
||||
int nb_packet = 5
|
||||
+ 3 + (int)sps.length()
|
||||
+ 3 + (int)pps.length();
|
||||
int nb_packet = 5 + (3 + (int)sps.length()) + (3 + (int)pps.length());
|
||||
char* packet = new char[nb_packet];
|
||||
SrsAutoFreeA(char, packet);
|
||||
|
||||
|
@ -216,9 +202,8 @@ srs_error_t SrsRawH264Stream::mux_sequence_header(string sps, string pps, uint32
|
|||
// TODO: FIXME: for more profile.
|
||||
// 5.3.4.2.1 Syntax, ISO_IEC_14496-15-AVC-format-2012.pdf, page 16
|
||||
// profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144
|
||||
|
||||
sh = "";
|
||||
sh.append(packet, nb_packet);
|
||||
|
||||
sh = string(packet, nb_packet);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -248,9 +233,8 @@ srs_error_t SrsRawH264Stream::mux_ipb_frame(char* frame, int nb_frame, string& i
|
|||
stream.write_4bytes(NAL_unit_length);
|
||||
// NALUnit
|
||||
stream.write_bytes(frame, nb_frame);
|
||||
|
||||
ibp = "";
|
||||
ibp.append(packet, nb_packet);
|
||||
|
||||
ibp = string(packet, nb_packet);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -421,7 +405,7 @@ srs_error_t SrsRawAacStream::adts_demux(SrsBuffer* stream, char** pframe, int* p
|
|||
codec.channel_configuration = channel_configuration;
|
||||
codec.frame_length = frame_length;
|
||||
|
||||
// @see srs_audio_write_raw_frame().
|
||||
// The aac sampleing rate defined in srs_aac_srates.
|
||||
// TODO: FIXME: maybe need to resample audio.
|
||||
codec.sound_format = 10; // AAC
|
||||
if (sampling_frequency_index <= 0x0c && sampling_frequency_index > 0x0a) {
|
||||
|
@ -477,27 +461,24 @@ srs_error_t SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, st
|
|||
break;
|
||||
}
|
||||
|
||||
sh = "";
|
||||
|
||||
char ch = 0;
|
||||
char chs[2];
|
||||
// @see ISO_IEC_14496-3-AAC-2001.pdf
|
||||
// AudioSpecificConfig (), page 33
|
||||
// 1.6.2.1 AudioSpecificConfig
|
||||
// audioObjectType; 5 bslbf
|
||||
ch = (audioObjectType << 3) & 0xf8;
|
||||
chs[0] = (audioObjectType << 3) & 0xf8;
|
||||
// 3bits left.
|
||||
|
||||
// samplingFrequencyIndex; 4 bslbf
|
||||
ch |= (samplingFrequencyIndex >> 1) & 0x07;
|
||||
sh += ch;
|
||||
ch = (samplingFrequencyIndex << 7) & 0x80;
|
||||
chs[0] |= (samplingFrequencyIndex >> 1) & 0x07;
|
||||
chs[1] = (samplingFrequencyIndex << 7) & 0x80;
|
||||
if (samplingFrequencyIndex == 0x0f) {
|
||||
return srs_error_new(ERROR_AAC_DATA_INVALID, "invalid sampling frequency index");
|
||||
}
|
||||
// 7bits left.
|
||||
|
||||
// channelConfiguration; 4 bslbf
|
||||
ch |= (channelConfiguration << 3) & 0x78;
|
||||
chs[1] |= (channelConfiguration << 3) & 0x78;
|
||||
// 3bits left.
|
||||
|
||||
// GASpecificConfig(), page 451
|
||||
|
@ -505,7 +486,7 @@ srs_error_t SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, st
|
|||
// frameLengthFlag; 1 bslbf
|
||||
// dependsOnCoreCoder; 1 bslbf
|
||||
// extensionFlag; 1 bslbf
|
||||
sh += ch;
|
||||
sh = string((char*)chs, sizeof(chs));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ SrsProtocol::SrsProtocol(ISrsProtocolReadWriter* io)
|
|||
in_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
|
||||
out_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
|
||||
|
||||
nb_out_iovs = SRS_CONSTS_IOVS_MAX;
|
||||
nb_out_iovs = 8 * SRS_CONSTS_IOVS_MAX;
|
||||
out_iovs = (iovec*)malloc(sizeof(iovec) * nb_out_iovs);
|
||||
// each chunk consumers atleast 2 iovs
|
||||
srs_assert(nb_out_iovs >= 2);
|
||||
|
@ -486,11 +486,11 @@ srs_error_t SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msg
|
|||
// for we donot know how many messges maybe to send entirely,
|
||||
// we just alloc the iovs, it's ok.
|
||||
if (iov_index >= nb_out_iovs - 2) {
|
||||
srs_warn("resize iovs %d => %d, max_msgs=%d", nb_out_iovs, nb_out_iovs + SRS_CONSTS_IOVS_MAX, SRS_PERF_MW_MSGS);
|
||||
|
||||
nb_out_iovs += SRS_CONSTS_IOVS_MAX;
|
||||
int ov = nb_out_iovs;
|
||||
nb_out_iovs = 2 * nb_out_iovs;
|
||||
int realloc_size = sizeof(iovec) * nb_out_iovs;
|
||||
out_iovs = (iovec*)realloc(out_iovs, realloc_size);
|
||||
srs_warn("resize iovs %d => %d, max_msgs=%d", ov, nb_out_iovs, SRS_PERF_MW_MSGS);
|
||||
}
|
||||
|
||||
// to next pair of iovs
|
||||
|
|
|
@ -67,6 +67,10 @@ extern srs_utime_t _srs_tmp_timeout;
|
|||
#define HELPER_BUFFER2STR(io) \
|
||||
string((const char*)(io)->bytes(), (size_t)(io)->length())
|
||||
|
||||
// Covert uint8_t array to string.
|
||||
#define HELPER_ARR2STR(arr, size) \
|
||||
string((char*)(arr), (int)size)
|
||||
|
||||
// the asserts of gtest:
|
||||
// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual
|
||||
// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
|
||||
|
|
587
trunk/src/utest/srs_utest_avc.cpp
Normal file
587
trunk/src/utest/srs_utest_avc.cpp
Normal file
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2019 Winlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <srs_utest_avc.hpp>
|
||||
|
||||
#include <srs_raw_avc.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
|
||||
VOID TEST(SrsAVCTest, H264ParseAnnexb)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Multiple frames.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0, 0, 1, 0xd, 0xa, 0xf, 0, 0, 1, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_SUCCESS(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(3, nb_frame);
|
||||
EXPECT_EQ((char*)(buf+3), frame);
|
||||
|
||||
HELPER_ASSERT_SUCCESS(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(1, nb_frame);
|
||||
EXPECT_EQ((char*)(buf+9), frame);
|
||||
}
|
||||
|
||||
// N prefix case, should success.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0, 0, 0, 1, 0xd, 0xa, 0xf, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_SUCCESS(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(4, nb_frame);
|
||||
EXPECT_EQ((char*)(buf+4), frame);
|
||||
}
|
||||
|
||||
// No prefix, should fail and return an empty frame.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0, 0, 2, 0xd, 0xa, 0xf, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_FAILED(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
}
|
||||
|
||||
// No prefix, should fail and return an empty frame.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0, 1, 0xd, 0xa, 0xf, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_FAILED(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
}
|
||||
|
||||
// No prefix, should fail and return an empty frame.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0xd, 0xa, 0xf, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_FAILED(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
}
|
||||
|
||||
// Normal case, should success.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
|
||||
uint8_t buf[] = {
|
||||
0, 0, 1, 0xd, 0xa, 0xf, 0xa,
|
||||
};
|
||||
SrsBuffer b((char*)buf, sizeof(buf));
|
||||
|
||||
char* frame = NULL; int nb_frame = 0;
|
||||
HELPER_ASSERT_SUCCESS(h.annexb_demux(&b, &frame, &nb_frame));
|
||||
EXPECT_EQ(4, nb_frame);
|
||||
EXPECT_EQ((char*)(buf+3), frame);
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(SrsAVCTest, H264SequenceHeader)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// For muxing sequence header.
|
||||
if (true) {
|
||||
SrsRawH264Stream h; string sh;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_sequence_header("Hello", "world", 0, 0, sh));
|
||||
EXPECT_EQ(11+5+5, sh.length());
|
||||
EXPECT_STREQ("Hello", sh.substr(8, 5).c_str());
|
||||
EXPECT_STREQ("world", sh.substr(16).c_str());
|
||||
}
|
||||
|
||||
// For PPS demuxing.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
string pps;
|
||||
HELPER_ASSERT_FAILED(h.pps_demux(NULL, 0, pps));
|
||||
EXPECT_TRUE(pps.empty());
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
string frame = "Hello, world!", pps;
|
||||
HELPER_ASSERT_SUCCESS(h.pps_demux((char*)frame.data(), frame.length(), pps));
|
||||
EXPECT_STREQ("Hello, world!", pps.c_str());
|
||||
}
|
||||
|
||||
// For SPS demuxing.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
string sps;
|
||||
HELPER_ASSERT_SUCCESS(h.sps_demux(NULL, 0, sps));
|
||||
EXPECT_TRUE(sps.empty());
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
string frame = "Hello, world!", sps;
|
||||
HELPER_ASSERT_SUCCESS(h.sps_demux((char*)frame.data(), frame.length(), sps));
|
||||
EXPECT_STREQ("Hello, world!", sps.c_str());
|
||||
}
|
||||
|
||||
// For PPS.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0x9,
|
||||
};
|
||||
EXPECT_FALSE(h.is_pps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0xf8,
|
||||
};
|
||||
EXPECT_FALSE(h.is_pps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0xe8,
|
||||
};
|
||||
EXPECT_TRUE(h.is_pps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
8,
|
||||
};
|
||||
EXPECT_TRUE(h.is_pps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
|
||||
// For SPS.
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0x8,
|
||||
};
|
||||
EXPECT_FALSE(h.is_sps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0xf7,
|
||||
};
|
||||
EXPECT_FALSE(h.is_sps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
0xe7,
|
||||
};
|
||||
EXPECT_TRUE(h.is_sps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
if (true) {
|
||||
SrsRawH264Stream h;
|
||||
uint8_t frame[] = {
|
||||
7,
|
||||
};
|
||||
EXPECT_TRUE(h.is_sps((char*)frame, sizeof(frame)));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(SrsAVCTest, H264IPBFrame)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// For muxing avc to flv frame.
|
||||
if (true) {
|
||||
SrsRawH264Stream h; int nb_flv = 0; char* flv = NULL;
|
||||
string video("Hello"); int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_avc2flv(video, frame_type, avc_packet_type, 0, 0x010203, &flv, &nb_flv));
|
||||
EXPECT_EQ(10, nb_flv);
|
||||
EXPECT_EQ(SrsVideoAvcFrameTypeKeyFrame, uint8_t((flv[0]>>4)&0x0f));
|
||||
EXPECT_EQ(SrsVideoAvcFrameTraitSequenceHeader, uint8_t(flv[1]));
|
||||
EXPECT_EQ(01, flv[2]); EXPECT_EQ(02, flv[3]); EXPECT_EQ(03, flv[4]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+5, 5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
|
||||
// For muxing I/P/B frame.
|
||||
if (true) {
|
||||
SrsRawH264Stream h; string frame;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_ipb_frame((char*)"Hello", 5, frame));
|
||||
EXPECT_EQ(4+5, frame.length());
|
||||
EXPECT_EQ(0, frame.at(0)); EXPECT_EQ(0, frame.at(1)); EXPECT_EQ(0, frame.at(2)); EXPECT_EQ(5, frame.at(3));
|
||||
EXPECT_STREQ("Hello", frame.substr(4).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(SrsAVCTest, AACDemuxADTS)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Fail if not adts format.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0x09, 0x2c,0x40, 0,0xe0,0}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_EXPECT_FAILED(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
}
|
||||
|
||||
// Fail if less than 7 bytes.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_EXPECT_FAILED(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
}
|
||||
|
||||
// For lower sampling rate, such as 5512HZ.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9, 0x2c,0x40, 0,0xe0,0}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(1, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(0xb, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(7, codec.frame_length); // b[5]
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate5512, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
|
||||
// For lower sampling rate, such as 22050HZ.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9, 0x18,0x40, 0,0xe0,0}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(1, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(6, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(7, codec.frame_length); // b[5]
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate22050, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
|
||||
// For higher sampling rate, use 44100HZ.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9, 0x04,0x40, 0,0xe0,0}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(1, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(1, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(7, codec.frame_length); // b[5]
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate44100, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
|
||||
// If protected, there are 2B signature.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf0, 0x10,0x40, 0x01,0x40,0, 0,0, 1}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(0, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(4, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(10, codec.frame_length); // b[4,5]
|
||||
ASSERT_EQ(1, nb_frame); EXPECT_EQ(1, (uint8_t)frame[0]);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate44100, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
|
||||
// Fail if not enough data.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf0, 0x10,0x40, 0x04,0,0, 1}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_EXPECT_FAILED(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
}
|
||||
|
||||
// If protected, there should be 2B signature.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf0, 0x10,0x40, 0x01,0,0, 1}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_EXPECT_FAILED(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
}
|
||||
|
||||
// ID should be 1, but we ignored.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf1, 0x10,0x40, 0x01,0,0, 1}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
}
|
||||
|
||||
// Minimum AAC frame, with raw data.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9, 0x10,0x40, 0x01,0,0, 1}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(1, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(4, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(8, codec.frame_length); // b[4]
|
||||
ASSERT_EQ(1, nb_frame); EXPECT_EQ(1, (uint8_t)frame[0]);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate44100, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
|
||||
// Minimum AAC frame, no raw data.
|
||||
if (true) {
|
||||
SrsRawAacStream h; char* frame = NULL; int nb_frame = 0; SrsRawAacStreamCodec codec;
|
||||
uint8_t b[] = {0xff, 0xf9, 0x10,0x40, 0,0xe0,0}; SrsBuffer buf((char*)b, sizeof(b));
|
||||
HELPER_ASSERT_SUCCESS(h.adts_demux(&buf, &frame, &nb_frame, codec));
|
||||
EXPECT_EQ(1, codec.protection_absent); // b[1]
|
||||
EXPECT_EQ(SrsAacObjectTypeAacMain, codec.aac_object); // b[2]
|
||||
EXPECT_EQ(4, codec.sampling_frequency_index); // b[2]
|
||||
EXPECT_EQ(1, codec.channel_configuration); // b[3]
|
||||
EXPECT_EQ(7, codec.frame_length); // b[5]
|
||||
EXPECT_EQ(0, nb_frame);
|
||||
|
||||
EXPECT_EQ(SrsAudioSampleRate44100, codec.sound_rate);
|
||||
EXPECT_EQ(0, codec.sound_type);
|
||||
EXPECT_EQ(1, codec.sound_size);
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(SrsAVCTest, AACMuxSequenceHeader)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// For sampling rate 22050HZ.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.aac_object = SrsAacObjectTypeAacMain;
|
||||
codec.channel_configuration = 1;
|
||||
codec.sound_rate = SrsAudioSampleRate22050;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh));
|
||||
EXPECT_EQ(2, sh.length());
|
||||
EXPECT_EQ(0x0b, (uint8_t)sh.at(0));
|
||||
EXPECT_EQ(0x88, (uint8_t)sh.at(1));
|
||||
}
|
||||
|
||||
// For sampling rate 11025HZ.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.aac_object = SrsAacObjectTypeAacMain;
|
||||
codec.channel_configuration = 1;
|
||||
codec.sound_rate = SrsAudioSampleRate11025;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh));
|
||||
EXPECT_EQ(2, sh.length());
|
||||
EXPECT_EQ(0x0d, (uint8_t)sh.at(0));
|
||||
EXPECT_EQ(0x08, (uint8_t)sh.at(1));
|
||||
}
|
||||
|
||||
// Fail for invalid sampling rate.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.sampling_frequency_index = 0xf;
|
||||
codec.sound_rate = SrsAudioSampleRateReserved;
|
||||
HELPER_EXPECT_FAILED(h.mux_sequence_header(&codec, sh));
|
||||
}
|
||||
|
||||
// Normal case.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.aac_object = SrsAacObjectTypeAacMain;
|
||||
codec.channel_configuration = 1;
|
||||
codec.sampling_frequency_index = 4;
|
||||
codec.sound_rate = SrsAudioSampleRateReserved;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh));
|
||||
EXPECT_EQ(2, sh.length());
|
||||
EXPECT_EQ(0x0a, (uint8_t)sh.at(0));
|
||||
EXPECT_EQ(0x08, (uint8_t)sh.at(1));
|
||||
}
|
||||
|
||||
// Fail for invalid aac object.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.aac_object = SrsAacObjectTypeReserved;
|
||||
HELPER_EXPECT_FAILED(h.mux_sequence_header(&codec, sh));
|
||||
}
|
||||
|
||||
// Normal case.
|
||||
if (true) {
|
||||
SrsRawAacStream h; string sh; SrsRawAacStreamCodec codec;
|
||||
codec.aac_object = SrsAacObjectTypeAacMain;
|
||||
codec.channel_configuration = 1;
|
||||
codec.sound_rate = SrsAudioSampleRate44100;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_sequence_header(&codec, sh));
|
||||
EXPECT_EQ(2, sh.length());
|
||||
EXPECT_EQ(0x0a, (uint8_t)sh.at(0));
|
||||
EXPECT_EQ(0x08, (uint8_t)sh.at(1));
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(SrsAVCTest, AACMuxToFLV)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// For MP3 frame.
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdMP3;
|
||||
codec.sound_rate = 0; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(6, nb_flv);
|
||||
EXPECT_EQ(0x23, (uint8_t)flv[0]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+1,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
|
||||
// For Opus frame.
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdOpus;
|
||||
codec.sound_rate = 0; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(6, nb_flv);
|
||||
EXPECT_EQ(0xd3, (uint8_t)flv[0]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+1,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
|
||||
// For Speex frame.
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdSpeex;
|
||||
codec.sound_rate = 0; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(6, nb_flv);
|
||||
EXPECT_EQ(0xb3, (uint8_t)flv[0]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+1,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
|
||||
// For AAC frame.
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdAAC;
|
||||
codec.sound_rate = 0; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(7, nb_flv);
|
||||
EXPECT_EQ(0xa3, (uint8_t)flv[0]);
|
||||
EXPECT_EQ(0x04, (uint8_t)flv[1]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+2,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdAAC;
|
||||
codec.sound_rate = 1; codec.sound_size = 1; codec.sound_type = 0;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(7, nb_flv);
|
||||
EXPECT_EQ(0xa6, (uint8_t)flv[0]);
|
||||
EXPECT_EQ(0x04, (uint8_t)flv[1]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+2,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdAAC;
|
||||
codec.sound_rate = 1; codec.sound_size = 0; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(7, nb_flv);
|
||||
EXPECT_EQ(0xa5, (uint8_t)flv[0]);
|
||||
EXPECT_EQ(0x04, (uint8_t)flv[1]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+2,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdAAC;
|
||||
codec.sound_rate = 1; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(7, nb_flv);
|
||||
EXPECT_EQ(0xa7, (uint8_t)flv[0]);
|
||||
EXPECT_EQ(0x04, (uint8_t)flv[1]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+2,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
if (true) {
|
||||
SrsRawAacStream h;
|
||||
string frame("Hello"); SrsRawAacStreamCodec codec;
|
||||
char* flv = NULL; int nb_flv = 0;
|
||||
codec.sound_format = SrsAudioCodecIdAAC;
|
||||
codec.sound_rate = 3; codec.sound_size = 1; codec.sound_type = 1;
|
||||
codec.aac_packet_type = 4;
|
||||
HELPER_ASSERT_SUCCESS(h.mux_aac2flv((char*)frame.data(), frame.length(), &codec, 0, &flv, &nb_flv));
|
||||
EXPECT_EQ(7, nb_flv);
|
||||
EXPECT_EQ(0xaf, (uint8_t)flv[0]);
|
||||
EXPECT_EQ(0x04, (uint8_t)flv[1]);
|
||||
EXPECT_STREQ("Hello", HELPER_ARR2STR(flv+2,5).c_str());
|
||||
srs_freep(flv);
|
||||
}
|
||||
}
|
||||
|
33
trunk/src/utest/srs_utest_avc.hpp
Normal file
33
trunk/src/utest/srs_utest_avc.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2019 Winlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SRS_UTEST_AVC_HPP
|
||||
#define SRS_UTEST_AVC_HPP
|
||||
|
||||
/*
|
||||
#include <srs_utest_avc.hpp>
|
||||
*/
|
||||
#include <srs_utest.hpp>
|
||||
|
||||
#endif
|
||||
|
|
@ -590,142 +590,6 @@ VOID TEST(ProtocolHandshakeTest, BytesEqual)
|
|||
EXPECT_FALSE(srs_bytes_equals(a1, b2, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* discovery tcUrl to schema/vhost/host/port/app
|
||||
*/
|
||||
VOID TEST(ProtocolUtilityTest, DiscoveryTcUrl)
|
||||
{
|
||||
std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
int port;
|
||||
|
||||
// general url
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
|
||||
tcUrl = "rtmp://winlin.cn:19351/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show?key=abc";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc", param.c_str());
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show?key=abc&&vhost=demo.com";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("demo.com", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc&&vhost=demo.com", param.c_str());
|
||||
|
||||
// vhost in app
|
||||
tcUrl = "rtmp://winlin.cn/live?key=abc"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc", param.c_str());
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live?key=abc&&vhost=demo.com"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("demo.com", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc&&vhost=demo.com", param.c_str());
|
||||
|
||||
// without stream
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:1935/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live?vhost=demo"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("demo", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
|
||||
// no vhost
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
|
||||
// ip and vhost
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream= "show?vhost=demo";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("demo", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
|
||||
// _definst_ at the end of app
|
||||
tcUrl = "rtmp://winlin.cn/live/_definst_"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* generate tcUrl from ip/vhost/app/port
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <srs_rtmp_stack.hpp>
|
||||
#include <srs_service_http_conn.hpp>
|
||||
#include <srs_kernel_buffer.hpp>
|
||||
#include <srs_kernel_codec.hpp>
|
||||
|
||||
#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072
|
||||
|
||||
|
@ -3118,3 +3119,364 @@ VOID TEST(ProtocolRTMPTest, MergeReadHandler)
|
|||
EXPECT_TRUE(h.nn > 0);
|
||||
}
|
||||
|
||||
char* _strcpy(const char* src)
|
||||
{
|
||||
return strcpy(new char[strlen(src)], src);
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, CreateRTMPMessage)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Invalid message type.
|
||||
if (true) {
|
||||
SrsSharedPtrMessage* msg = NULL;
|
||||
HELPER_EXPECT_FAILED(srs_rtmp_create_msg(SrsFrameTypeForbidden, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_TRUE(NULL == msg);
|
||||
}
|
||||
if (true) {
|
||||
SrsCommonMessage* msg = NULL;
|
||||
HELPER_EXPECT_FAILED(srs_rtmp_create_msg(SrsFrameTypeForbidden, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_TRUE(NULL == msg);
|
||||
}
|
||||
|
||||
// Normal script message.
|
||||
if (true) {
|
||||
SrsSharedPtrMessage* msg = NULL;
|
||||
HELPER_EXPECT_SUCCESS(srs_rtmp_create_msg(SrsFrameTypeScript, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_STREQ("Hello", msg->payload);
|
||||
srs_freep(msg);
|
||||
}
|
||||
|
||||
// Normal video message.
|
||||
if (true) {
|
||||
SrsSharedPtrMessage* msg = NULL;
|
||||
HELPER_EXPECT_SUCCESS(srs_rtmp_create_msg(SrsFrameTypeVideo, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_STREQ("Hello", msg->payload);
|
||||
srs_freep(msg);
|
||||
}
|
||||
|
||||
// Normal audio message.
|
||||
if (true) {
|
||||
SrsSharedPtrMessage* msg = NULL;
|
||||
HELPER_EXPECT_SUCCESS(srs_rtmp_create_msg(SrsFrameTypeAudio, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_STREQ("Hello", msg->payload);
|
||||
srs_freep(msg);
|
||||
}
|
||||
if (true) {
|
||||
SrsCommonMessage* msg = NULL;
|
||||
HELPER_EXPECT_SUCCESS(srs_rtmp_create_msg(SrsFrameTypeAudio, 0, _strcpy("Hello"), 5, 0, &msg));
|
||||
EXPECT_STREQ("Hello", msg->payload);
|
||||
srs_freep(msg);
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, OthersAll)
|
||||
{
|
||||
if (true) {
|
||||
vector<string> vs;
|
||||
vs.push_back("Hello");
|
||||
vs.push_back("world!");
|
||||
string v = srs_join_vector_string(vs, ", ");
|
||||
EXPECT_STREQ("Hello, world!", v.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
EXPECT_TRUE(srs_is_ipv4("1.2.3.4"));
|
||||
EXPECT_TRUE(srs_is_ipv4("255.2.3.4"));
|
||||
EXPECT_TRUE(srs_is_ipv4("1255.2.3.4"));
|
||||
}
|
||||
|
||||
if (true) {
|
||||
EXPECT_FALSE(srs_is_ipv4("ossrs.2.3.4"));
|
||||
EXPECT_FALSE(srs_is_ipv4("2.3.4.ossrs"));
|
||||
}
|
||||
|
||||
if (true) {
|
||||
SrsMessageArray h(10);
|
||||
h.msgs[0] = new SrsSharedPtrMessage();
|
||||
h.msgs[1] = new SrsSharedPtrMessage();
|
||||
EXPECT_TRUE(NULL != h.msgs[0]);
|
||||
EXPECT_TRUE(NULL != h.msgs[1]);
|
||||
|
||||
h.free(1);
|
||||
EXPECT_TRUE(NULL == h.msgs[0]);
|
||||
EXPECT_TRUE(NULL != h.msgs[1]);
|
||||
|
||||
h.free(2);
|
||||
EXPECT_TRUE(NULL == h.msgs[0]);
|
||||
EXPECT_TRUE(NULL == h.msgs[1]);
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, ParseRTMPURL)
|
||||
{
|
||||
if (true) {
|
||||
string url("rtmp://ossrs.net/live/show/livestream?token=abc"), tcUrl, stream;
|
||||
srs_parse_rtmp_url(url, tcUrl, stream);
|
||||
EXPECT_STREQ("rtmp://ossrs.net/live/show", tcUrl.c_str());
|
||||
EXPECT_STREQ("livestream?token=abc", stream.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string url("rtmp://ossrs.net/live/show/livestream"), tcUrl, stream;
|
||||
srs_parse_rtmp_url(url, tcUrl, stream);
|
||||
EXPECT_STREQ("rtmp://ossrs.net/live/show", tcUrl.c_str());
|
||||
EXPECT_STREQ("livestream", stream.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string url("rtmp://ossrs.net/live/livestream"), tcUrl, stream;
|
||||
srs_parse_rtmp_url(url, tcUrl, stream);
|
||||
EXPECT_STREQ("rtmp://ossrs.net/live", tcUrl.c_str());
|
||||
EXPECT_STREQ("livestream", stream.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, GenerateURL)
|
||||
{
|
||||
if (true) {
|
||||
string host("184.23.22.14"), vhost("ossrs.net"), stream("stream"), param("token=abc");
|
||||
string url = srs_generate_stream_with_query(host, vhost, stream, param);
|
||||
EXPECT_STREQ("stream?token=abc&vhost=ossrs.net", url.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string host("184.23.22.14"), vhost("__defaultVhost__"), stream("stream"), param("vhost=ossrs.net");
|
||||
string url = srs_generate_stream_with_query(host, vhost, stream, param);
|
||||
EXPECT_STREQ("stream?vhost=ossrs.net", url.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string host("184.23.22.14"), vhost("__defaultVhost__"), stream("stream"), param;
|
||||
string url = srs_generate_stream_with_query(host, vhost, stream, param);
|
||||
EXPECT_STREQ("stream", url.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string host("184.23.22.14"), vhost("ossrs.net"), stream("stream"), param;
|
||||
string url = srs_generate_stream_with_query(host, vhost, stream, param);
|
||||
EXPECT_STREQ("stream?vhost=ossrs.net", url.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
string host("ossrs.net"), vhost("__defaultVhost__"), stream("stream"), param;
|
||||
string url = srs_generate_stream_with_query(host, vhost, stream, param);
|
||||
EXPECT_STREQ("stream?vhost=ossrs.net", url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* discovery tcUrl to schema/vhost/host/port/app
|
||||
*/
|
||||
VOID TEST(ProtocolRTMPTest, DiscoveryTcUrl)
|
||||
{
|
||||
// default vhost in param
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show";
|
||||
param = "?vhost=__defaultVhost__";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
// default app
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("__defaultApp__", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
// general url
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn:19351/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show?key=abc";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc", param.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream= "show?key=abc&&vhost=demo.com";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("demo.com", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc&&vhost=demo.com", param.c_str());
|
||||
}
|
||||
|
||||
// vhost in app
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live?key=abc"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc", param.c_str());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live?key=abc&&vhost=demo.com"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("demo.com", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
EXPECT_STREQ("?key=abc&&vhost=demo.com", param.c_str());
|
||||
}
|
||||
|
||||
// without stream
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:1935/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live?vhost=demo"; stream="";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("demo", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
}
|
||||
|
||||
// no vhost
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
}
|
||||
|
||||
// ip and vhost
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://127.0.0.1:19351/live"; stream= "show?vhost=demo";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("127.0.0.1", ip.c_str());
|
||||
EXPECT_STREQ("demo", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(19351, port);
|
||||
}
|
||||
|
||||
// _definst_ at the end of app
|
||||
if (true) {
|
||||
int port; std::string tcUrl, schema, ip, vhost, app, stream, param;
|
||||
|
||||
tcUrl = "rtmp://winlin.cn/live/_definst_"; stream= "show";
|
||||
srs_discovery_tc_url(tcUrl, schema, ip, vhost, app, stream, port, param);
|
||||
EXPECT_STREQ("rtmp", schema.c_str());
|
||||
EXPECT_STREQ("winlin.cn", ip.c_str());
|
||||
EXPECT_STREQ("winlin.cn", vhost.c_str());
|
||||
EXPECT_STREQ("live", app.c_str());
|
||||
EXPECT_STREQ("show", stream.c_str());
|
||||
EXPECT_EQ(1935, port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue