Fix #2653: Remove HTTP RAW API. v4.0.170

pull/2683/head
winlin 3 years ago
parent 19e857ada4
commit cf99ebf186

@ -8,6 +8,7 @@ The changelog for SRS.
## SRS 4.0 Changelog
* v4.0, 2021-10-10, Fix [#2653](https://github.com/ossrs/srs/issues/2653) Remove HTTP RAW API. v4.0.170
* v4.0, 2021-10-08, Merge [#2654](https://github.com/ossrs/srs/pull/2654): Parse width and width from SPS/PPS. v4.0.169
* v4.0, 2021-10-08, Default to log to console for docker. v4.0.168
* v4.0, 2021-10-07, Fix bugs #2648, #2415. v4.0.167

@ -52,13 +52,13 @@ The features of SRS.
- [x] [Experimental] Support MPEG-DASH, the future live streaming protocol, read [#299][bug #299].
- [x] [Experimental] Support pushing MPEG-TS over UDP, please read [bug #250][bug #250].
- [x] [Experimental] Support pushing FLV over HTTP POST, please read wiki([CN][v4_CN_Streamer2], [EN][v4_EN_Streamer2]).
- [x] [Experimental] Support HTTP RAW API, please read [#459][bug #459], [#470][bug #470], [#319][bug #319].
- [x] [Experimental] Support SRT server, read [#1147][bug #1147].
- [x] [Experimental] Support transmux RTC to RTMP, [#2093][bug #2093].
- [x] [Deprecated] Support pushing RTSP, please read [bug #2304][bug #2304].
- [x] [Deprecated] Support Adobe HDS(f4m), please read wiki([CN][v4_CN_DeliveryHDS], [EN][v4_EN_DeliveryHDS]) and [#1535][bug #1535].
- [x] [Deprecated] Support bandwidth testing, please read [#1535][bug #1535].
- [x] [Deprecated] Support Adobe FMS/AMS token traverse([CN][v4_CN_DRM2], [EN][v4_EN_DRM2]) authentication, please read [#1535][bug #1535].
- [x] [Removed] Support HTTP RAW API, please read [#2653](https://github.com/ossrs/srs/issues/2653).
- [x] [Removed] Support RTMP client library: [srs-librtmp][srs-librtmp].
- [ ] Support Windows/Cygwin 64bits, [#2532](https://github.com/ossrs/srs/issues/2532).
- [ ] Support push stream by GB28181, [#1500][bug #1500].

@ -32,7 +32,6 @@
<li class="{{'/streams'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/streams')">Streams</a></li>
<li class="{{'/clients'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/clients')">Clients</a></li>
<li class="{{'/configs'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/configs')">Config</a></li>
<li class="{{'/dvr'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/dvr')">DVR</a></li>
<li><a href="javascript:void(0)" ng-click="redirect('en_index.html', 'ng_index.html')">Chinese</a></li>
<li>
<a href="https://github.com/ossrs/srs-console">

@ -12,9 +12,6 @@ scApp.config(["$routeProvider", function($routeProvider){
.when("/clients", {templateUrl:"views/clients.html", controller:"CSCClients"})
.when("/clients/:id", {templateUrl:"views/client.html", controller:"CSCClient"})
.when("/configs", {templateUrl:"views/configs.html", controller:"CSCConfigs"})
.when("/configs/:id", {templateUrl:"views/config.html", controller:"CSCConfig"})
.when("/dvr", {templateUrl:"views/dvrs.html", controller:"CSCDvrs"})
.when("/dvr/:vid/:sid/:app/:stream", {templateUrl:"views/dvr.html", controller:"CSCDvr"})
.when("/summaries", {templateUrl:"views/summary.html", controller:"CSCSummary"});
}]);

@ -296,268 +296,18 @@ scApp.controller("CSCClient", ["$scope", "$routeParams", "MSCApi", "$sc_nav", "$
$sc_utility.log("trace", "Retrieve client info from SRS");
}]);
scApp.controller("CSCDvrs", ['$scope', '$routeParams', 'MSCApi', '$sc_nav', '$sc_utility', function($scope, $routeParams, MSCApi, $sc_nav, $sc_utility){
$sc_nav.in_dvr();
$sc_utility.log("trace", "Please use DVR in streams.");
}]);
scApp.controller("CSCDvr", ['$scope', '$routeParams', 'MSCApi', '$sc_nav', '$sc_utility', function($scope, $routeParams, MSCApi, $sc_nav, $sc_utility){
$sc_nav.in_dvr();
$scope.vid = $routeParams.vid;
$scope.sid = $routeParams.sid;
$scope.app = $routeParams.app.replace('___', '/');
$scope.stream = $routeParams.stream.replace('___', '/');
$sc_utility.refresh.stop();
$scope.dvr = function(conf) {
//console.log(apply); return;
// submit to server.
$sc_utility.log("trace", "Submit to server for dvr apply=" + conf.value);
MSCApi.clients_update3("dvr", $scope.vid, $scope.app + '/' + $scope.stream, conf.value? 'enable':'disable', function(data){
$sc_utility.log("trace", "Server accepted, dvr apply=" + conf.value);
conf.error = false;
}, function(){
conf.error = true;
});
return true;
};
MSCApi.configs_get2($scope.vid, function(data){
data.vhost.stream = $scope.stream;
data.vhost.app = $scope.app;
data.vhost.vid = $scope.vid;
data.vhost.sid = $scope.sid;
var dvr = data.vhost.dvr;
if (!dvr) {
dvr = {
enabled: false
};
}
if (dvr.dvr_apply) {
if (dvr.dvr_apply.length === 1 && dvr.dvr_apply[0] === "all") {
dvr.apply = true;
} else {
dvr.apply = system_array_contains(dvr.dvr_apply, $scope.app + '/' + $scope.stream);
}
} else {
dvr.apply = true;
}
console.log(data.vhost);
$scope.global = $sc_utility.object2complex({}, data.vhost, null);
console.log($scope.global);
});
$sc_utility.log("trace", "Retrieve vhost config info from SRS");
}]);
scApp.controller("CSCConfigs", ["$scope", "$location", "MSCApi", "$sc_nav", "$sc_utility", "$sc_server", function($scope, $location, MSCApi, $sc_nav, $sc_utility, $sc_server){
$sc_nav.in_configs();
$sc_utility.refresh.stop();
$scope.support_raw_api = false;
$scope.warn_raw_api = false;
MSCApi.configs_raw(function(data){
$scope.http_api = data.http_api;
$scope.support_raw_api = $sc_utility.raw_api_enabled(data);
if (!$scope.support_raw_api) {
$scope.warn_raw_api = true;
return;
}
MSCApi.configs_get(function(data){
//console.log(data.global);
$scope.global = $sc_utility.object2complex({}, data.global, null);
//console.log($scope.global);
});
}, function(data){
$scope.warn_raw_api = true;
});
// operate vhost in client.
$scope.new_vhost = function() {
$scope.global.vhosts.push({
editable: true
});
};
$scope.edit_vhost = function(vhost) {
vhost.editable = true;
};
$scope.cancel_vhost = function(vhost) {
vhost.editable = false;
vhost.name = vhost.vid;
};
$scope.abort_vhost = function(vhost) {
system_array_remove($scope.global.vhosts, vhost);
};
// submit vhost to server
$scope.add_vhost = function(vhost) {
if (system_array_contains($scope.global.vhosts, function(e){ return vhost !== e && vhost.name === e.name; })) {
$sc_utility.log("warn", "vhost " + vhost.name + "已经存在");
return;
}
MSCApi.clients_update2("vhost", vhost.name, "create", function(data){
$sc_utility.copy_object(vhost, data.data);
vhost.enabled = true;
vhost.editable = false;
$sc_utility.log("trace", "创建vhost成功");
});
$sc_utility.log("trace", "提交vhost到服务器");
};
$scope.update_vhost = function(vhost) {
if (vhost.vid === vhost.name) {
$sc_utility.log("warn", "Vhost没有任何改变");
return;
}
MSCApi.clients_update3("vhost", vhost.vid, vhost.name, "update", function(data){
vhost.vid = vhost.name;
vhost.editable = false;
$sc_utility.log("trace", "修改vhost成功");
});
$sc_utility.log("trace", "提交修改vhost请求到服务器");
};
$scope.delete_vhost= function(vhost) {
MSCApi.clients_update2("vhost", vhost.vid, "delete", function(data){
system_array_remove($scope.global.vhosts, vhost);
$sc_utility.log("trace", "删除vhost成功");
});
$sc_utility.log("trace", "提交删除vhost请求到服务器");
};
$scope.disable_vhost = function(vhost) {
MSCApi.clients_update2("vhost", vhost.vid, "disable", function(data){
vhost.enabled = false;
$sc_utility.log("trace", "禁用vhost成功");
});
$sc_utility.log("trace", "提交禁用vhost请求到服务器");
};
$scope.enable_vhost = function(vhost) {
MSCApi.clients_update2("vhost", vhost.vid, "enable", function(data){
vhost.enabled = true;
$sc_utility.log("trace", "启用vhost成功");
});
$sc_utility.log("trace", "提交启用vhost请求到服务器");
};
// submit global config to server.
$scope.submit = function(conf) {
if (typeof conf.value !== "boolean" && !conf.value) {
$sc_utility.log("warn", "global." + conf.key + " should not be empty");
return false;
}
var v = conf.value;
if (conf.key === "listen") {
if (!system_array_foreach(v, function(e){ return e; })) {
$sc_utility.log("warn", "listen should not be empty");
return false;
}
} else if (conf.key === "pid") {
if (!system_string_startswith(v, ['./', '/var/', '/tmp/'])) {
$sc_utility.log("warn", "pid should starts with ./, /var/ or /tmp/");
return false;
}
if (!system_string_endswith(v, '.pid')) {
$sc_utility.log("warn", "pid should be *.pid");
return false;
}
} else if (conf.key === "chunk_size") {
if (parseInt(v) < 128 || parseInt(v) > 65535) {
$sc_utility.log("warn", "chunk_size should in [128, 65535], value=" + v);
return false;
}
} else if (conf.key === "ff_log_dir") {
if (v !== '/dev/null' && !system_string_startswith(v, ['/var/', '/tmp/', './'])) {
$sc_utility.log("warn", "ff_log_dir should be /dev/null or in ./, /var/ or /tmp/");
return false;
}
} else if (conf.key === "srs_log_tank") {
if (v !== "file" && v !== "console") {
$sc_utility.log("warn", "srs_log_tank should be file or console");
return false;
}
} else if (conf.key === "srs_log_level") {
if (v !== "verbose" && v !== "info" && v !== "trace" && v !== "warn" && v !== "error") {
$sc_utility.log("warn", "srs_log_level should be verbose, info, trace, warn, error");
return false;
}
} else if (conf.key === "srs_log_file") {
if (!system_string_startswith(v, ['./', '/var/', '/tmp/'])) {
$sc_utility.log("warn", "srs_log_file should be in ./, /var/ or /tmp/");
return false;
}
if (!system_string_endswith(v, '.log')) {
$sc_utility.log("warn", "srs_log_file should be *.log");
return false;
}
} else if (conf.key === "max_connections") {
if (parseInt(v) < 10 || parseInt(v) > 65535) {
$sc_utility.log("warn", "max_connections should in [10, 65535], value=" + v);
return false;
}
} else if (conf.key === "utc_time") {
if (v === undefined) {
$sc_utility.log("warn", "utc_time invalid");
return false;
}
} else if (conf.key === "pithy_print_ms") {
if (parseInt(v) < 100 || parseInt(v) > 300000) {
$sc_utility.log("warn", "pithy_print_ms invalid");
return false;
}
}
// submit to server.
$sc_utility.log("trace", "Submit to server ok, " + conf.key + "=" + conf.value);
MSCApi.clients_update(conf.key, conf.value, function(data){
$sc_utility.log("trace", "Server accepted, " + conf.key + "=" + conf.value);
conf.error = false;
// reload the rtmp service port when port changed.
if (conf.key === "listen") {
$sc_server.init($location, MSCApi);
}
}, function(){
conf.error = true;
});
return true;
};
$sc_utility.log("trace", "Retrieve config info from SRS");
}]);
scApp.controller("CSCConfig", ["$scope", "$routeParams", "MSCApi", "$sc_nav", "$sc_utility", function($scope, $routeParams, MSCApi, $sc_nav, $sc_utility){
$sc_nav.in_configs();
$sc_utility.refresh.stop();
MSCApi.configs_get2($routeParams.id, function(data){
$scope.vhost = data.vhost;
});
$sc_utility.log("trace", "Retrieve vhost config info from SRS");
}]);
scApp.factory("MSCApi", ["$http", "$sc_server", function($http, $sc_server){
return {
versions_get: function(success) {
@ -607,38 +357,6 @@ scApp.factory("MSCApi", ["$http", "$sc_server", function($http, $sc_server){
var url = $sc_server.jsonp_query("/api/v1/raw", "rpc=query&scope=global");
$http.jsonp(url).success(success);
},
configs_get2: function(id, success) {
var url = $sc_server.jsonp_query("/api/v1/raw", "rpc=query&scope=vhost&vhost=" + id);
$http.jsonp(url).success(success);
},
configs_get3: function(success) {
var url = $sc_server.jsonp_query("/api/v1/raw", "rpc=query&scope=minimal");
$http.jsonp(url).success(success);
},
clients_update: function(scope, value, success, error) {
var query = "rpc=update&scope=" + scope + "&value=" + value;
var url = $sc_server.jsonp_query("/api/v1/raw", query);
var obj = $http.jsonp(url).success(success);
if (error) {
obj.error(error);
}
},
clients_update2: function(scope, value, param, success, error) {
var query = "rpc=update&scope=" + scope + "&value=" + value + "&param=" + param;
var url = $sc_server.jsonp_query("/api/v1/raw", query);
var obj = $http.jsonp(url).success(success);
if (error) {
obj.error(error);
}
},
clients_update3: function(scope, value, data, param, success, error) {
var query = "rpc=update&scope=" + scope + "&value=" + value + "&param=" + param + "&data=" + data;
var url = $sc_server.jsonp_query("/api/v1/raw", query);
var obj = $http.jsonp(url).success(success);
if (error) {
obj.error(error);
}
}
};
}]);
@ -883,9 +601,6 @@ scApp.provider("$sc_nav", function(){
in_configs: function(){
this.selected = "/configs";
},
in_dvr: function(){
this.selected = "/dvr";
},
go_summary: function($location){
$location.path("/summaries");
},
@ -970,13 +685,6 @@ scApp.provider("$sc_server", [function(){
} else {
self.port = $location.port();
}
// optional, init the rtmp port.
MSCApi.configs_get3(function(data){
if (data.minimal) {
self.rtmp = data.minimal.listen;
}
});
}
};
return self;

@ -12,9 +12,6 @@ scApp.config(["$routeProvider", function($routeProvider){
.when("/clients", {templateUrl:"views/clients_en.html", controller:"CSCClients"})
.when("/clients/:id", {templateUrl:"views/client_en.html", controller:"CSCClient"})
.when("/configs", {templateUrl:"views/configs_en.html", controller:"CSCConfigs"})
.when("/configs/:id", {templateUrl:"views/config_en.html", controller:"CSCConfig"})
.when("/dvr", {templateUrl:"views/dvrs_en.html", controller:"CSCDvrs"})
.when("/dvr/:vid/:sid/:app/:stream", {templateUrl:"views/dvr_en.html", controller:"CSCDvr"})
.when("/summaries", {templateUrl:"views/summary_en.html", controller:"CSCSummary"});
}]);

@ -32,7 +32,6 @@
<li class="{{'/streams'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/streams')">视频流</a></li>
<li class="{{'/clients'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/clients')">客户端</a></li>
<li class="{{'/configs'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/configs')">配置</a></li>
<li class="{{'/dvr'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/dvr')">DVR</a></li>
<li><a href="javascript:void(0)" ng-click="redirect('ng_index.html', 'en_index.html')">English</a></li>
<li>
<a href="https://github.com/ossrs/srs-console">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -8,10 +8,10 @@
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="alert alert-block alert-danger" ng-show="warn_raw_api">
该服务器不支持HTTP RAW API或者配置中禁用了该功能。参考<a href='https://github.com/ossrs/srs/wiki/v3_CN_HTTPApi#http-raw-api'>WIKI</a>
<div class="alert alert-block alert-danger">
SRS 4.0不再支持HTTP RAW API。参考<a href='https://github.com/ossrs/srs/issues/2653'>#2653</a>
</div>
<table class="table table-striped table-hover table-bordered" ng-show="http_api">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
@ -43,410 +43,5 @@
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
服务器API配置(http_api)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP API开启后就可以访问SRS提供的API管理服务器。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.listen.value"
scp-desc="HTTP API的侦听地址格式是&lt;[address:]port&gt;。默认: 1985">
</tr>
<tr sc-pretty2 scp-data="global.http_api.crossdomain" scp-bool="true"
scp-desc="是否允许JS跨域开启后JS可以直接跨域(还可以通过JSONP访问)。默认: {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP RAW API允许API修改服务器配置。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="是否允许API进行Reload操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_query" scp-bool="true"
scp-desc="是否允许API进行Query查询操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_update" scp-bool="true"
scp-desc="是否允许API进行Update更新操作。默认: {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
服务器全局配置
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-directive scd-data="global.listen" scd-span="span3"
scd-desc="RTMP服务器侦听的端口提供RTMP服务格式是&lt;[address:]port&gt;[,&lt;addressN&gt;portN]"
scd-array="true" scd-default="1935"
scd-submit="submit(global.listen)">
</tr>
<tr sc-directive scd-data="global.pid" scd-span="span3"
scd-desc="服务器使用的PID文件每个进程必须用一个否则无法启动。默认: ./objs/srs.pid"
scd-default="./objs/srs.pid"
scd-submit="submit(global.pid)">
</tr>
<tr sc-directive scd-data="global.chunk_size" scd-span="span3"
scd-desc="默认的ChunkSize即RTMP分包的大小。默认: 60000"
scd-default="60000"
scd-submit="submit(global.chunk_size)">
</tr>
<tr sc-directive scd-data="global.ff_log_dir" scd-span="span4"
scd-desc="默认的FFMPEG日志目录/dev/null禁用日志。默认: ./objs"
scd-default="./objs"
scd-submit="submit(global.ff_log_dir)">
</tr>
<tr sc-directive scd-data="global.srs_log_tank" scd-span="span3"
scd-desc="系统的日志容器,即存储方式。默认: file"
scd-default="file", scd-select="file,console"
scd-submit="submit(global.srs_log_tank)">
</tr>
<tr sc-directive scd-data="global.srs_log_level" scd-span="span3"
scd-desc="系统的日志级别,低于该级别的日志不会打印。默认: trace"
scd-default="trace" scd-select="verbose,info,trace,warn,error"
scd-submit="submit(global.srs_log_level)">
</tr>
<tr sc-directive scd-data="global.srs_log_file" scd-span="span4"
scd-desc="当日志容器为file即日志写入文件时日志文件的路径。默认: ./objs/srs.log"
scd-default="./objs/srs.log"
scd-submit="submit(global.srs_log_file)"
ng-if="global.srs_log_tank.value == 'file'">
</tr>
<tr sc-directive scd-data="global.max_connections" scd-span="span3"
scd-desc="系统的最大连接数,超过后将拒绝新连接。默认: 1000"
scd-default="1000"
scd-submit="submit(global.max_connections)">
</tr>
<tr sc-pretty2 scp-data="global.daemon" scp-bool="true"
scp-desc="是否以后台启动SRS。默认: {{true| sc_filter_yesno}}">
</tr>
<tr sc-directive scd-data="global.utc_time" scd-span="span4"
scd-desc="是否启用UTC时间格式影响日志的时间和路径中包含时间变量的配置。默认: {{false| sc_filter_yesno}}"
scd-default="false" scd-bool="true"
scd-submit="submit(global.utc_time)">
</tr>
<tr sc-directive scd-data="global.pithy_print_ms" scd-span="span3"
scd-desc="简化形式的日志打印间隔,单位: 毫秒。默认: 10000"
scd-default="10000" scd-select="1000,3000,5000,10000,30000"
scd-submit="submit(global.pithy_print_ms)">
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
虚拟主机(Vhosts)列表, 共配置有{{global.nb_vhosts.value}}个Vhost
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>
<a href="javscript:void(0)" ng-click="new_vhost()" class="icon-plus"></a>
</th>
<th>主机名称</th>
<th>开启</th>
<th></th>
<th>DVR</th>
<th>HTTP</th>
<th>FLV</th>
<th>HLS</th>
<th>HDS</th>
<th>回调</th>
<th ng-show="false">Exec</th>
<th ng-show="false">BWT</th>
<th>转发</th>
<th>安全</th>
<th ng-show="false">盗链</th>
<th ng-show="false">MR</th>
<th ng-show="false">RTC</th>
<th ng-show="false">GOP</th>
<th ng-show="false">TcpNoDelay</th>
<th ng-show="false">MixCorrect</th>
<th ng-show="false">TimeJitter</th>
<th ng-show="false">ATC</th>
<th>转码</th>
<th>采集</th>
<th>
<a href="javascript:void(0)" ng-click="new_vhost()">添加</a>
</th>
</tr>
<tr ng-repeat="vhost in global.vhosts">
<td><a href="#/vhosts/{{vhost.id}}" ng-show="vhost.id">{{vhost.id}}</a><span ng-show="!vhost.id">无流</span> </td>
<td colspan="{{vhost.editable?13:1}}">
<span ng-show="!vhost.editable">{{vhost.name}}</span>
<span ng-show="vhost.editable">
<input class="text span5" ng-model="vhost.name"><br/>
请输入vhost的名称。
</span>
</td>
<td ng-show="!vhost.editable">{{vhost.enabled| sc_filter_yesno}}</td>
<td ng-show="!vhost.editable">{{vhost.origin| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.dvr| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_static| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_remux| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hls| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hds| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_hooks| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.exec| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.bandcheck| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.forward| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.security| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.refer| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mr| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.min_latency| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.gop_cache| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.tcp_nodelay| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mix_correct| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.time_jitter| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.atc| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.transcode| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.ingest| sc_filter_yn}}</td>
<td>
<span ng-show="vhost.editable">
<!-- vhost exists in server -->
<span ng-show="vhost.enabled != undefined">
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否提交{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="update_vhost(vhost)">
提交
</a>
<a href="javascript:void(0)" ng-click="cancel_vhost(vhost)">放弃</a>
</span>
<!-- vhost in adding. -->
<span ng-show="vhost.enabled == undefined">
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否提交{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="add_vhost(vhost)">
提交
</a>
<a href="javascript:void(0)" ng-click="abort_vhost(vhost)">放弃</a>
</span>
</span>
<span ng-show="!vhost.editable">
<span ng-show="vhost.enabled">
<span>编辑</span>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否禁用{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="disable_vhost(vhost)">
禁用
</a>
<span>删除</span>
</span>
<span ng-show="!vhost.enabled">
<a href="javascript:void(0)" ng-click="edit_vhost(vhost)">编辑</a>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否启用{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="enable_vhost(vhost)">
启用
</a>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否删除{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="delete_vhost(vhost)">
删除
</a>
</span>
<a href="#/configs/{{vhost.name}}" ng-show="!vhost.editable">详细</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Kafka集群配置(kafka)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.kafka.enabled" scp-bool="true"
scp-desc="是否开启Kafka开启后SRS将数据汇报给Kafka集群。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.kafka.brokers"
scp-desc="Broker列表首次连接到Broker获取集群信息格式是&lt;ip:port&gt;。">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
服务器心跳汇报(heartbeat)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.enabled"
scp-desc="是否开启服务器心跳,向外部系统汇报。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.interval"
scp-desc="心跳间隔的时间,单位: 秒。默认: 9.9"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.url"
scp-desc="汇报的HTTP服务器地址汇报信息格式是JSON。默认: http://127.0.0.1:8085/api/v1/servers"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.device_id"
scp-desc="设备IDSRS会写入汇报信息中。"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.smmaries"
scp-desc="是否汇报摘要即服务器的summaries信息。默认: {{false| sc_filter_yesno}}"
scd-bool="true"
scd-submit="submit(global.heartbeat.summaries)"
ng-show="global.heartbeat.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
服务器统计信息配置(stats)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.stats.network"
scp-desc="统计的网卡索引从0开始算第一块网卡。默认: 0">
</tr>
<tr sc-pretty2 scp-data="global.stats.disk"
scp-desc="统计的磁盘名称,可以在/proc/diskstats中查看。"
scd-array="true">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP服务器配置(http_server)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_server.enabled"
scp-desc="是否开启HTTP文件服务器和HTTP流服务器。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.http_server.listen"
scp-desc="HTTP服务器侦听的地址格式是&lt;[address:]port&gt;。默认: 8080"
ng-show="global.http_server.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.http_server.dir"
scp-desc="HTTP服务器的主目录。默认: ./objs/nginx/html"
ng-show="global.http_server.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
StreamCaster流转换器(stream_caster)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<td>Opt</td>
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.enabled"
scp-desc="是否开启StreamCaster即接收特殊的流后转换成RTMP送到SRS。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.caster"
scp-desc="转换器的类型不同的类型需要不同的配置mpegts_over_udp,rtsp,flv"
scd-select="mpegts_over_udp,rtsp,flv"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.output"
scp-desc="输出的RTMP地址转换器侦听端口接收特定的流转换成RTMP后送到SRS。"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.listen"
scp-desc="转换器侦听的地址,格式是&lt;port&gt;"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_min"
scp-desc="转换器会在[rtp_port_min, rtp_port_max]中间选择一个新端口并侦听参考RTP协议。"
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_max"
scp-desc="转换器会在[rtp_port_min, rtp_port_max]中间选择一个新端口并侦听参考RTP协议。"
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

@ -8,10 +8,10 @@
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="alert alert-block alert-danger" ng-show="warn_raw_api">
HTTP RAW API is not supported or disabled. Read <a href='https://github.com/ossrs/srs/wiki/v3_EN_HTTPApi#http-raw-api'>WIKI</a>
<div class="alert alert-block alert-danger">
HTTP RAW API is removed, please read <a href='https://github.com/ossrs/srs/issues/2653'>#2653</a>
</div>
<table class="table table-striped table-hover table-bordered" ng-show="http_api">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
@ -43,410 +43,5 @@
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP API Server
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_api.enabled" scp-bool="true"
scp-desc="Whether enabel HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.listen.value"
scp-desc="The HTTP API listen port, format is &lt;[address:]port&gt;. Default is 1985">
</tr>
<tr sc-pretty2 scp-data="global.http_api.crossdomain" scp-bool="true"
scp-desc="Whether allow CORS for js(JSONP). Default is {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.enabled" scp-bool="true"
scp-desc="Whether enable HTTP RAW API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="Whether allow reload by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_query" scp-bool="true"
scp-desc="Whether allow query by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_update" scp-bool="true"
scp-desc="Whether allow update by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
SRS Global Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-directive scd-data="global.listen" scd-span="span3"
scd-desc="RTMP listen port, format is &lt;[address:]port&gt;[,&lt;addressN&gt;portN]"
scd-array="true" scd-default="1935"
scd-submit="submit(global.listen)">
</tr>
<tr sc-directive scd-data="global.pid" scd-span="span3"
scd-desc="The pid file for SRS. Default is ./objs/srs.pid"
scd-default="./objs/srs.pid"
scd-submit="submit(global.pid)">
</tr>
<tr sc-directive scd-data="global.chunk_size" scd-span="span3"
scd-desc="RTMP chunk size. Default is 60000"
scd-default="60000"
scd-submit="submit(global.chunk_size)">
</tr>
<tr sc-directive scd-data="global.ff_log_dir" scd-span="span4"
scd-desc="FFMPEG log directory, use /dev/null to disable log. Default is ./objs"
scd-default="./objs"
scd-submit="submit(global.ff_log_dir)">
</tr>
<tr sc-directive scd-data="global.srs_log_tank" scd-span="span3"
scd-desc="System log store. Default is file"
scd-default="file", scd-select="file,console"
scd-submit="submit(global.srs_log_tank)">
</tr>
<tr sc-directive scd-data="global.srs_log_level" scd-span="span3"
scd-desc="System log level. Default is trace"
scd-default="trace" scd-select="verbose,info,trace,warn,error"
scd-submit="submit(global.srs_log_level)">
</tr>
<tr sc-directive scd-data="global.srs_log_file" scd-span="span4"
scd-desc="When log store is file, the log path. Default is ./objs/srs.log"
scd-default="./objs/srs.log"
scd-submit="submit(global.srs_log_file)"
ng-if="global.srs_log_tank.value == 'file'">
</tr>
<tr sc-directive scd-data="global.max_connections" scd-span="span3"
scd-desc="System maximum connections. Default is 1000"
scd-default="1000"
scd-submit="submit(global.max_connections)">
</tr>
<tr sc-pretty2 scp-data="global.daemon" scp-bool="true"
scp-desc="Whether start SRS as daemon. Default is {{true| sc_filter_yesno}}">
</tr>
<tr sc-directive scd-data="global.utc_time" scd-span="span4"
scd-desc="Whether use UTC time. Default is {{false| sc_filter_yesno}}"
scd-default="false" scd-bool="true"
scd-submit="submit(global.utc_time)">
</tr>
<tr sc-directive scd-data="global.pithy_print_ms" scd-span="span3"
scd-desc="Pithy log print interval in ms. Default is 10000"
scd-default="10000" scd-select="1000,3000,5000,10000,30000"
scd-submit="submit(global.pithy_print_ms)">
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
System Vhosts, total {{global.nb_vhosts.value}} vhosts configed.
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>
<a href="javscript:void(0)" ng-click="new_vhost()" class="icon-plus"></a>
</th>
<th>Name</th>
<th title="Whether Enabled">E</th>
<th title="Whether Origin Server">O</th>
<th title="DVR Configed">D</th>
<th title="HTTP Server Configed">H</th>
<th title="HTTP FLV Configed">FLV</th>
<th title="HLS Configed">HLS</th>
<th title="HDS Configed">HDS</th>
<th title="HTTP Callback">CB</th>
<th ng-show="false">Exec</th>
<th ng-show="false">BWT</th>
<th title="Forwarder">F</th>
<th title="Security Configed">S</th>
<th ng-show="false">Referer</th>
<th ng-show="false">MR</th>
<th ng-show="false">RTC</th>
<th ng-show="false">GOP</th>
<th ng-show="false">TcpNoDelay</th>
<th ng-show="false">MixCorrect</th>
<th ng-show="false">TimeJitter</th>
<th ng-show="false">ATC</th>
<th title="Transcode Configied">T</th>
<th title="Ingester Configed">I</th>
<th>
<a href="javascript:void(0)" ng-click="new_vhost()">Add</a>
</th>
</tr>
<tr ng-repeat="vhost in global.vhosts">
<td><a href="#/vhosts/{{vhost.id}}" ng-show="vhost.id">{{vhost.id}}</a><span ng-show="!vhost.id">No Stream</span> </td>
<td colspan="{{vhost.editable?13:1}}">
<span ng-show="!vhost.editable">{{vhost.name}}</span>
<span ng-show="vhost.editable">
<input class="text span5" ng-model="vhost.name"><br/>
Please input vhost name.
</span>
</td>
<td ng-show="!vhost.editable">{{vhost.enabled| sc_filter_yesno}}</td>
<td ng-show="!vhost.editable">{{vhost.origin| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.dvr| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_static| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_remux| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hls| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hds| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_hooks| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.exec| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.bandcheck| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.forward| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.security| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.refer| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mr| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.min_latency| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.gop_cache| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.tcp_nodelay| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mix_correct| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.time_jitter| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.atc| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.transcode| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.ingest| sc_filter_yn}}</td>
<td>
<span ng-show="vhost.editable">
<!-- vhost exists in server -->
<span ng-show="vhost.enabled != undefined">
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm submit vhost {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="update_vhost(vhost)">
Submit
</a>
<a href="javascript:void(0)" ng-click="cancel_vhost(vhost)">Cancel</a>
</span>
<!-- vhost in adding. -->
<span ng-show="vhost.enabled == undefined">
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm submit vhost{{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="add_vhost(vhost)">
Submit
</a>
<a href="javascript:void(0)" ng-click="abort_vhost(vhost)">Cancel</a>
</span>
</span>
<span ng-show="!vhost.editable">
<span ng-show="vhost.enabled">
<span>Update</span>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to disable {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="disable_vhost(vhost)">
Disable
</a>
<span>Delete</span>
</span>
<span ng-show="!vhost.enabled">
<a href="javascript:void(0)" ng-click="edit_vhost(vhost)">编辑</a>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to enable {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="enable_vhost(vhost)">
Enable
</a>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to delete {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="delete_vhost(vhost)">
Delete
</a>
</span>
<a href="#/configs/{{vhost.name}}" ng-show="!vhost.editable">Detail</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Config KAFKA
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.kafka.enabled" scp-bool="true"
scp-desc="Whether enable KAFKA. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.kafka.brokers"
scp-desc="The broker list, format is &lt;ip:port&gt;">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
SRS Heartbeat
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.enabled"
scp-desc="Whether enable heartbet. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.interval"
scp-desc="The interval in seconds. Default is 9.9"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.url"
scp-desc="The report url in json. Default is http://127.0.0.1:8085/api/v1/servers"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.device_id"
scp-desc="The device ID."
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.smmaries"
scp-desc="Whether report summaries. Default is {{false| sc_filter_yesno}}"
scd-bool="true"
scd-submit="submit(global.heartbeat.summaries)"
ng-show="global.heartbeat.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
STAT Hardward Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.stats.network"
scp-desc="The stat network index. Default is 0">
</tr>
<tr sc-pretty2 scp-data="global.stats.disk"
scp-desc="The stat disk name."
scd-array="true">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP Server Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_server.enabled"
scp-desc="Whether enable HTTP Server. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.http_server.listen"
scp-desc="The HTTP Server listen port, format is &lt;[address:]port&gt;. Default is 8080"
ng-show="global.http_server.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.http_server.dir"
scp-desc="The HTTP Server root directory. Default is ./objs/nginx/html"
ng-show="global.http_server.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Stream Caster Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<td>Opt</td>
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.enabled"
scp-desc="Whether enable StreamCaster. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.caster"
scp-desc="The type of StreamCaster. For example, mpegts_over_udp,rtsp,flv"
scd-select="mpegts_over_udp,rtsp,flv"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.output"
scp-desc="The output RTMP url."
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.listen"
scp-desc="The listen port, format is &lt;port&gt;"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_min"
scp-desc="The RTP min port."
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_max"
scp-desc="The RTP max port."
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

@ -1,62 +0,0 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Vhost({{global.vid.value}})录制(DVR)配置
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.name" scp-link="#/vhosts/{{global.id.value}}"
scp-desc="录制的流所属的Vhost的名称">
</tr>
<tr sc-pretty2 scp-data="global.app"
scp-desc="录制的流所属的App的名称">
</tr>
<tr sc-pretty2 scp-data="global.stream" scp-link="#/streams/{{global.sid.value}}"
scp-desc="录制的流的Stream地址即流名称">
</tr>
<tr sc-directive scd-data="global.dvr.apply" scd-span="span4"
scd-desc="是否开始录制该流。"
scd-default="false" scd-bool="true"
scd-submit="dvr(global.dvr.apply)"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.enabled"
scp-desc="是否对Vhost启用DVR配置。默认: {{false| sc_filter_yesno}}"
scp-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_plan"
scp-desc="录制计划session按会话segment分段append单文件。默认: session"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_path"
scp-desc="录制文件的路径模板。默认: ./objs/nginx/html/[app]/[stream].[timestamp].flv"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_duration"
scp-desc="单位:秒。录制文件的时长。默认: 30"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_wait_keyframe"
scp-desc="是否等待关键帧。默认: {{true| sc_filter_yesno}}"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.time_jitter"
scp-desc="录制的时间戳校正算法。full完全zero置零off关闭。默认: full"
ng-show="global.dvr.enabled.value">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

@ -1,62 +0,0 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Vhost({{global.vid.value}}) DVR Config
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.name" scp-link="#/vhosts/{{global.id.value}}"
scp-desc="The vhost name to DVR.">
</tr>
<tr sc-pretty2 scp-data="global.app"
scp-desc="The app name to DVR.">
</tr>
<tr sc-pretty2 scp-data="global.stream" scp-link="#/streams/{{global.sid.value}}"
scp-desc="The stream url to DVR.">
</tr>
<tr sc-directive scd-data="global.dvr.apply" scd-span="span4"
scd-desc="Whether record this stream."
scd-default="false" scd-bool="true"
scd-submit="dvr(global.dvr.apply)"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.enabled"
scp-desc="Whether enable DVR for vhost. Default is {{false| sc_filter_yesno}}"
scp-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_plan"
scp-desc="The DVR plan, can be session, segment or append. Default is session."
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_path"
scp-desc="The DVR file path template. Default is ./objs/nginx/html/[app]/[stream].[timestamp].flv"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_duration"
scp-desc="The DVR file duration in seconds. Default is 30"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_wait_keyframe"
scp-desc="Whether wait for keyframe when DVR. Default is {{true| sc_filter_yesno}}"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.time_jitter"
scp-desc="The time jitter algorithm for DVR, can be full, zero or off. Default is full."
ng-show="global.dvr.enabled.value">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

@ -1 +0,0 @@
DVR信息请在每个Stream中单独配置, 请前往<a href="javascript:void(0)" ng-click="gogogo('/streams/')">Streams</a>配置。

@ -1 +0,0 @@
Please config DVR in each stream by <a href="javascript:void(0)" ng-click="gogogo('/streams/')">here</a>.

File diff suppressed because it is too large Load Diff

@ -333,44 +333,8 @@ public:
private:
virtual srs_error_t do_persistence(SrsFileWriter* fw);
public:
// Dumps the global sections to json.
virtual srs_error_t global_to_json(SrsJsonObject* obj);
// Dumps the minimal sections to json.
virtual srs_error_t minimal_to_json(SrsJsonObject* obj);
// Dumps the vhost section to json.
virtual srs_error_t vhost_to_json(SrsConfDirective* vhost, SrsJsonObject* obj);
// Dumps the http_api sections to json for raw api info.
virtual srs_error_t raw_to_json(SrsJsonObject* obj);
// RAW set the global listen.
virtual srs_error_t raw_set_listen(const std::vector<std::string>& eps, bool& applied);
// RAW set the global pid.
virtual srs_error_t raw_set_pid(std::string pid, bool& applied);
// RAW set the global chunk size.
virtual srs_error_t raw_set_chunk_size(std::string chunk_size, bool& applied);
// RAW set the global ffmpeg log dir.
virtual srs_error_t raw_set_ff_log_dir(std::string ff_log_dir, bool& applied);
// RAW set the global log tank.
virtual srs_error_t raw_set_srs_log_tank(std::string srs_log_tank, bool& applied);
// RAW set the global log level.
virtual srs_error_t raw_set_srs_log_level(std::string srs_log_level, bool& applied);
// RAW set the global log file path for file tank.
virtual srs_error_t raw_set_srs_log_file(std::string srs_log_file, bool& applied);
// RAW set the global max connections of srs.
virtual srs_error_t raw_set_max_connections(std::string max_connections, bool& applied);
// RAW set the global whether use utc time.
virtual srs_error_t raw_set_utc_time(std::string utc_time, bool& applied);
// RAW set the global pithy print interval in ms.
virtual srs_error_t raw_set_pithy_print_ms(std::string pithy_print_ms, bool& applied);
// RAW create the new vhost.
virtual srs_error_t raw_create_vhost(std::string vhost, bool& applied);
// RAW update the disabled vhost name.
virtual srs_error_t raw_update_vhost(std::string vhost, std::string name, bool& applied);
// RAW delete the disabled vhost.
virtual srs_error_t raw_delete_vhost(std::string vhost, bool& applied);
// RAW disable the enabled vhost.
virtual srs_error_t raw_disable_vhost(std::string vhost, bool& applied);
// RAW enable the disabled vhost.
virtual srs_error_t raw_enable_vhost(std::string vhost, bool& applied);
private:
virtual srs_error_t do_reload_listen();
virtual srs_error_t do_reload_pid();

@ -902,7 +902,7 @@ srs_error_t SrsGoApiRaw::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
//////////////////////////////////////////////////////////////////////////
// the rpc is required.
// the allowd rpc method check.
if (rpc.empty() || (rpc != "reload" && rpc != "query" && rpc != "raw" && rpc != "update")) {
if (rpc.empty() || rpc != "reload") {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW);
}
@ -916,312 +916,6 @@ srs_error_t SrsGoApiRaw::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage*
return srs_api_response_code(w, r, ERROR_SUCCESS);
}
// for rpc=query, to get the configs of server.
// @param scope the scope to query for config, it can be:
// global, the configs belongs to the root, donot includes any sub directives.
// minimal, the minimal summary of server, for preview stream to got the port serving.
// vhost, the configs for specified vhost by @param vhost.
// @param vhost the vhost name for @param scope is vhost to query config.
// for the default vhost, must be __defaultVhost__
if (rpc == "query") {
if (!allow_query) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_DISABLED);
}
std::string scope = r->query_get("scope");
std::string vhost = r->query_get("vhost");
if (scope.empty() || (scope != "global" && scope != "vhost" && scope != "minimal")) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_NOT_ALLOWED);
}
if (scope == "vhost") {
// query vhost scope.
if (vhost.empty()) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
SrsConfDirective* root = _srs_config->get_root();
SrsConfDirective* conf = root->get("vhost", vhost);
if (!conf) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
SrsJsonObject* data = SrsJsonAny::object();
obj->set("vhost", data);
if ((err = _srs_config->vhost_to_json(conf, data)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "minimal") {
SrsJsonObject* data = SrsJsonAny::object();
obj->set("minimal", data);
// query minimal scope.
if ((err = _srs_config->minimal_to_json(data)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else {
SrsJsonObject* data = SrsJsonAny::object();
obj->set("global", data);
// query global scope.
if ((err = _srs_config->global_to_json(data)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
}
return srs_api_response(w, r, obj->dumps());
}
// for rpc=update, to update the configs of server.
// @scope the scope to update for config.
// @value the updated value for scope.
// @param the extra param for scope.
// @data the extra data for scope.
// possible updates:
// @scope @value value-description
// listen 1935,1936 the port list.
// pid ./objs/srs.pid the pid file of srs.
// chunk_size 60000 the global RTMP chunk_size.
// ff_log_dir ./objs the dir for ffmpeg log.
// srs_log_tank file the tank to log, file or console.
// srs_log_level trace the level of log, verbose, info, trace, warn, error.
// srs_log_file ./objs/srs.log the log file when tank is file.
// max_connections 1000 the max connections of srs.
// utc_time false whether enable utc time.
// pithy_print_ms 10000 the pithy print interval in ms.
// vhost specified updates:
// @scope @value @param @data description
// vhost ossrs.net create - create vhost ossrs.net
// vhost ossrs.net update new.ossrs.net the new name to update vhost
// dvr specified updates:
// @scope @value @param @data description
// dvr ossrs.net enable live/livestream enable the dvr of stream
// dvr ossrs.net disable live/livestream disable the dvr of stream
if (rpc == "update") {
if (!allow_update) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_DISABLED);
}
std::string scope = r->query_get("scope");
std::string value = r->query_get("value");
if (scope.empty()) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_NOT_ALLOWED);
}
if (scope != "listen" && scope != "pid" && scope != "chunk_size"
&& scope != "ff_log_dir" && scope != "srs_log_tank" && scope != "srs_log_level"
&& scope != "srs_log_file" && scope != "max_connections" && scope != "utc_time"
&& scope != "pithy_print_ms" && scope != "vhost" && scope != "dvr"
) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_NOT_ALLOWED);
}
bool applied = false;
string extra = "";
if (scope == "listen") {
vector<string> eps = srs_string_split(value, ",");
bool invalid = eps.empty();
for (int i = 0; i < (int)eps.size(); i++) {
string ep = eps.at(i);
int port = ::atoi(ep.c_str());
if (port <= 2 || port >= 65535) {
invalid = true;
break;
}
}
if (invalid) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_listen(eps, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "pid") {
if (value.empty() || !srs_string_starts_with(value, "./", "/tmp/", "/var/") || !srs_string_ends_with(value, ".pid")) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_pid(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "chunk_size") {
int csv = ::atoi(value.c_str());
if (csv < 128 || csv > 65535 || !srs_is_digit_number(value)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_chunk_size(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "ff_log_dir") {
if (value.empty() || (value != "/dev/null" && !srs_string_starts_with(value, "./", "/tmp/", "/var/"))) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_ff_log_dir(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "srs_log_tank") {
if (value.empty() || (value != "file" && value != "console")) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_srs_log_tank(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "srs_log_level") {
if (value != "verbose" && value != "info" && value != "trace" && value != "warn" && value != "error") {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_srs_log_level(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "srs_log_file") {
if (value.empty() || !srs_string_starts_with(value, "./", "/tmp/", "/var/") || !srs_string_ends_with(value, ".log")) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_srs_log_file(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "max_connections") {
int mcv = ::atoi(value.c_str());
if (mcv < 10 || mcv > 65535 || !srs_is_digit_number(value)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_max_connections(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "utc_time") {
if (!srs_is_boolean(value)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_utc_time(srs_config_bool2switch(value), applied)) != srs_success) {
return srs_api_response_code(w, r, srs_error_wrap(err, "raw api update utc_time=%s", value.c_str()));
}
} else if (scope == "pithy_print_ms") {
int ppmv = ::atoi(value.c_str());
if (ppmv < 100 || ppmv > 300000 || !srs_is_digit_number(value)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_set_pithy_print_ms(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (scope == "vhost") {
std::string param = r->query_get("param");
std::string data = r->query_get("data");
if (param != "create" && param != "update" && param != "delete" && param != "disable" && param != "enable") {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_NOT_ALLOWED);
}
extra += " " + param;
if (param == "create") {
// when create, the vhost must not exists.
if (param.empty() || _srs_config->get_vhost(value, false)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_create_vhost(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (param == "update") {
extra += " to " + data;
// when update, the vhost must exists and disabled.
SrsConfDirective* vhost = _srs_config->get_vhost(value, false);
if (data.empty() || data == value || param.empty() || !vhost || _srs_config->get_vhost_enabled(vhost)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_update_vhost(value, data, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (param == "delete") {
// when delete, the vhost must exists and disabled.
SrsConfDirective* vhost = _srs_config->get_vhost(value, false);
if (param.empty() || !vhost || _srs_config->get_vhost_enabled(vhost)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_delete_vhost(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (param == "disable") {
// when disable, the vhost must exists and enabled.
SrsConfDirective* vhost = _srs_config->get_vhost(value, false);
if (param.empty() || !vhost || !_srs_config->get_vhost_enabled(vhost)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_disable_vhost(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else if (param == "enable") {
// when enable, the vhost must exists and disabled.
SrsConfDirective* vhost = _srs_config->get_vhost(value, false);
if (param.empty() || !vhost || _srs_config->get_vhost_enabled(vhost)) {
return srs_api_response_code(w, r, ERROR_SYSTEM_CONFIG_RAW_PARAMS);
}
if ((err = _srs_config->raw_enable_vhost(value, applied)) != srs_success) {
int code = srs_error_code(err);
srs_error_reset(err);
return srs_api_response_code(w, r, code);
}
} else {
// TODO: support other param.
}
} else {
// TODO: support other scope.
}
// whether the config applied.
if (applied) {
server->on_signal(SRS_SIGNAL_PERSISTENCE_CONFIG);
srs_trace("raw api update %s=%s%s ok.", scope.c_str(), value.c_str(), extra.c_str());
} else {
srs_warn("raw api update not applied %s=%s%s.", scope.c_str(), value.c_str(), extra.c_str());
}
return srs_api_response(w, r, obj->dumps());
}
return err;
}

@ -9,6 +9,6 @@
#define VERSION_MAJOR 4
#define VERSION_MINOR 0
#define VERSION_REVISION 169
#define VERSION_REVISION 170
#endif

Loading…
Cancel
Save