package mjpeg import ( "bytes" "encoding/binary" "image" "image/jpeg" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/pion/rtp" ) func RTPDepay(handlerFunc core.HandlerFunc) core.HandlerFunc { buf := make([]byte, 0, 512*1024) // 512K 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 // 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 } 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) } } func cutSize(size uint16) uint16 { return ((size >> 3) & 0xFF) << 3 } func RTPPay(handlerFunc core.HandlerFunc) core.HandlerFunc { const packetSize = 1436 sequencer := rtp.NewRandomSequencer() 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 } 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 } 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, } handlerFunc(&clone) } } } func Transcode(b []byte) ([]byte, error) { img, err := jpeg.Decode(bytes.NewReader(b)) if err != nil { return nil, 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 nil, err } return buf.Bytes(), nil }