From d3bd5eeab597082d8ad8d6a96c45a735828ab491 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Thu, 1 Dec 2022 23:20:31 +0300 Subject: [PATCH] Added a blank payloader for MJPEG RTSP --- pkg/mjpeg/client.go | 11 +++- pkg/mjpeg/rtp.go | 144 ++++++++++++++++++++++++++++++++++++++++++-- pkg/rtsp/conn.go | 6 ++ 3 files changed, 153 insertions(+), 8 deletions(-) diff --git a/pkg/mjpeg/client.go b/pkg/mjpeg/client.go index 36d26987..2010dde1 100644 --- a/pkg/mjpeg/client.go +++ b/pkg/mjpeg/client.go @@ -11,6 +11,7 @@ import ( "net/textproto" "strconv" "strings" + "time" ) type Client struct { @@ -73,7 +74,7 @@ func (c *Client) startJPEG() error { return err } - packet := &rtp.Packet{Payload: buf} + packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} _ = c.track.WriteRTP(packet) req := c.res.Request @@ -93,7 +94,7 @@ func (c *Client) startJPEG() error { return err } - packet = &rtp.Packet{Payload: buf} + packet = &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} _ = c.track.WriteRTP(packet) } @@ -135,7 +136,7 @@ func (c *Client) startMJPEG(boundary string) error { return err } - packet := &rtp.Packet{Payload: buf} + packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} _ = c.track.WriteRTP(packet) if _, err = r.Discard(2); err != nil { @@ -145,3 +146,7 @@ func (c *Client) startMJPEG(boundary string) error { return nil } + +func now() uint32 { + return uint32(time.Now().UnixMilli() * 90) +} diff --git a/pkg/mjpeg/rtp.go b/pkg/mjpeg/rtp.go index a926d7c9..ef92d0eb 100644 --- a/pkg/mjpeg/rtp.go +++ b/pkg/mjpeg/rtp.go @@ -10,11 +10,7 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc { return func(push streamer.WriterFunc) streamer.WriterFunc { return func(packet *rtp.Packet) error { - //fmt.Printf( - // "[RTP] codec: %s, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d, mark: %v\n", - // track.Codec.Name, len(packet.Payload), packet.Timestamp, - // packet.PayloadType, packet.SSRC, packet.SequenceNumber, packet.Marker, - //) + //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 @@ -76,3 +72,141 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc { } } } + +func RTPPay() streamer.WrapperFunc { + return func(push streamer.WriterFunc) streamer.WriterFunc { + return func(packet *rtp.Packet) error { + return nil + } + } +} + +//func RTPPay() streamer.WrapperFunc { +// 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 +// img, err := jpeg.Decode(bytes.NewReader(packet.Payload)) +// if err != nil { +// return err +// } +// +// wh := img.Bounds().Size() +// w := wh.X +// h := wh.Y +// +// if w > 2040 { +// w = 2040 +// } else if w&3 > 0 { +// w &= 3 +// } +// if h > 2040 { +// h = 2040 +// } else if h&3 > 0 { +// h &= 3 +// } +// +// if w != wh.X || h != wh.Y { +// x0 := (wh.X - w) / 2 +// y0 := (wh.Y - h) / 2 +// rect := image.Rect(x0, y0, x0+w, y0+h) +// img = img.(*image.YCbCr).SubImage(rect) +// } +// +// buf := bytes.NewBuffer(nil) +// if err = jpeg.Encode(buf, img, nil); err != nil { +// return err +// } +// +// 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 +// +// p := buf.Bytes() +// +// for jpgData == nil { +// // 2 bytes h1 +// if p[0] != 0xFF { +// return nil +// } +// +// 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:] +// } +// +// p = p[size:] +// } +// +// offset := 0 +// p = make([]byte, 0) +// +// for jpgData != nil { +// p = p[: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...) +// } +// +// 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 +// } +// } +// +// return nil +// } +// } +//} diff --git a/pkg/rtsp/conn.go b/pkg/rtsp/conn.go index 49044eaf..dbf56eaa 100644 --- a/pkg/rtsp/conn.go +++ b/pkg/rtsp/conn.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/AlexxIT/go2rtc/pkg/aac" "github.com/AlexxIT/go2rtc/pkg/h264" + "github.com/AlexxIT/go2rtc/pkg/mjpeg" "github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/pion/rtcp" @@ -776,6 +777,8 @@ func (c *Conn) bindTrack( size := packet.MarshalSize() + //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) + data := make([]byte, 4+size) data[0] = '$' data[1] = channel @@ -802,6 +805,9 @@ func (c *Conn) bindTrack( case streamer.CodecAAC: wrapper := aac.RTPPay(1500) push = wrapper(push) + case streamer.CodecJPEG: + wrapper := mjpeg.RTPPay() + push = wrapper(push) } }