适配3.0

This commit is contained in:
langhuihui
2021-02-14 23:00:46 +08:00
parent 791368988b
commit 1e32b8dc1d
23 changed files with 97 additions and 34259 deletions

15
go.mod
View File

@@ -1,13 +1,12 @@
module github.com/Monibuca/plugin-jessica
module github.com/Monibuca/plugin-jessica/v3
go 1.13
require (
github.com/Monibuca/engine/v2 v2.0.0
github.com/Monibuca/utils v1.0.0 // indirect
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
github.com/gobwas/pool v0.2.0 // indirect
github.com/gobwas/ws v1.0.2
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
github.com/Monibuca/engine/v3 v3.0.0-alpha4
github.com/Monibuca/utils/v3 v3.0.0-alpha4
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.0.4
github.com/logrusorgru/aurora v2.0.3+incompatible
)

64
go.sum
View File

@@ -1,67 +1,39 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Monibuca/engine v1.2.1 h1:TJmC6eZA1lR1MScWgempZLiEZD4T6aY/nn/rlQ9UdK8=
github.com/Monibuca/engine v1.2.1/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g=
github.com/Monibuca/engine v1.2.2 h1:hNjsrZpOmui8lYhgCJ5ltJU8g/k0Rrdysx2tHNGGnbI=
github.com/Monibuca/engine v1.2.2/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g=
github.com/Monibuca/engine/v2 v2.0.0-alpha2 h1:45yazqnnxEEcfHcOJGuIr1xtnBzQT6cPvhlymeZrDmA=
github.com/Monibuca/engine/v2 v2.0.0-alpha2/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc=
github.com/Monibuca/engine/v2 v2.0.0 h1:8FjaScrtN8QdbcxO9zZYABMC0Re3I1O1T4p94zAXYb0=
github.com/Monibuca/engine/v2 v2.0.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc=
github.com/Monibuca/utils v1.0.0 h1:hbRvlDAzClTXtIQIeY1PMqkL36TWyRCLNULjY/sOT3w=
github.com/Monibuca/utils v1.0.0/go.mod h1:Iw3OnscHv6RFoi9nHdVLdPKkI9UfFmU5TgSQkMf+Yi8=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae/go.mod h1:wruC5r2gHdr/JIUs5Rr1V45YtsAzKXZxAnn/5rPC97g=
github.com/Monibuca/engine/v3 v3.0.0-alpha3 h1:NtFBMsu1nvEA09q64sW9xNzLdQ9RCKQXJlESM3GmGxU=
github.com/Monibuca/engine/v3 v3.0.0-alpha3/go.mod h1:K5FB3wk+iS/nPc+NS4XwObYQV4gtF6klEwDxaVM0BsQ=
github.com/Monibuca/engine/v3 v3.0.0-alpha4 h1:kAStDd1p9tlSQeNyAAmb7vrPL2UCz7LFTzw5LdbGxBI=
github.com/Monibuca/engine/v3 v3.0.0-alpha4/go.mod h1:V0/kfen6K5O/RLXHPsZj4DF/LboDZ0OqfeCfn35bWMo=
github.com/Monibuca/utils/v3 v3.0.0-alpha3 h1:n4Sq7mS1Iz8oBj2BcV4sXgKbZgix0fFLvjAfXYoiXl0=
github.com/Monibuca/utils/v3 v3.0.0-alpha3/go.mod h1:3xYmhQbgAZBHLyIMteUCd1va+1z/xnd72B585mCaT3c=
github.com/Monibuca/utils/v3 v3.0.0-alpha4 h1:pecYA89kWmtGOeY6R99d4T1epPJ1wc+jFrrJY13VD04=
github.com/Monibuca/utils/v3 v3.0.0-alpha4/go.mod h1:3xYmhQbgAZBHLyIMteUCd1va+1z/xnd72B585mCaT3c=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo=
github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg=
github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg=
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/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/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quangngotan95/go-m3u8 v0.1.0/go.mod h1:smzfWHlYpBATVNu1GapKLYiCtEo5JxridIgvvudZ+Wc=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shirou/gopsutil v2.19.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY=
github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/yakovlevdmv/Golang-iso8601-duration v0.0.0-20180403125811-e5db0413b903/go.mod h1:9o96byDMk+osDZqiIS2a7E7y0cWmg4rRTjQRWVHpFWE=
github.com/yakovlevdmv/WS-Discovery v0.0.0-20180512141937-16170c6c3677/go.mod h1:/VKdrRRbAVE0pvkoPTUlfXw1zxqEpflVsgF25aR5gbk=
github.com/yakovlevdmv/goonvif v0.0.0-20180517145634-8181eb3ef2fb/go.mod h1:Os0AToR0I28wSLpS4rQtZdMEcfGKJcSrTaJughAopv4=
github.com/yakovlevdmv/gosoap v0.0.0-20180512142237-299a954b1c6d/go.mod h1:NhCpqPG+N2wrLSqEHVG3FKl4uAPvtFHUx7IlCVpW1PU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200226051749-491c5fce7268/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

