123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- <!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;
- }
- .accordion-group {
- width: 310px;
- }
- </style>
- </head>
- <body>
- <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><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
- <li class="active"><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">
- <!-- for the log -->
- <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">输入名字,设点“加入会议”按钮</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>
- </div>
- <hr/>
- <footer>
- <p><a href="https://github.com/ossrs/srs">SRS Team © 2013</a></p>
- </footer>
- </div>
- </body>
- <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
- <script type="text/javascript" src="js/bootstrap.min.js"></script>
- <script type="text/javascript" src="js/swfobject.js"></script>
- <script type="text/javascript" src="js/json2.js"></script>
- <script type="text/javascript" src="js/srs.page.js"></script>
- <script type="text/javascript" src="js/srs.log.js"></script>
- <script type="text/javascript" src="js/srs.player.js"></script>
- <script type="text/javascript" src="js/srs.publisher.js"></script>
- <script type="text/javascript" src="js/srs.utility.js"></script>
- <script type="text/javascript" src="js/winlin.utility.js"></script>
- <script type="text/javascript">
- var 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>
|