BIG core logic rewrite

This commit is contained in:
Alexey Khit
2023-03-17 06:48:02 +03:00
parent 2146ea470b
commit 12a7b96289
107 changed files with 3000 additions and 3024 deletions
+18 -14
View File
@@ -3,7 +3,7 @@ package mjpeg
import (
"bufio"
"errors"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/tcp"
"github.com/pion/rtp"
"io"
@@ -11,12 +11,11 @@ import (
"net/textproto"
"strconv"
"strings"
"sync/atomic"
"time"
)
type Client struct {
streamer.Element
core.Listener
UserAgent string
RemoteAddr string
@@ -24,9 +23,10 @@ type Client struct {
closed bool
res *http.Response
medias []*streamer.Media
track *streamer.Track
recv uint32
medias []*core.Media
receiver *core.Receiver
recv int
}
func NewClient(res *http.Response) *Client {
@@ -40,9 +40,9 @@ func (c *Client) startJPEG() error {
}
packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet)
c.receiver.WriteRTP(packet)
atomic.AddUint32(&c.recv, uint32(len(buf)))
c.recv += len(buf)
req := c.res.Request
@@ -61,10 +61,12 @@ func (c *Client) startJPEG() error {
return err
}
packet = &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet)
if c.receiver != nil {
packet = &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
c.receiver.WriteRTP(packet)
}
atomic.AddUint32(&c.recv, uint32(len(buf)))
c.recv += len(buf)
}
return nil
@@ -109,10 +111,12 @@ func (c *Client) startMJPEG(boundary string) error {
return err
}
packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet)
if c.receiver != nil {
packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
c.receiver.WriteRTP(packet)
}
atomic.AddUint32(&c.recv, uint32(len(buf)))
c.recv += len(buf)
if _, err = r.Discard(2); err != nil {
return err
+44 -25
View File
@@ -2,52 +2,71 @@ package mjpeg
import (
"encoding/json"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtp"
"sync/atomic"
)
type Consumer struct {
streamer.Element
core.Listener
UserAgent string
RemoteAddr string
codecs []*streamer.Codec
start bool
medias []*core.Media
sender *core.Sender
send uint32
send int
}
func (c *Consumer) GetMedias() []*streamer.Media {
return []*streamer.Media{{
Kind: streamer.KindVideo,
Direction: streamer.DirectionRecvonly,
Codecs: []*streamer.Codec{{Name: streamer.CodecJPEG}},
}}
func (c *Consumer) GetMedias() []*core.Media {
if c.medias == nil {
c.medias = []*core.Media{
{
Kind: core.KindVideo,
Direction: core.DirectionSendonly,
Codecs: []*core.Codec{
{Name: core.CodecJPEG},
},
},
}
}
return c.medias
}
func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *streamer.Track {
push := func(packet *rtp.Packet) error {
c.Fire(packet.Payload)
atomic.AddUint32(&c.send, uint32(len(packet.Payload)))
return nil
func (c *Consumer) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiver) error {
if c.sender == nil {
c.sender = core.NewSender(media, track.Codec)
c.sender.Handler = func(packet *rtp.Packet) {
c.Fire(packet.Payload)
c.send += len(packet.Payload)
}
if track.Codec.IsRTP() {
c.sender.Handler = RTPDepay(c.sender.Handler)
}
}
if track.Codec.IsRTP() {
wrapper := RTPDepay(track)
push = wrapper(push)
}
c.sender.HandleRTP(track)
return nil
}
return track.Bind(push)
func (c *Consumer) Stop() error {
if c.sender != nil {
c.sender.Close()
}
return nil
}
func (c *Consumer) MarshalJSON() ([]byte, error) {
info := &streamer.Info{
Type: "MJPEG client",
info := &core.Info{
Type: "MJPEG passive consumer",
RemoteAddr: c.RemoteAddr,
UserAgent: c.UserAgent,
Send: atomic.LoadUint32(&c.send),
Medias: c.medias,
Send: c.send,
}
if c.sender != nil {
info.Senders = []*core.Sender{c.sender}
}
return json.Marshal(info)
}
+21 -15
View File
@@ -3,19 +3,18 @@ package mjpeg
import (
"encoding/json"
"errors"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/AlexxIT/go2rtc/pkg/core"
"strings"
"sync/atomic"
)
func (c *Client) GetMedias() []*streamer.Media {
func (c *Client) GetMedias() []*core.Media {
if c.medias == nil {
c.medias = []*streamer.Media{{
Kind: streamer.KindVideo,
Direction: streamer.DirectionSendonly,
Codecs: []*streamer.Codec{
c.medias = []*core.Media{{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{
{
Name: streamer.CodecJPEG, ClockRate: 90000, PayloadType: streamer.PayloadTypeRAW,
Name: core.CodecJPEG, ClockRate: 90000, PayloadType: core.PayloadTypeRAW,
},
},
}}
@@ -23,11 +22,11 @@ func (c *Client) GetMedias() []*streamer.Media {
return c.medias
}
func (c *Client) GetTrack(media *streamer.Media, codec *streamer.Codec) *streamer.Track {
if c.track == nil {
c.track = streamer.NewTrack(media, codec)
func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) {
if c.receiver == nil {
c.receiver = core.NewReceiver(media, codec)
}
return c.track
return c.receiver, nil
}
func (c *Client) Start() error {
@@ -46,6 +45,9 @@ func (c *Client) Start() error {
}
func (c *Client) Stop() error {
if c.receiver != nil {
c.receiver.Close()
}
// important for close reader/writer gorutines
_ = c.res.Body.Close()
c.closed = true
@@ -53,12 +55,16 @@ func (c *Client) Stop() error {
}
func (c *Client) MarshalJSON() ([]byte, error) {
info := &streamer.Info{
Type: "MJPEG source",
info := &core.Info{
Type: "MJPEG active producer",
URL: c.res.Request.URL.String(),
RemoteAddr: c.RemoteAddr,
UserAgent: c.UserAgent,
Recv: atomic.LoadUint32(&c.recv),
Medias: c.medias,
Recv: c.recv,
}
if c.receiver != nil {
info.Receivers = []*core.Receiver{c.receiver}
}
return json.Marshal(info)
}
+141 -149
View File
@@ -3,86 +3,84 @@ package mjpeg
import (
"bytes"
"encoding/binary"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtp"
"image"
"image/jpeg"
)
func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
func RTPDepay(handlerFunc core.HandlerFunc) core.HandlerFunc {
buf := make([]byte, 0, 512*1024) // 512K
return func(push streamer.WriterFunc) streamer.WriterFunc {
return func(packet *rtp.Packet) error {
//log.Printf("[RTP] codec: %s, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d, mark: %v", track.Codec.Name, len(packet.Payload), packet.Timestamp, packet.PayloadType, packet.SSRC, packet.SequenceNumber, packet.Marker)
return func(packet *rtp.Packet) {
//log.Printf("[RTP] codec: %s, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d, mark: %v", track.Codec.Name, len(packet.Payload), packet.Timestamp, packet.PayloadType, packet.SSRC, packet.SequenceNumber, packet.Marker)
// https://www.rfc-editor.org/rfc/rfc2435#section-3.1
b := packet.Payload
// https://www.rfc-editor.org/rfc/rfc2435#section-3.1
b := packet.Payload
// 3.1. JPEG header
t := b[4]
// 3.1. JPEG header
t := b[4]
// 3.1.7. Restart Marker header
if 64 <= t && t <= 127 {
b = b[12:] // skip it
} else {
b = b[8:]
}
if len(buf) == 0 {
var lqt, cqt []byte
// 3.1.8. Quantization Table header
q := packet.Payload[5]
if q >= 128 {
lqt = b[4:68]
cqt = b[68:132]
b = b[132:]
} else {
lqt, cqt = MakeTables(q)
}
// https://www.rfc-editor.org/rfc/rfc2435#section-3.1.5
// The maximum width is 2040 pixels.
w := uint16(packet.Payload[6]) << 3
h := uint16(packet.Payload[7]) << 3
// fix sizes more than 2040
switch {
// 512x1920 512x1440
case w == cutSize(2560) && (h == 1920 || h == 1440):
w = 2560
// 1792x112
case w == cutSize(3840) && h == cutSize(2160):
w = 3840
h = 2160
// 256x1296
case w == cutSize(2304) && h == 1296:
w = 2304
}
//fmt.Printf("t: %d, q: %d, w: %d, h: %d\n", t, q, w, h)
buf = MakeHeaders(buf, t, w, h, lqt, cqt)
}
// 3.1.9. JPEG Payload
buf = append(buf, b...)
if !packet.Marker {
return nil
}
if end := buf[len(buf)-2:]; end[0] != 0xFF && end[1] != 0xD9 {
buf = append(buf, 0xFF, 0xD9)
}
clone := *packet
clone.Payload = buf
buf = buf[:0] // clear buffer
return push(&clone)
// 3.1.7. Restart Marker header
if 64 <= t && t <= 127 {
b = b[12:] // skip it
} else {
b = b[8:]
}
if len(buf) == 0 {
var lqt, cqt []byte
// 3.1.8. Quantization Table header
q := packet.Payload[5]
if q >= 128 {
lqt = b[4:68]
cqt = b[68:132]
b = b[132:]
} else {
lqt, cqt = MakeTables(q)
}
// https://www.rfc-editor.org/rfc/rfc2435#section-3.1.5
// The maximum width is 2040 pixels.
w := uint16(packet.Payload[6]) << 3
h := uint16(packet.Payload[7]) << 3
// fix sizes more than 2040
switch {
// 512x1920 512x1440
case w == cutSize(2560) && (h == 1920 || h == 1440):
w = 2560
// 1792x112
case w == cutSize(3840) && h == cutSize(2160):
w = 3840
h = 2160
// 256x1296
case w == cutSize(2304) && h == 1296:
w = 2304
}
//fmt.Printf("t: %d, q: %d, w: %d, h: %d\n", t, q, w, h)
buf = MakeHeaders(buf, t, w, h, lqt, cqt)
}
// 3.1.9. JPEG Payload
buf = append(buf, b...)
if !packet.Marker {
return
}
if end := buf[len(buf)-2:]; end[0] != 0xFF && end[1] != 0xD9 {
buf = append(buf, 0xFF, 0xD9)
}
clone := *packet
clone.Payload = buf
buf = buf[:0] // clear buffer
handlerFunc(&clone)
}
}
@@ -90,102 +88,96 @@ func cutSize(size uint16) uint16 {
return ((size >> 3) & 0xFF) << 3
}
func RTPPay() streamer.WrapperFunc {
func RTPPay(handlerFunc core.HandlerFunc) core.HandlerFunc {
const packetSize = 1436
sequencer := rtp.NewRandomSequencer()
return func(push streamer.WriterFunc) streamer.WriterFunc {
return func(packet *rtp.Packet) error {
// reincode image to more common form
p, err := Transcode(packet.Payload)
if err != nil {
return err
return func(packet *rtp.Packet) {
// reincode image to more common form
p, err := Transcode(packet.Payload)
if err != nil {
return
}
h1 := make([]byte, 8)
h1[4] = 1 // Type
h1[5] = 255 // Q
// MBZ=0, Precision=0, Length=128
h2 := make([]byte, 4, 132)
h2[3] = 128
var jpgData []byte
for jpgData == nil {
// 2 bytes h1
if p[0] != 0xFF {
return
}
h1 := make([]byte, 8)
h1[4] = 1 // Type
h1[5] = 255 // Q
size := binary.BigEndian.Uint16(p[2:]) + 2
// MBZ=0, Precision=0, Length=128
h2 := make([]byte, 4, 132)
h2[3] = 128
var jpgData []byte
for jpgData == nil {
// 2 bytes h1
if p[0] != 0xFF {
return nil
// 2 bytes payload size (include 2 bytes)
switch p[1] {
case 0xD8: // 0. Start Of Image (size=0)
p = p[2:]
continue
case 0xDB: // 1. Define Quantization Table (size=130)
for i := uint16(4 + 1); i < size; i += 1 + 64 {
h2 = append(h2, p[i:i+64]...)
}
size := binary.BigEndian.Uint16(p[2:]) + 2
// 2 bytes payload size (include 2 bytes)
switch p[1] {
case 0xD8: // 0. Start Of Image (size=0)
p = p[2:]
continue
case 0xDB: // 1. Define Quantization Table (size=130)
for i := uint16(4 + 1); i < size; i += 1 + 64 {
h2 = append(h2, p[i:i+64]...)
}
case 0xC0: // 2. Start Of Frame (size=15)
if p[4] != 8 {
return nil
}
h := binary.BigEndian.Uint16(p[5:])
w := binary.BigEndian.Uint16(p[7:])
h1[6] = uint8(w >> 3)
h1[7] = uint8(h >> 3)
case 0xC4: // 3. Define Huffman Table (size=416)
case 0xDA: // 4. Start Of Scan (size=10)
jpgData = p[size:]
case 0xC0: // 2. Start Of Frame (size=15)
if p[4] != 8 {
return
}
p = p[size:]
h := binary.BigEndian.Uint16(p[5:])
w := binary.BigEndian.Uint16(p[7:])
h1[6] = uint8(w >> 3)
h1[7] = uint8(h >> 3)
case 0xC4: // 3. Define Huffman Table (size=416)
case 0xDA: // 4. Start Of Scan (size=10)
jpgData = p[size:]
}
offset := 0
p = make([]byte, 0)
p = p[size:]
}
for jpgData != nil {
p = p[:0]
offset := 0
p = make([]byte, 0)
if offset > 0 {
h1[1] = byte(offset >> 16)
h1[2] = byte(offset >> 8)
h1[3] = byte(offset)
p = append(p, h1...)
} else {
p = append(p, h1...)
p = append(p, h2...)
}
for jpgData != nil {
p = p[:0]
dataLen := packetSize - len(p)
if dataLen < len(jpgData) {
p = append(p, jpgData[:dataLen]...)
jpgData = jpgData[dataLen:]
offset += dataLen
} else {
p = append(p, jpgData...)
jpgData = nil
}
clone := rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: jpgData == nil,
SequenceNumber: sequencer.NextSequenceNumber(),
Timestamp: packet.Timestamp,
},
Payload: p,
}
if err := push(&clone); err != nil {
return err
}
if offset > 0 {
h1[1] = byte(offset >> 16)
h1[2] = byte(offset >> 8)
h1[3] = byte(offset)
p = append(p, h1...)
} else {
p = append(p, h1...)
p = append(p, h2...)
}
return nil
dataLen := packetSize - len(p)
if dataLen < len(jpgData) {
p = append(p, jpgData[:dataLen]...)
jpgData = jpgData[dataLen:]
offset += dataLen
} else {
p = append(p, jpgData...)
jpgData = nil
}
clone := rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: jpgData == nil,
SequenceNumber: sequencer.NextSequenceNumber(),
Timestamp: packet.Timestamp,
},
Payload: p,
}
handlerFunc(&clone)
}
}
}