27
main.go
View File

@@ -1,15 +1,11 @@
package jessica
import (
"io/ioutil"
"mime"
"net/http"
"path"
"path/filepath"
"strings"
. "github.com/Monibuca/engine/v2"
"github.com/Monibuca/utils"
. "github.com/Monibuca/engine/v3"
"github.com/Monibuca/utils/v3"
. "github.com/logrusorgru/aurora"
)
@@ -25,7 +21,6 @@ var publicPath string
func init() {
plugin := &PluginConfig{
Name: "Jessica",
Type: PLUGIN_SUBSCRIBER,
Config: &config,
Run: run,
}
@@ -33,25 +28,11 @@ func init() {
publicPath = filepath.Join(plugin.Dir, "ui", "public")
}
func run() {
http.HandleFunc("/jessibuca/", jessibuca)
if config.ListenAddr != "" || config.ListenAddrTLS != "" {
Print(Green("Jessica start at"), BrightBlue(config.ListenAddr), BrightBlue(config.ListenAddrTLS))
utils.Print(Green("Jessica start at"), BrightBlue(config.ListenAddr), BrightBlue(config.ListenAddrTLS))
utils.ListenAddrs(config.ListenAddr, config.ListenAddrTLS, config.CertFile, config.KeyFile, http.HandlerFunc(WsHandler))
} else {
Print(Green("Jessica start reuse gateway port"))
utils.Print(Green("Jessica start reuse gateway port"))
http.HandleFunc("/jessica/", WsHandler)
}
}
func jessibuca(w http.ResponseWriter, r *http.Request) {
filePath := strings.TrimPrefix(r.URL.Path, "/jessibuca")
if mime := mime.TypeByExtension(path.Ext(filePath)); mime != "" {
w.Header().Set("Content-Type", mime)
}
if f, err := ioutil.ReadFile(filepath.Join(publicPath, filePath)); err == nil {
if _, err = w.Write(f); err != nil {
w.WriteHeader(500)
}
} else {
w.WriteHeader(404)
}
}

View File

@@ -5,9 +5,9 @@ import (
"net/http"
"regexp"
. "github.com/Monibuca/engine/v2"
"github.com/Monibuca/engine/v2/avformat"
"github.com/Monibuca/engine/v2/pool"
. "github.com/Monibuca/engine/v3"
"github.com/Monibuca/utils/v3"
"github.com/Monibuca/utils/v3/codec"
"github.com/gobwas/ws"
)
@@ -16,10 +16,10 @@ var streamPathReg = regexp.MustCompile("/(jessica/)?((.+)(\\.flv)|(.+))")
func WsHandler(w http.ResponseWriter, r *http.Request) {
sign := r.URL.Query().Get("sign")
isFlv := false
if err := AuthHooks.Trigger(sign); err != nil {
w.WriteHeader(403)
return
}
// if err := AuthHooks.Trigger(sign); err != nil {
// w.WriteHeader(403)
// return
// }
parts := streamPathReg.FindStringSubmatch(r.RequestURI)
streamPath := parts[3]
if streamPath == "" {
@@ -31,8 +31,10 @@ func WsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
return
}
baseStream := Subscriber{Sign: sign}
baseStream.ID = conn.RemoteAddr().String()
baseStream := Subscriber{Sign: sign, ID: r.RemoteAddr, Type: "Jessica"}
if isFlv {
baseStream.Type = "JessicaFlv"
}
defer conn.Close()
go func() {
b := []byte{0}
@@ -41,55 +43,68 @@ func WsHandler(w http.ResponseWriter, r *http.Request) {
}
baseStream.Close()
}()
if isFlv {
baseStream.Type = "JessicaFlv"
baseStream.OnData = func(packet *avformat.SendPacket) error {
err := ws.WriteHeader(conn, ws.Header{
if baseStream.Subscribe(streamPath) == nil {
at := baseStream.GetAudioTrack("aac")
vt := baseStream.OriginVideoTrack
var writeAV func(byte, uint32, []byte)
if isFlv {
if err := ws.WriteHeader(conn, ws.Header{
Fin: true,
OpCode: ws.OpBinary,
Length: int64(len(packet.Payload) + 15),
})
if err != nil {
return err
Length: int64(13),
}); err != nil {
return
}
if _, err = conn.Write(codec.FLVHeader); err != nil {
return
}
writeAV = func(t byte, ts uint32, payload []byte) {
ws.WriteHeader(conn, ws.Header{
Fin: true,
OpCode: ws.OpBinary,
Length: int64(len(payload) + 15),
})
codec.WriteFLVTag(conn, t, ts, payload)
}
} else {
writeAV = func(t byte, ts uint32, payload []byte) {
ws.WriteHeader(conn, ws.Header{
Fin: true,
OpCode: ws.OpBinary,
Length: int64(len(payload) + 5),
})
head := utils.GetSlice(5)
defer utils.RecycleSlice(head)
head[0] = t - 7
binary.BigEndian.PutUint32(head[1:5], ts)
if _, err = conn.Write(head); err != nil {
return
}
conn.Write(payload)
}
return avformat.WriteFLVTag(conn, packet)
}
if err := ws.WriteHeader(conn, ws.Header{
Fin: true,
OpCode: ws.OpBinary,
Length: int64(13),
}); err != nil {
return
if vt != nil {
writeAV(codec.FLV_TAG_TYPE_VIDEO, 0, vt.RtmpTag)
baseStream.OnVideo = func(pack VideoPack) {
payload := codec.Nalu2RTMPTag(pack.Payload)
defer utils.RecycleSlice(payload)
writeAV(codec.FLV_TAG_TYPE_VIDEO, pack.Timestamp, payload)
}
}
if _, err = conn.Write(avformat.FLVHeader); err != nil {
return
if at != nil {
writeAV(codec.FLV_TAG_TYPE_AUDIO, 0, at.RtmpTag)
baseStream.OnAudio = func(pack AudioPack) {
var aac byte
if at.SoundFormat == 10 {
aac = at.RtmpTag[0]
}
payload := codec.Audio2RTMPTag(aac, pack.Payload)
defer utils.RecycleSlice(payload)
writeAV(codec.FLV_TAG_TYPE_AUDIO, pack.Timestamp, payload)
}
}
baseStream.Play(r.Context(), at, vt)
} else {
baseStream.Type = "Jessica"
baseStream.OnData = func(packet *avformat.SendPacket) error {
err := ws.WriteHeader(conn, ws.Header{
Fin: true,
OpCode: ws.OpBinary,
Length: int64(len(packet.Payload) + 5),
})
if err != nil {
return err
}
head := pool.GetSlice(5)
head[0] = packet.Type - 7
binary.BigEndian.PutUint32(head[1:5], packet.Timestamp)
if _, err = conn.Write(head); err != nil {
return err
}
pool.RecycleSlice(head)
//if p.Header[0] == 2 {
// fmt.Printf("%6d %X\n", (uint32(p.Packet.Payload[5])<<24)|(uint32(p.Packet.Payload[6])<<16)|(uint32(p.Packet.Payload[7])<<8)|uint32(p.Packet.Payload[8]), p.Packet.Payload[9])
//}
if _, err = conn.Write(packet.Payload); err != nil {
return err
}
return nil
}
w.WriteHeader(404)
}
baseStream.Subscribe(streamPath)
}

19
ui/dist/demo.html vendored
View File

@@ -1,19 +0,0 @@
<meta charset="utf-8">
<title>plugin-jessica demo</title>
<script src="https://unpkg.com/vue"></script>
<script src="./plugin-jessica.umd.js"></script>
<link rel="stylesheet" href="./plugin-jessica.css">
<div id="app">
<demo></demo>
</div>
<script>
new Vue({
components: {
demo: plugin-jessica
}
}).$mount('#app')
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
td[data-v-5c6a4048]{padding-left:5px;padding-right:5px}.empty[data-v-5c6a4048]{color:#eb5e46;width:100%;min-height:500px;display:flex;justify-content:center;align-items:center}.demo-spin-icon-load[data-v-5c6a4048]{-webkit-animation:ani-demo-spin 1s linear infinite;animation:ani-demo-spin 1s linear infinite}

File diff suppressed because it is too large Load Diff

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

9625
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +0,0 @@
{
"name": "dashboard",
"version": "1.0.0",
"description": "dashboard of jessica plugin for monibuca",
"main": "index.js",
"scripts": {
"build": "vue-cli-service build --target lib --name plugin-jessica"
},
"author": "dexter",
"license": "ISC",
"devDependencies": {
"@vue/cli-service": "^4.2.3",
"vue-template-compiler": "^2.6.11"
},
"dependencies": {
"flv.js": "^1.5.0",
"view-design": "^4.1.3"
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

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

View File

@@ -1,531 +0,0 @@
window.AudioContext = window.AudioContext || window.webkitAudioContext;
function Jessibuca(opt) {
this.canvasElement = opt.canvas;
this.contextOptions = opt.contextOptions;
this.videoBuffer = opt.videoBuffer || 0
if (!opt.forceNoGL) this.initContextGL();
this.audioContext = new window.AudioContext();
if (opt.mute) {
this.audioEnabled(true)
this.audioEnabled(false)
}
if (this.contextGL) {
this.initProgram();
this.initBuffers();
this.initTextures();
};
this.decoderWorker = new Worker(opt.decoder || 'ff.js')
var _this = this
this.decoderWorker.onmessage = function (event) {
var msg = event.data
switch (msg.cmd) {
case "init":
console.log("decoder worker init")
this.postMessage({ cmd: "setVideoBuffer", time: _this.videoBuffer })
if (_this.onLoad) {
_this.onLoad()
delete _this.onLoad;
}
break
case "initSize":
_this.width = msg.w
_this.height = msg.h
if (_this.isWebGL()) {
// var buffer = new ArrayBuffer(msg.w * msg.h + (msg.w * msg.h >> 1))
// this.postMessage({ cmd: "setBuffer", buffer: buffer }, [buffer])
}
else {
_this.initRGB(msg.w, msg.h)
}
break
case "render":
if (_this.onPlay) {
_this.onPlay()
delete _this.onPlay;
}
// if (msg.compositionTime) {
// console.log(msg.compositionTime)
// setTimeout(draw, msg.compositionTime, msg.output)
// } else {
// draw(msg.output)
// }
if (_this.contextGL) {
_this.drawNextOuptutPictureGL(_this.width, _this.height, null, msg.output);
// this.postMessage({ cmd: "setBuffer", buffer: msg.output }, [msg.output[0].buffer, msg.output[1].buffer, msg.output[2].buffer])
} else {
_this.drawNextOuptutPictureRGBA(_this.width, _this.height, null, msg.buffer);
}
break
case "initAudio":
_this.initAudioPlay(msg.frameCount, msg.samplerate, msg.channels)
break
case "playAudio":
_this.playAudio(msg.buffer)
break
case "print":
console.log(msg.text);
break
case "printErr":
console.error(msg.text);
break
default:
_this[msg.cmd](msg)
}
}
};
Jessibuca.prototype.initAudioPlanar = function (msg) {
var channels = msg.channels
var samplerate = msg.samplerate
console.log("initAudioPlanar:","channles:",channels,"samplerate:",samplerate)
var context = this.audioContext;
var isPlaying = false;
var audioBuffers = [];
if (!context) return false;
var _this = this
this.playAudio = function (buffer) {
var frameCount = buffer[0][0].length
var audioBuffer = context.createBuffer(channels, frameCount*buffer.length, samplerate);
var copyToCtxBuffer = function (fromBuffer) {
for (var channel = 0; channel < channels; channel++) {
var nowBuffering = audioBuffer.getChannelData(channel);
for( var j=0;j<buffer.length;j++){
for (var i = 0; i < frameCount; i++) {
nowBuffering[i+j*frameCount] = fromBuffer[j][channel][i]
}
//postMessage({ cmd: "setBufferA", buffer: fromBuffer[j] }, '*', fromBuffer[j].map(x => x.buffer))
}
}
}
var playNextBuffer = function () {
isPlaying = false;
//console.log("~", audioBuffers.length)
if (audioBuffers.length) {
playAudio(audioBuffers.shift());
}
//if (audioBuffers.length > 1) audioBuffers.shift();
};
var playAudio = function (fromBuffer) {
if(!fromBuffer)return
if (isPlaying) {
audioBuffers.push(fromBuffer);
//console.log(audioBuffers.length)
return;
}
isPlaying = true;
copyToCtxBuffer(fromBuffer);
var source = context.createBufferSource();
source.buffer = audioBuffer;
source.connect(context.destination);
// source.onended = playNextBuffer;
source.start();
};
_this.playAudio = playAudio
_this.audioInterval = setInterval(playNextBuffer, audioBuffer.duration * 1000);
playAudio(buffer)
};
}
function _unlock(context) {
context.resume();
var source = context.createBufferSource();
source.buffer = context.createBuffer(1, 1, 22050);
source.connect(context.destination);
if (source.noteOn)
source.noteOn(0);
else
source.start(0);
}
// document.addEventListener("mousedown", _unlock, true);
// document.addEventListener("touchend", _unlock, true);
Jessibuca.prototype.audioEnabled = function (flag) {
if (flag) {
_unlock(this.audioContext)
this.audioEnabled = function (flag) {
if (flag) {
this.audioContext.resume();
} else {
this.audioContext.suspend();
}
}
} else {
this.audioContext.suspend();
}
}
Jessibuca.prototype.playAudio = function (data) {
var context = this.audioContext;
var isPlaying = false;
var isDecoding = false;
if (!context) return false;
var audioBuffers = [];
var decodeQueue = []
var _this = this
var playNextBuffer = function (e) {
// isPlaying = false;
if (audioBuffers.length) {
playBuffer(audioBuffers.shift())
}
//if (audioBuffers.length > 1) audioBuffers.shift();
};
var playBuffer = function (buffer) {
isPlaying = true;
var audioBufferSouceNode = context.createBufferSource();
audioBufferSouceNode.buffer = buffer;
audioBufferSouceNode.connect(context.destination);
// audioBufferSouceNode.onended = playNextBuffer;
audioBufferSouceNode.start();
if (!_this.audioInterval) {
_this.audioInterval = setInterval(playNextBuffer, buffer.duration * 1000 - 1);
}
// setTimeout(playNextBuffer, buffer.duration * 1000)
}
var decodeAudio = function () {
if (decodeQueue.length) {
context.decodeAudioData(decodeQueue.shift(), tryPlay, decodeAudio);
} else {
isDecoding = false
}
}
var tryPlay = function (buffer) {
decodeAudio()
if (isPlaying) {
audioBuffers.push(buffer);
} else {
playBuffer(buffer)
}
}
var playAudio = function (data) {
decodeQueue.push(...data)
if (!isDecoding) {
isDecoding = true
decodeAudio()
}
}
this.playAudio = playAudio
playAudio(data)
}
Jessibuca.prototype.initAudioPlay = function (frameCount, samplerate, channels) {
var context = this.audioContext;
var isPlaying = false;
var audioBuffers = [];
if (!context) return false;
var _this = this
var resampled = samplerate < 22050;
if (resampled) {
console.log("resampled!")
}
var audioBuffer = resampled ? context.createBuffer(channels, frameCount << 1, samplerate << 1) : context.createBuffer(channels, frameCount, samplerate);
var playNextBuffer = function () {
isPlaying = false;
//console.log("~", audioBuffers.length)
if (audioBuffers.length) {
playAudio(audioBuffers.shift());
}
//if (audioBuffers.length > 1) audioBuffers.shift();
};
var copyToCtxBuffer = channels > 1 ? function (fromBuffer) {
for (var channel = 0; channel < channels; channel++) {
var nowBuffering = audioBuffer.getChannelData(channel);
if (resampled) {
for (var i = 0; i < frameCount; i++) {
nowBuffering[i * 2] = nowBuffering[i * 2 + 1] = fromBuffer[i * (channel + 1)] / 32768;
}
} else
for (var i = 0; i < frameCount; i++) {
nowBuffering[i] = fromBuffer[i * (channel + 1)] / 32768;
}
}
} : function (fromBuffer) {
var nowBuffering = audioBuffer.getChannelData(0);
for (var i = 0; i < nowBuffering.length; i++) {
nowBuffering[i] = fromBuffer[i] / 32768;
}
// nowBuffering.set(fromBuffer);
};
var playAudio = function (fromBuffer) {
if (isPlaying) {
audioBuffers.push(fromBuffer);
//console.log(audioBuffers.length)
return;
}
isPlaying = true;
copyToCtxBuffer(fromBuffer);
var source = context.createBufferSource();
source.buffer = audioBuffer;
source.connect(context.destination);
// source.onended = playNextBuffer;
if (!_this.audioInterval) {
_this.audioInterval = setInterval(playNextBuffer, audioBuffer.duration * 1000);
}
source.start();
};
this.playAudio = playAudio;
}
/**
* Returns true if the canvas supports WebGL
*/
Jessibuca.prototype.isWebGL = function () {
return !!this.contextGL;
};
/**
* Create the GL context from the canvas element
*/
Jessibuca.prototype.initContextGL = function () {
var canvas = this.canvasElement;
var gl = null;
var validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"];
var nameIndex = 0;
while (!gl && nameIndex < validContextNames.length) {
var contextName = validContextNames[nameIndex];
try {
if (this.contextOptions) {
gl = canvas.getContext(contextName, this.contextOptions);
} else {
gl = canvas.getContext(contextName);
};
} catch (e) {
gl = null;
}
if (!gl || typeof gl.getParameter !== "function") {
gl = null;
}
++nameIndex;
};
this.contextGL = gl;
};
/**
* Initialize GL shader program
*/
Jessibuca.prototype.initProgram = function () {
var gl = this.contextGL;
var vertexShaderScript = [
'attribute vec4 vertexPos;',
'attribute vec4 texturePos;',
'varying vec2 textureCoord;',
'void main()',
'{',
'gl_Position = vertexPos;',
'textureCoord = texturePos.xy;',
'}'
].join('\n');
var fragmentShaderScript = [
'precision highp float;',
'varying highp vec2 textureCoord;',
'uniform sampler2D ySampler;',
'uniform sampler2D uSampler;',
'uniform sampler2D vSampler;',
'const mat4 YUV2RGB = mat4',
'(',
'1.1643828125, 0, 1.59602734375, -.87078515625,',
'1.1643828125, -.39176171875, -.81296875, .52959375,',
'1.1643828125, 2.017234375, 0, -1.081390625,',
'0, 0, 0, 1',
');',
'void main(void) {',
'highp float y = texture2D(ySampler, textureCoord).r;',
'highp float u = texture2D(uSampler, textureCoord).r;',
'highp float v = texture2D(vSampler, textureCoord).r;',
'gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;',
'}'
].join('\n');
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderScript);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.log('Vertex shader failed to compile: ' + gl.getShaderInfoLog(vertexShader));
}
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderScript);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.log('Fragment shader failed to compile: ' + gl.getShaderInfoLog(fragmentShader));
}
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log('Program failed to compile: ' + gl.getProgramInfoLog(program));
}
gl.useProgram(program);
this.shaderProgram = program;
};
/**
* Initialize vertex buffers and attach to shader program
*/
Jessibuca.prototype.initBuffers = function () {
var gl = this.contextGL;
var program = this.shaderProgram;
var vertexPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW);
var vertexPosRef = gl.getAttribLocation(program, 'vertexPos');
gl.enableVertexAttribArray(vertexPosRef);
gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0);
var texturePosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW);
var texturePosRef = gl.getAttribLocation(program, 'texturePos');
gl.enableVertexAttribArray(texturePosRef);
gl.vertexAttribPointer(texturePosRef, 2, gl.FLOAT, false, 0, 0);
this.texturePosBuffer = texturePosBuffer;
};
/**
* Initialize GL textures and attach to shader program
*/
Jessibuca.prototype.initTextures = function () {
var gl = this.contextGL;
var program = this.shaderProgram;
var yTextureRef = this.initTexture();
var ySamplerRef = gl.getUniformLocation(program, 'ySampler');
gl.uniform1i(ySamplerRef, 0);
this.yTextureRef = yTextureRef;
var uTextureRef = this.initTexture();
var uSamplerRef = gl.getUniformLocation(program, 'uSampler');
gl.uniform1i(uSamplerRef, 1);
this.uTextureRef = uTextureRef;
var vTextureRef = this.initTexture();
var vSamplerRef = gl.getUniformLocation(program, 'vSampler');
gl.uniform1i(vSamplerRef, 2);
this.vTextureRef = vTextureRef;
};
/**
* Create and configure a single texture
*/
Jessibuca.prototype.initTexture = function () {
var gl = this.contextGL;
var textureRef = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textureRef);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D, null);
return textureRef;
};
/**
* Draw picture data to the canvas.
* If this object is using WebGL, the data must be an I420 formatted ArrayBuffer,
* Otherwise, data must be an RGBA formatted ArrayBuffer.
*/
Jessibuca.prototype.drawNextOutputPicture = function (width, height, croppingParams, data) {
var gl = this.contextGL;
if (gl) {
this.drawNextOuptutPictureGL(width, height, croppingParams, data);
} else {
this.drawNextOuptutPictureRGBA(width, height, croppingParams, data);
}
};
/**
* Draw the next output picture using WebGL
*/
Jessibuca.prototype.drawNextOuptutPictureGL = function (width, height, croppingParams, data) {
var gl = this.contextGL;
var texturePosBuffer = this.texturePosBuffer;
var yTextureRef = this.yTextureRef;
var uTextureRef = this.uTextureRef;
var vTextureRef = this.vTextureRef;
this.contextGL.viewport(0, 0, this.canvasElement.width, this.canvasElement.height);
// if (!croppingParams) {
// gl.viewport(0, 0, width, height);
// } else {
// gl.viewport(0, 0, croppingParams.width, croppingParams.height);
// var tTop = croppingParams.top / height;
// var tLeft = croppingParams.left / width;
// var tBottom = croppingParams.height / height;
// var tRight = croppingParams.width / width;
// var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
// gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
// gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW);
// }
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, yTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[0]);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, uTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[1]);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, vTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width / 2, height / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data[2]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
};
/**
* Draw next output picture using ARGB data on a 2d canvas.
*/
Jessibuca.prototype.drawNextOuptutPictureRGBA = function (width, height, croppingParams, data) {
// var canvas = this.canvasElement;
//var argbData = data;
//var ctx = canvas.getContext('2d');
// var imageData = ctx.getImageData(0, 0, width, height);
//this.imageData = this.ctx2d.getImageData(0, 0, width, height);
this.imageData.data.set(data);
//Module.print(typeof this.imageData.data);
if (!croppingParams) {
this.ctx2d.putImageData(this.imageData, 0, 0);
} else {
this.ctx2d.putImageData(this.imageData, -croppingParams.left, -croppingParams.top, 0, 0, croppingParams.width, croppingParams.height);
}
};
Jessibuca.prototype.ctx2d = null;
Jessibuca.prototype.imageData = null;
Jessibuca.prototype.initRGB = function (width, height) {
this.ctx2d = this.canvasElement.getContext('2d');
this.imageData = this.ctx2d.getImageData(0, 0, width, height);
this.clear = function () {
this.ctx2d.clearRect(0, 0, width, height)
};
//Module.print(this.imageData);
};
Jessibuca.prototype.close = function () {
if (this.audioInterval) {
clearInterval(this.audioInterval)
}
delete this.playAudio
this.decoderWorker.postMessage({ cmd: "close" })
this.contextGL.clear(this.contextGL.COLOR_BUFFER_BIT);
}
Jessibuca.prototype.destroy = function () {
this.decoderWorker.terminate()
}
Jessibuca.prototype.play = function (url) {
this.decoderWorker.postMessage({ cmd: "play", url: url, isWebGL: this.isWebGL() })
}

