|
|
|
|
@ -2,6 +2,7 @@ package converterhls
@@ -2,6 +2,7 @@ package converterhls
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"bytes" |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"net" |
|
|
|
|
@ -146,6 +147,7 @@ type Converter struct {
@@ -146,6 +147,7 @@ type Converter struct {
|
|
|
|
|
// in
|
|
|
|
|
request chan Request |
|
|
|
|
terminate chan struct{} |
|
|
|
|
parentTerminate chan struct{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// New allocates a Converter.
|
|
|
|
|
@ -171,7 +173,8 @@ func New(
@@ -171,7 +173,8 @@ func New(
|
|
|
|
|
lastRequestTime: time.Now().Unix(), |
|
|
|
|
tsByName: make(map[string]*tsFile), |
|
|
|
|
request: make(chan Request), |
|
|
|
|
terminate: make(chan struct{}), |
|
|
|
|
terminate: make(chan struct{}, 1), |
|
|
|
|
parentTerminate: make(chan struct{}), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.log(logger.Info, "opened") |
|
|
|
|
@ -185,12 +188,15 @@ func New(
@@ -185,12 +188,15 @@ func New(
|
|
|
|
|
// ParentClose closes a Converter.
|
|
|
|
|
func (c *Converter) ParentClose() { |
|
|
|
|
c.log(logger.Info, "closed") |
|
|
|
|
close(c.terminate) |
|
|
|
|
close(c.parentTerminate) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Close closes a Converter.
|
|
|
|
|
func (c *Converter) Close() { |
|
|
|
|
c.parent.OnConverterClose(c) |
|
|
|
|
select { |
|
|
|
|
case c.terminate <- struct{}{}: |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsReadPublisher implements readpublisher.ReadPublisher.
|
|
|
|
|
@ -211,15 +217,42 @@ func (c *Converter) PathName() string {
@@ -211,15 +217,42 @@ func (c *Converter) PathName() string {
|
|
|
|
|
func (c *Converter) run() { |
|
|
|
|
defer c.wg.Done() |
|
|
|
|
|
|
|
|
|
var videoTrack *gortsplib.Track |
|
|
|
|
var h264SPS []byte |
|
|
|
|
var h264PPS []byte |
|
|
|
|
var h264Decoder *rtph264.Decoder |
|
|
|
|
var audioTrack *gortsplib.Track |
|
|
|
|
var aacConfig rtpaac.MPEG4AudioConfig |
|
|
|
|
var aacDecoder *rtpaac.Decoder |
|
|
|
|
ctx, cancel := context.WithCancel(context.Background()) |
|
|
|
|
runErr := make(chan error) |
|
|
|
|
go func() { |
|
|
|
|
runErr <- c.runInner(ctx) |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
select { |
|
|
|
|
case err := <-runErr: |
|
|
|
|
cancel() |
|
|
|
|
c.log(logger.Info, "ERR: %s", err) |
|
|
|
|
|
|
|
|
|
case <-c.terminate: |
|
|
|
|
cancel() |
|
|
|
|
<-runErr |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
go func() { |
|
|
|
|
for req := range c.request { |
|
|
|
|
req.W.WriteHeader(http.StatusInternalServerError) |
|
|
|
|
req.Res <- nil |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
err := func() error { |
|
|
|
|
if c.path != nil { |
|
|
|
|
res := make(chan struct{}) |
|
|
|
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
|
|
|
|
<-res |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.parent.OnConverterClose(c) |
|
|
|
|
<-c.parentTerminate |
|
|
|
|
|
|
|
|
|
close(c.request) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Converter) runInner(ctx context.Context) error { |
|
|
|
|
pres := make(chan readpublisher.SetupPlayRes) |
|
|
|
|
c.pathMan.OnReadPublisherSetupPlay(readpublisher.SetupPlayReq{ |
|
|
|
|
Author: c, |
|
|
|
|
@ -235,6 +268,13 @@ func (c *Converter) run() {
@@ -235,6 +268,13 @@ func (c *Converter) run() {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.path = res.Path |
|
|
|
|
var videoTrack *gortsplib.Track |
|
|
|
|
var h264SPS []byte |
|
|
|
|
var h264PPS []byte |
|
|
|
|
var h264Decoder *rtph264.Decoder |
|
|
|
|
var audioTrack *gortsplib.Track |
|
|
|
|
var aacConfig rtpaac.MPEG4AudioConfig |
|
|
|
|
var aacDecoder *rtpaac.Decoder |
|
|
|
|
|
|
|
|
|
for i, t := range res.Tracks { |
|
|
|
|
if t.IsH264() { |
|
|
|
|
@ -275,31 +315,6 @@ func (c *Converter) run() {
@@ -275,31 +315,6 @@ func (c *Converter) run() {
|
|
|
|
|
return fmt.Errorf("unable to find a video or audio track") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
}() |
|
|
|
|
if err != nil { |
|
|
|
|
c.log(logger.Info, "ERR: %s", err) |
|
|
|
|
|
|
|
|
|
go func() { |
|
|
|
|
for req := range c.request { |
|
|
|
|
req.W.WriteHeader(http.StatusNotFound) |
|
|
|
|
req.Res <- nil |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
if c.path != nil { |
|
|
|
|
res := make(chan struct{}) |
|
|
|
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
|
|
|
|
<-res |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.parent.OnConverterClose(c) |
|
|
|
|
<-c.terminate |
|
|
|
|
|
|
|
|
|
close(c.request) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
curTSFile := newTSFile(videoTrack, audioTrack) |
|
|
|
|
c.tsByName[curTSFile.Name()] = curTSFile |
|
|
|
|
c.tsQueue = append(c.tsQueue, curTSFile) |
|
|
|
|
@ -308,12 +323,13 @@ func (c *Converter) run() {
@@ -308,12 +323,13 @@ func (c *Converter) run() {
|
|
|
|
|
curTSFile.Close() |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
requestDone := make(chan struct{}) |
|
|
|
|
go c.runRequestHandler(requestDone) |
|
|
|
|
requestHandlerTerminate := make(chan struct{}) |
|
|
|
|
requestHandlerDone := make(chan struct{}) |
|
|
|
|
go c.runRequestHandler(requestHandlerTerminate, requestHandlerDone) |
|
|
|
|
|
|
|
|
|
defer func() { |
|
|
|
|
close(c.request) |
|
|
|
|
<-requestDone |
|
|
|
|
close(requestHandlerTerminate) |
|
|
|
|
<-requestHandlerDone |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
c.ringBuffer = ringbuffer.New(uint64(c.readBufferCount)) |
|
|
|
|
@ -478,46 +494,29 @@ func (c *Converter) run() {
@@ -478,46 +494,29 @@ func (c *Converter) run() {
|
|
|
|
|
case <-closeCheckTicker.C: |
|
|
|
|
t := time.Unix(atomic.LoadInt64(&c.lastRequestTime), 0) |
|
|
|
|
if time.Since(t) >= closeAfterInactivity { |
|
|
|
|
|
|
|
|
|
c.ringBuffer.Close() |
|
|
|
|
<-writerDone |
|
|
|
|
|
|
|
|
|
res := make(chan struct{}) |
|
|
|
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
|
|
|
|
<-res |
|
|
|
|
|
|
|
|
|
c.parent.OnConverterClose(c) |
|
|
|
|
<-c.terminate |
|
|
|
|
return |
|
|
|
|
return fmt.Errorf("TODO") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case err := <-writerDone: |
|
|
|
|
c.log(logger.Info, "ERR: %s", err) |
|
|
|
|
|
|
|
|
|
res := make(chan struct{}) |
|
|
|
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
|
|
|
|
<-res |
|
|
|
|
|
|
|
|
|
c.parent.OnConverterClose(c) |
|
|
|
|
<-c.terminate |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
case <-c.terminate: |
|
|
|
|
res := make(chan struct{}) |
|
|
|
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
|
|
|
|
<-res |
|
|
|
|
return err |
|
|
|
|
|
|
|
|
|
c.ringBuffer.Close() |
|
|
|
|
<-writerDone |
|
|
|
|
return |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
return fmt.Errorf("TODO") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Converter) runRequestHandler(done chan struct{}) { |
|
|
|
|
func (c *Converter) runRequestHandler(terminate chan struct{}, done chan struct{}) { |
|
|
|
|
defer close(done) |
|
|
|
|
|
|
|
|
|
for preq := range c.request { |
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case <-terminate: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
case preq := <-c.request: |
|
|
|
|
req := preq |
|
|
|
|
|
|
|
|
|
atomic.StoreInt64(&c.lastRequestTime, time.Now().Unix()) |
|
|
|
|
@ -592,6 +591,7 @@ func (c *Converter) runRequestHandler(done chan struct{}) {
@@ -592,6 +591,7 @@ func (c *Converter) runRequestHandler(done chan struct{}) {
|
|
|
|
|
req.Res <- nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// OnRequest is called by serverhls.Server.
|
|
|
|
|
|