func (v *Rtmp) serve(c net.Conn) { // use gfork to serve the connection. v.wc.GFork("", func(wc core.WorkerContainer) { defer func() { if r := recover(); r != nil { if !core.IsNormalQuit(r) { core.Warn.Println("rtmp ignore", r) } core.Error.Println(string(debug.Stack())) } }() core.Trace.Println("rtmp accept", c.RemoteAddr()) conn := protocol.NewRtmpConnection(c, v.wc) defer conn.Close() if err := v.cycle(conn); !core.IsNormalQuit(err) { core.Warn.Println("ignore error when cycle rtmp. err is", err) return } core.Info.Println("rtmp cycle ok.") return }) }
func (v *Rtmp) serve(c net.Conn) { // use gfork to serve the connection. v.wc.GFork("", func(wc core.WorkerContainer) { defer func() { if r := recover(); r != nil { if !core.IsNormalQuit(r) { core.Warn.Println("rtmp ignore", r) } core.Error.Println(string(debug.Stack())) } }() defer func() { if err := c.Close(); err != nil { core.Info.Println("ignore close failed. err is", err) } }() // for tcp connections. if c, ok := c.(*net.TCPConn); ok { // set TCP_NODELAY to false for performance issue. // TODO: FIXME: config it. // TODO: FIXME: refine for the realtime streaming. if err := c.SetNoDelay(false); err != nil { core.Error.Println("set TCP_NODELAY failed. err is", err) return } } core.Trace.Println("rtmp accept", c.RemoteAddr()) conn := protocol.NewRtmpConnection(c, v.wc) defer conn.Close() if err := v.cycle(conn); err != nil { if !core.IsNormalQuit(err) && !IsControlError(err) { core.Warn.Println("ignore error when cycle rtmp. err is", err) } else { core.Info.Println("rtmp cycle ok.") } return } return }) }
func (s *Server) GFork(name string, f func(core.WorkerContainer)) { s.wg.Add(1) go func() { defer s.wg.Done() defer func() { if r := recover(); r != nil { if !core.IsNormalQuit(r) { core.Warn.Println("rtmp ignore", r) } core.Error.Println(string(debug.Stack())) s.Quit() } }() f(s) if name != "" { core.Trace.Println(name, "worker terminated.") } }() }
func (v *Rtmp) cycle(conn *protocol.RtmpConnection) (err error) { r := conn.Req // handshake with client. if err = conn.Handshake(); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("rtmp handshake failed. err is", err) } return } core.Info.Println("rtmp handshake ok.") // expoect connect app. if err = conn.ExpectConnectApp(r); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("rtmp connnect app failed. err is", err) } return } core.Info.Println("rtmp connect app ok, tcUrl is", r.TcUrl) if err = conn.SetWindowAckSize(uint32(2.5 * 1000 * 1000)); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("rtmp set ack size failed. err is", err) } return } if err = conn.SetPeerBandwidth(uint32(2.5*1000*1000), uint8(2)); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("rtmp set peer bandwidth failed. err is", err) } return } // do bandwidth test if connect to the vhost which is for bandwidth check. // TODO: FIXME: support bandwidth check. // do token traverse before serve it. // @see https://github.com/ossrs/srs/pull/239 // TODO: FIXME: support edge token tranverse. // set chunk size to larger. // set the chunk size before any larger response greater than 128, // to make OBS happy, @see https://github.com/ossrs/srs/issues/454 // TODO: FIXME: support set chunk size. // response the client connect ok and onBWDone. if err = conn.ResponseConnectApp(); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("response connect app failed. err is", err) } return } if err = conn.OnBwDone(); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("response onBWDone failed. err is", err) } return } // identify the client, publish or play. if r.Type, r.Stream, r.Duration, err = conn.Identify(); err != nil { if !core.IsNormalQuit(err) { core.Error.Println("identify client failed. err is", err) } return } core.Trace.Println(fmt.Sprintf( "client identified, type=%s, stream_name=%s, duration=%.2f", r.Type, r.Stream, r.Duration)) // reparse the request by connect and play/publish. if err = r.Reparse(); err != nil { core.Error.Println("reparse request failed. err is", err) return } // security check // TODO: FIXME: implements it. // set the TCP_NODELAY to false for high performance. // or set tot true for realtime stream. // TODO: FIXME: implements it. // check vhost. // for standard rtmp, the vhost specified in connectApp(tcUrl), // while some new client specifies the vhost in stream. // for example, // connect("rtmp://vhost/app"), specified in tcUrl. // connect("rtmp://ip/app?vhost=vhost"), specified in tcUrl. // connect("rtmp://ip/app") && play("stream?vhost=vhost"), specified in stream. var vhost *core.Vhost if vhost, err = core.Conf.Vhost(r.Vhost); err != nil { core.Error.Println("check vhost failed, vhost is", r.Vhost, "and err is", err) return } else if r.Vhost != vhost.Name { core.Trace.Println("redirect vhost", r.Vhost, "to", vhost.Name) r.Vhost = vhost.Name } var agent core.Agent if conn.Req.Type.IsPlay() { // TODO: FIXME: implements it. } else if conn.Req.Type.IsPublish() { if agent, err = Manager.NewRtmpPublishAgent(conn, v.wc); err != nil { core.Error.Println("create rtmp publish agent failed. err is", err) return } } else { core.Warn.Println("close invalid", conn.Req.Type, "client") return } // always create the agent when work done. defer func() { if err := agent.Close(); err != nil { core.Warn.Println("ignore agent close failed. err is", err) } }() if err = agent.Open(); err != nil { core.Warn.Println("ignore rtmp publish agent open failed. err is", err) return } if err = agent.Work(); err != nil { core.Warn.Println("ignore rtmp publish agent work failed. err is", err) return } return }