yapingcat / gomedia

golang library for rtmp, mpeg-ts,mpeg-ps,flv,mp4,ogg,rtsp
MIT License
383 stars 66 forks source link

httpflv使用问题 #70

Open zxs943023403 opened 1 year ago

zxs943023403 commented 1 year ago

使用httpflv进行播放的时候,通过<-request.Context().Done()方法获取到客户端在播放结束前关闭了当前链接,此时http处理方法返回会导致panic panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x1110196]

goroutine 12 [running]: bufio.(Writer).Flush(0xc00007ea40) /sdk/go1.17/src/bufio/bufio.go:607 +0x56 bufio.(Writer).Write(0xc00007ea40, {0xc00025e000, 0xc00025e000, 0xc000028d75}) /sdk/go1.17/src/bufio/bufio.go:643 +0xd8 net/http.(response).write(0xc000152380, 0x90a, {0xc00025e000, 0x90a, 0xa80}, {0x0, 0x0}) /sdk/go1.17/src/net/http/server.go:1592 +0x21e net/http.(response).Write(0x0, {0xc00025e000, 0x1052306, 0x1064be0}) /sdk/go1.17/src/net/http/server.go:1550 +0x30 github.com/yapingcat/gomedia/go-flv.(FlvWriter).writeVideo(0xc00000ec18, {0xc000233b00, 0xc000257d80, 0x10601ce}, 0x87b20a44, 0xc7c) /Desktop/gopath/pkg/mod/github.com/yapingcat/gomedia@v0.0.0-20230222121919-c67df405bf33/go-flv/flv-file.go:373 +0x9f github.com/yapingcat/gomedia/go-flv.(FlvWriter).WriteH264(0x989680, {0xc000233b00, 0xc000544230, 0x0}, 0x4bfb, 0x4bfb) /Desktop/gopath/pkg/mod/github.com/yapingcat/gomedia@v0.0.0-20230222121919-c67df405bf33/go-flv/flv-file.go:354 +0x155 是否可以在flvwriter增加一个writer是否已关闭的判断,或者手动关闭的方法?

yapingcat commented 1 year ago

目前gomedia不管理网络连接,连接交由调用方来管理

针对这个问题,可以把http.ResponseWriter封装一下,在封装的接口里面实现上述的逻辑判断 类似下面这个简单实现

type HttpWriterWrapper struct {
    closed    chan struct{}
    w         http.ResponseWriter
    sendChan  chan []byte
    die       chan struct{}
    lastError error
    once      sync.Once
}

func NewHttpWriterWrapper(w http.ResponseWriter, closed chan struct{}) *HttpWriterWrapper {
    return &HttpWriterWrapper{
        closed:   closed,
        w:        w,
        sendChan: make(chan []byte, 100),
        die:      make(chan struct{}),
    }
}

func (w *HttpWriterWrapper) Start() {
    go w.writeImpl()
}

func (w *HttpWriterWrapper) Stop() {
    w.once.Do(func() {
        close(w.closed)
    })
}

func (w *HttpWriterWrapper) Write(b []byte) (int, error) {
    if w.lastError != nil {
        return 0, w.lastError
    }
    w.sendChan <- b
    return len(b), nil
}

func (w *HttpWriterWrapper) writeImpl() {
    for {
        select {
        case <-w.closed:
            // reset connection by client
            // stop send
            // release some resource
            w.lastError = io.EOF
            return
        case b := <-w.sendChan:
            _, w.lastError = w.w.Write(b)
        case <-w.die:
            w.lastError = errors.New("closed by custom")
            return
        }
    }
}

//使用的时候
write_wrapper := NewHttpWriterWrapper(w,r.Context().Done())
writer := flv.CreateFlvWriter(write_wrapper)