View File

@@ -1,77 +0,0 @@
<template>
<div>
<stream-table>
<template v-slot="scope">
<m-button @click="preview(scope)">预览</m-button>
<template>
</stream-table>
<Jessibuca ref="jessibuca" v-model="showPreview"
:port="ListenAddr.split(':').pop()"
:videoCodec="currentStream && CodecID(currentStream.VideoInfo.CodecID)"
:audioCodec="currentStream && SoundFormat(currentStream.AudioInfo.SoundFormat)"></Jessibuca>
<!-- <Subscribers :data="currentStream && currentStream.SubscriberInfo || []" v-model="showSubscribers" /> -->
</div>
</template>
<script>
import Jessibuca from "./components/Jessibuca";
// import Subscribers from "./components/Subscribers";
let summaryES = null;
export default {
components: {
Jessibuca,
// Subscribers,
},
props: {
ListenAddr: String
},
data() {
return {
showPreview: false,
currentStream: null,
showSubscribers: false,
};
},
computed: {
host() {
return location.hostname + ":" + this.ListenAddr.split(":").pop();
}
},
methods: {
preview({row}) {
this.currentStream = row;
this.onPlay(this.host + "/" + row.StreamPath);
},
onPlay(url) {
this.showPreview = true;
this.$nextTick(() => this.$refs.jessibuca.play(url));
},
onShowDetail(item) {
this.showSubscribers = true;
this.currentStream = item;
}
},
destroyed() {
if (this.$refs.jessibuca) this.$refs.jessibuca.destroy();
}
};
</script>
<style scoped>
td {
padding-left: 5px;
padding-right: 5px;
}
.empty {
color: #eb5e46;
width: 100%;
min-height: 500px;
display: flex;
justify-content: center;
align-items: center;
}
.demo-spin-icon-load {
animation: ani-demo-spin 1s linear infinite;
}
</style>

