mirror of
https://github.com/lkmio/lkm.git
synced 2025-09-27 03:26:01 +08:00
优化网络IO使用buffer读取
This commit is contained in:
@@ -73,7 +73,7 @@ func (t *httpTransStream) Input(packet utils.AVPacket) error {
|
|||||||
var separatorSize int
|
var separatorSize int
|
||||||
|
|
||||||
//新的合并写切片, 预留包长字节
|
//新的合并写切片, 预留包长字节
|
||||||
if t.mwBuffer.IsCompeted() {
|
if t.mwBuffer.IsCompleted() {
|
||||||
separatorSize = HttpFlvBlockLengthSize
|
separatorSize = HttpFlvBlockLengthSize
|
||||||
//10字节描述flv包长, 前2个字节描述无效字节长度
|
//10字节描述flv包长, 前2个字节描述无效字节长度
|
||||||
n = HttpFlvBlockLengthSize
|
n = HttpFlvBlockLengthSize
|
||||||
@@ -186,8 +186,6 @@ func (t *httpTransStream) writeSeparator(dst []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *httpTransStream) WriteHeader() error {
|
func (t *httpTransStream) WriteHeader() error {
|
||||||
t.Init()
|
|
||||||
|
|
||||||
t.headerSize += t.muxer.WriteHeader(t.header[HttpFlvBlockLengthSize:])
|
t.headerSize += t.muxer.WriteHeader(t.header[HttpFlvBlockLengthSize:])
|
||||||
|
|
||||||
for _, track := range t.BaseTransStream.Tracks {
|
for _, track := range t.BaseTransStream.Tracks {
|
||||||
|
@@ -1,56 +1,9 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pion/rtp"
|
|
||||||
"github.com/yangjiechina/avformat/utils"
|
|
||||||
"github.com/yangjiechina/lkm/log"
|
|
||||||
"github.com/yangjiechina/lkm/stream"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Filter interface {
|
type Filter interface {
|
||||||
AddSource(ssrc uint32, source GBSource) bool
|
AddSource(ssrc uint32, source GBSource) bool
|
||||||
|
|
||||||
RemoveSource(ssrc uint32)
|
RemoveSource(ssrc uint32)
|
||||||
|
|
||||||
Input(conn net.Conn, data []byte) GBSource
|
FindSource(ssrc uint32) GBSource
|
||||||
|
|
||||||
ParseRtpPacket(conn net.Conn, data []byte) (*rtp.Packet, error)
|
|
||||||
|
|
||||||
PreparePublishSource(conn net.Conn, ssrc uint32, source GBSource)
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseFilter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r BaseFilter) ParseRtpPacket(conn net.Conn, data []byte) (*rtp.Packet, error) {
|
|
||||||
packet := rtp.Packet{}
|
|
||||||
err := packet.Unmarshal(data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Sugar.Errorf("解析rtp失败 err:%s conn:%s", err.Error(), conn.RemoteAddr().String())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &packet, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r BaseFilter) PreparePublishSource(conn net.Conn, ssrc uint32, source GBSource) {
|
|
||||||
source.SetConn(conn)
|
|
||||||
source.SetSSRC(ssrc)
|
|
||||||
|
|
||||||
source.SetState(stream.SessionStateTransferring)
|
|
||||||
|
|
||||||
if stream.AppConfig.Hook.EnablePublishEvent() {
|
|
||||||
go func() {
|
|
||||||
_, state := stream.HookPublishEvent(source)
|
|
||||||
if utils.HookStateOK != state {
|
|
||||||
log.Sugar.Errorf("GB28181 推流失败")
|
|
||||||
|
|
||||||
if conn != nil {
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,21 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
type singleFilter struct {
|
||||||
"github.com/yangjiechina/lkm/stream"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SingleFilter struct {
|
|
||||||
BaseFilter
|
|
||||||
|
|
||||||
source GBSource
|
source GBSource
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSingleFilter(source GBSource) *SingleFilter {
|
func NewSingleFilter(source GBSource) Filter {
|
||||||
return &SingleFilter{source: source}
|
return &singleFilter{source: source}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SingleFilter) AddSource(ssrc uint32, source GBSource) bool {
|
func (s *singleFilter) AddSource(ssrc uint32, source GBSource) bool {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SingleFilter) RemoveSource(ssrc uint32) {
|
func (s *singleFilter) RemoveSource(ssrc uint32) {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SingleFilter) Input(conn net.Conn, data []byte) GBSource {
|
func (s *singleFilter) FindSource(ssrc uint32) GBSource {
|
||||||
packet, err := s.ParseRtpPacket(conn, data)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.source == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if stream.SessionStateHandshakeDone == s.source.State() {
|
|
||||||
s.PreparePublishSource(conn, packet.SSRC, s.source)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.source.InputRtp(packet)
|
|
||||||
return s.source
|
return s.source
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,21 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/yangjiechina/lkm/stream"
|
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSRCFilter struct {
|
type ssrcFilter struct {
|
||||||
BaseFilter
|
|
||||||
|
|
||||||
sources map[uint32]GBSource
|
sources map[uint32]GBSource
|
||||||
mute sync.RWMutex
|
mute sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSharedFilter(guestCount int) *SSRCFilter {
|
func NewSharedFilter(guestCount int) Filter {
|
||||||
return &SSRCFilter{sources: make(map[uint32]GBSource, guestCount)}
|
return &ssrcFilter{sources: make(map[uint32]GBSource, guestCount)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SSRCFilter) AddSource(ssrc uint32, source GBSource) bool {
|
func (r *ssrcFilter) AddSource(ssrc uint32, source GBSource) bool {
|
||||||
r.mute.Lock()
|
r.mute.Lock()
|
||||||
defer r.mute.Lock()
|
defer r.mute.Unlock()
|
||||||
|
|
||||||
if _, ok := r.sources[ssrc]; !ok {
|
if _, ok := r.sources[ssrc]; !ok {
|
||||||
r.sources[ssrc] = source
|
r.sources[ssrc] = source
|
||||||
@@ -29,34 +25,14 @@ func (r SSRCFilter) AddSource(ssrc uint32, source GBSource) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SSRCFilter) RemoveSource(ssrc uint32) {
|
func (r *ssrcFilter) RemoveSource(ssrc uint32) {
|
||||||
r.mute.Lock()
|
r.mute.Lock()
|
||||||
defer r.mute.Lock()
|
defer r.mute.Unlock()
|
||||||
delete(r.sources, ssrc)
|
delete(r.sources, ssrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SSRCFilter) Input(conn net.Conn, data []byte) GBSource {
|
func (r *ssrcFilter) FindSource(ssrc uint32) GBSource {
|
||||||
packet, err := r.ParseRtpPacket(conn, data)
|
r.mute.RLock()
|
||||||
if err != nil {
|
defer r.mute.RUnlock()
|
||||||
return nil
|
return r.sources[ssrc]
|
||||||
}
|
|
||||||
|
|
||||||
var source GBSource
|
|
||||||
var ok bool
|
|
||||||
{
|
|
||||||
r.mute.RLock()
|
|
||||||
source, ok = r.sources[packet.SSRC]
|
|
||||||
r.mute.RUnlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if stream.SessionStateHandshakeDone == source.State() {
|
|
||||||
r.PreparePublishSource(conn, packet.SSRC, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
source.InputRtp(packet)
|
|
||||||
return source
|
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,8 @@ type GBSource interface {
|
|||||||
|
|
||||||
PrepareTransDeMuxer(id string, ssrc uint32)
|
PrepareTransDeMuxer(id string, ssrc uint32)
|
||||||
|
|
||||||
|
PreparePublishSource(conn net.Conn, ssrc uint32, source GBSource)
|
||||||
|
|
||||||
SetConn(conn net.Conn)
|
SetConn(conn net.Conn)
|
||||||
|
|
||||||
SetSSRC(ssrc uint32)
|
SetSSRC(ssrc uint32)
|
||||||
@@ -54,8 +56,9 @@ type BaseGBSource struct {
|
|||||||
audioStream utils.AVStream
|
audioStream utils.AVStream
|
||||||
videoStream utils.AVStream
|
videoStream utils.AVStream
|
||||||
|
|
||||||
ssrc uint32
|
ssrc uint32
|
||||||
transport transport.ITransport
|
transport transport.ITransport
|
||||||
|
receiveBuffer *stream.ReceiveBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGBSource(id string, ssrc uint32, tcp bool, active bool) (GBSource, uint16, error) {
|
func NewGBSource(id string, ssrc uint32, tcp bool, active bool) (GBSource, uint16, error) {
|
||||||
@@ -144,8 +147,15 @@ func NewGBSource(id string, ssrc uint32, tcp bool, active bool) (GBSource, uint1
|
|||||||
return nil, 0, fmt.Errorf("error code %d", state)
|
return nil, 0, fmt.Errorf("error code %d", state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bufferBlockCount int
|
||||||
|
if active || tcp {
|
||||||
|
bufferBlockCount = stream.ReceiveBufferTCPBlockCount
|
||||||
|
} else {
|
||||||
|
bufferBlockCount = stream.ReceiveBufferUdpBlockCount
|
||||||
|
}
|
||||||
|
|
||||||
source.SetType(stream.SourceType28181)
|
source.SetType(stream.SourceType28181)
|
||||||
source.Init(source.Input, source.Close)
|
source.Init(source.Input, source.Close, bufferBlockCount)
|
||||||
go source.LoopEvent()
|
go source.LoopEvent()
|
||||||
return source, port, err
|
return source, port, err
|
||||||
}
|
}
|
||||||
@@ -334,3 +344,31 @@ func (source *BaseGBSource) SetConn(conn net.Conn) {
|
|||||||
func (source *BaseGBSource) SetSSRC(ssrc uint32) {
|
func (source *BaseGBSource) SetSSRC(ssrc uint32) {
|
||||||
source.ssrc = ssrc
|
source.ssrc = ssrc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (source *BaseGBSource) SetReceiveBuffer(buffer *stream.ReceiveBuffer) {
|
||||||
|
source.receiveBuffer = buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (source *BaseGBSource) ReceiveBuffer() *stream.ReceiveBuffer {
|
||||||
|
return source.receiveBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (source *BaseGBSource) PreparePublishSource(conn net.Conn, ssrc uint32, source_ GBSource) {
|
||||||
|
source.SetConn(conn)
|
||||||
|
source.SetSSRC(ssrc)
|
||||||
|
|
||||||
|
source.SetState(stream.SessionStateTransferring)
|
||||||
|
|
||||||
|
if stream.AppConfig.Hook.EnablePublishEvent() {
|
||||||
|
go func() {
|
||||||
|
_, state := stream.HookPublishEvent(source_)
|
||||||
|
if utils.HookStateOK != state {
|
||||||
|
log.Sugar.Errorf("GB28181 推流失败 source:%s", source.Id())
|
||||||
|
|
||||||
|
if conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -17,6 +17,7 @@ func (t PassiveSource) TransportType() TransportType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t PassiveSource) InputRtp(pkt *rtp.Packet) error {
|
func (t PassiveSource) InputRtp(pkt *rtp.Packet) error {
|
||||||
t.PublishSource.Input(pkt.Payload)
|
//TCP收流, 解析rtp后直接送给ps解析
|
||||||
|
t.Input(pkt.Payload)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -1,24 +1,23 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/yangjiechina/lkm/jitterbuffer"
|
"github.com/yangjiechina/lkm/jitterbuffer"
|
||||||
"github.com/yangjiechina/lkm/stream"
|
"github.com/yangjiechina/lkm/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UDPSource GB28181 UDP推流源
|
||||||
type UDPSource struct {
|
type UDPSource struct {
|
||||||
BaseGBSource
|
BaseGBSource
|
||||||
|
|
||||||
rtpDeMuxer *jitterbuffer.JitterBuffer
|
jitterBuffer *jitterbuffer.JitterBuffer
|
||||||
|
receiveBuffer *stream.ReceiveBuffer
|
||||||
rtpBuffer stream.MemoryPool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPSource() *UDPSource {
|
func NewUDPSource() *UDPSource {
|
||||||
return &UDPSource{
|
return &UDPSource{
|
||||||
rtpDeMuxer: jitterbuffer.New(),
|
jitterBuffer: jitterbuffer.New(),
|
||||||
rtpBuffer: stream.NewDirectMemoryPool(JitterBufferSize),
|
receiveBuffer: stream.NewReceiveBuffer(1500, stream.ReceiveBufferUdpBlockCount+50),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,25 +25,20 @@ func (u UDPSource) TransportType() TransportType {
|
|||||||
return TransportTypeUDP
|
return TransportTypeUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InputRtp UDP收流会先拷贝rtp包,交给jitter buffer处理后再发给source
|
||||||
func (u UDPSource) InputRtp(pkt *rtp.Packet) error {
|
func (u UDPSource) InputRtp(pkt *rtp.Packet) error {
|
||||||
n := u.rtpBuffer.Capacity() - u.rtpBuffer.Size()
|
block := u.receiveBuffer.GetBlock()
|
||||||
if n < len(pkt.Payload) {
|
|
||||||
return fmt.Errorf("RTP receive buffer overflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
allocate := u.rtpBuffer.Allocate(len(pkt.Payload))
|
copy(block, pkt.Payload)
|
||||||
copy(allocate, pkt.Payload)
|
pkt.Payload = block[:len(pkt.Payload)]
|
||||||
pkt.Payload = allocate
|
u.jitterBuffer.Push(pkt)
|
||||||
u.rtpDeMuxer.Push(pkt)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pkt, _ := u.rtpDeMuxer.Pop()
|
pkt, _ := u.jitterBuffer.Pop()
|
||||||
if pkt == nil {
|
if pkt == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
u.rtpBuffer.FreeHead()
|
|
||||||
|
|
||||||
u.PublishSource.Input(pkt.Payload)
|
u.PublishSource.Input(pkt.Payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pion/rtp"
|
||||||
"github.com/yangjiechina/avformat/transport"
|
"github.com/yangjiechina/avformat/transport"
|
||||||
"github.com/yangjiechina/lkm/log"
|
"github.com/yangjiechina/lkm/log"
|
||||||
|
"github.com/yangjiechina/lkm/stream"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,8 +14,9 @@ type TCPServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TCPSession struct {
|
type TCPSession struct {
|
||||||
source GBSource
|
source GBSource
|
||||||
decoder *transport.LengthFieldFrameDecoder
|
decoder *transport.LengthFieldFrameDecoder
|
||||||
|
receiveBuffer *stream.ReceiveBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPServer(addr net.Addr, filter Filter) (*TCPServer, error) {
|
func NewTCPServer(addr net.Addr, filter Filter) (*TCPServer, error) {
|
||||||
@@ -31,33 +34,80 @@ func NewTCPServer(addr net.Addr, filter Filter) (*TCPServer, error) {
|
|||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (T *TCPServer) OnConnected(conn net.Conn) {
|
func (T *TCPServer) OnConnected(conn net.Conn) []byte {
|
||||||
log.Sugar.Infof("GB28181连接 conn:%s", conn.RemoteAddr().String())
|
log.Sugar.Infof("GB28181连接 conn:%s", conn.RemoteAddr().String())
|
||||||
}
|
|
||||||
|
|
||||||
func (T *TCPServer) OnPacket(conn net.Conn, data []byte) {
|
|
||||||
con := conn.(*transport.Conn)
|
con := conn.(*transport.Conn)
|
||||||
if con.Data == nil {
|
session := &TCPSession{}
|
||||||
session := &TCPSession{}
|
if stream.AppConfig.GB28181.IsMultiPort() {
|
||||||
session.decoder = transport.NewLengthFieldFrameDecoder(0xFFFF, 2, func(bytes []byte) {
|
session.source = T.filter.(*singleFilter).source
|
||||||
source := T.filter.Input(con, bytes[2:])
|
session.source.SetConn(con)
|
||||||
if source != nil && session.source == nil {
|
session.receiveBuffer = stream.NewTCPReceiveBuffer()
|
||||||
session.source = source
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
con.Data = session
|
|
||||||
}
|
}
|
||||||
|
|
||||||
con.Data.(*TCPSession).decoder.Input(data)
|
session.decoder = transport.NewLengthFieldFrameDecoder(0xFFFF, 2, func(bytes []byte) {
|
||||||
|
packet := rtp.Packet{}
|
||||||
|
err := packet.Unmarshal(bytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Sugar.Errorf("解析rtp失败 err:%s conn:%s", err.Error(), conn.RemoteAddr().String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//单端口模式,ssrc匹配source
|
||||||
|
if session.source == nil {
|
||||||
|
//匹配不到直接关闭链接
|
||||||
|
source := T.filter.FindSource(packet.SSRC)
|
||||||
|
if source == nil {
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session.source = source
|
||||||
|
session.receiveBuffer = stream.NewTCPReceiveBuffer()
|
||||||
|
session.source.SetConn(con)
|
||||||
|
|
||||||
|
//直接丢给ps解析器, 虽然是非线程安全, 但是是阻塞执行的, 不会和后续走loop event的包冲突
|
||||||
|
session.source.InputRtp(&packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stream.SessionStateHandshakeDone == session.source.State() {
|
||||||
|
session.source.PreparePublishSource(conn, packet.SSRC, session.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.source.InputRtp(&packet)
|
||||||
|
})
|
||||||
|
|
||||||
|
con.Data = session
|
||||||
|
|
||||||
|
if session.source != nil {
|
||||||
|
return session.receiveBuffer.GetBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (T *TCPServer) OnPacket(conn net.Conn, data []byte) []byte {
|
||||||
|
session := conn.(*transport.Conn).Data.(*TCPSession)
|
||||||
|
|
||||||
|
//单端口收流
|
||||||
|
if session.source == nil {
|
||||||
|
//直接传给解码器, 先根据ssrc找到source. 后续还是会直接传给source
|
||||||
|
if err := session.decoder.Input(data); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session.source.Input(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return session.receiveBuffer.GetBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (T *TCPServer) OnDisConnected(conn net.Conn, err error) {
|
func (T *TCPServer) OnDisConnected(conn net.Conn, err error) {
|
||||||
log.Sugar.Infof("GB28181断开连接 conn:%s", conn.RemoteAddr().String())
|
log.Sugar.Infof("GB28181断开连接 conn:%s", conn.RemoteAddr().String())
|
||||||
|
|
||||||
con := conn.(*transport.Conn)
|
con := conn.(*transport.Conn)
|
||||||
if con.Data != nil {
|
if con.Data != nil && con.Data.(*TCPSession).source != nil {
|
||||||
con.Data.(*TCPSession).source.Close()
|
con.Data.(*TCPSession).source.Close()
|
||||||
con.Data = nil
|
|
||||||
}
|
}
|
||||||
|
con.Data = nil
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
package gb28181
|
package gb28181
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pion/rtp"
|
||||||
"github.com/yangjiechina/avformat/transport"
|
"github.com/yangjiechina/avformat/transport"
|
||||||
|
"github.com/yangjiechina/lkm/log"
|
||||||
|
"github.com/yangjiechina/lkm/stream"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UDPServer struct {
|
type UDPServer struct {
|
||||||
udp *transport.UDPTransport
|
udp *transport.UDPServer
|
||||||
filter Filter
|
filter Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,12 +27,31 @@ func NewUDPServer(addr net.Addr, filter Filter) (*UDPServer, error) {
|
|||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (U UDPServer) OnConnected(conn net.Conn) {
|
func (U UDPServer) OnConnected(conn net.Conn) []byte {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (U UDPServer) OnPacket(conn net.Conn, data []byte) {
|
func (U UDPServer) OnPacket(conn net.Conn, data []byte) []byte {
|
||||||
U.filter.Input(conn, data)
|
packet := rtp.Packet{}
|
||||||
|
err := packet.Unmarshal(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Sugar.Errorf("解析rtp失败 err:%s conn:%s", err.Error(), conn.RemoteAddr().String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source := U.filter.FindSource(packet.SSRC)
|
||||||
|
if source == nil {
|
||||||
|
log.Sugar.Errorf("ssrc匹配source失败 ssrc:%x conn:%s", packet.SSRC, conn.RemoteAddr().String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if stream.SessionStateHandshakeDone == source.State() {
|
||||||
|
source.PreparePublishSource(conn, packet.SSRC, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
source.InputRtp(&packet)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (U UDPServer) OnDisConnected(conn net.Conn, err error) {
|
func (U UDPServer) OnDisConnected(conn net.Conn, err error) {
|
||||||
|
@@ -36,59 +36,6 @@ type transStream struct {
|
|||||||
m3u8Sinks map[stream.SinkId]stream.Sink
|
m3u8Sinks map[stream.SinkId]stream.Sink
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTransStream 创建HLS传输流
|
|
||||||
// @url url前缀
|
|
||||||
// @m3u8Name m3u8文件名
|
|
||||||
// @tsFormat ts文件格式, 例如: %d.ts
|
|
||||||
// @parentDir 保存切片的绝对路径. mu38和ts切片放在同一目录下, 目录地址使用parentDir+urlPrefix
|
|
||||||
// @segmentDuration 单个切片时长
|
|
||||||
// @playlistLength 缓存多少个切片
|
|
||||||
func NewTransStream(url, m3u8Name, tsFormat, dir string, segmentDuration, playlistLength int) (stream.TransStream, error) {
|
|
||||||
//创建文件夹
|
|
||||||
if err := os.MkdirAll(dir, 0666); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//创建m3u8文件
|
|
||||||
m3u8Path := fmt.Sprintf("%s/%s", dir, m3u8Name)
|
|
||||||
file, err := os.OpenFile(m3u8Path, os.O_CREATE|os.O_RDWR, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_ := &transStream{
|
|
||||||
url: url,
|
|
||||||
m3u8Name: m3u8Name,
|
|
||||||
tsFormat: tsFormat,
|
|
||||||
dir: dir,
|
|
||||||
duration: segmentDuration,
|
|
||||||
playlistLength: playlistLength,
|
|
||||||
}
|
|
||||||
|
|
||||||
//创建TS封装器
|
|
||||||
muxer := libmpeg.NewTSMuxer()
|
|
||||||
muxer.SetWriteHandler(stream_.onTSWrite)
|
|
||||||
muxer.SetAllocHandler(stream_.onTSAlloc)
|
|
||||||
|
|
||||||
stream_.context = &tsContext{
|
|
||||||
segmentSeq: 0,
|
|
||||||
writeBuffer: make([]byte, 1024*1024),
|
|
||||||
writeBufferSize: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_.muxer = muxer
|
|
||||||
stream_.m3u8 = NewM3U8Writer(playlistLength)
|
|
||||||
stream_.m3u8File = file
|
|
||||||
|
|
||||||
stream_.m3u8Sinks = make(map[stream.SinkId]stream.Sink, 24)
|
|
||||||
return stream_, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
|
||||||
id := source.Id()
|
|
||||||
return NewTransStream("", stream.AppConfig.Hls.M3U8Format(id), stream.AppConfig.Hls.TSFormat(id, "%d"), stream.AppConfig.Hls.Dir, stream.AppConfig.Hls.Duration, stream.AppConfig.Hls.PlaylistLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *transStream) Input(packet utils.AVPacket) error {
|
func (t *transStream) Input(packet utils.AVPacket) error {
|
||||||
if packet.Index() >= t.muxer.TrackCount() {
|
if packet.Index() >= t.muxer.TrackCount() {
|
||||||
return fmt.Errorf("track not available")
|
return fmt.Errorf("track not available")
|
||||||
@@ -137,8 +84,6 @@ func (t *transStream) AddTrack(stream utils.AVStream) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *transStream) WriteHeader() error {
|
func (t *transStream) WriteHeader() error {
|
||||||
t.Init()
|
|
||||||
|
|
||||||
return t.createSegment()
|
return t.createSegment()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,3 +191,56 @@ func (t *transStream) Close() error {
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTransStream 创建HLS传输流
|
||||||
|
// @url url前缀
|
||||||
|
// @m3u8Name m3u8文件名
|
||||||
|
// @tsFormat ts文件格式, 例如: %d.ts
|
||||||
|
// @parentDir 保存切片的绝对路径. mu38和ts切片放在同一目录下, 目录地址使用parentDir+urlPrefix
|
||||||
|
// @segmentDuration 单个切片时长
|
||||||
|
// @playlistLength 缓存多少个切片
|
||||||
|
func NewTransStream(url, m3u8Name, tsFormat, dir string, segmentDuration, playlistLength int) (stream.TransStream, error) {
|
||||||
|
//创建文件夹
|
||||||
|
if err := os.MkdirAll(dir, 0666); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建m3u8文件
|
||||||
|
m3u8Path := fmt.Sprintf("%s/%s", dir, m3u8Name)
|
||||||
|
file, err := os.OpenFile(m3u8Path, os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_ := &transStream{
|
||||||
|
url: url,
|
||||||
|
m3u8Name: m3u8Name,
|
||||||
|
tsFormat: tsFormat,
|
||||||
|
dir: dir,
|
||||||
|
duration: segmentDuration,
|
||||||
|
playlistLength: playlistLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建TS封装器
|
||||||
|
muxer := libmpeg.NewTSMuxer()
|
||||||
|
muxer.SetWriteHandler(stream_.onTSWrite)
|
||||||
|
muxer.SetAllocHandler(stream_.onTSAlloc)
|
||||||
|
|
||||||
|
stream_.context = &tsContext{
|
||||||
|
segmentSeq: 0,
|
||||||
|
writeBuffer: make([]byte, 1024*1024),
|
||||||
|
writeBufferSize: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_.muxer = muxer
|
||||||
|
stream_.m3u8 = NewM3U8Writer(playlistLength)
|
||||||
|
stream_.m3u8File = file
|
||||||
|
|
||||||
|
stream_.m3u8Sinks = make(map[stream.SinkId]stream.Sink, 24)
|
||||||
|
return stream_, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
||||||
|
id := source.Id()
|
||||||
|
return NewTransStream("", stream.AppConfig.Hls.M3U8Format(id), stream.AppConfig.Hls.TSFormat(id, "%d"), stream.AppConfig.Hls.Dir, stream.AppConfig.Hls.Duration, stream.AppConfig.Hls.PlaylistLength)
|
||||||
|
}
|
||||||
|
@@ -21,15 +21,18 @@ func NewServer() Server {
|
|||||||
return &jtServer{}
|
return &jtServer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s jtServer) OnConnected(conn net.Conn) {
|
func (s jtServer) OnConnected(conn net.Conn) []byte {
|
||||||
log.Sugar.Debugf("jtserver连接 conn:%s", conn.RemoteAddr().String())
|
log.Sugar.Debugf("jtserver连接 conn:%s", conn.RemoteAddr().String())
|
||||||
|
|
||||||
t := conn.(*transport.Conn)
|
t := conn.(*transport.Conn)
|
||||||
t.Data = NewSession(conn)
|
t.Data = NewSession(conn)
|
||||||
|
|
||||||
|
return t.Data.(*Session).receiveBuffer.GetBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s jtServer) OnPacket(conn net.Conn, data []byte) {
|
func (s jtServer) OnPacket(conn net.Conn, data []byte) []byte {
|
||||||
conn.(*transport.Conn).Data.(*Session).Input(data)
|
conn.(*transport.Conn).Data.(*Session).PublishSource.Input(data)
|
||||||
|
return conn.(*transport.Conn).Data.(*Session).receiveBuffer.GetBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s jtServer) OnDisConnected(conn net.Conn, err error) {
|
func (s jtServer) OnDisConnected(conn net.Conn, err error) {
|
||||||
|
@@ -37,13 +37,14 @@ type Session struct {
|
|||||||
phone string
|
phone string
|
||||||
decoder *transport.DelimiterFrameDecoder
|
decoder *transport.DelimiterFrameDecoder
|
||||||
|
|
||||||
audioIndex int
|
audioIndex int
|
||||||
videoIndex int
|
videoIndex int
|
||||||
audioStream utils.AVStream
|
audioStream utils.AVStream
|
||||||
videoStream utils.AVStream
|
videoStream utils.AVStream
|
||||||
audioBuffer stream.MemoryPool
|
audioBuffer stream.MemoryPool
|
||||||
videoBuffer stream.MemoryPool
|
videoBuffer stream.MemoryPool
|
||||||
rtpPacket *RtpPacket
|
rtpPacket *RtpPacket
|
||||||
|
receiveBuffer *stream.ReceiveBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
type RtpPacket struct {
|
type RtpPacket struct {
|
||||||
@@ -298,8 +299,9 @@ func NewSession(conn net.Conn) *Session {
|
|||||||
}
|
}
|
||||||
delimiter := [4]byte{0x30, 0x31, 0x63, 0x64}
|
delimiter := [4]byte{0x30, 0x31, 0x63, 0x64}
|
||||||
session.decoder = transport.NewDelimiterFrameDecoder(1024*1024*2, delimiter[:], session.OnJtPTPPacket)
|
session.decoder = transport.NewDelimiterFrameDecoder(1024*1024*2, delimiter[:], session.OnJtPTPPacket)
|
||||||
|
session.receiveBuffer = stream.NewTCPReceiveBuffer()
|
||||||
|
|
||||||
session.Init(session.Input, session.Close)
|
session.Init(session.Input, session.Close, stream.ReceiveBufferTCPBlockCount)
|
||||||
go session.LoopEvent()
|
go session.LoopEvent()
|
||||||
return &session
|
return &session
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ type transStream struct {
|
|||||||
|
|
||||||
func NewTransStream() stream.TransStream {
|
func NewTransStream() stream.TransStream {
|
||||||
t := &transStream{}
|
t := &transStream{}
|
||||||
t.Init()
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@ package rtmp
|
|||||||
import (
|
import (
|
||||||
"github.com/yangjiechina/avformat/libflv"
|
"github.com/yangjiechina/avformat/libflv"
|
||||||
"github.com/yangjiechina/avformat/librtmp"
|
"github.com/yangjiechina/avformat/librtmp"
|
||||||
"github.com/yangjiechina/avformat/transport"
|
|
||||||
"github.com/yangjiechina/avformat/utils"
|
"github.com/yangjiechina/avformat/utils"
|
||||||
"github.com/yangjiechina/lkm/stream"
|
"github.com/yangjiechina/lkm/stream"
|
||||||
"net"
|
"net"
|
||||||
@@ -22,7 +21,7 @@ func NewPublisher(sourceId string, stack *librtmp.Stack, conn net.Conn) *Publish
|
|||||||
//设置回调,从flv解析出来的Stream和AVPacket都将统一回调到stream.PublishSource
|
//设置回调,从flv解析出来的Stream和AVPacket都将统一回调到stream.PublishSource
|
||||||
deMuxer.SetHandler(publisher_)
|
deMuxer.SetHandler(publisher_)
|
||||||
//为推流方分配足够多的缓冲区
|
//为推流方分配足够多的缓冲区
|
||||||
conn.(*transport.Conn).ReallocateRecvBuffer(1024 * 1024)
|
//conn.(*transport.Conn).ReallocateRecvBuffer(1024 * 1024)
|
||||||
return publisher_
|
return publisher_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -41,16 +41,20 @@ func (s *server) Close() {
|
|||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnConnected(conn net.Conn) {
|
func (s *server) OnConnected(conn net.Conn) []byte {
|
||||||
log.Sugar.Debugf("rtmp连接 conn:%s", conn.RemoteAddr().String())
|
log.Sugar.Debugf("rtmp连接 conn:%s", conn.RemoteAddr().String())
|
||||||
|
|
||||||
t := conn.(*transport.Conn)
|
t := conn.(*transport.Conn)
|
||||||
t.Data = NewSession(conn)
|
t.Data = NewSession(conn)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnPacket(conn net.Conn, data []byte) {
|
func (s *server) OnPacket(conn net.Conn, data []byte) []byte {
|
||||||
|
log.Sugar.Infof("rtmp包大小:%d", len(data))
|
||||||
|
|
||||||
t := conn.(*transport.Conn)
|
t := conn.(*transport.Conn)
|
||||||
err := t.Data.(*Session).Input(conn, data)
|
session := t.Data.(*Session)
|
||||||
|
err := session.Input(conn, data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Sugar.Errorf("处理rtmp包失败 err:%s conn:%s", err.Error(), conn.RemoteAddr().String())
|
log.Sugar.Errorf("处理rtmp包失败 err:%s conn:%s", err.Error(), conn.RemoteAddr().String())
|
||||||
@@ -59,6 +63,12 @@ func (s *server) OnPacket(conn net.Conn, data []byte) {
|
|||||||
t.Data.(*Session).Close()
|
t.Data.(*Session).Close()
|
||||||
t.Data = nil
|
t.Data = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session.isPublisher {
|
||||||
|
return session.receiveBuffer.GetBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnDisConnected(conn net.Conn, err error) {
|
func (s *server) OnDisConnected(conn net.Conn, err error) {
|
||||||
|
@@ -16,7 +16,8 @@ type Session struct {
|
|||||||
handle interface{}
|
handle interface{}
|
||||||
isPublisher bool
|
isPublisher bool
|
||||||
|
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
|
receiveBuffer *stream.ReceiveBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) generateSourceId(app, stream_ string) string {
|
func (s *Session) generateSourceId(app, stream_ string) string {
|
||||||
@@ -37,6 +38,9 @@ func (s *Session) OnPublish(app, stream_ string, response chan utils.HookState)
|
|||||||
//设置推流的音视频回调
|
//设置推流的音视频回调
|
||||||
s.stack.SetOnPublishHandler(source)
|
s.stack.SetOnPublishHandler(source)
|
||||||
|
|
||||||
|
//初始化放在add source前面, 以防add-init空窗期, 拉流队列空指针.
|
||||||
|
source.Init(source.Input, source.Close, stream.ReceiveBufferTCPBlockCount)
|
||||||
|
|
||||||
//推流事件Source统一处理, 是否已经存在, Hook回调....
|
//推流事件Source统一处理, 是否已经存在, Hook回调....
|
||||||
_, state := stream.PreparePublishSource(source, true)
|
_, state := stream.PreparePublishSource(source, true)
|
||||||
if utils.HookStateOK != state {
|
if utils.HookStateOK != state {
|
||||||
@@ -44,8 +48,8 @@ func (s *Session) OnPublish(app, stream_ string, response chan utils.HookState)
|
|||||||
} else {
|
} else {
|
||||||
s.handle = source
|
s.handle = source
|
||||||
s.isPublisher = true
|
s.isPublisher = true
|
||||||
|
s.receiveBuffer = stream.NewTCPReceiveBuffer()
|
||||||
|
|
||||||
source.Init(source.Input, source.Close)
|
|
||||||
go source.LoopEvent()
|
go source.LoopEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/yangjiechina/lkm/stream"
|
"github.com/yangjiechina/lkm/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TransStream struct {
|
type transStream struct {
|
||||||
stream.BaseTransStream
|
stream.BaseTransStream
|
||||||
|
|
||||||
chunkSize int
|
chunkSize int
|
||||||
@@ -21,7 +21,7 @@ type TransStream struct {
|
|||||||
mwBuffer stream.MergeWritingBuffer
|
mwBuffer stream.MergeWritingBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) Input(packet utils.AVPacket) error {
|
func (t *transStream) Input(packet utils.AVPacket) error {
|
||||||
utils.Assert(t.BaseTransStream.Completed)
|
utils.Assert(t.BaseTransStream.Completed)
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
@@ -96,7 +96,7 @@ func (t *TransStream) Input(packet utils.AVPacket) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) AddSink(sink stream.Sink) error {
|
func (t *transStream) AddSink(sink stream.Sink) error {
|
||||||
utils.Assert(t.headerSize > 0)
|
utils.Assert(t.headerSize > 0)
|
||||||
|
|
||||||
t.BaseTransStream.AddSink(sink)
|
t.BaseTransStream.AddSink(sink)
|
||||||
@@ -113,12 +113,10 @@ func (t *TransStream) AddSink(sink stream.Sink) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) WriteHeader() error {
|
func (t *transStream) WriteHeader() error {
|
||||||
utils.Assert(t.Tracks != nil)
|
utils.Assert(t.Tracks != nil)
|
||||||
utils.Assert(!t.BaseTransStream.Completed)
|
utils.Assert(!t.BaseTransStream.Completed)
|
||||||
|
|
||||||
t.Init()
|
|
||||||
|
|
||||||
var audioStream utils.AVStream
|
var audioStream utils.AVStream
|
||||||
var videoStream utils.AVStream
|
var videoStream utils.AVStream
|
||||||
var audioCodecId utils.AVCodecID
|
var audioCodecId utils.AVCodecID
|
||||||
@@ -181,7 +179,7 @@ func (t *TransStream) WriteHeader() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) Close() error {
|
func (t *transStream) Close() error {
|
||||||
//发送剩余的流
|
//发送剩余的流
|
||||||
segment := t.mwBuffer.PopSegment()
|
segment := t.mwBuffer.PopSegment()
|
||||||
if len(segment) > 0 {
|
if len(segment) > 0 {
|
||||||
@@ -191,7 +189,7 @@ func (t *TransStream) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewTransStream(chunkSize int) stream.TransStream {
|
func NewTransStream(chunkSize int) stream.TransStream {
|
||||||
return &TransStream{chunkSize: chunkSize}
|
return &transStream{chunkSize: chunkSize}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
||||||
|
@@ -51,21 +51,22 @@ func (s *server) Close() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnConnected(conn net.Conn) {
|
func (s *server) OnConnected(conn net.Conn) []byte {
|
||||||
log.Sugar.Debugf("rtsp连接 conn:%s", conn.RemoteAddr().String())
|
log.Sugar.Debugf("rtsp连接 conn:%s", conn.RemoteAddr().String())
|
||||||
|
|
||||||
t := conn.(*transport.Conn)
|
t := conn.(*transport.Conn)
|
||||||
t.Data = NewSession(conn)
|
t.Data = NewSession(conn)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnPacket(conn net.Conn, data []byte) {
|
func (s *server) OnPacket(conn net.Conn, data []byte) []byte {
|
||||||
t := conn.(*transport.Conn)
|
t := conn.(*transport.Conn)
|
||||||
|
|
||||||
method, url, header, err := parseMessage(data)
|
method, url, header, err := parseMessage(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Sugar.Errorf("failed to prase message:%s. err:%s conn:%s", string(data), err.Error(), conn.RemoteAddr().String())
|
log.Sugar.Errorf("failed to prase message:%s. err:%s conn:%s", string(data), err.Error(), conn.RemoteAddr().String())
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.handler.Process(t.Data.(*session), method, url, header)
|
err = s.handler.Process(t.Data.(*session), method, url, header)
|
||||||
@@ -73,6 +74,9 @@ func (s *server) OnPacket(conn net.Conn, data []byte) {
|
|||||||
log.Sugar.Errorf("failed to process message of RTSP. err:%s conn:%s msg:%s", err.Error(), conn.RemoteAddr().String(), string(data))
|
log.Sugar.Errorf("failed to process message of RTSP. err:%s conn:%s msg:%s", err.Error(), conn.RemoteAddr().String(), string(data))
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//后续实现rtsp推流, 需要返回收流buffer
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) OnDisConnected(conn net.Conn, err error) {
|
func (s *server) OnDisConnected(conn net.Conn, err error) {
|
||||||
|
@@ -30,30 +30,6 @@ type tranStream struct {
|
|||||||
sdp string
|
sdp string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransStream(addr net.IPAddr, urlFormat string) stream.TransStream {
|
|
||||||
t := &tranStream{
|
|
||||||
addr: addr,
|
|
||||||
urlFormat: urlFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
if addr.IP.To4() != nil {
|
|
||||||
t.addrType = "IP4"
|
|
||||||
} else {
|
|
||||||
t.addrType = "IP6"
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Init()
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
|
||||||
trackFormat := source.Id() + "?track=%d"
|
|
||||||
return NewTransStream(net.IPAddr{
|
|
||||||
IP: net.ParseIP(stream.AppConfig.PublicIP),
|
|
||||||
Zone: "",
|
|
||||||
}, trackFormat), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rtpMuxer申请输出流内存的回调
|
// rtpMuxer申请输出流内存的回调
|
||||||
// 无论是tcp/udp拉流, 均使用同一块内存, 并且给tcp预留4字节的包长.
|
// 无论是tcp/udp拉流, 均使用同一块内存, 并且给tcp预留4字节的包长.
|
||||||
func (t *tranStream) onAllocBuffer(params interface{}) []byte {
|
func (t *tranStream) onAllocBuffer(params interface{}) []byte {
|
||||||
@@ -274,3 +250,26 @@ func (t *tranStream) WriteHeader() error {
|
|||||||
t.sdp = string(marshal)
|
t.sdp = string(marshal)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTransStream(addr net.IPAddr, urlFormat string) stream.TransStream {
|
||||||
|
t := &tranStream{
|
||||||
|
addr: addr,
|
||||||
|
urlFormat: urlFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.IP.To4() != nil {
|
||||||
|
t.addrType = "IP4"
|
||||||
|
} else {
|
||||||
|
t.addrType = "IP6"
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func TransStreamFactory(source stream.Source, protocol stream.Protocol, streams []utils.AVStream) (stream.TransStream, error) {
|
||||||
|
trackFormat := source.Id() + "?track=%d"
|
||||||
|
return NewTransStream(net.IPAddr{
|
||||||
|
IP: net.ParseIP(stream.AppConfig.PublicIP),
|
||||||
|
Zone: "",
|
||||||
|
}, trackFormat), nil
|
||||||
|
}
|
||||||
|
@@ -17,7 +17,7 @@ type MergeWritingBuffer interface {
|
|||||||
|
|
||||||
IsFull(ts int64) bool
|
IsFull(ts int64) bool
|
||||||
|
|
||||||
IsCompeted() bool
|
IsCompleted() bool
|
||||||
|
|
||||||
IsEmpty() bool
|
IsEmpty() bool
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ func (m *mergeWritingBuffer) IsFull(ts int64) bool {
|
|||||||
return int(ts-m.prePacketTS) >= AppConfig.MergeWriteLatency
|
return int(ts-m.prePacketTS) >= AppConfig.MergeWriteLatency
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mergeWritingBuffer) IsCompeted() bool {
|
func (m *mergeWritingBuffer) IsCompleted() bool {
|
||||||
data, _ := m.transStreamBuffer.Data()
|
data, _ := m.transStreamBuffer.Data()
|
||||||
return m.segmentOffset == len(data)
|
return m.segmentOffset == len(data)
|
||||||
}
|
}
|
||||||
|
36
stream/receive_buffer.go
Normal file
36
stream/receive_buffer.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package stream
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReceiveBufferUdpBlockCount = 200
|
||||||
|
|
||||||
|
ReceiveBufferTCPBlockCount = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReceiveBuffer 收流缓冲区. 网络收流->解析流->封装流->发送流是同步的,从解析到发送可能耗时,从而影响读取网络流. 使用收流缓冲区,可有效降低出现此问题的概率.
|
||||||
|
// 从网络IO读取数据->送给解复用器, 此过程需做到无内存拷贝
|
||||||
|
// rtmp和1078推流直接使用ReceiveBuffer
|
||||||
|
// 国标推流,UDP收流都要经过jitter buffer处理, 还是需要拷贝一次, 没必要使用ReceiveBuffer. TCP全都使用ReceiveBuffer, 区别在于多端口模式, 第一包传给source, 单端口模式先解析出ssrc, 找到source. 后续再传给source.
|
||||||
|
type ReceiveBuffer struct {
|
||||||
|
blockSize int //单个缓存块大小
|
||||||
|
blockCount int //缓存块数据流. 应当和Source的数据输入管道容量保持一致.
|
||||||
|
data []byte
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReceiveBuffer) GetBlock() []byte {
|
||||||
|
bytes := r.data[r.index*r.blockSize:]
|
||||||
|
r.index = r.index + 1%r.blockCount
|
||||||
|
return bytes[:r.blockSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReceiveBuffer(blockSize, blockCount int) *ReceiveBuffer {
|
||||||
|
return &ReceiveBuffer{blockSize: blockSize, blockCount: blockCount, data: make([]byte, blockSize*blockCount), index: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPReceiveBuffer() *ReceiveBuffer {
|
||||||
|
return NewReceiveBuffer(1500, ReceiveBufferUdpBlockCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTCPReceiveBuffer() *ReceiveBuffer {
|
||||||
|
return NewReceiveBuffer(4096*20, ReceiveBufferTCPBlockCount)
|
||||||
|
}
|
@@ -108,7 +108,7 @@ type Source interface {
|
|||||||
// OnDeMuxDone 所有流解析完毕回调
|
// OnDeMuxDone 所有流解析完毕回调
|
||||||
OnDeMuxDone()
|
OnDeMuxDone()
|
||||||
|
|
||||||
Init(inputCB func(data []byte) error, closeCB func())
|
Init(inputCB func(data []byte) error, closeCB func(), receiveQueueSize int)
|
||||||
|
|
||||||
LoopEvent()
|
LoopEvent()
|
||||||
|
|
||||||
@@ -123,6 +123,8 @@ type Source interface {
|
|||||||
StartIdleTimer()
|
StartIdleTimer()
|
||||||
|
|
||||||
State() SessionState
|
State() SessionState
|
||||||
|
|
||||||
|
SetInputCb(func(data []byte) error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublishSource struct {
|
type PublishSource struct {
|
||||||
@@ -154,7 +156,6 @@ type PublishSource struct {
|
|||||||
//sink的拉流和断开拉流事件,都通过管道交给Source处理. 意味着Source内部解析流、封装流、传输流都可以做到无锁操作
|
//sink的拉流和断开拉流事件,都通过管道交给Source处理. 意味着Source内部解析流、封装流、传输流都可以做到无锁操作
|
||||||
//golang的管道是有锁的(https://github.com/golang/go/blob/d38f1d13fa413436d38d86fe86d6a146be44bb84/src/runtime/chan.go#L202), 后面使用cas队列传输事件, 并且可以做到一次读取多个事件
|
//golang的管道是有锁的(https://github.com/golang/go/blob/d38f1d13fa413436d38d86fe86d6a146be44bb84/src/runtime/chan.go#L202), 后面使用cas队列传输事件, 并且可以做到一次读取多个事件
|
||||||
inputDataEvent chan []byte
|
inputDataEvent chan []byte
|
||||||
dataConsumedEvent chan byte //解析完input的数据后,才能继续从网络io中读取流
|
|
||||||
closedEvent chan byte
|
closedEvent chan byte
|
||||||
playingEventQueue chan Sink
|
playingEventQueue chan Sink
|
||||||
playingDoneEventQueue chan Sink
|
playingDoneEventQueue chan Sink
|
||||||
@@ -172,15 +173,15 @@ func (s *PublishSource) Id() string {
|
|||||||
return s.Id_
|
return s.Id_
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublishSource) Init(inputCB func(data []byte) error, closeCB func()) {
|
func (s *PublishSource) Init(inputCB func(data []byte) error, closeCB func(), receiveQueueSize int) {
|
||||||
s.inputCB = inputCB
|
s.inputCB = inputCB
|
||||||
s.closeCB = closeCB
|
s.closeCB = closeCB
|
||||||
|
|
||||||
s.SetState(SessionStateHandshakeDone)
|
s.SetState(SessionStateHandshakeDone)
|
||||||
//初始化事件接收缓冲区
|
//初始化事件接收缓冲区
|
||||||
//收流和网络断开的chan都阻塞执行
|
//收流和网络断开的chan都阻塞执行
|
||||||
s.inputDataEvent = make(chan []byte)
|
//-1是为了保证从管道取到流, 到解析流是安全的, 不会被覆盖
|
||||||
s.dataConsumedEvent = make(chan byte)
|
s.inputDataEvent = make(chan []byte, receiveQueueSize-1)
|
||||||
s.closedEvent = make(chan byte)
|
s.closedEvent = make(chan byte)
|
||||||
s.playingEventQueue = make(chan Sink, 128)
|
s.playingEventQueue = make(chan Sink, 128)
|
||||||
s.playingDoneEventQueue = make(chan Sink, 128)
|
s.playingDoneEventQueue = make(chan Sink, 128)
|
||||||
@@ -234,22 +235,21 @@ func (s *PublishSource) LoopEvent() {
|
|||||||
select {
|
select {
|
||||||
case data := <-s.inputDataEvent:
|
case data := <-s.inputDataEvent:
|
||||||
if !s.closed {
|
if !s.closed {
|
||||||
if AppConfig.ReceiveTimeout > 0 {
|
break
|
||||||
s.lastPacketTime = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.state == SessionStateHandshakeDone {
|
|
||||||
s.state = SessionStateTransferring
|
|
||||||
//不在父类处理hook和prepare事情
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.inputCB(data); err != nil {
|
|
||||||
log.Sugar.Errorf("处理输入流失败 释放source:%s err:%s", s.Id_, err.Error())
|
|
||||||
s.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dataConsumedEvent <- 0
|
if AppConfig.ReceiveTimeout > 0 {
|
||||||
|
s.lastPacketTime = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.state == SessionStateHandshakeDone {
|
||||||
|
s.state = SessionStateTransferring
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.inputCB(data); err != nil {
|
||||||
|
log.Sugar.Errorf("处理输入流失败 释放source:%s err:%s", s.Id_, err.Error())
|
||||||
|
s.Close()
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case sink := <-s.playingEventQueue:
|
case sink := <-s.playingEventQueue:
|
||||||
if !s.completed {
|
if !s.completed {
|
||||||
@@ -363,6 +363,7 @@ func (s *PublishSource) AddSink(sink Sink) bool {
|
|||||||
transStream.AddTrack(streams[i])
|
transStream.AddTrack(streams[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transStream.Init()
|
||||||
_ = transStream.WriteHeader()
|
_ = transStream.WriteHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +414,6 @@ func (s *PublishSource) RemoveSink(sink Sink) bool {
|
|||||||
func (s *PublishSource) AddEvent(event SourceEvent, data interface{}) {
|
func (s *PublishSource) AddEvent(event SourceEvent, data interface{}) {
|
||||||
if SourceEventInput == event {
|
if SourceEventInput == event {
|
||||||
s.inputDataEvent <- data.([]byte)
|
s.inputDataEvent <- data.([]byte)
|
||||||
<-s.dataConsumedEvent
|
|
||||||
} else if SourceEventPlay == event {
|
} else if SourceEventPlay == event {
|
||||||
s.playingEventQueue <- data.(Sink)
|
s.playingEventQueue <- data.(Sink)
|
||||||
} else if SourceEventPlayDone == event {
|
} else if SourceEventPlayDone == event {
|
||||||
@@ -650,3 +650,7 @@ func (s *PublishSource) StartIdleTimer() {
|
|||||||
func (s *PublishSource) State() SessionState {
|
func (s *PublishSource) State() SessionState {
|
||||||
return s.state
|
return s.state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PublishSource) SetInputCb(cb func(data []byte) error) {
|
||||||
|
s.inputCB = cb
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user