mirror of
https://github.com/lkmio/lkm.git
synced 2025-09-27 11:32:26 +08:00
封装hls
This commit is contained in:
@@ -1,37 +1,204 @@
|
|||||||
package hls
|
package hls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/yangjiechina/avformat/libmpeg"
|
"github.com/yangjiechina/avformat/libmpeg"
|
||||||
"github.com/yangjiechina/avformat/utils"
|
"github.com/yangjiechina/avformat/utils"
|
||||||
"github.com/yangjiechina/live-server/stream"
|
"github.com/yangjiechina/live-server/stream"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type tsContext struct {
|
||||||
|
segmentSeq int
|
||||||
|
writeBuffer []byte
|
||||||
|
writeBufferSize int
|
||||||
|
|
||||||
|
duration int
|
||||||
|
playlistLength int
|
||||||
|
url string
|
||||||
|
path string
|
||||||
|
|
||||||
|
file *os.File
|
||||||
|
}
|
||||||
|
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
stream.TransStreamImpl
|
stream.TransStreamImpl
|
||||||
muxer libmpeg.TSMuxer
|
muxer libmpeg.TSMuxer
|
||||||
|
context *tsContext
|
||||||
|
|
||||||
|
m3u8 M3U8Writer
|
||||||
|
url string
|
||||||
|
m3u8Name string
|
||||||
|
tsFormat string
|
||||||
|
dir string
|
||||||
|
m3u8File *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransStream(segmentDuration, playlistLength int) stream.ITransStream {
|
// NewTransStream 创建HLS传输流
|
||||||
return &Stream{muxer: libmpeg.NewTSMuxer()}
|
// @url url前缀
|
||||||
|
// @m3u8Name m3u8文件名
|
||||||
|
// @tsFormat ts文件格式, 例如: test_%d.ts
|
||||||
|
// @parentDir 保存切片的绝对路径. mu38和ts切片放在同一目录下, 目录地址使用parentDir+urlPrefix
|
||||||
|
// @segmentDuration 单个切片时长
|
||||||
|
// @playlistLength 缓存多少个切片
|
||||||
|
func NewTransStream(url, m3u8Name, tsFormat, dir string, segmentDuration, playlistLength int) (stream.ITransStream, error) {
|
||||||
|
//创建文件夹
|
||||||
|
if err := os.MkdirAll(dir, 0666); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m3u8Path := fmt.Sprintf("%s/%s", dir, m3u8Name)
|
||||||
|
file, err := os.OpenFile(m3u8Path, os.O_CREATE|os.O_RDWR, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_ := &Stream{
|
||||||
|
url: url,
|
||||||
|
m3u8Name: m3u8Name,
|
||||||
|
tsFormat: tsFormat,
|
||||||
|
dir: dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
muxer := libmpeg.NewTSMuxer()
|
||||||
|
muxer.SetWriteHandler(stream_.onTSWrite)
|
||||||
|
muxer.SetAllocHandler(stream_.onTSAlloc)
|
||||||
|
|
||||||
|
stream_.context = &tsContext{
|
||||||
|
segmentSeq: 0,
|
||||||
|
writeBuffer: make([]byte, 1024*1024),
|
||||||
|
writeBufferSize: 0,
|
||||||
|
duration: segmentDuration,
|
||||||
|
playlistLength: playlistLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_.muxer = muxer
|
||||||
|
stream_.m3u8 = NewM3U8Writer(playlistLength)
|
||||||
|
stream_.m3u8File = file
|
||||||
|
return stream_, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Stream) Input(packet utils.AVPacket) {
|
func (t *Stream) Input(packet utils.AVPacket) error {
|
||||||
if utils.AVMediaTypeVideo == packet.MediaType() {
|
if packet.Index() >= t.muxer.TrackCount() {
|
||||||
if packet.KeyFrame() {
|
return fmt.Errorf("track not available")
|
||||||
t.Tracks[packet.Index()].AnnexBExtraData()
|
}
|
||||||
t.muxer.Input()
|
|
||||||
|
if utils.AVMediaTypeVideo == packet.MediaType() && packet.KeyFrame() || t.context.file == nil {
|
||||||
|
if err := t.createSegment(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if utils.AVMediaTypeVideo == packet.MediaType() {
|
||||||
|
return t.muxer.Input(packet.Index(), packet.AnnexBPacketData(), packet.Pts()*90, packet.Dts()*90, packet.KeyFrame())
|
||||||
|
} else {
|
||||||
|
return t.muxer.Input(packet.Index(), packet.Data(), packet.Pts()*90, packet.Dts()*90, packet.KeyFrame())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Stream) AddTrack(stream utils.AVStream) {
|
func (t *Stream) AddTrack(stream utils.AVStream) error {
|
||||||
t.TransStreamImpl.AddTrack(stream)
|
err := t.TransStreamImpl.AddTrack(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
t.muxer.AddTrack(stream.Type(), stream.CodecId())
|
if stream.CodecId() == utils.AVCodecIdH264 {
|
||||||
|
data, err := stream.AnnexBExtraData()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = t.muxer.AddTrack(stream.Type(), stream.CodecId(), data)
|
||||||
|
} else {
|
||||||
|
_, err = t.muxer.AddTrack(stream.Type(), stream.CodecId(), stream.Extra())
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Stream) WriteHeader() error {
|
func (t *Stream) WriteHeader() error {
|
||||||
t.muxer.WriteHeader()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Stream) onTSWrite(data []byte) {
|
||||||
|
t.context.writeBufferSize += len(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Stream) onTSAlloc(size int) []byte {
|
||||||
|
n := len(t.context.writeBuffer) - t.context.writeBufferSize
|
||||||
|
if n < size {
|
||||||
|
_, _ = t.context.file.Write(t.context.writeBuffer[:t.context.writeBufferSize])
|
||||||
|
t.context.writeBufferSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.context.writeBuffer[t.context.writeBufferSize : t.context.writeBufferSize+size]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Stream) flushSegment() error {
|
||||||
|
//将剩余数据写入缓冲区
|
||||||
|
if t.context.writeBufferSize > 0 {
|
||||||
|
_, _ = t.context.file.Write(t.context.writeBuffer[:t.context.writeBufferSize])
|
||||||
|
t.context.writeBufferSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.context.file.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := float32(t.muxer.Duration()) / 90000
|
||||||
|
t.m3u8.AddSegment(duration, t.context.url, t.context.segmentSeq)
|
||||||
|
|
||||||
|
//更新m3u8
|
||||||
|
if _, err := t.m3u8File.Seek(0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := t.m3u8File.Truncate(0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m3u8Txt := t.m3u8.ToString()
|
||||||
|
if _, err := t.m3u8File.Write([]byte(m3u8Txt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Stream) createSegment() error {
|
||||||
|
if t.context.file != nil {
|
||||||
|
err := t.flushSegment()
|
||||||
|
t.context.segmentSeq++
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tsName := fmt.Sprintf(t.tsFormat, t.context.segmentSeq)
|
||||||
|
t.context.path = fmt.Sprintf("%s%s", t.dir, tsName)
|
||||||
|
t.context.url = fmt.Sprintf("%s%s", t.url, tsName)
|
||||||
|
file, err := os.OpenFile(t.context.path, os.O_WRONLY|os.O_CREATE, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.context.file = file
|
||||||
|
|
||||||
|
t.muxer.Reset()
|
||||||
|
err = t.muxer.WriteHeader()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Stream) Close() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if t.context.file != nil {
|
||||||
|
err = t.flushSegment()
|
||||||
|
err = t.context.file.Close()
|
||||||
|
t.context.file = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.m3u8File != nil {
|
||||||
|
err = t.m3u8File.Close()
|
||||||
|
t.m3u8File = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@@ -47,7 +47,7 @@ type M3U8Writer interface {
|
|||||||
|
|
||||||
func NewM3U8Writer(len int) M3U8Writer {
|
func NewM3U8Writer(len int) M3U8Writer {
|
||||||
return &m3u8Writer{
|
return &m3u8Writer{
|
||||||
stringBuffer: bytes.NewBuffer(make([]byte, 1024*10)),
|
stringBuffer: bytes.NewBuffer(make([]byte, 0, 1024*10)),
|
||||||
playlist: stream.NewQueue(len),
|
playlist: stream.NewQueue(len),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,7 @@ func (m *m3u8Writer) ToString() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.stringBuffer.Reset()
|
||||||
m.stringBuffer.WriteString("#EXTM3U\r\n")
|
m.stringBuffer.WriteString("#EXTM3U\r\n")
|
||||||
//暂时只实现第三个版本
|
//暂时只实现第三个版本
|
||||||
m.stringBuffer.WriteString("#EXT-X-VERSION:3\r\n")
|
m.stringBuffer.WriteString("#EXT-X-VERSION:3\r\n")
|
||||||
|
18
main.go
18
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/yangjiechina/live-server/hls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -12,18 +13,25 @@ import (
|
|||||||
"github.com/yangjiechina/live-server/stream"
|
"github.com/yangjiechina/live-server/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateTransStream(protocol stream.Protocol, streams []utils.AVStream) stream.ITransStream {
|
func CreateTransStream(source stream.ISource, protocol stream.Protocol, streams []utils.AVStream) stream.ITransStream {
|
||||||
if stream.ProtocolRtmp == protocol {
|
if stream.ProtocolRtmp == protocol {
|
||||||
return rtmp.NewTransStream(librtmp.ChunkSize)
|
return rtmp.NewTransStream(librtmp.ChunkSize)
|
||||||
|
} else if stream.ProtocolHls == protocol {
|
||||||
|
id := source.Id()
|
||||||
|
m3u8Name := id + ".m3u8"
|
||||||
|
tsFormat := id + "_%d.ts"
|
||||||
|
|
||||||
|
transStream, err := hls.NewTransStream("/live/hls/", m3u8Name, tsFormat, "../tmp/", 2, 10)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transStream
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestStream(sourceId string) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
stream.TransStreamFactory = CreateTransStream
|
stream.TransStreamFactory = CreateTransStream
|
||||||
}
|
}
|
||||||
|
@@ -79,7 +79,7 @@ func (s *sessionImpl) Close() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := s.handle.(*Publisher)
|
_, ok := s.handle.(*publisher)
|
||||||
if ok {
|
if ok {
|
||||||
if s.isPublisher {
|
if s.isPublisher {
|
||||||
s.handle.(*publisher).AddEvent(stream.SourceEventClose, nil)
|
s.handle.(*publisher).AddEvent(stream.SourceEventClose, nil)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package rtmp
|
package rtmp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/yangjiechina/avformat/libflv"
|
"github.com/yangjiechina/avformat/libflv"
|
||||||
"github.com/yangjiechina/avformat/librtmp"
|
"github.com/yangjiechina/avformat/librtmp"
|
||||||
"github.com/yangjiechina/avformat/utils"
|
"github.com/yangjiechina/avformat/utils"
|
||||||
@@ -46,7 +47,7 @@ func NewTransStream(chunkSize int) stream.ITransStream {
|
|||||||
return transStream
|
return transStream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) Input(packet utils.AVPacket) {
|
func (t *TransStream) Input(packet utils.AVPacket) error {
|
||||||
utils.Assert(t.TransStreamImpl.Completed)
|
utils.Assert(t.TransStreamImpl.Completed)
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
@@ -66,7 +67,7 @@ func (t *TransStream) Input(packet utils.AVPacket) {
|
|||||||
//首帧必须要视频关键帧
|
//首帧必须要视频关键帧
|
||||||
if !t.firstVideoPacket {
|
if !t.firstVideoPacket {
|
||||||
if !packet.KeyFrame() {
|
if !packet.KeyFrame() {
|
||||||
return
|
return fmt.Errorf("the first video frame must be a keyframe")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.firstVideoPacket = true
|
t.firstVideoPacket = true
|
||||||
@@ -83,7 +84,7 @@ func (t *TransStream) Input(packet utils.AVPacket) {
|
|||||||
//即不开启GOP缓存又不合并发送. 直接使用AVPacket的预留头封装发送
|
//即不开启GOP缓存又不合并发送. 直接使用AVPacket的预留头封装发送
|
||||||
if !stream.AppConfig.GOPCache || t.onlyAudio {
|
if !stream.AppConfig.GOPCache || t.onlyAudio {
|
||||||
//首帧视频帧必须要关键帧
|
//首帧视频帧必须要关键帧
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if videoKey {
|
if videoKey {
|
||||||
@@ -168,11 +169,11 @@ func (t *TransStream) Input(packet utils.AVPacket) {
|
|||||||
t.incompleteSinks = nil
|
t.incompleteSinks = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.segmentDuration < stream.AppConfig.MergeWriteLatency {
|
if t.segmentDuration < stream.AppConfig.MergeWriteLatency {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
head, tail := t.memoryPool[0].Data()
|
head, tail := t.memoryPool[0].Data()
|
||||||
@@ -188,9 +189,11 @@ func (t *TransStream) Input(packet utils.AVPacket) {
|
|||||||
if t.segmentOffset > len(head) && t.memoryPool[1] != nil && !t.memoryPool[1].Empty() {
|
if t.segmentOffset > len(head) && t.memoryPool[1] != nil && !t.memoryPool[1].Empty() {
|
||||||
t.memoryPool[1].Clear()
|
t.memoryPool[1].Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) AddSink(sink stream.ISink) {
|
func (t *TransStream) AddSink(sink stream.ISink) error {
|
||||||
utils.Assert(t.headerSize > 0)
|
utils.Assert(t.headerSize > 0)
|
||||||
|
|
||||||
t.TransStreamImpl.AddSink(sink)
|
t.TransStreamImpl.AddSink(sink)
|
||||||
@@ -198,7 +201,7 @@ func (t *TransStream) AddSink(sink stream.ISink) {
|
|||||||
sink.Input(t.header[:t.headerSize])
|
sink.Input(t.header[:t.headerSize])
|
||||||
|
|
||||||
if !stream.AppConfig.GOPCache || t.onlyAudio {
|
if !stream.AppConfig.GOPCache || t.onlyAudio {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//发送当前内存池已有的合并写切片
|
//发送当前内存池已有的合并写切片
|
||||||
@@ -207,7 +210,7 @@ func (t *TransStream) AddSink(sink stream.ISink) {
|
|||||||
utils.Assert(len(data) > 0)
|
utils.Assert(len(data) > 0)
|
||||||
utils.Assert(len(tail) == 0)
|
utils.Assert(len(tail) == 0)
|
||||||
sink.Input(data[:t.segmentOffset])
|
sink.Input(data[:t.segmentOffset])
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//发送上一组GOP
|
//发送上一组GOP
|
||||||
@@ -216,7 +219,7 @@ func (t *TransStream) AddSink(sink stream.ISink) {
|
|||||||
utils.Assert(len(data) > 0)
|
utils.Assert(len(data) > 0)
|
||||||
utils.Assert(len(tail) == 0)
|
utils.Assert(len(tail) == 0)
|
||||||
sink.Input(data)
|
sink.Input(data)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//不足一个合并写切片, 有多少发多少
|
//不足一个合并写切片, 有多少发多少
|
||||||
@@ -226,6 +229,8 @@ func (t *TransStream) AddSink(sink stream.ISink) {
|
|||||||
sink.Input(data)
|
sink.Input(data)
|
||||||
t.incompleteSinks = append(t.incompleteSinks, sink)
|
t.incompleteSinks = append(t.incompleteSinks, sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) WriteHeader() error {
|
func (t *TransStream) WriteHeader() error {
|
||||||
|
@@ -34,7 +34,7 @@ const (
|
|||||||
HookEventPlayDone = HookEvent(0x4)
|
HookEventPlayDone = HookEvent(0x4)
|
||||||
HookEventRecord = HookEvent(0x5)
|
HookEventRecord = HookEvent(0x5)
|
||||||
HookEventIdleTimeout = HookEvent(0x6)
|
HookEventIdleTimeout = HookEvent(0x6)
|
||||||
HookEventRecvTimeout = HookEvent(0x6)
|
HookEventRecvTimeout = HookEvent(0x7)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 每个通知的时间都需要携带的字段
|
// 每个通知的时间都需要携带的字段
|
||||||
|
@@ -154,6 +154,8 @@ type SourceImpl struct {
|
|||||||
closeEvent chan byte
|
closeEvent chan byte
|
||||||
playingEventQueue chan ISink
|
playingEventQueue chan ISink
|
||||||
playingDoneEventQueue chan ISink
|
playingDoneEventQueue chan ISink
|
||||||
|
|
||||||
|
testTransStream ITransStream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceImpl) Id() string {
|
func (s *SourceImpl) Id() string {
|
||||||
@@ -169,6 +171,13 @@ func (s *SourceImpl) Init() {
|
|||||||
s.closeEvent = make(chan byte)
|
s.closeEvent = make(chan byte)
|
||||||
s.playingEventQueue = make(chan ISink, 128)
|
s.playingEventQueue = make(chan ISink, 128)
|
||||||
s.playingDoneEventQueue = make(chan ISink, 128)
|
s.playingDoneEventQueue = make(chan ISink, 128)
|
||||||
|
|
||||||
|
if s.transStreams == nil {
|
||||||
|
s.transStreams = make(map[TransStreamId]ITransStream, 10)
|
||||||
|
}
|
||||||
|
//测试传输流
|
||||||
|
s.testTransStream = TransStreamFactory(s, ProtocolHls, nil)
|
||||||
|
s.transStreams[0x100] = s.testTransStream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceImpl) LoopEvent() {
|
func (s *SourceImpl) LoopEvent() {
|
||||||
@@ -191,6 +200,10 @@ func (s *SourceImpl) LoopEvent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SourceImpl) Input(data []byte) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SourceImpl) OriginStreams() []utils.AVStream {
|
func (s *SourceImpl) OriginStreams() []utils.AVStream {
|
||||||
return s.originStreams.All()
|
return s.originStreams.All()
|
||||||
}
|
}
|
||||||
@@ -304,11 +317,11 @@ func (s *SourceImpl) AddSink(sink ISink) bool {
|
|||||||
transStreamId := GenerateTransStreamId(sink.Protocol(), streams[:size]...)
|
transStreamId := GenerateTransStreamId(sink.Protocol(), streams[:size]...)
|
||||||
transStream, ok := s.transStreams[transStreamId]
|
transStream, ok := s.transStreams[transStreamId]
|
||||||
if !ok {
|
if !ok {
|
||||||
//创建一个新的传输流
|
|
||||||
transStream = TransStreamFactory(sink.Protocol(), streams[:size])
|
|
||||||
if s.transStreams == nil {
|
if s.transStreams == nil {
|
||||||
s.transStreams = make(map[TransStreamId]ITransStream, 10)
|
s.transStreams = make(map[TransStreamId]ITransStream, 10)
|
||||||
}
|
}
|
||||||
|
//创建一个新的传输流
|
||||||
|
transStream = TransStreamFactory(s, sink.Protocol(), streams[:size])
|
||||||
s.transStreams[transStreamId] = transStream
|
s.transStreams[transStreamId] = transStream
|
||||||
|
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
@@ -433,6 +446,14 @@ func (s *SourceImpl) writeHeader() {
|
|||||||
for _, sink := range sinks {
|
for _, sink := range sinks {
|
||||||
s.AddSink(sink)
|
s.AddSink(sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.testTransStream != nil {
|
||||||
|
for _, stream_ := range s.originStreams.All() {
|
||||||
|
s.testTransStream.AddTrack(stream_)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.testTransStream.WriteHeader()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceImpl) OnDeMuxStreamDone() {
|
func (s *SourceImpl) OnDeMuxStreamDone() {
|
||||||
@@ -445,6 +466,7 @@ func (s *SourceImpl) OnDeMuxPacket(packet utils.AVPacket) {
|
|||||||
buffer.AddPacket(packet, packet.KeyFrame(), packet.Dts())
|
buffer.AddPacket(packet, packet.KeyFrame(), packet.Dts())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//分发给各个传输流
|
||||||
for _, stream := range s.transStreams {
|
for _, stream := range s.transStreams {
|
||||||
stream.Input(packet)
|
stream.Input(packet)
|
||||||
}
|
}
|
||||||
|
@@ -62,23 +62,25 @@ func GenerateTransStreamId(protocol Protocol, ids ...utils.AVStream) TransStream
|
|||||||
return TransStreamId(streamId)
|
return TransStreamId(streamId)
|
||||||
}
|
}
|
||||||
|
|
||||||
var TransStreamFactory func(protocol Protocol, streams []utils.AVStream) ITransStream
|
var TransStreamFactory func(source ISource, protocol Protocol, streams []utils.AVStream) ITransStream
|
||||||
|
|
||||||
// ITransStream 讲AVPacket封装成传输流,转发给各个Sink
|
// ITransStream 讲AVPacket封装成传输流,转发给各个Sink
|
||||||
type ITransStream interface {
|
type ITransStream interface {
|
||||||
Input(packet utils.AVPacket)
|
Input(packet utils.AVPacket) error
|
||||||
|
|
||||||
AddTrack(stream utils.AVStream)
|
AddTrack(stream utils.AVStream) error
|
||||||
|
|
||||||
WriteHeader() error
|
WriteHeader() error
|
||||||
|
|
||||||
AddSink(sink ISink)
|
AddSink(sink ISink) error
|
||||||
|
|
||||||
RemoveSink(id SinkId) (ISink, bool)
|
RemoveSink(id SinkId) (ISink, bool)
|
||||||
|
|
||||||
PopAllSink(handler func(sink ISink))
|
PopAllSink(handler func(sink ISink))
|
||||||
|
|
||||||
AllSink() []ISink
|
AllSink() []ISink
|
||||||
|
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type TransStreamImpl struct {
|
type TransStreamImpl struct {
|
||||||
@@ -87,18 +89,24 @@ type TransStreamImpl struct {
|
|||||||
Tracks []utils.AVStream
|
Tracks []utils.AVStream
|
||||||
transBuffer MemoryPool //每个TransStream也缓存封装后的流
|
transBuffer MemoryPool //每个TransStream也缓存封装后的流
|
||||||
Completed bool
|
Completed bool
|
||||||
|
existVideo bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStreamImpl) Input(packet utils.AVPacket) {
|
func (t *TransStreamImpl) Input(packet utils.AVPacket) error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStreamImpl) AddTrack(stream utils.AVStream) {
|
func (t *TransStreamImpl) AddTrack(stream utils.AVStream) error {
|
||||||
t.Tracks = append(t.Tracks, stream)
|
t.Tracks = append(t.Tracks, stream)
|
||||||
|
if utils.AVMediaTypeVideo == stream.Type() {
|
||||||
|
t.existVideo = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStreamImpl) AddSink(sink ISink) {
|
func (t *TransStreamImpl) AddSink(sink ISink) error {
|
||||||
t.Sinks[sink.Id()] = sink
|
t.Sinks[sink.Id()] = sink
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStreamImpl) RemoveSink(id SinkId) (ISink, bool) {
|
func (t *TransStreamImpl) RemoveSink(id SinkId) (ISink, bool) {
|
||||||
@@ -122,3 +130,7 @@ func (t *TransStreamImpl) AllSink() []ISink {
|
|||||||
//TODO implement me
|
//TODO implement me
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TransStreamImpl) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user