Compare commits

..

1 Commits

Author SHA1 Message Date
李宇翔
55bd2ce785 增加中止操作 2020-05-19 19:25:44 +08:00
11 changed files with 403 additions and 343 deletions

158
client.go
View File

@@ -6,8 +6,6 @@ import (
"crypto/md5" "crypto/md5"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
. "github.com/Monibuca/engine/v2"
"github.com/pixelbender/go-sdp/sdp"
"io" "io"
"net" "net"
"net/url" "net/url"
@@ -15,6 +13,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
. "github.com/Monibuca/engine/v2"
) )
// PullStream 从外部拉流 // PullStream 从外部拉流
@@ -28,7 +28,7 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (result bool) {
rtsp.aRTPChannel = 2 rtsp.aRTPChannel = 2
rtsp.aRTPControlChannel = 3 rtsp.aRTPControlChannel = 3
rtsp.URL = rtspUrl rtsp.URL = rtspUrl
if err := rtsp.requestStream();err != nil { if err := rtsp.requestStream(); err != nil {
rtsp.Close() rtsp.Close()
return false return false
} }
@@ -184,97 +184,87 @@ func (client *RTSP) requestStream() (err error) {
return err return err
} }
} }
_sdp, err := sdp.ParseString(resp.Body)
if err != nil {
return err
}
client.Sdp = _sdp
client.SDPRaw = resp.Body client.SDPRaw = resp.Body
client.SDPMap = ParseSDP(client.SDPRaw) client.SDPMap = ParseSDP(client.SDPRaw)
session := "" session := ""
for _, media := range _sdp.Media { if videoInfo, ok := client.SDPMap["video"]; ok {
switch media.Type { client.VControl = videoInfo.Control
case "video": client.VCodec = videoInfo.Codec
client.VControl = media.Attributes.Get("control") client.SPS = videoInfo.SpropParameterSets[0]
client.VCodec = media.Format[0].Name client.PPS = videoInfo.SpropParameterSets[1]
client.SPS = client.SDPMap["video"].SpropParameterSets[0] var _url = ""
client.PPS = client.SDPMap["video"].SpropParameterSets[1] if strings.Index(strings.ToLower(client.VControl), "rtsp://") == 0 {
var _url = "" _url = client.VControl
if strings.Index(strings.ToLower(client.VControl), "rtsp://") == 0 { } else {
_url = client.VControl _url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.VControl, "/")
} else {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.VControl, "/")
}
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
}
//RTP/AVP;unicast;client_port=64864-64865
err = client.UDPServer.SetupVideo()
if err != nil {
Printf("Setup video err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, VIDEO VControl:%s, VCode:%s, url:%s,Session:%s,vRTPChannel:%d,vRTPControlChannel:%d", client.VControl, client.VCodec, _url, session, client.vRTPChannel, client.vRTPControlChannel)
resp, err = client.RequestWithPath("SETUP", _url, headers, true)
if err != nil {
return err
}
session, _ = resp.Header["Session"].(string)
case "audio":
client.AControl = media.Attributes.Get("control")
client.ACodec = media.Format[0].Name
client.AudioSpecificConfig = client.SDPMap["audio"].Config
var _url = ""
if strings.Index(strings.ToLower(client.AControl), "rtsp://") == 0 {
_url = client.AControl
} else {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.AControl, "/")
}
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
}
err = client.UDPServer.SetupAudio()
if err != nil {
Printf("Setup audio err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, AUDIO AControl:%s, ACodec:%s, url:%s,Session:%s, aRTPChannel:%d,aRTPControlChannel:%d", client.AControl, client.ACodec, _url, session, client.aRTPChannel, client.aRTPControlChannel)
resp, err = client.RequestWithPath("SETUP", _url, headers, true)
if err != nil {
return err
}
session, _ = resp.Header["Session"].(string)
} }
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
}
//RTP/AVP;unicast;client_port=64864-64865
err = client.UDPServer.SetupVideo()
if err != nil {
Printf("Setup video err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, VIDEO VControl:%s, VCode:%s, url:%s,Session:%s,vRTPChannel:%d,vRTPControlChannel:%d", client.VControl, client.VCodec, _url, session, client.vRTPChannel, client.vRTPControlChannel)
if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil {
return err
}
session, _ = resp.Header["Session"].(string)
session = strings.Split(session, ";")[0]
}
if audioInfo, ok := client.SDPMap["audio"]; ok {
client.AControl = audioInfo.Control
client.ACodec = audioInfo.Codec
client.AudioSpecificConfig = audioInfo.Config
var _url = ""
if strings.Index(strings.ToLower(client.AControl), "rtsp://") == 0 {
_url = client.AControl
} else {
_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.AControl, "/")
}
headers = make(map[string]string)
if client.TransType == TRANS_TYPE_TCP {
headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel)
} else {
if client.UDPServer == nil {
client.UDPServer = &UDPServer{Session: client}
}
err = client.UDPServer.SetupAudio()
if err != nil {
Printf("Setup audio err.%v", err)
return err
}
headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort)
client.Conn.timeout = 0 // UDP ignore timeout
}
if session != "" {
headers["Session"] = session
}
Printf("Parse DESCRIBE response, AUDIO AControl:%s, ACodec:%s, url:%s,Session:%s, aRTPChannel:%d,aRTPControlChannel:%d", client.AControl, client.ACodec, _url, session, client.aRTPChannel, client.aRTPControlChannel)
if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil {
return err
}
session, _ = resp.Header["Session"].(string)
session = strings.Split(session, ";")[0]
} }
headers = make(map[string]string) headers = make(map[string]string)
if session != "" { if session != "" {
headers["Session"] = session headers["Session"] = session
} }
resp, err = client.Request("PLAY", headers) resp, err = client.Request("PLAY", headers)
if err != nil { return err
return err
}
return nil
} }
func (client *RTSP) startStream() { func (client *RTSP) startStream() {

3
go.mod
View File

@@ -4,6 +4,9 @@ go 1.13
require ( require (
github.com/Monibuca/engine/v2 v2.0.0 github.com/Monibuca/engine/v2 v2.0.0
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
github.com/gobwas/pool v0.2.0 // indirect
github.com/gobwas/ws v1.0.3 // indirect
github.com/jinzhu/gorm v1.9.12 // indirect github.com/jinzhu/gorm v1.9.12 // indirect
github.com/pixelbender/go-sdp v1.0.0 github.com/pixelbender/go-sdp v1.0.0
github.com/reactivex/rxgo v1.0.0 // indirect github.com/reactivex/rxgo v1.0.0 // indirect

6
go.sum
View File

@@ -25,6 +25,12 @@ github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

33
main.go
View File

@@ -14,18 +14,16 @@ import (
. "github.com/Monibuca/engine/v2" . "github.com/Monibuca/engine/v2"
. "github.com/Monibuca/engine/v2/avformat" . "github.com/Monibuca/engine/v2/avformat"
"github.com/Monibuca/engine/v2/util" "github.com/Monibuca/engine/v2/util"
"github.com/pixelbender/go-sdp/sdp"
"github.com/teris-io/shortid" "github.com/teris-io/shortid"
) )
var collection = sync.Map{} var collection = sync.Map{}
var config = struct { var config = struct {
ListenAddr string ListenAddr string
BufferLength int AutoPull bool
AutoPull bool RemoteAddr string
RemoteAddr string Timeout int
Timeout int }{":554", false, "rtsp://localhost/${streamPath}", 0}
}{":554", 2048, true, "rtsp://localhost/${streamPath}", 0}
func init() { func init() {
InstallPlugin(&PluginConfig{ InstallPlugin(&PluginConfig{
@@ -54,7 +52,6 @@ func runPlugin() {
collection.Range(func(key, value interface{}) bool { collection.Range(func(key, value interface{}) bool {
rtsp := value.(*RTSP) rtsp := value.(*RTSP)
pinfo := &rtsp.RTSPInfo pinfo := &rtsp.RTSPInfo
//pinfo.BufferRate = len(rtsp.OutGoing) * 100 / config.BufferLength
info = append(info, pinfo) info = append(info, pinfo)
return true return true
}) })
@@ -158,19 +155,17 @@ type RTSP struct {
Auth func(string) string Auth func(string) string
} }
type RTSPClientInfo struct { type RTSPClientInfo struct {
Agent string Agent string
Session string Session string
Sdp *sdp.Session authLine string
authLine string Seq int
Seq int
} }
type RTSPInfo struct { type RTSPInfo struct {
URL string URL string
SyncCount int64 SyncCount int64
Header *string Header *string
BufferRate int InBytes int
InBytes int OutBytes int
OutBytes int
StreamInfo *StreamInfo StreamInfo *StreamInfo
} }

View File

@@ -172,14 +172,14 @@ if (typeof window !== 'undefined') {
// Indicate to webpack that this file can be concatenated // Indicate to webpack that this file can be concatenated
/* harmony default export */ var setPublicPath = (null); /* harmony default export */ var setPublicPath = (null);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=3ee6bce0& // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=18f38531&
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){
var item = ref.row; var item = ref.row;
return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_c('Progress',{attrs:{"stroke-width":20,"percent":Math.ceil(item.BufferRate),"text-inside":""}})],1),_c('td',[_vm._v(_vm._s(item.SyncCount))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_vm._v(_vm._s(item.InBytes))]),_c('td',[_vm._v(_vm._s(item.OutBytes))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")]),_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.stop(item)}}},[_vm._v("中止")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)}
var staticRenderFns = [] var staticRenderFns = []
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=3ee6bce0& // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=18f38531&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
// //
@@ -207,78 +207,100 @@ var staticRenderFns = []
// //
// //
// //
//
//
//
//
//
//
//
//
//
let listES = null; let listES = null;
/* harmony default export */ var Appvue_type_script_lang_js_ = ({ /* harmony default export */ var Appvue_type_script_lang_js_ = ({
data() { data() {
return { return {
currentStream: null, currentStream: null,
Streams: null, Streams: null,
remoteAddr: "", remoteAddr: "",
streamPath: "", streamPath: "",
openPull: false, openPull: false,
columns: [ columns: [
"StreamPath", "StreamPath",
"开始时间", "开始时间",
"缓冲", "总接收",
"同步数", "总发送",
"操作" "操作"
].map(title => ({ title })) ].map(title => ({ title }))
}; };
}, },
methods: { methods: {
fetchlist() { fetchlist() {
listES = new EventSource(this.apiHost + "/rtsp/list"); listES = new EventSource(this.apiHost + "/rtsp/list");
listES.onmessage = evt => { listES.onmessage = evt => {
if (!evt.data) return; if (!evt.data) return;
this.Streams = JSON.parse(evt.data) || []; this.Streams = JSON.parse(evt.data) || [];
this.Streams.sort((a, b) => this.Streams.sort((a, b) =>
a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1 a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1
); );
}; };
},
showHeader(item) {
this.$Modal.info({
title: "RTSP Header",
width: "1000px",
scrollable: true,
content: item.Header
});
},
addPull() {
this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
}
}, },
mounted() { showHeader(item) {
this.fetchlist(); this.$Modal.info({
let _this = this; title: "RTSP Header",
this.$parent.titleOps = [ width: "1000px",
{ scrollable: true,
template: '<m-button @click="onClick">拉流转发</m-button>', content: item.Header
methods: { });
onClick() {
_this.openPull = true;
}
}
}
];
}, },
destroyed() { addPull() {
listES.close(); this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
},
stop(item) {
this.ajax
.get(this.apiHost + "/api/stop", {
stream: this.streamPath
})
.then(x => {
if (x == "success") {
this.$toast.success("已停止拉流");
} else {
this.$toast.error(x.msg);
}
});
} }
},
mounted() {
this.fetchlist();
let _this = this;
this.$parent.titleOps = [
{
template: '<m-button @click="onClick">拉流转发</m-button>',
methods: {
onClick() {
_this.openPull = true;
}
}
}
];
},
destroyed() {
listES.close();
}
}); });
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js& // CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&

File diff suppressed because one or more lines are too long

View File

@@ -181,14 +181,14 @@ if (typeof window !== 'undefined') {
// Indicate to webpack that this file can be concatenated // Indicate to webpack that this file can be concatenated
/* harmony default export */ var setPublicPath = (null); /* harmony default export */ var setPublicPath = (null);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=3ee6bce0& // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=18f38531&
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){
var item = ref.row; var item = ref.row;
return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_c('Progress',{attrs:{"stroke-width":20,"percent":Math.ceil(item.BufferRate),"text-inside":""}})],1),_c('td',[_vm._v(_vm._s(item.SyncCount))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_vm._v(_vm._s(item.InBytes))]),_c('td',[_vm._v(_vm._s(item.OutBytes))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")]),_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.stop(item)}}},[_vm._v("中止")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)}
var staticRenderFns = [] var staticRenderFns = []
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=3ee6bce0& // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=18f38531&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
// //
@@ -216,78 +216,100 @@ var staticRenderFns = []
// //
// //
// //
//
//
//
//
//
//
//
//
//
let listES = null; let listES = null;
/* harmony default export */ var Appvue_type_script_lang_js_ = ({ /* harmony default export */ var Appvue_type_script_lang_js_ = ({
data() { data() {
return { return {
currentStream: null, currentStream: null,
Streams: null, Streams: null,
remoteAddr: "", remoteAddr: "",
streamPath: "", streamPath: "",
openPull: false, openPull: false,
columns: [ columns: [
"StreamPath", "StreamPath",
"开始时间", "开始时间",
"缓冲", "总接收",
"同步数", "总发送",
"操作" "操作"
].map(title => ({ title })) ].map(title => ({ title }))
}; };
}, },
methods: { methods: {
fetchlist() { fetchlist() {
listES = new EventSource(this.apiHost + "/rtsp/list"); listES = new EventSource(this.apiHost + "/rtsp/list");
listES.onmessage = evt => { listES.onmessage = evt => {
if (!evt.data) return; if (!evt.data) return;
this.Streams = JSON.parse(evt.data) || []; this.Streams = JSON.parse(evt.data) || [];
this.Streams.sort((a, b) => this.Streams.sort((a, b) =>
a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1 a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1
); );
}; };
},
showHeader(item) {
this.$Modal.info({
title: "RTSP Header",
width: "1000px",
scrollable: true,
content: item.Header
});
},
addPull() {
this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
}
}, },
mounted() { showHeader(item) {
this.fetchlist(); this.$Modal.info({
let _this = this; title: "RTSP Header",
this.$parent.titleOps = [ width: "1000px",
{ scrollable: true,
template: '<m-button @click="onClick">拉流转发</m-button>', content: item.Header
methods: { });
onClick() {
_this.openPull = true;
}
}
}
];
}, },
destroyed() { addPull() {
listES.close(); this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
},
stop(item) {
this.ajax
.get(this.apiHost + "/api/stop", {
stream: this.streamPath
})
.then(x => {
if (x == "success") {
this.$toast.success("已停止拉流");
} else {
this.$toast.error(x.msg);
}
});
} }
},
mounted() {
this.fetchlist();
let _this = this;
this.$parent.titleOps = [
{
template: '<m-button @click="onClick">拉流转发</m-button>',
methods: {
onClick() {
_this.openPull = true;
}
}
}
];
},
destroyed() {
listES.close();
}
}); });
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js& // CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
(function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["plugin-rtsp"]=e():t["plugin-rtsp"]=e()})("undefined"!==typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s="fb15")}({"034f":function(t,e,r){"use strict";var n=r("85ec"),o=r.n(n);o.a},"85ec":function(t,e,r){},f6fd:function(t,e){(function(t){var e="currentScript",r=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(n){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(n.stack)||[!1])[1];for(t in r)if(r[t].src==e||"interactive"==r[t].readyState)return r[t];return null}}})})(document)},fb15:function(t,e,r){"use strict";var n;(r.r(e),"undefined"!==typeof window)&&(r("f6fd"),(n=window.document.currentScript)&&(n=n.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(r.p=n[1]));var o=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",[r("mu-data-table",{attrs:{data:t.Streams,columns:t.columns},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[r("td",[t._v(t._s(n.StreamInfo.StreamPath))]),r("td",[r("StartTime",{attrs:{value:n.StreamInfo.StartTime}})],1),r("td",[r("Progress",{attrs:{"stroke-width":20,percent:Math.ceil(n.BufferRate),"text-inside":""}})],1),r("td",[t._v(t._s(n.SyncCount))]),r("td",[r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.showHeader(n)}}},[t._v("头信息")])],1)]}}])}),r("mu-dialog",{attrs:{title:"拉流转发",width:"360",open:t.openPull},on:{"update:open":function(e){t.openPull=e}}},[r("mu-text-field",{attrs:{label:"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:t.remoteAddr,callback:function(e){t.remoteAddr=e},expression:"remoteAddr"}}),r("mu-text-field",{attrs:{label:"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:t.streamPath,callback:function(e){t.streamPath=e},expression:"streamPath"}}),r("mu-button",{attrs:{slot:"actions",flat:"",color:"primary"},on:{click:t.addPull},slot:"actions"},[t._v("确定")])],1)],1)},a=[];let s=null;var i={data(){return{currentStream:null,Streams:null,remoteAddr:"",streamPath:"",openPull:!1,columns:["StreamPath","开始时间","缓冲","同步数","操作"].map(t=>({title:t}))}},methods:{fetchlist(){s=new EventSource(this.apiHost+"/rtsp/list"),s.onmessage=t=>{t.data&&(this.Streams=JSON.parse(t.data)||[],this.Streams.sort((t,e)=>t.StreamInfo.StreamPath>e.StreamInfo.StreamPath?1:-1))}},showHeader(t){this.$Modal.info({title:"RTSP Header",width:"1000px",scrollable:!0,content:t.Header})},addPull(){this.openPull=!1,this.ajax.getJSON(this.apiHost+"/rtsp/pull",{target:this.remoteAddr,streamPath:this.streamPath}).then(t=>{0==t.code?this.$toast.success("已启动拉流"):this.$toast.error(t.msg)})}},mounted(){this.fetchlist();let t=this;this.$parent.titleOps=[{template:'<m-button @click="onClick">拉流转发</m-button>',methods:{onClick(){t.openPull=!0}}}]},destroyed(){s.close()}},l=i;r("034f");function u(t,e,r,n,o,a,s,i){var l,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=r,u._compiled=!0),n&&(u.functional=!0),a&&(u._scopeId="data-v-"+a),s?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(s)},u._ssrRegister=l):o&&(l=i?function(){o.call(this,this.$root.$options.shadowRoot)}:o),l)if(u.functional){u._injectStyles=l;var c=u.render;u.render=function(t,e){return l.call(e),c(t,e)}}else{var d=u.beforeCreate;u.beforeCreate=d?[].concat(d,l):[l]}return{exports:t,options:u}}var c=u(l,o,a,!1,null,null,null),d=c.exports;e["default"]=d}})["default"]})); (function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["plugin-rtsp"]=e():t["plugin-rtsp"]=e()})("undefined"!==typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s="fb15")}({"034f":function(t,e,r){"use strict";var n=r("85ec"),o=r.n(n);o.a},"85ec":function(t,e,r){},f6fd:function(t,e){(function(t){var e="currentScript",r=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(n){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(n.stack)||[!1])[1];for(t in r)if(r[t].src==e||"interactive"==r[t].readyState)return r[t];return null}}})})(document)},fb15:function(t,e,r){"use strict";var n;(r.r(e),"undefined"!==typeof window)&&(r("f6fd"),(n=window.document.currentScript)&&(n=n.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(r.p=n[1]));var o=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",[r("mu-data-table",{attrs:{data:t.Streams,columns:t.columns},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[r("td",[t._v(t._s(n.StreamInfo.StreamPath))]),r("td",[r("StartTime",{attrs:{value:n.StreamInfo.StartTime}})],1),r("td",[t._v(t._s(n.InBytes))]),r("td",[t._v(t._s(n.OutBytes))]),r("td",[r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.showHeader(n)}}},[t._v("头信息")]),r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.stop(n)}}},[t._v("中止")])],1)]}}])}),r("mu-dialog",{attrs:{title:"拉流转发",width:"360",open:t.openPull},on:{"update:open":function(e){t.openPull=e}}},[r("mu-text-field",{attrs:{label:"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:t.remoteAddr,callback:function(e){t.remoteAddr=e},expression:"remoteAddr"}}),r("mu-text-field",{attrs:{label:"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:t.streamPath,callback:function(e){t.streamPath=e},expression:"streamPath"}}),r("mu-button",{attrs:{slot:"actions",flat:"",color:"primary"},on:{click:t.addPull},slot:"actions"},[t._v("确定")])],1)],1)},a=[];let s=null;var i={data(){return{currentStream:null,Streams:null,remoteAddr:"",streamPath:"",openPull:!1,columns:["StreamPath","开始时间","总接收","总发送","操作"].map(t=>({title:t}))}},methods:{fetchlist(){s=new EventSource(this.apiHost+"/rtsp/list"),s.onmessage=t=>{t.data&&(this.Streams=JSON.parse(t.data)||[],this.Streams.sort((t,e)=>t.StreamInfo.StreamPath>e.StreamInfo.StreamPath?1:-1))}},showHeader(t){this.$Modal.info({title:"RTSP Header",width:"1000px",scrollable:!0,content:t.Header})},addPull(){this.openPull=!1,this.ajax.getJSON(this.apiHost+"/rtsp/pull",{target:this.remoteAddr,streamPath:this.streamPath}).then(t=>{0==t.code?this.$toast.success("已启动拉流"):this.$toast.error(t.msg)})},stop(t){this.ajax.get(this.apiHost+"/api/stop",{stream:this.streamPath}).then(t=>{"success"==t?this.$toast.success("已停止拉流"):this.$toast.error(t.msg)})}},mounted(){this.fetchlist();let t=this;this.$parent.titleOps=[{template:'<m-button @click="onClick">拉流转发</m-button>',methods:{onClick(){t.openPull=!0}}}]},destroyed(){s.close()}},l=i;r("034f");function u(t,e,r,n,o,a,s,i){var l,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=r,u._compiled=!0),n&&(u.functional=!0),a&&(u._scopeId="data-v-"+a),s?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(s)},u._ssrRegister=l):o&&(l=i?function(){o.call(this,this.$root.$options.shadowRoot)}:o),l)if(u.functional){u._injectStyles=l;var c=u.render;u.render=function(t,e){return l.call(e),c(t,e)}}else{var d=u.beforeCreate;u.beforeCreate=d?[].concat(d,l):[l]}return{exports:t,options:u}}var c=u(l,o,a,!1,null,null,null),d=c.exports;e["default"]=d}})["default"]}));
//# sourceMappingURL=plugin-rtsp.umd.min.js.map //# sourceMappingURL=plugin-rtsp.umd.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,125 +1,147 @@
<template> <template>
<div> <div>
<mu-data-table :data="Streams" :columns="columns"> <mu-data-table :data="Streams" :columns="columns">
<template #default="{row:item}"> <template #default="{row:item}">
<td>{{item.StreamInfo.StreamPath}}</td> <td>{{item.StreamInfo.StreamPath}}</td>
<td> <td>
<StartTime :value="item.StreamInfo.StartTime"></StartTime> <StartTime :value="item.StreamInfo.StartTime"></StartTime>
</td> </td>
<td><Progress :stroke-width="20" :percent="Math.ceil(item.BufferRate)" text-inside /></td> <td>{{item.InBytes}}</td>
<td>{{item.SyncCount}}</td> <td>{{item.OutBytes}}</td>
<td> <td>
<mu-button flat @click="showHeader(item)">头信息</mu-button> <mu-button flat @click="showHeader(item)">头信息</mu-button>
</td> <mu-button flat @click="stop(item)">中止</mu-button>
</template> </td>
</mu-data-table> </template>
<mu-dialog title="拉流转发" width="360" :open.sync="openPull"> </mu-data-table>
<mu-text-field v-model="remoteAddr" label="rtsp url" label-float help-text="Please enter URL of rtsp..."> <mu-dialog title="拉流转发" width="360" :open.sync="openPull">
</mu-text-field> <mu-text-field
<mu-text-field v-model="streamPath" label="streamPath" label-float v-model="remoteAddr"
help-text="Please enter streamPath to publish."></mu-text-field> label="rtsp url"
<mu-button slot="actions" flat color="primary" @click="addPull">确定</mu-button> label-float
</mu-dialog> help-text="Please enter URL of rtsp..."
</div> ></mu-text-field>
<mu-text-field
v-model="streamPath"
label="streamPath"
label-float
help-text="Please enter streamPath to publish."
></mu-text-field>
<mu-button slot="actions" flat color="primary" @click="addPull">确定</mu-button>
</mu-dialog>
</div>
</template> </template>
<script> <script>
let listES = null; let listES = null;
export default { export default {
data() { data() {
return { return {
currentStream: null, currentStream: null,
Streams: null, Streams: null,
remoteAddr: "", remoteAddr: "",
streamPath: "", streamPath: "",
openPull: false, openPull: false,
columns: [ columns: [
"StreamPath", "StreamPath",
"开始时间", "开始时间",
"缓冲", "总接收",
"同步数", "总发送",
"操作" "操作"
].map(title => ({ title })) ].map(title => ({ title }))
}; };
}, },
methods: { methods: {
fetchlist() { fetchlist() {
listES = new EventSource(this.apiHost + "/rtsp/list"); listES = new EventSource(this.apiHost + "/rtsp/list");
listES.onmessage = evt => { listES.onmessage = evt => {
if (!evt.data) return; if (!evt.data) return;
this.Streams = JSON.parse(evt.data) || []; this.Streams = JSON.parse(evt.data) || [];
this.Streams.sort((a, b) => this.Streams.sort((a, b) =>
a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1 a.StreamInfo.StreamPath > b.StreamInfo.StreamPath ? 1 : -1
); );
}; };
},
showHeader(item) {
this.$Modal.info({
title: "RTSP Header",
width: "1000px",
scrollable: true,
content: item.Header
});
},
addPull() {
this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
}
}, },
mounted() { showHeader(item) {
this.fetchlist(); this.$Modal.info({
let _this = this; title: "RTSP Header",
this.$parent.titleOps = [ width: "1000px",
{ scrollable: true,
template: '<m-button @click="onClick">拉流转发</m-button>', content: item.Header
methods: { });
onClick() {
_this.openPull = true;
}
}
}
];
}, },
destroyed() { addPull() {
listES.close(); this.openPull = false;
this.ajax
.getJSON(this.apiHost + "/rtsp/pull", {
target: this.remoteAddr,
streamPath: this.streamPath
})
.then(x => {
if (x.code == 0) {
this.$toast.success("已启动拉流");
} else {
this.$toast.error(x.msg);
}
});
},
stop(item) {
this.ajax
.get(this.apiHost + "/api/stop", {
stream: item.StreamInfo.StreamPath
})
.then(x => {
if (x == "success") {
this.$toast.success("已停止拉流");
} else {
this.$toast.error(x.msg);
}
});
} }
},
mounted() {
this.fetchlist();
let _this = this;
this.$parent.titleOps = [
{
template: '<m-button @click="onClick">拉流转发</m-button>',
methods: {
onClick() {
_this.openPull = true;
}
}
}
];
},
destroyed() {
listES.close();
}
}; };
</script> </script>
<style> <style>
.empty { .empty {
color: #eb5e46; color: #eb5e46;
width: 100%; width: 100%;
min-height: 500px; min-height: 500px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.layout { .layout {
padding-bottom: 30px; padding-bottom: 30px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.ts-info { .ts-info {
width: 300px; width: 300px;
} }
.hls-info { .hls-info {
width: 350px; width: 350px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
</style> </style>