|
|
|
@ -380,112 +380,85 @@ func (pa *path) runInner() error { |
|
|
|
for { |
|
|
|
for { |
|
|
|
select { |
|
|
|
select { |
|
|
|
case <-pa.onDemandStaticSourceReadyTimer.C: |
|
|
|
case <-pa.onDemandStaticSourceReadyTimer.C: |
|
|
|
for _, req := range pa.describeRequestsOnHold { |
|
|
|
pa.doOnDemandStaticSourceReadyTimer() |
|
|
|
req.res <- pathDescribeRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceStop() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case <-pa.onDemandStaticSourceCloseTimer.C: |
|
|
|
case <-pa.onDemandStaticSourceCloseTimer.C: |
|
|
|
pa.setNotReady() |
|
|
|
pa.doOnDemandStaticSourceCloseTimer() |
|
|
|
pa.onDemandStaticSourceStop() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case <-pa.onDemandPublisherReadyTimer.C: |
|
|
|
case <-pa.onDemandPublisherReadyTimer.C: |
|
|
|
for _, req := range pa.describeRequestsOnHold { |
|
|
|
pa.doOnDemandPublisherReadyTimer() |
|
|
|
req.res <- pathDescribeRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStopPublisher() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case <-pa.onDemandPublisherCloseTimer.C: |
|
|
|
case <-pa.onDemandPublisherCloseTimer.C: |
|
|
|
pa.onDemandStopPublisher() |
|
|
|
pa.doOnDemandPublisherCloseTimer() |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case newConf := <-pa.chReloadConf: |
|
|
|
case newConf := <-pa.chReloadConf: |
|
|
|
if pa.conf.HasStaticSource() { |
|
|
|
pa.doReloadConf(newConf) |
|
|
|
go pa.source.(*sourceStatic).reloadConf(newConf) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.confMutex.Lock() |
|
|
|
|
|
|
|
pa.conf = newConf |
|
|
|
|
|
|
|
pa.confMutex.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chSourceStaticSetReady: |
|
|
|
case req := <-pa.chSourceStaticSetReady: |
|
|
|
pa.handleSourceStaticSetReady(req) |
|
|
|
pa.doSourceStaticSetReady(req) |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chSourceStaticSetNotReady: |
|
|
|
case req := <-pa.chSourceStaticSetNotReady: |
|
|
|
pa.handleSourceStaticSetNotReady(req) |
|
|
|
pa.doSourceStaticSetNotReady(req) |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chDescribe: |
|
|
|
case req := <-pa.chDescribe: |
|
|
|
pa.handleDescribe(req) |
|
|
|
pa.doDescribe(req) |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chRemovePublisher: |
|
|
|
case req := <-pa.chRemovePublisher: |
|
|
|
pa.handleRemovePublisher(req) |
|
|
|
pa.doRemovePublisher(req) |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chAddPublisher: |
|
|
|
case req := <-pa.chAddPublisher: |
|
|
|
pa.handleAddPublisher(req) |
|
|
|
pa.doAddPublisher(req) |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chStartPublisher: |
|
|
|
case req := <-pa.chStartPublisher: |
|
|
|
pa.handleStartPublisher(req) |
|
|
|
pa.doStartPublisher(req) |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chStopPublisher: |
|
|
|
case req := <-pa.chStopPublisher: |
|
|
|
pa.handleStopPublisher(req) |
|
|
|
pa.doStopPublisher(req) |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chAddReader: |
|
|
|
case req := <-pa.chAddReader: |
|
|
|
pa.handleAddReader(req) |
|
|
|
pa.doAddReader(req) |
|
|
|
|
|
|
|
|
|
|
|
if pa.shouldClose() { |
|
|
|
if pa.shouldClose() { |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
return fmt.Errorf("not in use") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chRemoveReader: |
|
|
|
case req := <-pa.chRemoveReader: |
|
|
|
pa.handleRemoveReader(req) |
|
|
|
pa.doRemoveReader(req) |
|
|
|
|
|
|
|
|
|
|
|
case req := <-pa.chAPIPathsGet: |
|
|
|
case req := <-pa.chAPIPathsGet: |
|
|
|
pa.handleAPIPathsGet(req) |
|
|
|
pa.doAPIPathsGet(req) |
|
|
|
|
|
|
|
|
|
|
|
case <-pa.ctx.Done(): |
|
|
|
case <-pa.ctx.Done(): |
|
|
|
return fmt.Errorf("terminated") |
|
|
|
return fmt.Errorf("terminated") |
|
|
|
@ -493,167 +466,54 @@ func (pa *path) runInner() error { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) shouldClose() bool { |
|
|
|
func (pa *path) doOnDemandStaticSourceReadyTimer() { |
|
|
|
return pa.conf.Regexp != nil && |
|
|
|
for _, req := range pa.describeRequestsOnHold { |
|
|
|
pa.source == nil && |
|
|
|
req.res <- pathDescribeRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
len(pa.readers) == 0 && |
|
|
|
|
|
|
|
len(pa.describeRequestsOnHold) == 0 && |
|
|
|
|
|
|
|
len(pa.readerAddRequestsOnHold) == 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) externalCmdEnv() externalcmd.Environment { |
|
|
|
|
|
|
|
_, port, _ := net.SplitHostPort(pa.rtspAddress) |
|
|
|
|
|
|
|
env := externalcmd.Environment{ |
|
|
|
|
|
|
|
"MTX_PATH": pa.name, |
|
|
|
|
|
|
|
"RTSP_PATH": pa.name, // deprecated
|
|
|
|
|
|
|
|
"RTSP_PORT": port, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(pa.matches) > 1 { |
|
|
|
|
|
|
|
for i, ma := range pa.matches[1:] { |
|
|
|
|
|
|
|
env["G"+strconv.FormatInt(int64(i+1), 10)] = ma |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return env |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceStart() { |
|
|
|
|
|
|
|
pa.source.(*sourceStatic).start() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceReadyTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceReadyTimer = time.NewTimer(time.Duration(pa.conf.SourceOnDemandStartTimeout)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateWaitingReady |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceScheduleClose() { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = time.NewTimer(time.Duration(pa.conf.SourceOnDemandCloseAfter)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateClosing |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceStop() { |
|
|
|
|
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateInitial |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.source.(*sourceStatic).stop() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStartPublisher() { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command started") |
|
|
|
|
|
|
|
pa.onDemandCmd = externalcmd.NewCmd( |
|
|
|
|
|
|
|
pa.externalCmdPool, |
|
|
|
|
|
|
|
pa.conf.RunOnDemand, |
|
|
|
|
|
|
|
pa.conf.RunOnDemandRestart, |
|
|
|
|
|
|
|
pa.externalCmdEnv(), |
|
|
|
|
|
|
|
func(err error) { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command exited: %v", err) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherReadyTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherReadyTimer = time.NewTimer(time.Duration(pa.conf.RunOnDemandStartTimeout)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateWaitingReady |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandPublisherScheduleClose() { |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer = time.NewTimer(time.Duration(pa.conf.RunOnDemandCloseAfter)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateClosing |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStopPublisher() { |
|
|
|
|
|
|
|
if pa.source != nil { |
|
|
|
|
|
|
|
pa.source.(publisher).close() |
|
|
|
|
|
|
|
pa.doPublisherRemove() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateClosing { |
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
req.res <- pathAddReaderRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
pa.onDemandPublisherCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateInitial |
|
|
|
pa.onDemandStaticSourceStop() |
|
|
|
|
|
|
|
|
|
|
|
if pa.onDemandCmd != nil { |
|
|
|
|
|
|
|
pa.onDemandCmd.Close() |
|
|
|
|
|
|
|
pa.onDemandCmd = nil |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command stopped") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) setReady(desc *description.Session, allocateEncoder bool) error { |
|
|
|
func (pa *path) doOnDemandStaticSourceCloseTimer() { |
|
|
|
stream, err := stream.New( |
|
|
|
pa.setNotReady() |
|
|
|
pa.udpMaxPayloadSize, |
|
|
|
pa.onDemandStaticSourceStop() |
|
|
|
desc, |
|
|
|
|
|
|
|
allocateEncoder, |
|
|
|
|
|
|
|
pa.bytesReceived, |
|
|
|
|
|
|
|
newLimitedLogger(pa.source), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.stream = stream |
|
|
|
|
|
|
|
pa.readyTime = time.Now() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.conf.RunOnReady != "" { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command started") |
|
|
|
|
|
|
|
pa.onReadyCmd = externalcmd.NewCmd( |
|
|
|
|
|
|
|
pa.externalCmdPool, |
|
|
|
|
|
|
|
pa.conf.RunOnReady, |
|
|
|
|
|
|
|
pa.conf.RunOnReadyRestart, |
|
|
|
|
|
|
|
pa.externalCmdEnv(), |
|
|
|
|
|
|
|
func(err error) { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command exited: %v", err) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.parent.pathReady(pa) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) setNotReady() { |
|
|
|
func (pa *path) doOnDemandPublisherReadyTimer() { |
|
|
|
pa.parent.pathNotReady(pa) |
|
|
|
for _, req := range pa.describeRequestsOnHold { |
|
|
|
|
|
|
|
req.res <- pathDescribeRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
for r := range pa.readers { |
|
|
|
|
|
|
|
pa.doRemoveReader(r) |
|
|
|
|
|
|
|
r.close() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
if pa.onReadyCmd != nil { |
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
pa.onReadyCmd.Close() |
|
|
|
req.res <- pathAddReaderRes{err: fmt.Errorf("source of path '%s' has timed out", pa.name)} |
|
|
|
pa.onReadyCmd = nil |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command stopped") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
if pa.stream != nil { |
|
|
|
pa.onDemandStopPublisher() |
|
|
|
pa.stream.Close() |
|
|
|
|
|
|
|
pa.stream = nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) doRemoveReader(r reader) { |
|
|
|
func (pa *path) doOnDemandPublisherCloseTimer() { |
|
|
|
delete(pa.readers, r) |
|
|
|
pa.onDemandStopPublisher() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) doPublisherRemove() { |
|
|
|
func (pa *path) doReloadConf(newConf *conf.PathConf) { |
|
|
|
if pa.stream != nil { |
|
|
|
if pa.conf.HasStaticSource() { |
|
|
|
pa.setNotReady() |
|
|
|
go pa.source.(*sourceStatic).reloadConf(newConf) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pa.source = nil |
|
|
|
pa.confMutex.Lock() |
|
|
|
|
|
|
|
pa.conf = newConf |
|
|
|
|
|
|
|
pa.confMutex.Unlock() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleSourceStaticSetReady(req pathSourceStaticSetReadyReq) { |
|
|
|
func (pa *path) doSourceStaticSetReady(req pathSourceStaticSetReadyReq) { |
|
|
|
err := pa.setReady(req.desc, req.generateRTPPackets) |
|
|
|
err := pa.setReady(req.desc, req.generateRTPPackets) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
req.res <- pathSourceStaticSetReadyRes{err: err} |
|
|
|
req.res <- pathSourceStaticSetReadyRes{err: err} |
|
|
|
@ -674,7 +534,7 @@ func (pa *path) handleSourceStaticSetReady(req pathSourceStaticSetReadyReq) { |
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
pa.handleAddReaderPost(req) |
|
|
|
pa.addReaderPost(req) |
|
|
|
} |
|
|
|
} |
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
} |
|
|
|
} |
|
|
|
@ -682,7 +542,7 @@ func (pa *path) handleSourceStaticSetReady(req pathSourceStaticSetReadyReq) { |
|
|
|
req.res <- pathSourceStaticSetReadyRes{stream: pa.stream} |
|
|
|
req.res <- pathSourceStaticSetReadyRes{stream: pa.stream} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleSourceStaticSetNotReady(req pathSourceStaticSetNotReadyReq) { |
|
|
|
func (pa *path) doSourceStaticSetNotReady(req pathSourceStaticSetNotReadyReq) { |
|
|
|
pa.setNotReady() |
|
|
|
pa.setNotReady() |
|
|
|
|
|
|
|
|
|
|
|
// send response before calling onDemandStaticSourceStop()
|
|
|
|
// send response before calling onDemandStaticSourceStop()
|
|
|
|
@ -694,7 +554,7 @@ func (pa *path) handleSourceStaticSetNotReady(req pathSourceStaticSetNotReadyReq |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleDescribe(req pathDescribeReq) { |
|
|
|
func (pa *path) doDescribe(req pathDescribeReq) { |
|
|
|
if _, ok := pa.source.(*sourceRedirect); ok { |
|
|
|
if _, ok := pa.source.(*sourceRedirect); ok { |
|
|
|
req.res <- pathDescribeRes{ |
|
|
|
req.res <- pathDescribeRes{ |
|
|
|
redirect: pa.conf.SourceRedirect, |
|
|
|
redirect: pa.conf.SourceRedirect, |
|
|
|
@ -745,14 +605,14 @@ func (pa *path) handleDescribe(req pathDescribeReq) { |
|
|
|
req.res <- pathDescribeRes{err: errPathNoOnePublishing{pathName: pa.name}} |
|
|
|
req.res <- pathDescribeRes{err: errPathNoOnePublishing{pathName: pa.name}} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleRemovePublisher(req pathRemovePublisherReq) { |
|
|
|
func (pa *path) doRemovePublisher(req pathRemovePublisherReq) { |
|
|
|
if pa.source == req.author { |
|
|
|
if pa.source == req.author { |
|
|
|
pa.doPublisherRemove() |
|
|
|
pa.executeRemovePublisher() |
|
|
|
} |
|
|
|
} |
|
|
|
close(req.res) |
|
|
|
close(req.res) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleAddPublisher(req pathAddPublisherReq) { |
|
|
|
func (pa *path) doAddPublisher(req pathAddPublisherReq) { |
|
|
|
if pa.conf.Source != "publisher" { |
|
|
|
if pa.conf.Source != "publisher" { |
|
|
|
req.res <- pathAddPublisherRes{ |
|
|
|
req.res <- pathAddPublisherRes{ |
|
|
|
err: fmt.Errorf("can't publish to path '%s' since 'source' is not 'publisher'", pa.name), |
|
|
|
err: fmt.Errorf("can't publish to path '%s' since 'source' is not 'publisher'", pa.name), |
|
|
|
@ -768,7 +628,7 @@ func (pa *path) handleAddPublisher(req pathAddPublisherReq) { |
|
|
|
|
|
|
|
|
|
|
|
pa.Log(logger.Info, "closing existing publisher") |
|
|
|
pa.Log(logger.Info, "closing existing publisher") |
|
|
|
pa.source.(publisher).close() |
|
|
|
pa.source.(publisher).close() |
|
|
|
pa.doPublisherRemove() |
|
|
|
pa.executeRemovePublisher() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pa.source = req.author |
|
|
|
pa.source = req.author |
|
|
|
@ -776,7 +636,7 @@ func (pa *path) handleAddPublisher(req pathAddPublisherReq) { |
|
|
|
req.res <- pathAddPublisherRes{path: pa} |
|
|
|
req.res <- pathAddPublisherRes{path: pa} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleStartPublisher(req pathStartPublisherReq) { |
|
|
|
func (pa *path) doStartPublisher(req pathStartPublisherReq) { |
|
|
|
if pa.source != req.author { |
|
|
|
if pa.source != req.author { |
|
|
|
req.res <- pathStartPublisherRes{err: fmt.Errorf("publisher is not assigned to this path anymore")} |
|
|
|
req.res <- pathStartPublisherRes{err: fmt.Errorf("publisher is not assigned to this path anymore")} |
|
|
|
return |
|
|
|
return |
|
|
|
@ -806,7 +666,7 @@ func (pa *path) handleStartPublisher(req pathStartPublisherReq) { |
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
pa.describeRequestsOnHold = nil |
|
|
|
|
|
|
|
|
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
for _, req := range pa.readerAddRequestsOnHold { |
|
|
|
pa.handleAddReaderPost(req) |
|
|
|
pa.addReaderPost(req) |
|
|
|
} |
|
|
|
} |
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
pa.readerAddRequestsOnHold = nil |
|
|
|
} |
|
|
|
} |
|
|
|
@ -814,35 +674,16 @@ func (pa *path) handleStartPublisher(req pathStartPublisherReq) { |
|
|
|
req.res <- pathStartPublisherRes{stream: pa.stream} |
|
|
|
req.res <- pathStartPublisherRes{stream: pa.stream} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleStopPublisher(req pathStopPublisherReq) { |
|
|
|
func (pa *path) doStopPublisher(req pathStopPublisherReq) { |
|
|
|
if req.author == pa.source && pa.stream != nil { |
|
|
|
if req.author == pa.source && pa.stream != nil { |
|
|
|
pa.setNotReady() |
|
|
|
pa.setNotReady() |
|
|
|
} |
|
|
|
} |
|
|
|
close(req.res) |
|
|
|
close(req.res) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleRemoveReader(req pathRemoveReaderReq) { |
|
|
|
func (pa *path) doAddReader(req pathAddReaderReq) { |
|
|
|
if _, ok := pa.readers[req.author]; ok { |
|
|
|
|
|
|
|
pa.doRemoveReader(req.author) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
close(req.res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(pa.readers) == 0 { |
|
|
|
|
|
|
|
if pa.conf.HasOnDemandStaticSource() { |
|
|
|
|
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateReady { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceScheduleClose() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if pa.conf.HasOnDemandPublisher() { |
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateReady { |
|
|
|
|
|
|
|
pa.onDemandPublisherScheduleClose() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleAddReader(req pathAddReaderReq) { |
|
|
|
|
|
|
|
if pa.stream != nil { |
|
|
|
if pa.stream != nil { |
|
|
|
pa.handleAddReaderPost(req) |
|
|
|
pa.addReaderPost(req) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -865,45 +706,26 @@ func (pa *path) handleAddReader(req pathAddReaderReq) { |
|
|
|
req.res <- pathAddReaderRes{err: errPathNoOnePublishing{pathName: pa.name}} |
|
|
|
req.res <- pathAddReaderRes{err: errPathNoOnePublishing{pathName: pa.name}} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleAddReaderPost(req pathAddReaderReq) { |
|
|
|
func (pa *path) doRemoveReader(req pathRemoveReaderReq) { |
|
|
|
if _, ok := pa.readers[req.author]; ok { |
|
|
|
if _, ok := pa.readers[req.author]; ok { |
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
pa.executeRemoveReader(req.author) |
|
|
|
path: pa, |
|
|
|
|
|
|
|
stream: pa.stream, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
close(req.res) |
|
|
|
|
|
|
|
|
|
|
|
if pa.conf.MaxReaders != 0 && len(pa.readers) >= pa.conf.MaxReaders { |
|
|
|
if len(pa.readers) == 0 { |
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
if pa.conf.HasOnDemandStaticSource() { |
|
|
|
err: fmt.Errorf("maximum reader count reached"), |
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateReady { |
|
|
|
} |
|
|
|
pa.onDemandStaticSourceScheduleClose() |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
} else if pa.conf.HasOnDemandPublisher() { |
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateReady { |
|
|
|
pa.readers[req.author] = struct{}{} |
|
|
|
pa.onDemandPublisherScheduleClose() |
|
|
|
|
|
|
|
} |
|
|
|
if pa.conf.HasOnDemandStaticSource() { |
|
|
|
|
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateReady |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if pa.conf.HasOnDemandPublisher() { |
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateReady |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
|
|
|
|
path: pa, |
|
|
|
|
|
|
|
stream: pa.stream, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) handleAPIPathsGet(req pathAPIPathsGetReq) { |
|
|
|
func (pa *path) doAPIPathsGet(req pathAPIPathsGetReq) { |
|
|
|
req.res <- pathAPIPathsGetRes{ |
|
|
|
req.res <- pathAPIPathsGetRes{ |
|
|
|
data: &apiPath{ |
|
|
|
data: &apiPath{ |
|
|
|
Name: pa.name, |
|
|
|
Name: pa.name, |
|
|
|
@ -942,6 +764,204 @@ func (pa *path) handleAPIPathsGet(req pathAPIPathsGetReq) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) shouldClose() bool { |
|
|
|
|
|
|
|
return pa.conf.Regexp != nil && |
|
|
|
|
|
|
|
pa.source == nil && |
|
|
|
|
|
|
|
len(pa.readers) == 0 && |
|
|
|
|
|
|
|
len(pa.describeRequestsOnHold) == 0 && |
|
|
|
|
|
|
|
len(pa.readerAddRequestsOnHold) == 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) externalCmdEnv() externalcmd.Environment { |
|
|
|
|
|
|
|
_, port, _ := net.SplitHostPort(pa.rtspAddress) |
|
|
|
|
|
|
|
env := externalcmd.Environment{ |
|
|
|
|
|
|
|
"MTX_PATH": pa.name, |
|
|
|
|
|
|
|
"RTSP_PATH": pa.name, // deprecated
|
|
|
|
|
|
|
|
"RTSP_PORT": port, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(pa.matches) > 1 { |
|
|
|
|
|
|
|
for i, ma := range pa.matches[1:] { |
|
|
|
|
|
|
|
env["G"+strconv.FormatInt(int64(i+1), 10)] = ma |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return env |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceStart() { |
|
|
|
|
|
|
|
pa.source.(*sourceStatic).start() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceReadyTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceReadyTimer = time.NewTimer(time.Duration(pa.conf.SourceOnDemandStartTimeout)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateWaitingReady |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceScheduleClose() { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = time.NewTimer(time.Duration(pa.conf.SourceOnDemandCloseAfter)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateClosing |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStaticSourceStop() { |
|
|
|
|
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateInitial |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.source.(*sourceStatic).stop() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStartPublisher() { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command started") |
|
|
|
|
|
|
|
pa.onDemandCmd = externalcmd.NewCmd( |
|
|
|
|
|
|
|
pa.externalCmdPool, |
|
|
|
|
|
|
|
pa.conf.RunOnDemand, |
|
|
|
|
|
|
|
pa.conf.RunOnDemandRestart, |
|
|
|
|
|
|
|
pa.externalCmdEnv(), |
|
|
|
|
|
|
|
func(err error) { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command exited: %v", err) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherReadyTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherReadyTimer = time.NewTimer(time.Duration(pa.conf.RunOnDemandStartTimeout)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateWaitingReady |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandPublisherScheduleClose() { |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer = time.NewTimer(time.Duration(pa.conf.RunOnDemandCloseAfter)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateClosing |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) onDemandStopPublisher() { |
|
|
|
|
|
|
|
if pa.source != nil { |
|
|
|
|
|
|
|
pa.source.(publisher).close() |
|
|
|
|
|
|
|
pa.executeRemovePublisher() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateInitial |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.onDemandCmd != nil { |
|
|
|
|
|
|
|
pa.onDemandCmd.Close() |
|
|
|
|
|
|
|
pa.onDemandCmd = nil |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnDemand command stopped") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) setReady(desc *description.Session, allocateEncoder bool) error { |
|
|
|
|
|
|
|
var err error |
|
|
|
|
|
|
|
pa.stream, err = stream.New( |
|
|
|
|
|
|
|
pa.udpMaxPayloadSize, |
|
|
|
|
|
|
|
desc, |
|
|
|
|
|
|
|
allocateEncoder, |
|
|
|
|
|
|
|
pa.bytesReceived, |
|
|
|
|
|
|
|
logger.NewLimitedLogger(pa.source), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.readyTime = time.Now() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.conf.RunOnReady != "" { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command started") |
|
|
|
|
|
|
|
pa.onReadyCmd = externalcmd.NewCmd( |
|
|
|
|
|
|
|
pa.externalCmdPool, |
|
|
|
|
|
|
|
pa.conf.RunOnReady, |
|
|
|
|
|
|
|
pa.conf.RunOnReadyRestart, |
|
|
|
|
|
|
|
pa.externalCmdEnv(), |
|
|
|
|
|
|
|
func(err error) { |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command exited: %v", err) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.parent.pathReady(pa) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) setNotReady() { |
|
|
|
|
|
|
|
pa.parent.pathNotReady(pa) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for r := range pa.readers { |
|
|
|
|
|
|
|
pa.executeRemoveReader(r) |
|
|
|
|
|
|
|
r.close() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.onReadyCmd != nil { |
|
|
|
|
|
|
|
pa.onReadyCmd.Close() |
|
|
|
|
|
|
|
pa.onReadyCmd = nil |
|
|
|
|
|
|
|
pa.Log(logger.Info, "runOnReady command stopped") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.stream != nil { |
|
|
|
|
|
|
|
pa.stream.Close() |
|
|
|
|
|
|
|
pa.stream = nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) executeRemoveReader(r reader) { |
|
|
|
|
|
|
|
delete(pa.readers, r) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) executeRemovePublisher() { |
|
|
|
|
|
|
|
if pa.stream != nil { |
|
|
|
|
|
|
|
pa.setNotReady() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.source = nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (pa *path) addReaderPost(req pathAddReaderReq) { |
|
|
|
|
|
|
|
if _, ok := pa.readers[req.author]; ok { |
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
|
|
|
|
path: pa, |
|
|
|
|
|
|
|
stream: pa.stream, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.conf.MaxReaders != 0 && len(pa.readers) >= pa.conf.MaxReaders { |
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
|
|
|
|
err: fmt.Errorf("maximum reader count reached"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa.readers[req.author] = struct{}{} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pa.conf.HasOnDemandStaticSource() { |
|
|
|
|
|
|
|
if pa.onDemandStaticSourceState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandStaticSourceState = pathOnDemandStateReady |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandStaticSourceCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if pa.conf.HasOnDemandPublisher() { |
|
|
|
|
|
|
|
if pa.onDemandPublisherState == pathOnDemandStateClosing { |
|
|
|
|
|
|
|
pa.onDemandPublisherState = pathOnDemandStateReady |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer.Stop() |
|
|
|
|
|
|
|
pa.onDemandPublisherCloseTimer = newEmptyTimer() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.res <- pathAddReaderRes{ |
|
|
|
|
|
|
|
path: pa, |
|
|
|
|
|
|
|
stream: pa.stream, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// reloadConf is called by pathManager.
|
|
|
|
// reloadConf is called by pathManager.
|
|
|
|
func (pa *path) reloadConf(newConf *conf.PathConf) { |
|
|
|
func (pa *path) reloadConf(newConf *conf.PathConf) { |
|
|
|
select { |
|
|
|
select { |
|
|
|
|