1
0
Fork 0
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:
winlin 2019-12-19 19:17:17 +08:00
commit 94b1cf29c3
29 changed files with 1986 additions and 1840 deletions

View file

@ -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.)

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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;
}
}

View 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
View file

@ -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")

View file

@ -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;
}

View file

@ -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);
}
}
);

View file

@ -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 &copy; 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) {

View file

@ -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>

View file

@ -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/AACHLS流会自动禁用</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/AACHLS流会自动禁用</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 &copy; 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>

View file

@ -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 &copy; 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>

View 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 &copy; 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>

View file

@ -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");

View file

@ -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.

View file

@ -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) {

View file

@ -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>

View file

@ -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();
}

View file

@ -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.

View file

@ -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, "?")) {

View file

@ -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;
}

View file

@ -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

View file

@ -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

View 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);
}
}

View 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

View file

@ -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
*/

View file

@ -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);
}
}