pull & push ui done

pull/132/head
penggy 2018-11-24 22:07:02 +08:00
parent 1a6cb3260a
commit a7f5b1ce96
13 changed files with 92 additions and 38 deletions

View File

@ -60,7 +60,9 @@ func (h *APIHandler) Pushers(c *gin.Context) {
}
pushers = append(pushers, map[string]interface{}{
"id": pusher.ID(),
"path": rtsp,
"url": rtsp,
"path": pusher.Path(),
"source": pusher.Source(),
"transType": pusher.TransType(),
"inBytes": pusher.InBytes(),
"outBytes": pusher.OutBytes(),

View File

@ -3,9 +3,6 @@ package routers
import (
"bytes"
"fmt"
"github.com/EasyDarwin/EasyDarwin/rtsp"
"github.com/gin-gonic/gin"
"github.com/penggy/EasyGoLib/utils"
"log"
"math"
"net/http"
@ -16,12 +13,18 @@ import (
"strconv"
"strings"
"time"
"github.com/EasyDarwin/EasyDarwin/rtsp"
"github.com/gin-gonic/gin"
"github.com/penggy/EasyGoLib/utils"
)
func (h *APIHandler) StreamStart(c *gin.Context) {
type Form struct {
URL string `form:"url" binding:"required"`
IdleTimeout int `form:"idleTimeout"`
URL string `form:"url" binding:"required"`
CustomPath string `form:"customPath"`
IdleTimeout int `form:"idleTimeout"`
HeartbeatInterval int `form:"heartbeatInterval"`
}
var form Form
err := c.Bind(&form)
@ -29,8 +32,21 @@ func (h *APIHandler) StreamStart(c *gin.Context) {
log.Printf("Pull to push err:%v", err)
return
}
client := rtsp.NewRTSPClient(rtsp.GetServer(), form.URL, 0)
client, err := rtsp.NewRTSPClient(rtsp.GetServer(), form.URL, int64(form.HeartbeatInterval)*1000)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, err.Error())
return
}
if form.CustomPath != "" && !strings.HasPrefix(form.CustomPath, "/") {
form.CustomPath = "/" + form.CustomPath
}
client.CustomPath = form.CustomPath
pusher := rtsp.NewClientPusher(client)
if rtsp.GetServer().GetPusher(pusher.Path()) != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Path %s already exists", client.Path))
return
}
err = client.Start(time.Duration(form.IdleTimeout) * time.Second)
if err != nil {
log.Printf("Pull stream err :%v", err)
@ -57,7 +73,6 @@ func (h *APIHandler) StreamStop(c *gin.Context) {
if v.ID() == form.ID {
v.Stop()
c.IndentedJSON(200, "OK")
log.Printf("Stop %v success ", v)
return
}

View File

@ -55,6 +55,9 @@ func (pusher *Pusher) Path() string {
if pusher.Session != nil {
return pusher.Session.Path
}
if pusher.RTSPClient.CustomPath != "" {
return pusher.RTSPClient.CustomPath
}
return pusher.RTSPClient.Path
}
@ -136,6 +139,13 @@ func (pusher *Pusher) StartAt() time.Time {
return pusher.RTSPClient.StartAt
}
func (pusher *Pusher) Source() string {
if pusher.Session != nil {
return pusher.Session.URL
}
return pusher.RTSPClient.URL
}
func NewClientPusher(client *RTSPClient) (pusher *Pusher) {
pusher = &Pusher{
RTSPClient: client,

View File

@ -13,6 +13,8 @@ import (
"strings"
"time"
"github.com/teris-io/shortid"
"github.com/penggy/EasyGoLib/utils"
"github.com/pixelbender/go-sdp/sdp"
@ -24,6 +26,7 @@ type RTSPClient struct {
Status string
URL string
Path string
CustomPath string //custom path for pusher
ID string
Conn net.Conn
AuthHeaders bool
@ -56,24 +59,25 @@ func (client *RTSPClient) String() string {
return fmt.Sprintf("client[%s]", client.URL)
}
func NewRTSPClient(server *Server, rawUrl string, sendOptionMillis int64) *RTSPClient {
func NewRTSPClient(server *Server, rawUrl string, sendOptionMillis int64) (client *RTSPClient, err error) {
url, err := url.Parse(rawUrl)
if err != nil {
return nil
return
}
client := &RTSPClient{
client = &RTSPClient{
Server: server,
Stoped: false,
URL: rawUrl,
ID: url.Path,
ID: shortid.MustGenerate(),
Path: url.Path,
vRTPChannel: 0,
vRTPControlChannel: 1,
aRTPChannel: 2,
aRTPControlChannel: 3,
OptionIntervalMillis: sendOptionMillis,
StartAt: time.Now(),
}
return client
return
}
func (client *RTSPClient) Start(timeout time.Duration) error {
@ -197,7 +201,7 @@ func (client *RTSPClient) Start(timeout time.Duration) error {
for !client.Stoped {
if OptionIntervalMillis > 0 {
elapse := time.Now().Sub(startTime)
if elapse > time.Duration(OptionIntervalMillis*int64(time.Millisecond)) {
if elapse > time.Duration(OptionIntervalMillis)*time.Millisecond {
startTime = time.Now()
headers := make(map[string]string)
headers["Require"] = "implicit-play"

View File

@ -7,13 +7,27 @@
<span class="help-block">{{errors.first('url')}}</span>
</div>
</div>
<div :class="['form-group', { 'has-error': errors.has('idleTimeout')}]">
<label for="input-url" class="col-sm-3 control-label">空闲超时()</label>
<div :class="['form-group', { 'has-error': errors.has('customPath')}]">
<label for="input-custom-path" class="col-sm-3 control-label">自定义路径</label>
<div class="col-sm-8">
<input type="text" id="input-idle-timeout" class="form-control" name="idleTimeout" data-vv-as="" v-model.trim="form.idleTimeout">
<input type="text" id="input-custom-path" class="form-control" name="customPath" data-vv-as="" v-model.trim="form.customPath" placeholder="/your/custom/path">
<span class="help-block">{{errors.first('customPath')}}</span>
</div>
</div>
<div :class="['form-group', { 'has-error': errors.has('idleTimeout')}]">
<label for="input-idle-timeout" class="col-sm-3 control-label">空闲超时()</label>
<div class="col-sm-8">
<input type="text" id="input-idle-timeout" class="form-control" name="idleTimeout" data-vv-as="" v-validate="'numeric'" v-model.trim="form.idleTimeout" placeholder="默认使用系统配置">
<span class="help-block">{{errors.first('idleTimeout')}}</span>
</div>
</div>
</div>
<div :class="['form-group', { 'has-error': errors.has('heartbeatInterval')}]">
<label for="input-heartbeat-interval" class="col-sm-3 control-label">心跳间隔()</label>
<div class="col-sm-8">
<input type="text" id="input-heartbeat-interval" class="form-control" name="heartbeatInterval" data-vv-as="" v-validate="'numeric'" v-model.trim="form.heartbeatInterval" placeholder="默认使用系统配置">
<span class="help-block">{{errors.first('heartbeatInterval')}}</span>
</div>
</div>
</FormDlg>
</template>
@ -36,7 +50,9 @@ export default {
defForm() {
return {
url: '',
idleTimeout: ''
customPath: '',
idleTimeout: '',
heartbeatInterval: ''
}
},
onHide() {

View File

@ -32,20 +32,23 @@
<div class="box-body">
<el-table :data="pushers" stripe class="view-list" :default-sort="{prop: 'startAt', order: 'descending'}" @sort-change="sortChange">
<el-table-column prop="id" label="ID" min-width="120"></el-table-column>
<el-table-column prop="path" label="播放地址" min-width="240" show-overflow-tooltip>
<el-table-column label="播放地址" min-width="240" show-overflow-tooltip>
<template slot-scope="scope">
<span>
<i class="fa fa-copy" role="button" v-clipboard="scope.row.path" title="点击拷贝" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
{{scope.row.path}}
<i class="fa fa-copy" role="button" v-clipboard="scope.row.url" title="点击拷贝" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
{{scope.row.url}}
</span>
</template>
</el-table-column>
<!-- <el-table-column prop="source" label="源地址" min-width="240" show-overflow-tooltip>
<el-table-column label="源地址" min-width="240" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.source">{{scope.row.source}}</span>
<span v-if="scope.row.source">
<i class="fa fa-copy" role="button" v-clipboard="scope.row.source" title="点击拷贝" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
{{scope.row.source}}
</span>
<span v-else>-</span>
</template>
</el-table-column> -->
</el-table-column>
<el-table-column prop="transType" label="传输方式" min-width="100"></el-table-column>
<el-table-column prop="inBytes" label="上行流量" min-width="120" :formatter="formatBytes" sortable="custom"></el-table-column>
<el-table-column prop="outBytes" label="下行流量" min-width="120" :formatter="formatBytes" sortable="custom"></el-table-column>
@ -54,7 +57,9 @@
<el-table-column label="操作" min-width="120" fixed="right">
<template slot-scope="scope">
<div class="btn-group">
<a role="button" class="btn btn-xs btn-danger" @click.prevent="stop(scope.row)" v-if="scope.row.source"><i class="fa fa-stop"></i> 停止</a>
<a role="button" class="btn btn-xs btn-danger" @click.prevent="stop(scope.row)">
<i class="fa fa-stop"></i> 停止
</a>
</div>
</template>
</el-table-column>
@ -97,7 +102,7 @@ export default {
}
},
mounted() {
this.$refs["q"].focus();
// this.$refs["q"].focus();
this.timer = setInterval(() => {
this.getPushers();
}, 3000);
@ -144,11 +149,13 @@ export default {
return prettyBytes(val);
},
stop(row) {
$.get("/api/v1/stream/stop", {
streamID: row.id
}).then(data => {
this.getPushers();
})
this.$confirm(`确认停止 ${row.path} ?`, "提示").then(() => {
$.get("/api/v1/stream/stop", {
id: row.id
}).then(data => {
this.getPushers();
})
}).catch(() => {});
}
},
beforeRouteEnter(to, from, next) {

View File

@ -19,7 +19,7 @@ define({
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-24T02:46:15.810Z",
"time": "2018-11-24T14:05:41.551Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@ -19,7 +19,7 @@
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-24T02:46:15.810Z",
"time": "2018-11-24T14:05:41.551Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@ -7,9 +7,9 @@
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<script src="js/jquery-2.2.4.js"></script>
<script src="js/easy-player-lib.min.js"></script>
<link href="css/index.eba7eb98.css" rel="stylesheet"></head>
<link href="css/index.09a1f19a.css" rel="stylesheet"></head>
<body class="skin-green sidebar-mini">
<div id="app"></div>
<script type="text/javascript" src="js/index.eba7eb98.js"></script></body>
<script type="text/javascript" src="js/index.09a1f19a.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long