diff --git a/pkg/mjpeg/rtp.go b/pkg/mjpeg/rtp.go index d3db445f..ae983329 100644 --- a/pkg/mjpeg/rtp.go +++ b/pkg/mjpeg/rtp.go @@ -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 +}