diff --git a/actioncam.go b/actioncam.go index 88224f4..444b768 100644 --- a/actioncam.go +++ b/actioncam.go @@ -273,7 +273,10 @@ func main() { } relay := libipcamera.CreateRTPRelay(applicationContext) rtspServer, err := rtsp.CreateServer(applicationContext, host, port, relay) - defer rtspServer.Close() + if rtspServer != nil { + defer rtspServer.Close() + } + if err := camera.StartPreviewStream(); err != nil { diff --git a/rtsp/RTSPServer.go b/rtsp/RTSPServer.go index b3daf28..04e256a 100644 --- a/rtsp/RTSPServer.go +++ b/rtsp/RTSPServer.go @@ -24,45 +24,47 @@ type Server struct { wg sync.WaitGroup } -// CreateServer now requires relay as input func CreateServer(parentCtx context.Context, host string, port int, relay *libipcamera.RTPRelay) (*Server, error) { - ctx, cancel := context.WithCancel(parentCtx) + ctx, cancel := context.WithCancel(parentCtx) - stream, media, err := newH264Stream() - if err != nil { - cancel() - return nil, fmt.Errorf("create H264 stream: %w", err) - } + // Create the RTSP server instance first + h := &handler{} + gs := &gortsplib.Server{ + Handler: h, + RTSPAddress: fmt.Sprintf("%s:%d", host, port), + } - srv := &Server{ - ctx: ctx, - cancel: cancel, - stream: stream, - media: media, - } + // Now create the H264 stream bound to this server + stream, media, err := newH264Stream(gs) + if err != nil { + cancel() + return nil, fmt.Errorf("create H264 stream: %w", err) + } - h := &handler{srv: srv} + // Final server struct assembly + srv := &Server{ + ctx: ctx, + cancel: cancel, + gs: gs, + stream: stream, + media: media, + } + h.srv = srv // link handler to server instance - gs := &gortsplib.Server{ - Handler: h, - RTSPAddress: fmt.Sprintf("%s:%d", host, port), - } - srv.gs = gs + // Start pumping RTP frames from relay into RTSP + srv.wg.Add(1) + go srv.relayPump(relay) - // 🧠 Pump from relay to RTSP - srv.wg.Add(1) - go srv.relayPump(relay) + // Start the RTSP server + go func() { + log.Printf("RTSP server ready on rtsp://%s:%d/", host, port) + if err := gs.StartAndWait(); err != nil { + log.Printf("RTSP stopped: %v", err) + } + cancel() + }() - go func() { - log.Printf("RTSP server ready on rtsp://%s:%d/", host, port) - err := gs.StartAndWait() - if err != nil { - log.Printf("RTSP stopped: %v", err) - } - cancel() - }() - - return srv, nil + return srv, nil } func (s *Server) Close() { @@ -113,27 +115,25 @@ func (h *handler) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, } // Build simple baseline H264 SDP -func newH264Stream() (*gortsplib.ServerStream, *description.Media, error) { - h264 := &format.H264{ - PayloadTyp: 96, - PacketizationMode: 1, - SPS: []byte{0x67, 0x42, 0xC0, 0x1F, 0x96, 0x54, 0x05, 0x01, 0xED, 0x00, 0xF0, 0x88, 0x45, 0x80}, - PPS: []byte{0x68, 0xCE, 0x38, 0x80}, - } +func newH264Stream(gs *gortsplib.Server) (*gortsplib.ServerStream, *description.Media, error) { + h264 := &format.H264{ + PayloadTyp: 96, + PacketizationMode: 1, + SPS: []byte{0x67, 0x42, 0xC0, 0x1F, 0x96, 0x54, 0x05, 0x01, 0xED, 0x00, 0xF0, 0x88, 0x45, 0x80}, + PPS: []byte{0x68, 0xCE, 0x38, 0x80}, + } - media := &description.Media{ - Type: description.MediaTypeVideo, - Formats: []format.Format{h264}, - } + media := &description.Media{ + Type: description.MediaTypeVideo, + Formats: []format.Format{h264}, + } - desc := &description.Session{ - Medias: []*description.Media{media}, - } + desc := &description.Session{ + Medias: []*description.Media{media}, + } - stream := &gortsplib.ServerStream{Desc: desc} - if err := stream.Initialize(); err != nil { - return nil, nil, err - } + // REQUIRED: link stream to the running server + stream := gortsplib.NewServerStream(gs, desc) - return stream, media, nil + return stream, media, nil }