pull & push ui

pull/132/head
penggy 2018-11-24 10:46:37 +08:00
parent 3d2663239b
commit af2c2b152a
30 changed files with 1598 additions and 23 deletions

View File

@ -135,6 +135,9 @@ func Init() (err error) {
api.GET("/pushers", API.Pushers)
api.GET("/players", API.Players)
api.GET("/stream/start", API.StreamStart)
api.GET("/stream/stop", API.StreamStop)
}
return

View File

@ -61,10 +61,10 @@ func (h *APIHandler) Pushers(c *gin.Context) {
pushers = append(pushers, map[string]interface{}{
"id": pusher.ID(),
"path": rtsp,
"transType": pusher.TransType.String(),
"transType": pusher.TransType(),
"inBytes": pusher.InBytes(),
"outBytes": pusher.OutBytes(),
"startAt": utils.DateTime(pusher.StartAt),
"startAt": utils.DateTime(pusher.StartAt()),
"onlines": len(pusher.GetPlayers()),
})
}

41
routers/streams.go Normal file
View File

@ -0,0 +1,41 @@
package routers
import (
"log"
"github.com/EasyDarwin/EasyDarwin/rtsp"
"github.com/gin-gonic/gin"
"github.com/reactivex/rxgo/handlers"
"github.com/reactivex/rxgo/observer"
)
func (h *APIHandler) StreamStart(c *gin.Context) {
type Form struct {
URL string `form:"url" binding:"required"`
IdleTimeout int `form:"idleTimeout"`
}
var form Form
err := c.Bind(&form)
if err != nil {
return
}
client := rtsp.NewRTSPClient(rtsp.GetServer(), form.URL, int64(form.IdleTimeout)*1000)
pusher := rtsp.NewClientPusher(client)
rtsp.GetServer().AddPusher(pusher)
onNext := handlers.NextFunc(func(item interface{}) {
log.Printf("CLIENT:RTSP拉流成功:%v", item)
})
onDone := handlers.DoneFunc(func() {
log.Println("CLIENT done")
})
onError := handlers.ErrFunc(func(err error) {
log.Println("CLIENT Error :", err.Error())
})
watcher := observer.New(onNext, onDone, onError)
client.Start().Subscribe(watcher)
c.IndentedJSON(200, "OK")
}
func (h *APIHandler) StreamStop(c *gin.Context) {
}

View File

