package libipcamera import ( "bytes" "context" "encoding/binary" "encoding/hex" "io" "log" "net" "time" ) // Frame is a decoded RTP packet buffer forwarded to the RTSP server type Frame struct { Payload []byte } // RTPRelay holds information on the relaying stream listener type RTPRelay struct { close bool listener net.PacketConn context context.Context Output chan Frame // 🚨 NEW: channel to push decoded RTP frames } var closeFlag bool // CreateRTPRelay creates a UDP listener that handles live data // and pushes RTP-ready payloads into Output channel. func CreateRTPRelay(ctx context.Context) *RTPRelay { conn, err := net.ListenPacket("udp", ":6669") if err != nil { log.Printf("ERROR: %s\n", err) } closeFlag = false relay := RTPRelay{ close: false, listener: conn, context: ctx, Output: make(chan Frame, 100), // buffered for smoother streaming } go handleCameraStream(relay, conn) return &relay } func handleCameraStream(relay RTPRelay, conn net.PacketConn) { buffer := make([]byte, 2048) packetReader := bytes.NewReader(buffer) header := streamHeader{} var payload []byte var sequenceNumber uint16 var elapsed uint32 frameBuffer := bytes.Buffer{} packetBuffer := bytes.Buffer{} T: for { conn.SetReadDeadline(time.Now().Add(10 * time.Second)) select { case <-relay.context.Done(): log.Println("Context Done") relay.listener.Close() close(relay.Output) break T default: if closeFlag { relay.listener.Close() close(relay.Output) break T } conn.ReadFrom(buffer) packetReader.Reset(buffer) binary.Read(packetReader, binary.BigEndian, &header) if header.Magic != 0xBCDE { log.Printf("Received message with invalid magic (%x).", header.Magic) break } if header.Length > 0 { payload = make([]byte, header.Length) _, err := io.ReadFull(packetReader, payload) if err != nil { log.Printf("Read Error: %s\n", err) break } } else { payload = []byte{} } switch header.MessageType { case 0x0001: // H.264 Data frameBuffer.Write(payload) case 0x0002: // Time // Append full frame packetBuffer.Write(frameBuffer.Bytes()) // Push to RTSP server (in-memory) relay.Output <- Frame{Payload: append([]byte{}, packetBuffer.Bytes()...)} // Reset the next packet packetBuffer.Reset() packetBuffer.Write([]byte{0x80, 0x63}) binary.Write(&packetBuffer, binary.BigEndian, sequenceNumber+1) binary.Write(&packetBuffer, binary.BigEndian, (uint32)(elapsed)*90) binary.Write(&packetBuffer, binary.BigEndian, (uint64(0))) frameBuffer.Reset() sequenceNumber++ elapsed = binary.LittleEndian.Uint32(payload[12:]) default: log.Printf("Received Unknown Message: %+v\n", header) log.Printf("Payload:\n%s\n", hex.Dump(payload)) } } } close(relay.Output) } // Stop stops listening for packets func (r *RTPRelay) Stop() { closeFlag = true r.close = true r.listener.Close() }