Add support HTTP MJPEG to RTSP MJPEG
This commit is contained in:
+134
-132
@@ -1,8 +1,12 @@
|
||||
package mjpeg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/AlexxIT/go2rtc/pkg/streamer"
|
||||
"github.com/pion/rtp"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
)
|
||||
|
||||
func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
|
||||
@@ -82,144 +86,142 @@ func RTPDepay(track *streamer.Track) streamer.WrapperFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func cutSize(size uint16) uint16 {
|
||||
return ((size >> 3) & 0xFF) << 3
|
||||
}
|
||||
|
||||
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
|
||||
p, err := Transcode(packet.Payload)
|
||||
if 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
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cutSize(size uint16) uint16 {
|
||||
return ((size >> 3) & 0xFF) << 3
|
||||
}
|
||||
func Transcode(b []byte) ([]byte, error) {
|
||||
img, err := jpeg.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//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
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
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 nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user