@ -4,6 +4,7 @@ import (
"log"
"strings"
"sync"
"time"
"github.com/penggy/EasyGoLib/utils"
)
@ -121,6 +122,20 @@ func (pusher *Pusher) OutBytes() int {
return pusher.RTSPClient.OutBytes
}
func (pusher *Pusher) TransType() string {
if pusher.Session != nil {
return pusher.Session.TransType.String()
}
return pusher.RTSPClient.TransType.String()
}
func (pusher *Pusher) StartAt() time.Time {
if pusher.Session != nil {
return pusher.Session.StartAt
}
return pusher.RTSPClient.StartAt
}
func NewClientPusher(client *RTSPClient) (pusher *Pusher) {
pusher = &Pusher{
RTSPClient: client,
@ -270,14 +285,14 @@ func (pusher *Pusher) ClearPlayer() {
// copy a new map to avoid deadlock
players := make(map[string]*Player)
pusher.playersLock.Lock()
for k,v := range pusher.players {
for k, v := range pusher.players {
//v.Stop()
players[k] = v
}
pusher.players = make(map[string]*Player)
pusher.playersLock.Unlock()
for _,v:=range players{
for _, v := range players {
v.Stop()
}
}

View File

@ -5,7 +5,6 @@ import (
"bytes"
"encoding/binary"
"fmt"
"github.com/penggy/EasyGoLib/utils"
"io"
"log"
"net"
@ -14,6 +13,8 @@ import (
"strings"
"time"
"github.com/penggy/EasyGoLib/utils"
"github.com/pixelbender/go-sdp/sdp"
"github.com/reactivex/rxgo/observable"
)
@ -32,6 +33,8 @@ type RTSPClient struct {
connRW *bufio.ReadWriter
InBytes int
OutBytes int
TransType TransType
StartAt time.Time
Sdp *sdp.Session
AControl string
VControl string

21
vendor/github.com/pixelbender/go-sdp/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Vasily Vasilyev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

54
vendor/github.com/pixelbender/go-sdp/sdp/attribute.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
package sdp
// Attributes represent a list of SDP attributes.
type Attributes []*Attr
// Has returns presence of attribute by name.
func (a Attributes) Has(name string) bool {
for _, it := range a {
if it.Name == name {
return true
}
}
return false
}
// Get returns first attribute value by name.
func (a Attributes) Get(name string) string {
for _, it := range a {
if it.Name == name {
return it.Value
}
}
return ""
}
// Attr represents session or media attribute.
type Attr struct {
Name, Value string
}
// NewAttr returns a=<attribute>:<value> attribute.
func NewAttr(attr, value string) *Attr {
return &Attr{attr, value}
}
// NewAttrFlag returns a=<flag> attribute.
func NewAttrFlag(flag string) *Attr {
return &Attr{flag, ""}
}
func (a *Attr) String() string {
if a.Value == "" {
return a.Name
}
return a.Name + ":" + a.Value
}
// Session or media attribute values for indication of a streaming mode.
const (
ModeSendRecv = "sendrecv"
ModeRecvOnly = "recvonly"
ModeSendOnly = "sendonly"
ModeInactive = "inactive"
)

479
vendor/github.com/pixelbender/go-sdp/sdp/decoder.go generated vendored Normal file
View File

@ -0,0 +1,479 @@
package sdp
import (
"bufio"
"errors"
"fmt"
"io"
"strconv"
"time"
)
// Parse reads session description from the buffer.
func Parse(b []byte) (*Session, error) {
return ParseString(string(b))
}
// ParseString reads session description from the string.
func ParseString(s string) (*Session, error) {
return NewDecoderString(s).Decode()
}
// A Decoder reads a session description from a stream.
type Decoder struct {
r lineReader
p []string
}
// NewDecoder returns new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: &reader{b: bufio.NewReaderSize(r, maxLineSize)}}
}
// NewDecoderString returns new decoder that reads from s.
func NewDecoderString(s string) *Decoder {
return &Decoder{r: &stringReader{s: s}}
}
// Decode encodes the session description.
func (d *Decoder) Decode() (*Session, error) {
line := 0
sess := new(Session)
var media *Media
for {
line++
s, err := d.r.ReadLine()
if err != nil {
if err == io.EOF && sess.Origin != nil {
break
}
return nil, err
}
if len(s) == 0 && sess.Origin != nil {
break
}
if len(s) < 2 || s[1] != '=' {
return nil, &errDecode{errFormat, line, s}
}
f, v := s[0], s[2:]
if f == 'm' {
media = new(Media)
err = d.media(media, f, v)
if err == nil {
sess.Media = append(sess.Media, media)
}
} else if media == nil {
err = d.session(sess, f, v)
} else {
err = d.media(media, f, v)
}
if err != nil {
return nil, &errDecode{err, line, s}
}
}
return sess, nil
}
func (d *Decoder) session(s *Session, f byte, v string) error {
var err error
switch f {
case 'v':
s.Version, err = strconv.Atoi(v)
case 'o':
if s.Origin != nil {
return errUnexpectedField
}
s.Origin, err = d.origin(v)
case 's':
s.Name = v
case 'i':
s.Information = v
case 'u':
s.URI = v
case 'e':
s.Email = append(s.Email, v)
case 'p':
s.Phone = append(s.Phone, v)
case 'c':
if s.Connection != nil {
return errUnexpectedField
}
s.Connection, err = d.connection(v)
case 'b':
if s.Bandwidth == nil {
s.Bandwidth = make(Bandwidth)
}
err = d.bandwidth(s.Bandwidth, v)
case 'z':
s.TimeZone, err = d.timezone(v)
case 'k':
s.Key = append(s.Key, d.key(v))
case 'a':
a := d.attr(v)
switch a.Name {
case ModeInactive, ModeRecvOnly, ModeSendOnly, ModeSendRecv:
s.Mode = a.Name
default:
s.Attributes = append(s.Attributes, a)
}
case 't':
s.Timing, err = d.timing(v)
case 'r':
r, err := d.repeat(v)
if err != nil {
return err
}
s.Repeat = append(s.Repeat, r)
default:
return errUnexpectedField
}
return err
}
func (d *Decoder) media(m *Media, f byte, v string) error {
var err error
switch f {
case 'm':
err = d.proto(m, v)
case 'i':
m.Information = v
case 'c':
conn, err := d.connection(v)
if err != nil {
return err
}
m.Connection = append(m.Connection, conn)
case 'b':
if m.Bandwidth == nil {
m.Bandwidth = make(Bandwidth)
}
err = d.bandwidth(m.Bandwidth, v)
case 'k':
m.Key = append(m.Key, d.key(v))
case 'a':
a := d.attr(v)
switch a.Name {
case ModeInactive, ModeRecvOnly, ModeSendOnly, ModeSendRecv:
m.Mode = a.Name
case "rtpmap", "rtcp-fb", "fmtp":
err = d.format(m, a)
default:
m.Attributes = append(m.Attributes, a)
}
default:
return errUnexpectedField
}
return err
}
func (d *Decoder) format(m *Media, a *Attr) error {
p, ok := d.fields(a.Value, 2)
if !ok {
return errFormat
}
pt, err := strconv.Atoi(p[0])
if err != nil {
return err
}
f, v := m.Format(pt), p[1]
if f == nil {
return nil
}
switch a.Name {
case "rtpmap":
err = d.rtpmap(f, v)
case "rtcp-fb":
f.Feedback = append(f.Feedback, v)
case "fmtp":
f.Params = append(f.Params, v)
}
return err
}
func (d *Decoder) rtpmap(f *Format, v string) error {
p, ok := d.split(v, '/', 3)
if len(p) < 2 {
return errFormat
}
f.Name = p[0]
var err error
if ok {
if f.Channels, err = strconv.Atoi(p[2]); err != nil {
return err
}
}
if f.ClockRate, err = strconv.Atoi(p[1]); err != nil {
return err
}
return nil
}
func (d *Decoder) proto(m *Media, v string) error {
p, ok := d.fields(v, 4)
if !ok {
return errFormat
}
formats := p[3]
m.Type, m.Proto = p[0], p[2]
p, ok = d.split(p[1], '/', 2)
var err error
if ok {
if m.PortNum, err = strconv.Atoi(p[1]); err != nil {
return err
}
}
if m.Port, err = strconv.Atoi(p[0]); err != nil {
return err
}
p, _ = d.fields(formats, maxLineSize)
for _, it := range p {
if it == "*" {
continue
}
pt, err := strconv.Atoi(it)
if err != nil {
return err
}
m.Formats = append(m.Formats, &Format{Payload: pt})
}
return nil
}
func (d *Decoder) origin(v string) (*Origin, error) {
p, ok := d.fields(v, 6)
if !ok {
return nil, errFormat
}
o := new(Origin)
o.Username, o.Network, o.Type, o.Address = p[0], p[3], p[4], p[5]
var err error
if o.SessionID, err = d.int(p[1]); err != nil {
return nil, err
}
if o.SessionVersion, err = d.int(p[2]); err != nil {
return nil, err
}
return o, nil
}
func (d *Decoder) connection(v string) (*Connection, error) {
p, ok := d.fields(v, 3)
if !ok {
return nil, errFormat
}
c := new(Connection)
c.Network, c.Type, c.Address = p[0], p[1], p[2]
p, ok = d.split(c.Address, '/', 3)
if ok {
ttl, err := d.int(p[1])
if err != nil {
return nil, err
}
c.TTL = int(ttl)
p = p[1:]
}
if len(p) > 1 {
num, err := d.int(p[1])
if err != nil {
return nil, err
}
c.Address, c.AddressNum = p[0], int(num)
}
return c, nil
}
func (d *Decoder) bandwidth(b Bandwidth, v string) error {
p, ok := d.split(v, ':', 2)
if !ok {
return errFormat
}
val, err := d.int(p[1])
if err != nil {
return err
}
b[p[0]] = int(val)
return nil
}
func (d *Decoder) timezone(v string) ([]*TimeZone, error) {
p, _ := d.fields(v, 40)
zone := make([]*TimeZone, 0, 1)
var err error
for len(p) > 1 {
it := new(TimeZone)
if it.Time, err = d.time(p[0]); err != nil {
return nil, err
}
if it.Offset, err = d.duration(p[1]); err != nil {
return nil, err
}
zone = append(zone, it)
p = p[2:]
}
return zone, nil
}
func (d *Decoder) key(v string) *Key {
if p, ok := d.split(v, ':', 2); ok {
return &Key{p[0], p[1]}
}
return &Key{v, ""}
}
func (d *Decoder) attr(v string) *Attr {
if p, ok := d.split(v, ':', 2); ok {
return &Attr{p[0], p[1]}
}
return &Attr{v, ""}
}
func (d *Decoder) timing(v string) (*Timing, error) {
p, ok := d.fields(v, 2)
if !ok {
return nil, errFormat
}
start, err := d.time(p[0])
if err != nil {
return nil, err
}
stop, err := d.time(p[1])
if err != nil {
return nil, err
}
return &Timing{start, stop}, nil
}
func (d *Decoder) repeat(v string) (*Repeat, error) {
p, _ := d.fields(v, maxLineSize)
if len(p) < 2 {
return nil, errFormat
}
r := new(Repeat)
var err error
if r.Interval, err = d.duration(p[0]); err != nil {
return nil, err
}
if r.Duration, err = d.duration(p[1]); err != nil {
return nil, err
}
for _, it := range p[2:] {
off, err := d.duration(it)
if err != nil {
return nil, err
}
r.Offsets = append(r.Offsets, off)
}
return r, nil
}
func (d *Decoder) time(v string) (time.Time, error) {
sec, err := d.int(v)
if err != nil || sec == 0 {
return time.Time{}, err
}
return epoch.Add(time.Second * time.Duration(sec)), nil
}
func (d *Decoder) duration(v string) (time.Duration, error) {
m := int64(1)
if n := len(v) - 1; n >= 0 {
switch v[n] {
case 'd':
m, v = 86400, v[:n]
case 'h':
m, v = 3600, v[:n]
case 'm':
m, v = 60, v[:n]
case 's':
v = v[:n]
}
}
sec, err := d.int(v)
if err != nil {
return 0, err
}
return time.Duration(sec*m) * time.Second, nil
}
func (d *Decoder) int(v string) (int64, error) {
return strconv.ParseInt(v, 10, 64)
}
func (d *Decoder) fields(s string, n int) ([]string, bool) {
return d.split(s, ' ', n)
}
func (d *Decoder) split(s string, sep rune, n int) ([]string, bool) {
p, pos := d.p[:0], 0
for i, c := range s {
if c != sep {
continue
}
p = append(p, s[pos:i])
pos = i + 1
if len(p) >= n-1 {
break
}
}
p = append(p, s[pos:])
d.p = p[:0]
return p, len(p) == n
}
const maxLineSize = 1024
type lineReader interface {
ReadLine() (string, error)
}
type stringReader struct {
s string
}
func (r *stringReader) ReadLine() (string, error) {
s, n := r.s, len(r.s)
if n == 0 {
return "", io.EOF
}
for i, ch := range s {
if ch == '\n' {
r.s = s[i+1:]
for i > 0 && s[i-1] == '\r' {
i--
}
return s[:i], nil
}
}
r.s = ""
return s, nil
}
type reader struct {
b *bufio.Reader
}
func (r *reader) ReadLine() (string, error) {
b, prefix, err := r.b.ReadLine()
if prefix && err == nil {
err = errLineTooLong
}
if err != nil {
return "", err
}
return string(b), nil
}
var errLineTooLong = errors.New("sdp: line is too long")
var errUnexpectedField = errors.New("unexpected field")
var errFormat = errors.New("format error")
type errDecode struct {
err error
line int
text string
}
func (e *errDecode) Error() string {
return fmt.Sprintf("sdp: %s on line %d '%s'", e.err.Error(), e.line, e.text)
}

318
vendor/github.com/pixelbender/go-sdp/sdp/encoder.go generated vendored Normal file
View File

@ -0,0 +1,318 @@
package sdp
import (
"io"
"strconv"
"time"
)
// An Encoder writes a session description to a buffer.
type Encoder struct {
w io.Writer
buf []byte
pos int
newline bool
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
// Encode encodes the session description.
func (e *Encoder) Encode(s *Session) error {
e.Reset()
e.session(s)
if e.w != nil {
_, err := e.w.Write(e.Bytes())
if err != nil {
return err
}
}
return nil
}
// Reset resets encoder state to be empty.
func (e *Encoder) Reset() {
e.pos, e.newline = 0, false
}
func (e *Encoder) session(s *Session) *Encoder {
e.add('v').int(int64(s.Version))
if s.Origin != nil {
e.add('o').origin(s.Origin)
}
e.add('s').str(s.Name)
if s.Information != "" {
e.add('i').str(s.Information)
}
if s.URI != "" {
e.add('u').str(s.URI)
}
for _, it := range s.Email {
e.add('e').str(it)
}
for _, it := range s.Phone {
e.add('p').str(it)
}
if s.Connection != nil {
e.add('c').connection(s.Connection)
}
for t, v := range s.Bandwidth {
e.add('b').bandwidth(t, v)
}
if len(s.TimeZone) > 0 {
e.add('z').timezone(s.TimeZone)
}
for _, it := range s.Key {
e.add('k').key(it)
}
e.add('t').timing(s.Timing)
for _, it := range s.Repeat {
e.add('r').repeat(it)
}
if s.Mode != "" {
e.add('a').str(s.Mode)
}
for _, it := range s.Attributes {
e.add('a').attr(it)
}
for _, it := range s.Media {
e.media(it)
}
return e
}
func (e *Encoder) media(m *Media) *Encoder {
e.add('m').str(m.Type).sp().int(int64(m.Port))
if m.PortNum > 0 {
e.char('/').int(int64(m.PortNum))
}
e.sp().str(m.Proto)
for _, it := range m.Formats {
e.sp().int(int64(it.Payload))
}
if len(m.Formats) == 0 {
e.sp().char('*')
}
if m.Information != "" {
e.add('i').str(m.Information)
}
for _, it := range m.Connection {
e.add('c').connection(it)
}
for t, v := range m.Bandwidth {
e.add('b').bandwidth(t, v)
}
for _, it := range m.Key {
e.add('k').key(it)
}
for _, it := range m.Formats {
e.format(it)
}
if m.Mode != "" {
e.add('a').str(m.Mode)
}
for _, it := range m.Attributes {
e.add('a').attr(it)
}
return e
}
func (e *Encoder) format(f *Format) *Encoder {
p := int64(f.Payload)
if f.Name != "" {
e.add('a').str("rtpmap:").int(p).sp().str(f.Name).char('/').int(int64(f.ClockRate))
if f.Channels > 0 {
e.char('/').int(int64(f.Channels))
}
}
for _, it := range f.Feedback {
e.add('a').str("rtcp-fb:").int(p).sp().str(it)
}
for _, it := range f.Params {
e.add('a').str("fmtp:").int(p).sp().str(it)
}
return e
}
func (e *Encoder) attr(a *Attr) *Encoder {
if a.Value == "" {
return e.str(a.Name)
}
return e.str(a.Name).char(':').str(a.Value)
}
func (e *Encoder) timezone(z []*TimeZone) *Encoder {
for i, it := range z {
if i > 0 {
e.char(' ')
}
e.time(it.Time).sp().duration(it.Offset)
}
return e
}
func (e *Encoder) timing(t *Timing) *Encoder {
if t == nil {
return e.str("0 0")
}
return e.time(t.Start).sp().time(t.Stop)
}
func (e *Encoder) repeat(r *Repeat) *Encoder {
e.duration(r.Interval).sp().duration(r.Duration)
for _, it := range r.Offsets {
e.sp().duration(it)
}
return e
}
func (e *Encoder) time(t time.Time) *Encoder {
if t.IsZero() {
return e.char('0')
}
return e.int(int64(t.Sub(epoch).Seconds()))
}
func (e *Encoder) duration(d time.Duration) *Encoder {
v := int64(d.Seconds())
switch {
case v == 0:
return e.char('0')
case v%86400 == 0:
return e.int(v / 86400).char('d')
case v%3600 == 0:
return e.int(v / 3600).char('h')
case v%60 == 0:
return e.int(v / 60).char('m')
default:
return e.int(v)
}
}
func (e *Encoder) bandwidth(m string, v int) *Encoder {
return e.str(m).char(':').int(int64(v))
}
func (e *Encoder) key(k *Key) *Encoder {
if k.Value == "" {
return e.str(k.Method)
}
return e.str(k.Method).char(':').str(k.Value)
}
func (e *Encoder) origin(o *Origin) *Encoder {
return e.str(strd(o.Username, "-")).sp().int(o.SessionID).sp().int(o.SessionVersion).sp().transport(o.Network, o.Type, o.Address)
}
func (e *Encoder) connection(c *Connection) *Encoder {
e.transport(c.Network, c.Type, c.Address)
if c.TTL > 0 {
e.char('/').int(int64(c.TTL))
}
if c.AddressNum > 1 {
e.char('/').int(int64(c.AddressNum))
}
return e
}
func (e *Encoder) transport(network, typ, addr string) *Encoder {
return e.fields(strd(network, "IN"), strd(typ, "IP4"), strd(addr, "127.0.0.1"))
}
func strd(v, def string) string {
if v == "" {
return def
}
return v
}
func (e *Encoder) str(v string) *Encoder {
if v == "" {
return e.char('-')
}
copy(e.next(len(v)), v)
return e
}
func (e *Encoder) fields(v ...string) *Encoder {
n := len(v) - 1
for _, it := range v {
n += len(it)
}
p, b := 0, e.next(n)
for _, it := range v {
if p > 0 {
b[p] = ' '
p++
}
p += copy(b[p:], it)
}
return e
}
func (e *Encoder) sp() *Encoder {
return e.char(' ')
}
func (e *Encoder) char(v byte) *Encoder {
e.next(1)[0] = v
return e
}
func (e *Encoder) int(v int64) *Encoder {
b := e.next(20)
e.pos += len(strconv.AppendInt(b[:0], v, 10)) - len(b)
return e
}
func (e *Encoder) add(n byte) *Encoder {
if e.newline {
b := e.next(4)
b[0], b[1], b[2], b[3] = '\r', '\n', n, '='
} else {
b := e.next(2)
b[0], b[1] = n, '='
e.newline = true
}
return e
}
func (e *Encoder) next(n int) (b []byte) {
p := e.pos + n
if len(e.buf) < p {
e.grow(p)
}
b, e.pos = e.buf[e.pos:p], p
return
}
func (e *Encoder) grow(p int) {
if p < 1024 {
p = 1024
} else if s := len(e.buf) << 1; p < s {
p = s
}
b := make([]byte, p)
if e.pos > 0 {
copy(b, e.buf[:e.pos])
}
e.buf = b
}
// Bytes returns encoded bytes of the last session description.
// The bytes stop being valid at the next encoder call.
func (e *Encoder) Bytes() []byte {
if e.newline {
b := e.next(2)
b[0], b[1] = '\r', '\n'
e.newline = false
}
return e.buf[:e.pos]
}
// Bytes returns the encoded session description as str.
func (e *Encoder) String() string {
return string(e.Bytes())
}

192
vendor/github.com/pixelbender/go-sdp/sdp/sdp.go generated vendored Normal file
View File

@ -0,0 +1,192 @@
package sdp
import (
"time"
)
// ContentType is the media type for an SDP session description.
const ContentType = "application/sdp"
// Session represents an SDP session description.
type Session struct {
Version int // Protocol Version ("v=")
Origin *Origin // Origin ("o=")
Name string // Session Name ("s=")
Information string // Session Information ("i=")
URI string // URI ("u=")
Email []string // Email Address ("e=")
Phone []string // Phone Number ("p=")
Connection *Connection // Connection Data ("c=")
Bandwidth Bandwidth // Bandwidth ("b=")
TimeZone []*TimeZone // TimeZone ("z=")
Key []*Key // Encryption Keys ("k=")
Timing *Timing // Timing ("t=")
Repeat []*Repeat // Repeat Times ("r=")
Attributes // Session Attributes ("a=")
Media []*Media // Media Descriptions ("m=")
Mode string // Streaming mode ("sendrecv", "recvonly", "sendonly", or "inactive")
}
// String returns the encoded session description as string.
func (s *Session) String() string {
return string(s.Bytes())
}
// Bytes returns the encoded session description as buffer.
func (s *Session) Bytes() []byte {
return new(Encoder).session(s).Bytes()
}
// Origin represents an originator of the session.
type Origin struct {
Username string
SessionID int64
SessionVersion int64
Network string
Type string
Address string
}
// Connection contains connection data.
type Connection struct {
Network string
Type string
Address string
TTL int
AddressNum int
}
// Bandwidth contains session or media bandwidth information.
type Bandwidth map[string]int
// TimeZone represents a time zones change information for a repeated session.
type TimeZone struct {
Time time.Time
Offset time.Duration
}
// Key contains a key exchange information.
// Deprecated: Not recommended, supported for compatibility with older implementations.
type Key struct {
Method, Value string
}
// Timing specifies start and stop times for a session.
type Timing struct {
Start time.Time
Stop time.Time
}
// Repeat specifies repeat times for a session.
type Repeat struct {
Interval time.Duration
Duration time.Duration
Offsets []time.Duration
}
// Media contains media description.
type Media struct {
Type string
Port int
PortNum int
Proto string
Information string // Media Information ("i=")
Connection []*Connection // Connection Data ("c=")
Bandwidth Bandwidth // Bandwidth ("b=")
Key []*Key // Encryption Keys ("k=")
Attributes // Attributes ("a=")
Mode string // Streaming mode ("sendrecv", "recvonly", "sendonly", or "inactive")
Formats []*Format // Media Formats ("rtpmap")
}
// Streaming modes.
const (
SendRecv = "sendrecv"
SendOnly = "sendonly"
RecvOnly = "recvonly"
Inactive = "inactive"
)
// NegotiateMode negotiates streaming mode.
func NegotiateMode(local, remote string) string {
switch local {
case SendRecv:
switch remote {
case RecvOnly:
return SendOnly
case SendOnly:
return RecvOnly
default:
return remote
}
case SendOnly:
switch remote {
case SendRecv, RecvOnly:
return SendOnly
}
case RecvOnly:
switch remote {
case SendRecv, SendOnly:
return RecvOnly
}
}
return Inactive
}
// DeleteAttr removes all elements with name from attrs.
func DeleteAttr(attrs Attributes, name ... string) Attributes {
n := 0
loop:
for _, it := range attrs {
for _, v := range name {
if it.Name == v {
continue loop
}
}
attrs[n] = it
n++
}
return attrs[:n]
}
// Format returns format description by payload type.
func (m *Media) Format(pt int) *Format {
for _, f := range m.Formats {
if f.Payload == pt {
return f
}
}
return nil
}
// Format is a media format description represented by "rtpmap" attributes.
type Format struct {
Payload int
Name string
ClockRate int
Channels int
Feedback []string // "rtcp-fb" attributes
Params []string // "fmtp" attributes
}
var epoch = time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
// GetAttribute returns session or first determined media attribute.
func (sess *Session) GetAttribute(name string) string {
for _, it := range sess.Attributes {
if it.Name == name {
return it.Value
}
}
for _, media := range sess.Media {
for _, it := range media.Attributes {
if it.Name == name {
return it.Value
}
}
}
return ""
}

22
vendor/github.com/reactivex/rxgo/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2016 Joe Chasinga
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,415 @@
package observable
import (
"sync"
"time"
"github.com/reactivex/rxgo"
"github.com/reactivex/rxgo/errors"
"github.com/reactivex/rxgo/fx"
"github.com/reactivex/rxgo/handlers"
"github.com/reactivex/rxgo/observer"
"github.com/reactivex/rxgo/subscription"
)
// Observable is a basic observable channel
type Observable <-chan interface{}
var DefaultObservable = make(Observable)
// New creates an Observable
func New(buffer uint) Observable {
return make(Observable, int(buffer))
}
// CheckHandler checks the underlying type of an EventHandler.
func CheckEventHandler(handler rx.EventHandler) observer.Observer {
ob := observer.DefaultObserver
switch handler := handler.(type) {
case handlers.NextFunc:
ob.NextHandler = handler
case handlers.ErrFunc:
ob.ErrHandler = handler
case handlers.DoneFunc:
ob.DoneHandler = handler
case observer.Observer:
ob = handler
}
return ob
}
// Next returns the next item on the Observable.
func (o Observable) Next() (interface{}, error) {
if next, ok := <-o; ok {
return next, nil
}
return nil, errors.New(errors.EndOfIteratorError)
}
// Subscribe subscribes an EventHandler and returns a Subscription channel.
func (o Observable) Subscribe(handler rx.EventHandler) <-chan subscription.Subscription {
done := make(chan subscription.Subscription)
sub := subscription.New().Subscribe()
ob := CheckEventHandler(handler)
go func() {
OuterLoop:
for item := range o {
switch item := item.(type) {
case error:
ob.OnError(item)
// Record the error and break the loop.
sub.Error = item
break OuterLoop
default:
ob.OnNext(item)
}
}
// OnDone only gets executed if there's no error.
if sub.Error == nil {
ob.OnDone()
}
done <- sub.Unsubscribe()
return
}()
return done
}
/*
func (o Observable) Unsubscribe() subscription.Subscription {
// Stub: to be implemented
return subscription.New()
}
*/
// Map maps a MappableFunc predicate to each item in Observable and
// returns a new Observable with applied items.
func (o Observable) Map(apply fx.MappableFunc) Observable {
out := make(chan interface{})
go func() {
for item := range o {
out <- apply(item)
}
close(out)
}()
return Observable(out)
}
// Take takes first n items in the original Obserable and returns
// a new Observable with the taken items.
func (o Observable) Take(nth uint) Observable {
out := make(chan interface{})
go func() {
takeCount := 0
for item := range o {
if (takeCount < int(nth)) {
takeCount += 1
out <- item
continue
}
break
}
close(out)
}()
return Observable(out)
}
// TakeLast takes last n items in the original Observable and returns
// a new Observable with the taken items.
func (o Observable) TakeLast(nth uint) Observable {
out := make(chan interface{})
go func() {
buf := make([]interface{}, nth)
for item := range o {
if (len(buf) >= int(nth)) {
buf = buf[1:]
}
buf = append(buf, item)
}
for _, takenItem := range buf {
out <- takenItem
}
close(out)
}()
return Observable(out)
}
// Filter filters items in the original Observable and returns
// a new Observable with the filtered items.
func (o Observable) Filter(apply fx.FilterableFunc) Observable {
out := make(chan interface{})
go func() {
for item := range o {
if apply(item) {
out <- item
}
}
close(out)
}()
return Observable(out)
}
// First returns new Observable which emit only first item.
func (o Observable) First() Observable {
out := make(chan interface{})
go func() {
for item := range o {
out <- item
break
}
close(out)
}()
return Observable(out)
}
// Last returns a new Observable which emit only last item.
func (o Observable) Last() Observable {
out := make(chan interface{})
go func() {
var last interface{}
for item := range o {
last = item
}
out <- last
close(out)
}()
return Observable(out)
}
// Distinct suppresses duplicate items in the original Observable and returns
// a new Observable.
func (o Observable) Distinct(apply fx.KeySelectorFunc) Observable {
out := make(chan interface{})
go func() {
keysets := make(map[interface{}]struct{})
for item := range o {
key := apply(item)
_, ok := keysets[key]
if !ok {
out <- item
}
keysets[key] = struct{}{}
}
close(out)
}()
return Observable(out)
}
// DistinctUntilChanged suppresses consecutive duplicate items in the original
// Observable and returns a new Observable.
func (o Observable) DistinctUntilChanged(apply fx.KeySelectorFunc) Observable {
out := make(chan interface{})
go func() {
var current interface{}
for item := range o {
key := apply(item)
if current != key {
out <- item
current = key
}
}
close(out)
}()
return Observable(out)
}
// Skip suppresses the first n items in the original Observable and
// returns a new Observable with the rest items.
func (o Observable) Skip(nth uint) Observable {
out := make(chan interface{})
go func() {
skipCount := 0
for item := range o {
if (skipCount < int(nth)) {
skipCount += 1
continue
}
out <- item
}
close(out)
}()
return Observable(out)
}
// SkipLast suppresses the last n items in the original Observable and
// returns a new Observable with the rest items.
func (o Observable) SkipLast(nth uint) Observable {
out := make(chan interface{})
go func() {
buf := make(chan interface{}, nth)
for item := range o {
select {
case buf <- item:
default:
out <- (<- buf)
buf <- item
}
}
close(buf)
close(out)
}()
return Observable(out)
}
// Scan applies ScannableFunc predicate to each item in the original
// Observable sequentially and emits each successive value on a new Observable.
func (o Observable) Scan(apply fx.ScannableFunc) Observable {
out := make(chan interface{})
go func() {
var current interface{}
for item := range o {
out <- apply(current, item)
current = apply(current, item)
}
close(out)
}()
return Observable(out)
}
// From creates a new Observable from an Iterator.
func From(it rx.Iterator) Observable {
source := make(chan interface{})
go func() {
for {
val, err := it.Next()
if err != nil {
break
}
source <- val
}
close(source)
}()
return Observable(source)
}
// Empty creates an Observable with no item and terminate immediately.
func Empty() Observable {
source := make(chan interface{})
go func() {
close(source)
}()
return Observable(source)
}
// Interval creates an Observable emitting incremental integers infinitely between
// each given time interval.
func Interval(term chan struct{}, interval time.Duration) Observable {
source := make(chan interface{})
go func(term chan struct{}) {
i := 0
OuterLoop:
for {
select {
case <-term:
break OuterLoop
case <-time.After(interval):
source <- i
}
i++
}
close(source)
}(term)
return Observable(source)
}
// Repeat creates an Observable emitting a given item repeatedly
func Repeat(item interface{}, ntimes ...int) Observable {
source := make(chan interface{})
// this is the infinity case no ntime parameter is given
if len(ntimes) == 0 {
go func() {
for {
source <- item
}
close(source)
}()
return Observable(source)
}
// this repeat the item ntime
if len(ntimes) > 0 {
count := ntimes[0]
if count <= 0 {
return Empty()
}
go func() {
for i := 0; i < count; i++ {
source <- item
}
close(source)
}()
return Observable(source)
}
return Empty()
}
// Range creates an Observable that emits a particular range of sequential integers.
func Range(start, end int) Observable {
source := make(chan interface{})
go func() {
i := start
for i < end {
source <- i
i++
}
close(source)
}()
return Observable(source)
}
// Just creates an Observable with the provided item(s).
func Just(item interface{}, items ...interface{}) Observable {
source := make(chan interface{})
if len(items) > 0 {
items = append([]interface{}{item}, items...)
} else {
items = []interface{}{item}
}
go func() {
for _, item := range items {
source <- item
}
close(source)
}()
return Observable(source)
}
// Start creates an Observable from one or more directive-like EmittableFunc
// and emits the result of each operation asynchronously on a new Observable.
func Start(f fx.EmittableFunc, fs ...fx.EmittableFunc) Observable {
if len(fs) > 0 {
fs = append([]fx.EmittableFunc{f}, fs...)
} else {
fs = []fx.EmittableFunc{f}
}
source := make(chan interface{})
var wg sync.WaitGroup
for _, f := range fs {
wg.Add(1)
go func(f fx.EmittableFunc) {
source <- f()
wg.Done()
}(f)
}
// Wait in another goroutine to not block
go func() {
wg.Wait()
close(source)
}()
return Observable(source)
}

14
vendor/vendor.json vendored
View File

@ -272,6 +272,12 @@
"revision": "c3d6048651cd9420b049fc7ddfc232002d36e800",
"revisionTime": "2018-05-29T03:02:45Z"
},
{
"checksumSHA1": "tQsy6xGhp1I9d+quwDQZWMtSVeQ=",
"path": "github.com/pixelbender/go-sdp/sdp",
"revision": "100bc9371a0caf8b1797c82bcc9c36854b059b51",
"revisionTime": "2018-11-23T09:41:52Z"
},
{
"checksumSHA1": "d6BycwPpKXW09I/tXMqcItE8SA4=",
"origin": "github.com/penggy/EasyGoLib/vendor/github.com/pkg/errors",
@ -279,6 +285,12 @@
"revision": "d0926230dda8c9e4e61040cb7825a026dee7d2d3",
"revisionTime": "2018-09-07T02:33:35Z"
},
{
"checksumSHA1": "wGV+EeSd5YGVLiYL36qT65GWahg=",
"path": "github.com/reactivex/rxgo/observable",
"revision": "e715dd83f030be66a2cbef90b842fc3caedfcc69",
"revisionTime": "2018-10-31T19:04:19Z"
},
{
"checksumSHA1": "q14d3C3xvWevU3dSv4P5K0+OSD0=",
"path": "github.com/shirou/gopsutil/cpu",
@ -362,5 +374,5 @@
"revisionTime": "2018-03-28T19:50:20Z"
}
],
"rootPath": "github.com/EasyDarwin"
"rootPath": "github.com/EasyDarwin/EasyDarwin"
}

View File

@ -58,7 +58,7 @@ export default {
return;
}
this.bLoading = true;
$.get('/stream/start', this.form).then(data => {
$.get('/api/v1/stream/start', this.form).then(data => {
this.$refs['dlg'].hide();
this.$emit('submit');
}).always(() => {

View File

@ -14,7 +14,7 @@
<div class="box-header">
<h4 class="text-success text-center">推流列表</h4>
<form class="form-inline">
<div class="form-group hide">
<div class="form-group">
<button type="button" class="btn btn-sm btn-success" @click.prevent="$refs['pullRTSPDlg'].show()"><i class="fa fa-plus"></i> 拉流分发</button>
</div>
<div class="form-group pull-right">
@ -51,13 +51,13 @@
<el-table-column prop="outBytes" label="下行流量" min-width="120" :formatter="formatBytes" sortable="custom"></el-table-column>
<el-table-column prop="onlines" label="在线人数" min-width="100" sortable="custom"></el-table-column>
<el-table-column prop="startAt" label="开始时间" min-width="200" sortable="custom"></el-table-column>
<!-- <el-table-column label="操作" min-width="120" fixed="right">
<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>
</div>
</template>
</el-table-column> -->
</el-table-column>
</el-table>
</div>
<div class="box-footer clearfix" v-if="total > 0">

View File

@ -12,14 +12,14 @@ define({
"ModifyPassword",
"GetServerInfo"
],
"version": "1.0.0",
"version": "8.1.0",
"description": "EasyDarwin Open Source Media Server",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-03T06:17:10.067Z",
"time": "2018-11-24T02:46:15.810Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@ -12,14 +12,14 @@
"ModifyPassword",
"GetServerInfo"
],
"version": "1.0.0",
"version": "8.1.0",
"description": "EasyDarwin Open Source Media Server",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-03T06:17:10.067Z",
"time": "2018-11-24T02:46:15.810Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

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

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

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.8178b6a2.css" rel="stylesheet"></head>
<link href="css/index.eba7eb98.css" rel="stylesheet"></head>
<body class="skin-green sidebar-mini">
<div id="app"></div>
<script type="text/javascript" src="js/index.8178b6a2.js"></script></body>
<script type="text/javascript" src="js/index.eba7eb98.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

1
www/js/index.eba7eb98.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
www/js/login.cf365b39.js Normal file

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

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<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>
<link href="css/login.729a9cc8.css" rel="stylesheet"></head>
<link href="css/login.cf365b39.css" rel="stylesheet"></head>
<body class="hold-transition login-page">
<div id="app"></div>
<script type="text/javascript" src="js/login.729a9cc8.js"></script></body>
<script type="text/javascript" src="js/login.cf365b39.js"></script></body>
</html>