package stream import ( "github.com/lkmio/avformat/utils" ) // TransStream 将AVPacket封装成传输流 type TransStream interface { GetID() TransStreamID SetID(id TransStreamID) Input(packet utils.AVPacket) ([][]byte, int64, bool, error) AddTrack(stream utils.AVStream) error TrackCount() int GetTracks() []utils.AVStream WriteHeader() error // GetProtocol 返回输出流协议 GetProtocol() TransStreamProtocol // ReadExtraData 获取封装后的编码器扩展数据 ReadExtraData(timestamp int64) ([][]byte, int64, error) // ReadKeyFrameBuffer 读取已经缓存的包含关键视频帧的输出流 ReadKeyFrameBuffer() ([][]byte, int64, error) Close() ([][]byte, int64, error) ClearOutStreamBuffer() AppendOutStreamBuffer(buffer []byte) // OutStreamBufferCapacity 返回输出流缓冲区的容量大小, 输出流缓冲区同时作为向sink推流的发送缓冲区, 容量大小决定向sink异步推流的队列大小; OutStreamBufferCapacity() int IsExistVideo() bool } type BaseTransStream struct { //muxer stream.Muxer ID TransStreamID Tracks []utils.AVStream Completed bool ExistVideo bool Protocol TransStreamProtocol OutBuffer [][]byte // 完成封装的输出流队列 OutBufferSize int } func (t *BaseTransStream) GetID() TransStreamID { return t.ID } func (t *BaseTransStream) SetID(id TransStreamID) { t.ID = id } func (t *BaseTransStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error) { return nil, -1, false, nil } func (t *BaseTransStream) AddTrack(stream utils.AVStream) error { t.Tracks = append(t.Tracks, stream) if utils.AVMediaTypeVideo == stream.Type() { t.ExistVideo = true } return nil } func (t *BaseTransStream) Close() ([][]byte, int64, error) { return nil, 0, nil } func (t *BaseTransStream) GetProtocol() TransStreamProtocol { return t.Protocol } func (t *BaseTransStream) ClearOutStreamBuffer() { t.OutBufferSize = 0 } func (t *BaseTransStream) AppendOutStreamBuffer(buffer []byte) { if t.OutBufferSize+1 > len(t.OutBuffer) { // 扩容 size := (t.OutBufferSize + 1) * 2 newBuffer := make([][]byte, size) for i := 0; i < t.OutBufferSize; i++ { newBuffer[i] = t.OutBuffer[i] } t.OutBuffer = newBuffer } t.OutBuffer[t.OutBufferSize] = buffer t.OutBufferSize++ } func (t *BaseTransStream) OutStreamBufferCapacity() int { return 0 } func (t *BaseTransStream) TrackCount() int { return len(t.Tracks) } func (t *BaseTransStream) GetTracks() []utils.AVStream { return t.Tracks } func (t *BaseTransStream) IsExistVideo() bool { return t.ExistVideo } func (t *BaseTransStream) ReadExtraData(timestamp int64) ([][]byte, int64, error) { return nil, 0, nil } func (t *BaseTransStream) ReadKeyFrameBuffer() ([][]byte, int64, error) { return nil, 0, nil } type TCPTransStream struct { BaseTransStream // 合并写内存泄露问题: 推流结束后, mwBuffer的data一直释放不掉, 只有拉流全部断开之后, 才会释放该内存. // 起初怀疑是代码层哪儿有问题, 但是测试发现如果将合并写切片再拷贝一次发送 给sink, 推流结束后,mwBuffer的data内存块释放没问题, 只有拷贝的内存块未释放. 所以排除了代码层造成内存泄露的可能性. // 看来是conn在write后还会持有data. 查阅代码发现, 的确如此. 向fd发送数据前buffer会引用data, 但是后续没有赋值为nil, 取消引用. https://github.com/golang/go/blob/d38f1d13fa413436d38d86fe86d6a146be44bb84/src/internal/poll/fd_windows.go#L694 MWBuffer MergeWritingBuffer //合并写缓冲区, 同时作为用户态的发送缓冲区 } func (t *TCPTransStream) OutStreamBufferCapacity() int { utils.Assert(t.MWBuffer != nil) return t.MWBuffer.Capacity() }