1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

GB28181: Support GB28181-2016 protocol. v5.0.74 (#3201)

01. Support GB config as StreamCaster.
02. Support disable GB by --gb28181=off.
03. Add utests for SIP examples.
04. Wireshark plugin to decode TCP/9000 as rtp.rfc4571
05. Support MPEGPS program stream codec.
06. Add utest for PS stream codec.
07. Decode MPEGPS packet stream.
08. Carry RTP and PS packet as helper in PS message.
09. Support recover from error mode.
10. Support process by a pack of PS/TS messages.
11. Add statistic for recovered and msgs dropped.
12. Recover from err position fastly.
13. Define state machine for GB session.
14. Bind context to GB session.
15. Re-invite when media disconnected.
16. Update GitHub actions with GB28181.
17. Support parse CANDIDATE by env or pip.
18. Support mux GB28181 to RTMP.
19. Support regression test by srs-bench.
This commit is contained in:
Winlin 2022-10-06 17:40:58 +08:00 committed by GitHub
parent 9c81a0e1bd
commit 5a420ece3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
298 changed files with 43343 additions and 763 deletions

View file

@ -0,0 +1,147 @@
// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
package util
import (
"fmt"
"sync"
"github.com/ghettovoice/gosip/log"
)
// The buffer size of the primitive input and output chans.
const c_ELASTIC_CHANSIZE = 3
// A dynamic channel that does not block on send, but has an unlimited buffer capacity.
// ElasticChan uses a dynamic slice to buffer signals received on the input channel until
// the output channel is ready to process them.
type ElasticChan struct {
In chan interface{}
Out chan interface{}
buffer []interface{}
stopped bool
done chan struct{}
log log.Logger
logMu sync.RWMutex
}
// Initialise the Elastic channel, and start the management goroutine.
func (c *ElasticChan) Init() {
c.In = make(chan interface{}, c_ELASTIC_CHANSIZE)
c.Out = make(chan interface{}, c_ELASTIC_CHANSIZE)
c.buffer = make([]interface{}, 0)
c.done = make(chan struct{})
}
func (c *ElasticChan) Run() {
go c.manage()
}
func (c *ElasticChan) Stop() {
select {
case <-c.done:
return
default:
}
logger := c.Log()
if logger != nil {
logger.Trace("stopping elastic chan...")
}
close(c.In)
<-c.done
if logger != nil {
logger.Trace("elastic chan stopped")
}
}
func (c *ElasticChan) Log() log.Logger {
c.logMu.RLock()
defer c.logMu.RUnlock()
return c.log
}
func (c *ElasticChan) SetLog(logger log.Logger) {
c.logMu.Lock()
c.log = logger.
WithPrefix("util.ElasticChan").
WithFields(log.Fields{
"elastic_chan_ptr": fmt.Sprintf("%p", c),
})
c.logMu.Unlock()
}
// Poll for input from one end of the channel and add it to the buffer.
// Also poll sending buffered signals out over the output chan.
// TODO: add cancel chan
func (c *ElasticChan) manage() {
defer close(c.done)
loop:
for {
logger := c.Log()
if len(c.buffer) > 0 {
// The buffer has something in it, so try to send as well as
// receive.
// (Receive first in order to minimize blocked Send() calls).
select {
case in, ok := <-c.In:
if !ok {
if logger != nil {
logger.Trace("elastic chan will dispose")
}
break loop
}
c.Log().Tracef("ElasticChan %p gets '%v'", c, in)
c.buffer = append(c.buffer, in)
case c.Out <- c.buffer[0]:
c.Log().Tracef("ElasticChan %p sends '%v'", c, c.buffer[0])
c.buffer = c.buffer[1:]
}
} else {
// The buffer is empty, so there's nothing to send.
// Just wait to receive.
in, ok := <-c.In
if !ok {
if logger != nil {
logger.Trace("elastic chan will dispose")
}
break loop
}
c.Log().Tracef("ElasticChan %p gets '%v'", c, in)
c.buffer = append(c.buffer, in)
}
}
c.dispose()
}
func (c *ElasticChan) dispose() {
logger := c.Log()
if logger != nil {
logger.Trace("elastic chan disposing...")
}
for len(c.buffer) > 0 {
select {
case c.Out <- c.buffer[0]:
c.buffer = c.buffer[1:]
default:
}
}
if logger != nil {
logger.Trace("elastic chan disposed")
}
}

View file

@ -0,0 +1,104 @@
// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
package util
import (
"errors"
"net"
"sync"
)
// Check two string pointers for equality as follows:
// - If neither pointer is nil, check equality of the underlying strings.
// - If either pointer is nil, return true if and only if they both are.
func StrPtrEq(a *string, b *string) bool {
if a == nil || b == nil {
return a == b
}
return *a == *b
}
// Check two uint16 pointers for equality as follows:
// - If neither pointer is nil, check equality of the underlying uint16s.
// - If either pointer is nil, return true if and only if they both are.
func Uint16PtrEq(a *uint16, b *uint16) bool {
if a == nil || b == nil {
return a == b
}
return *a == *b
}
func Coalesce(arg1 interface{}, arg2 interface{}, args ...interface{}) interface{} {
all := append([]interface{}{arg1, arg2}, args...)
for _, arg := range all {
if arg != nil {
return arg
}
}
return nil
}
func Noop() {}
func MergeErrs(chs ...<-chan error) <-chan error {
wg := new(sync.WaitGroup)
out := make(chan error)
pipe := func(ch <-chan error) {
defer wg.Done()
for err := range ch {
out <- err
}
}
wg.Add(len(chs))
for _, ch := range chs {
go pipe(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
func ResolveSelfIP() (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To4()
if ip == nil {
continue // not an ipv4 address
}
return ip, nil
}
}
return nil, errors.New("server not connected to any network")
}

View file

@ -0,0 +1,38 @@
package util
import (
"math/rand"
"time"
)
const (
letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
// https://github.com/kpbird/golang_random_string
func RandString(n int) string {
output := make([]byte, n)
// We will take n bytes, one byte for each character of output.
randomness := make([]byte, n)
// read all random
_, err := rand.Read(randomness)
if err != nil {
panic(err)
}
l := len(letterBytes)
// fill output
for pos := range output {
// get random item
random := randomness[pos]
// random % 64
randomPos := random % uint8(l)
// put into output
output[pos] = letterBytes[randomPos]
}
return string(output)
}

View file

@ -0,0 +1,75 @@
// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
package util
import "sync"
// Simple semaphore implementation.
// Any number of calls to Acquire() can be made; these will not block.
// If the semaphore has been acquired more times than it has been released, it is called 'blocked'.
// Otherwise, it is called 'free'.
type Semaphore interface {
// Take a semaphore lock.
Acquire()
// Release an acquired semaphore lock.
// This should only be called when the semaphore is blocked, otherwise behaviour is undefined
Release()
// Block execution until the semaphore is free.
Wait()
// Clean up the semaphore object.
Dispose()
}
func NewSemaphore() Semaphore {
sem := new(semaphore)
sem.cond = sync.NewCond(&sync.Mutex{})
go func(s *semaphore) {
select {
case <-s.stop:
return
case <-s.acquired:
s.locks += 1
case <-s.released:
s.locks -= 1
if s.locks == 0 {
s.cond.Broadcast()
}
}
}(sem)
return sem
}
// Concrete implementation of Semaphore.
type semaphore struct {
held bool
locks int
acquired chan bool
released chan bool
stop chan bool
cond *sync.Cond
}
// Implements Semaphore.Acquire()
func (sem *semaphore) Acquire() {
sem.acquired <- true
}
// Implements Semaphore.Release()
func (sem *semaphore) Release() {
sem.released <- true
}
// Implements Semaphore.Wait()
func (sem *semaphore) Wait() {
sem.cond.L.Lock()
for sem.locks != 0 {
sem.cond.Wait()
}
}
// Implements Semaphore.Dispose()
func (sem *semaphore) Dispose() {
sem.stop <- true
}