View File

@@ -1,149 +0,0 @@
<template>
<Modal
v-bind="$attrs"
draggable
v-on="$listeners"
:title="targetURL"
@on-ok="onClosePreview"
@on-cancel="onClosePreview"
>
<video ref="flvjs" v-show="protocol=='flv.js'" style="width:488px;height:275px"></video>
<canvas id="canvas" width="488" height="275" style="background: black" v-show="protocol!='flv.js'" />
<div slot="footer">
<mu-radio value="ws-raw" v-model="protocol" label="ws-raw"></mu-radio>
<mu-radio value="ws-flv" v-model="protocol" label="ws-flv"></mu-radio>
<mu-radio value="http-flv" v-model="protocol" label="http-flv"></mu-radio>
<mu-radio value="flv.js" v-model="protocol" label="flv.js"></mu-radio>
<!-- <Button v-if="audioEnabled" @click="turnOff" icon="md-volume-up" />
<Button v-else @click="turnOn" icon="md-volume-off"></Button>-->
</div>
</Modal>
</template>
<style scoped>
</style>
<script>
let h5lc = null;
let flvPlayer = null;
import flvjs from "flv.js";
export default {
name: "Jessibuca",
props: {
audioCodec: String,
videoCodec: String,
port:String
},
data() {
return {
audioEnabled: true,
protocol: "ws-raw",
url: "",
targetURL: "",
decoderTable: {
AAC_AVC: "ff",
AAC_H265: "hevc_aac",
MP3_AVC: "ff_mp3",
MP3_H265: "hevc_mp3",
AVC: "ff",
H265: "hevc_aac"
}
};
},
watch: {
audioEnabled(value) {
h5lc.audioEnabled(value);
},
decoder(value) {
if (h5lc) {
h5lc.destroy();
}
h5lc = new window.Jessibuca({
canvas: document.getElementById("canvas"),
decoder: value,
videoBuffer:0.2
});
},
protocol(v) {
switch (v) {
case "ws-raw":
this.targetURL = "ws://" + this.url;
break;
case "ws-flv":
this.targetURL = "ws://" + this.url + ".flv";
break;
case "http-flv":
this.targetURL = "http://" + this.url.replace(this.port,"2020") + ".flv";
break;
case "flv.js":
h5lc.close();
this.targetURL = "ws://" + this.url + ".flv"
flvPlayer = flvjs.createPlayer(
{
type: "flv",
isLive: true,
url: this.targetURL
},
{
fixAudioTimestampGap: false
}
);
flvPlayer.attachMediaElement(this.$refs.flvjs);
flvPlayer.load();
flvPlayer.play();
}
if(v!="flv.js"){
h5lc.play(this.targetURL);
if(flvPlayer) {
flvPlayer.destroy();
flvPlayer = null;
}
}
}
},
computed: {
decoder() {
let js = this.decoderTable[this.audioCodec + "_" + this.videoCodec];
if (!js) {
js = this.decoderTable[this.videoCodec] || "ff";
}
return "jessibuca/" + js + ".js";
}
},
mounted() {
let s = document.createElement("script");
s.src = "/jessibuca/renderer.js";
s.onload = function() {
s.onload = null;
h5lc = new window.Jessibuca({
canvas: document.getElementById("canvas"),
decoder: "jessibuca/ff.js"
});
};
this.$root.$el.append(s);
},
methods: {
play(url) {
this.url = url;
if (this.protocol == "ws-raw") {
this.targetURL = "ws://" + url;
h5lc.play(this.targetURL);
} else {
this.protocol = "ws-raw";
}
},
onClosePreview() {
h5lc.close();
flvPlayer.destroy();
},
destroy() {
h5lc.destroy();
},
turnOn() {
this.audioEnabled = true;
},
turnOff() {
this.audioEnabled = false;
}
}
};
</script>

View File

@@ -1,62 +0,0 @@
<template>
<Modal v-bind="$attrs" draggable v-on="$listeners" title="查看订阅者">
<Table :columns="subtableColumns" :data="data"></Table>
</Modal>
</template>
<script>
export default {
props: {
data: Array
},
data() {
return {
subtableColumns: [
{
title: "类型",
key: "Type"
},
{
title: "Name",
key: "ID"
},
{
title: "订阅时间",
render(h, { row }) {
return h(StartTime, {
props: {
value: row.SubscribeTime
}
});
}
},
{
title: "丢帧",
render(h, { row }) {
return h(
"span",
row.TotalPacket ? row.TotalDrop + "/" + row.TotalPacket : ""
);
}
},
{
title: "Buffer",
render(h, { row }) {
return h("Progress", {
props: {
percent: Math.floor((row.BufferLength * 99) / 1024),
"text-inside": true,
"stroke-width": 20,
"stroke-color": ["#87d068", "#ff0000"]
}
});
}
}
]
};
}
};
</script>
<style>
</style>