func (v *AgentManager) NewRtmpPublishAgent(conn *protocol.RtmpConnection, wc core.WorkerContainer) (core.Agent, error) { v.lock.Lock() defer v.lock.Unlock() // finger the source agent out, which dup to other agent. var ok bool var dup core.Agent if dup, ok = v.sources[conn.Req.Uri()]; !ok { dup = NewDupAgent() v.sources[conn.Req.Uri()] = dup if err := dup.Open(); err != nil { core.Error.Println("open dup agent failed. err is", err) return nil, err } // start async work for dup worker. wait := make(chan bool, 1) core.Recover("", func() (err error) { wait <- true if err = dup.Work(); err != nil { core.Error.Println("dup agent work failed. err is", err) return } return }) <-wait } // when dup source not nil, then the source is using. if dup.Source().GetSink() != nil { err := AgentBusyError core.Error.Println("source busy. err is", err) return nil, err } // create the publish agent r := &RtmpPublishAgent{ conn: conn, wc: wc, } // tie the publish agent to dup source. if err := dup.Source().Tie(r.Sink()); err != nil { core.Error.Println("tie agent publish sink to dup source failed. err is", err) return nil, err } return r, nil }
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 }