diff --git a/av/format/flv/aac_packetizer.go b/av/format/flv/aac_packetizer.go new file mode 100644 index 0000000..5ed0e3d --- /dev/null +++ b/av/format/flv/aac_packetizer.go @@ -0,0 +1,94 @@ +// Copyright (c) 2019,CAOHONGJU All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package flv + +import "github.com/cnotch/ipchub/av/codec" + +type aacPacketizer struct { + meta *codec.AudioMeta + dataTemplate *AudioData + tagWriter TagWriter + spsMuxed bool +} + +func NewAacPacketizer(meta *codec.AudioMeta, tagWriter TagWriter) Packetizer { + ap := &aacPacketizer{ + meta: meta, + tagWriter: tagWriter, + } + ap.prepareTemplate() + return ap +} + +func (ap *aacPacketizer) prepareTemplate() { + audioData := &AudioData{ + SoundFormat: SoundFormatAAC, + AACPacketType: AACPacketTypeRawData, + Body: nil, + } + + switch ap.meta.SampleRate { + case 5512: + audioData.SoundRate = SoundRate5512 + case 11025: + audioData.SoundRate = SoundRate11025 + case 22050: + audioData.SoundRate = SoundRate22050 + case 44100: + audioData.SoundRate = SoundRate44100 + default: + audioData.SoundRate = SoundRate44100 + } + + if ap.meta.SampleSize == 8 { + audioData.SoundSize = SoundeSize8bit + } else { + audioData.SoundSize = SoundeSize16bit + } + + if ap.meta.Channels > 1 { + audioData.SoundType = SoundTypeStereo + } else { + audioData.SoundType = SoundTypeMono + } + + ap.dataTemplate = audioData +} + +func (ap *aacPacketizer) PacketizeSequenceHeader() error { + if ap.spsMuxed { + return nil + } + + ap.spsMuxed = true + audioData := *ap.dataTemplate + audioData.AACPacketType = AACPacketTypeSequenceHeader + audioData.Body = ap.meta.Sps + data, _ := audioData.Marshal() + + tag := &Tag{ + TagType: TagTypeAudio, + DataSize: uint32(len(data)), + Timestamp: 0, + StreamID: 0, + Data: data, + } + return ap.tagWriter.WriteFlvTag(tag) +} + +func (ap *aacPacketizer) Packetize(basePts int64, frame *codec.Frame) error { + audioData := *ap.dataTemplate + audioData.Body = frame.Payload + data, _ := audioData.Marshal() + + tag := &Tag{ + TagType: TagTypeAudio, + DataSize: uint32(len(data)), + Timestamp: uint32(frame.AbsTimestamp-basePts) + ptsDelay, + StreamID: 0, + Data: data, + } + return ap.tagWriter.WriteFlvTag(tag) +} diff --git a/av/format/flv/flv_test.go b/av/format/flv/flv_test.go index 181a190..f6a9fd7 100644 --- a/av/format/flv/flv_test.go +++ b/av/format/flv/flv_test.go @@ -40,7 +40,7 @@ func TestFlvWriter(t *testing.T) { var audio codec.AudioMeta sdp.ParseMetadata(string(sdpraw), &video, &audio) writer, err := NewWriter(out, 5) - flvMuxer := NewMuxerAvcAac(video, audio, writer, xlog.L()) + flvMuxer,_ := NewMuxer(&video, &audio, writer, xlog.L()) rtpDemuxer,_ := rtp.NewDemuxer(&video,&audio,flvMuxer, xlog.L()) channels := []int{int(rtp.ChannelVideo), int(rtp.ChannelVideoControl), int(rtp.ChannelAudio), int(rtp.ChannelAudioControl)} diff --git a/av/format/flv/h264_packetizer.go b/av/format/flv/h264_packetizer.go new file mode 100644 index 0000000..0ca65fe --- /dev/null +++ b/av/format/flv/h264_packetizer.go @@ -0,0 +1,111 @@ +// Copyright (c) 2019,CAOHONGJU All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package flv + +import ( + "github.com/cnotch/ipchub/av/codec" + "github.com/cnotch/ipchub/av/codec/h264" +) + +type h264Packetizer struct { + meta *codec.VideoMeta + tagWriter TagWriter + spsMuxed bool + nextDts float64 + dtsStep float64 +} + +func NewH264Packetizer(meta *codec.VideoMeta, tagWriter TagWriter) Packetizer { + h264p := &h264Packetizer{ + meta: meta, + tagWriter: tagWriter, + } + + if meta.FrameRate > 0 { + h264p.dtsStep = 1000.0 / meta.FrameRate + } + return h264p +} + +func (h264p *h264Packetizer) PacketizeSequenceHeader() error { + if h264p.spsMuxed { + return nil + } + + if !h264.MetadataIsReady(h264p.meta) { + // not enough + return nil + } + + h264p.spsMuxed = true + + if h264p.meta.FixedFrameRate { + h264p.dtsStep = 1000.0 / h264p.meta.FrameRate + } else { // TODO: + h264p.dtsStep = 1000.0 / 30 + } + + record := NewAVCDecoderConfigurationRecord(h264p.meta.Sps, h264p.meta.Pps) + body, _ := record.Marshal() + + videoData := &VideoData{ + FrameType: FrameTypeKeyFrame, + CodecID: CodecIDAVC, + AVCPacketType: AVCPacketTypeSequenceHeader, + CompositionTime: 0, + Body: body, + } + data, _ := videoData.Marshal() + + tag := &Tag{ + TagType: TagTypeVideo, + DataSize: uint32(len(data)), + Timestamp: 0, + StreamID: 0, + Data: data, + } + + return h264p.tagWriter.WriteFlvTag(tag) +} + +func (h264p *h264Packetizer) Packetize(basePts int64, frame *codec.Frame) error { + if frame.Payload[0]&0x1F == h264.NalSps { + return h264p.PacketizeSequenceHeader() + } + + if frame.Payload[0]&0x1F == h264.NalPps { + return h264p.PacketizeSequenceHeader() + } + + dts := int64(h264p.nextDts) + h264p.nextDts += h264p.dtsStep + pts := frame.AbsTimestamp - basePts + ptsDelay + if dts > pts { + pts = dts + } + + videoData := &VideoData{ + FrameType: FrameTypeInterFrame, + CodecID: CodecIDAVC, + AVCPacketType: AVCPacketTypeNALU, + CompositionTime: uint32(pts - dts), + Body: frame.Payload, + } + + if frame.Payload[0]&0x1F == h264.NalIdrSlice { + videoData.FrameType = FrameTypeKeyFrame + } + data, _ := videoData.Marshal() + + tag := &Tag{ + TagType: TagTypeVideo, + DataSize: uint32(len(data)), + Timestamp: uint32(dts), + StreamID: 0, + Data: data, + } + + return h264p.tagWriter.WriteFlvTag(tag) +} diff --git a/av/format/flv/muxer.go b/av/format/flv/muxer.go new file mode 100644 index 0000000..69bf7fb --- /dev/null +++ b/av/format/flv/muxer.go @@ -0,0 +1,222 @@ +// Copyright (c) 2019,CAOHONGJU All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package flv + +import ( + "fmt" + "runtime/debug" + "time" + + "github.com/cnotch/ipchub/av/codec" + "github.com/cnotch/ipchub/av/format/amf" + "github.com/cnotch/queue" + "github.com/cnotch/xlog" +) + +// Packetizer 封包器 +type Packetizer interface { + PacketizeSequenceHeader() error + Packetize(basePts int64, frame *codec.Frame) error +} + +type emptyPacketizer struct{} + +func (emptyPacketizer) PacketizeSequenceHeader() error { return nil } +func (emptyPacketizer) Packetize(basePts int64, frame *codec.Frame) error { return nil } + +// 网络播放时 PTS(Presentation Time Stamp)的延时 +// 影响视频 Tag 的 CTS 和音频的 DTS(Decoding Time Stamp) +const ( + ptsDelay = 1000 +) + +// Muxer flv muxer from av.Frame(H264[+AAC]) +type Muxer struct { + videoMeta *codec.VideoMeta + audioMeta *codec.AudioMeta + vp Packetizer + ap Packetizer + typeFlags byte + recvQueue *queue.SyncQueue + tagWriter TagWriter + closed bool + + logger *xlog.Logger // 日志对象 +} + +// NewMuxer . +func NewMuxer(videoMeta *codec.VideoMeta, audioMeta *codec.AudioMeta, tagWriter TagWriter, logger *xlog.Logger) (*Muxer, error) { + muxer := &Muxer{ + recvQueue: queue.NewSyncQueue(), + videoMeta: videoMeta, + audioMeta: audioMeta, + vp: emptyPacketizer{}, + ap: emptyPacketizer{}, + typeFlags: byte(TypeFlagsVideo), + tagWriter: tagWriter, + closed: false, + logger: logger, + } + switch videoMeta.Codec { + case "H264": + muxer.vp = NewH264Packetizer(videoMeta, tagWriter) + default: + return nil, fmt.Errorf("flv muxer unsupport video codec type:%s", videoMeta.Codec) + } + + if audioMeta.Codec == "AAC" { + muxer.typeFlags |= TypeFlagsAudio + muxer.ap = NewAacPacketizer(audioMeta, tagWriter) + } + + go muxer.process() + return muxer, nil +} + +// WriteFrame . +func (muxer *Muxer) WriteFrame(frame *codec.Frame) error { + muxer.recvQueue.Push(frame) + return nil +} + +// Close . +func (muxer *Muxer) Close() error { + if muxer.closed { + return nil + } + + muxer.closed = true + muxer.recvQueue.Signal() + return nil +} + +// TypeFlags 返回 flv header 中的 TypeFlags +func (muxer *Muxer) TypeFlags() byte { + return muxer.typeFlags +} + +func (muxer *Muxer) process() { + defer func() { + defer func() { // 避免 handler 再 panic + recover() + }() + + if r := recover(); r != nil { + muxer.logger.Errorf("flvmuxer routine panic;r = %v \n %s", r, debug.Stack()) + } + + // 尽早通知GC,回收内存 + muxer.recvQueue.Reset() + }() + + var basePts int64 + for !muxer.closed { + f := muxer.recvQueue.Pop() + if f == nil { + if !muxer.closed { + muxer.logger.Warn("flvmuxer:receive nil frame") + } + continue + } + + frame := f.(*codec.Frame) + if basePts == 0 { + basePts = frame.AbsTimestamp + muxer.muxMetadataTag() + muxer.vp.PacketizeSequenceHeader() + muxer.ap.PacketizeSequenceHeader() + } + + switch frame.MediaType { + case codec.MediaTypeVideo: + if err := muxer.vp.Packetize(basePts, frame); err != nil { + muxer.logger.Errorf("flvmuxer: muxVideoTag error - %s", err.Error()) + } + case codec.MediaTypeAudio: + if err := muxer.ap.Packetize(basePts, frame); err != nil { + muxer.logger.Errorf("flvmuxer: muxAudioTag error - %s", err.Error()) + } + default: + } + } +} + +func (muxer *Muxer) muxMetadataTag() error { + properties := make(amf.EcmaArray, 0, 12) + + properties = append(properties, + amf.ObjectProperty{ + Name: "creator", + Value: "ipchub stream media server"}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataCreationDate, + Value: time.Now().Format(time.RFC3339)}) + + if muxer.typeFlags&TypeFlagsAudio > 0 { + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataAudioCodecID, + Value: SoundFormatAAC}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataAudioDateRate, + Value: muxer.audioMeta.DataRate}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataAudioSampleRate, + Value: muxer.audioMeta.SampleRate}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataAudioSampleSize, + Value: muxer.audioMeta.SampleSize}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataStereo, + Value: muxer.audioMeta.Channels > 1}) + } + + vcodecID := CodecIDAVC + if muxer.videoMeta.Codec == "H265" { + vcodecID = CodecIDHEVC + } + + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataVideoCodecID, + Value: vcodecID}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataVideoDataRate, + Value: muxer.videoMeta.DataRate}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataFrameRate, + Value: muxer.videoMeta.FrameRate}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataWidth, + Value: muxer.videoMeta.Width}) + properties = append(properties, + amf.ObjectProperty{ + Name: MetaDataHeight, + Value: muxer.videoMeta.Height}) + + scriptData := ScriptData{ + Name: ScriptOnMetaData, + Value: properties, + } + data, _ := scriptData.Marshal() + + tag := &Tag{ + TagType: TagTypeAmf0Data, + DataSize: uint32(len(data)), + Timestamp: 0, + StreamID: 0, + Data: data, + } + + return muxer.tagWriter.WriteFlvTag(tag) +} diff --git a/av/format/flv/muxeravcaac.go b/av/format/flv/muxeravcaac.go deleted file mode 100644 index 3d55377..0000000 --- a/av/format/flv/muxeravcaac.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (c) 2019,CAOHONGJU All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package flv - -import ( - "runtime/debug" - "time" - - "github.com/cnotch/ipchub/av/codec" - "github.com/cnotch/ipchub/av/codec/h264" - "github.com/cnotch/ipchub/av/format/amf" - "github.com/cnotch/queue" - "github.com/cnotch/xlog" -) - -// 网络播放时 PTS(Presentation Time Stamp)的延时 -// 影响视频 Tag 的 CTS 和音频的 DTS(Decoding Time Stamp) -const ( - dtsDelay = 0 - ptsDelay = 1000 -) - -// MuxerAvcAac flv muxer from av.Frame(H264[+AAC]) -type MuxerAvcAac struct { - videoMeta codec.VideoMeta - audioMeta codec.AudioMeta - typeFlags byte - audioDataTemplate *AudioData - recvQueue *queue.SyncQueue - tagWriter TagWriter - closed bool - spsMuxed bool - basePts int64 - nextDts float64 - dtsStep float64 - logger *xlog.Logger // 日志对象 -} - -// NewMuxerAvcAac . -func NewMuxerAvcAac(videoMeta codec.VideoMeta, audioMeta codec.AudioMeta, tagWriter TagWriter, logger *xlog.Logger) *MuxerAvcAac { - muxer := &MuxerAvcAac{ - recvQueue: queue.NewSyncQueue(), - videoMeta: videoMeta, - audioMeta: audioMeta, - typeFlags: byte(TypeFlagsVideo), - tagWriter: tagWriter, - closed: false, - nextDts: dtsDelay, - logger: logger, - } - - if videoMeta.FrameRate > 0 { - muxer.dtsStep = 1000.0 / videoMeta.FrameRate - } - if audioMeta.Codec == "AAC" { - muxer.typeFlags |= TypeFlagsAudio - muxer.prepareTemplate() - } - - go muxer.process() - return muxer -} - -// WriteFrame . -func (muxer *MuxerAvcAac) WriteFrame(frame *codec.Frame) error { - muxer.recvQueue.Push(frame) - return nil -} - -// Close . -func (muxer *MuxerAvcAac) Close() error { - if muxer.closed { - return nil - } - - muxer.closed = true - muxer.recvQueue.Signal() - return nil -} - -// TypeFlags 返回 flv header 中的 TypeFlags -func (muxer *MuxerAvcAac) TypeFlags() byte { - return muxer.typeFlags -} - -func (muxer *MuxerAvcAac) process() { - defer func() { - defer func() { // 避免 handler 再 panic - recover() - }() - - if r := recover(); r != nil { - muxer.logger.Errorf("flvmuxer routine panic;r = %v \n %s", r, debug.Stack()) - } - - // 尽早通知GC,回收内存 - muxer.recvQueue.Reset() - }() - - muxer.muxMetadataTag() - muxer.muxSequenceHeaderTag() - - for !muxer.closed { - f := muxer.recvQueue.Pop() - if f == nil { - if !muxer.closed { - muxer.logger.Warn("flvmuxer:receive nil frame") - } - continue - } - - frame := f.(*codec.Frame) - if muxer.basePts == 0 { - muxer.basePts = frame.AbsTimestamp - } - - if frame.MediaType == codec.MediaTypeVideo { - if err := muxer.muxVideoTag(frame); err != nil { - muxer.logger.Errorf("flvmuxer: muxVideoTag error - %s", err.Error()) - } - } else { - if err := muxer.muxAudioTag(frame); err != nil { - muxer.logger.Errorf("flvmuxer: muxAudioTag error - %s", err.Error()) - } - } - } -} - -func (muxer *MuxerAvcAac) muxVideoTag(frame *codec.Frame) error { - if frame.Payload[0]&0x1F == h264.NalSps { - if len(muxer.videoMeta.Sps) == 0 { - muxer.videoMeta.Sps = frame.Payload - } - return muxer.muxSequenceHeaderTag() - } - - if frame.Payload[0]&0x1F == h264.NalPps { - if len(muxer.videoMeta.Pps) == 0 { - muxer.videoMeta.Pps = frame.Payload - } - return muxer.muxSequenceHeaderTag() - } - - dts := int64(muxer.nextDts) - muxer.nextDts += muxer.dtsStep - pts := frame.AbsTimestamp - muxer.basePts + ptsDelay - if dts > pts { - pts = dts - } - - videoData := &VideoData{ - FrameType: FrameTypeInterFrame, - CodecID: CodecIDAVC, - AVCPacketType: AVCPacketTypeNALU, - CompositionTime: uint32(pts - dts), - Body: frame.Payload, - } - - if frame.Payload[0]&0x1F == h264.NalIdrSlice { - videoData.FrameType = FrameTypeKeyFrame - } - data, _ := videoData.Marshal() - - tag := &Tag{ - TagType: TagTypeVideo, - DataSize: uint32(len(data)), - Timestamp: uint32(dts), - StreamID: 0, - Data: data, - } - - return muxer.tagWriter.WriteFlvTag(tag) -} - -func (muxer *MuxerAvcAac) muxAudioTag(frame *codec.Frame) error { - audioData := *muxer.audioDataTemplate - audioData.Body = frame.Payload - data, _ := audioData.Marshal() - - tag := &Tag{ - TagType: TagTypeAudio, - DataSize: uint32(len(data)), - Timestamp: uint32(frame.AbsTimestamp-muxer.basePts) + ptsDelay, - StreamID: 0, - Data: data, - } - return muxer.tagWriter.WriteFlvTag(tag) -} - -func (muxer *MuxerAvcAac) muxMetadataTag() error { - properties := make(amf.EcmaArray, 0, 12) - - properties = append(properties, - amf.ObjectProperty{ - Name: "creator", - Value: "ipchub stream media server"}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataCreationDate, - Value: time.Now().Format(time.RFC3339)}) - - if muxer.typeFlags&TypeFlagsAudio > 0 { - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataAudioCodecID, - Value: SoundFormatAAC}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataAudioDateRate, - Value: muxer.audioMeta.DataRate}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataAudioSampleRate, - Value: muxer.audioMeta.SampleRate}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataAudioSampleSize, - Value: muxer.audioMeta.SampleSize}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataStereo, - Value: muxer.audioMeta.Channels > 1}) - } - - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataVideoCodecID, - Value: CodecIDAVC}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataVideoDataRate, - Value: muxer.videoMeta.DataRate}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataFrameRate, - Value: muxer.videoMeta.FrameRate}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataWidth, - Value: muxer.videoMeta.Width}) - properties = append(properties, - amf.ObjectProperty{ - Name: MetaDataHeight, - Value: muxer.videoMeta.Height}) - - scriptData := ScriptData{ - Name: ScriptOnMetaData, - Value: properties, - } - data, _ := scriptData.Marshal() - - tag := &Tag{ - TagType: TagTypeAmf0Data, - DataSize: uint32(len(data)), - Timestamp: 0, - StreamID: 0, - Data: data, - } - - return muxer.tagWriter.WriteFlvTag(tag) -} - -func (muxer *MuxerAvcAac) muxSequenceHeaderTag() error { - if muxer.spsMuxed { - return nil - } - - if !h264.MetadataIsReady(&muxer.videoMeta) { - // not enough - return nil - } - - if muxer.videoMeta.FixedFrameRate { - muxer.dtsStep = 1000.0 / muxer.videoMeta.FrameRate - } else { // TODO: - muxer.dtsStep = 1000.0 / 30 - } - muxer.spsMuxed = true - - record := NewAVCDecoderConfigurationRecord(muxer.videoMeta.Sps, muxer.videoMeta.Pps) - body, _ := record.Marshal() - - videoData := &VideoData{ - FrameType: FrameTypeKeyFrame, - CodecID: CodecIDAVC, - AVCPacketType: AVCPacketTypeSequenceHeader, - CompositionTime: 0, - Body: body, - } - data, _ := videoData.Marshal() - - tag := &Tag{ - TagType: TagTypeVideo, - DataSize: uint32(len(data)), - Timestamp: 0, - StreamID: 0, - Data: data, - } - - if err := muxer.tagWriter.WriteFlvTag(tag); err != nil { - return err - } - - return muxer.muxAudioSequenceHeaderTag() -} - -func (muxer *MuxerAvcAac) muxAudioSequenceHeaderTag() error { - if muxer.typeFlags&TypeFlagsAudio == 0 { - return nil - } - - audioData := *muxer.audioDataTemplate - audioData.AACPacketType = AACPacketTypeSequenceHeader - audioData.Body = muxer.audioMeta.Sps - data, _ := audioData.Marshal() - - tag := &Tag{ - TagType: TagTypeAudio, - DataSize: uint32(len(data)), - Timestamp: 0, - StreamID: 0, - Data: data, - } - return muxer.tagWriter.WriteFlvTag(tag) -} - -func (muxer *MuxerAvcAac) prepareTemplate() { - audioData := &AudioData{ - SoundFormat: SoundFormatAAC, - AACPacketType: AACPacketTypeRawData, - Body: nil, - } - - switch muxer.audioMeta.SampleRate { - case 5512: - audioData.SoundRate = SoundRate5512 - case 11025: - audioData.SoundRate = SoundRate11025 - case 22050: - audioData.SoundRate = SoundRate22050 - case 44100: - audioData.SoundRate = SoundRate44100 - default: - audioData.SoundRate = SoundRate44100 - } - - if muxer.audioMeta.SampleSize == 8 { - audioData.SoundSize = SoundeSize8bit - } else { - audioData.SoundSize = SoundeSize16bit - } - - if muxer.audioMeta.Channels > 1 { - audioData.SoundType = SoundTypeStereo - } else { - audioData.SoundType = SoundTypeMono - } - - muxer.audioDataTemplate = audioData -} diff --git a/av/format/rtp/aac_depacketizer.go b/av/format/rtp/aac_depacketizer.go index 4f29922..069ef33 100644 --- a/av/format/rtp/aac_depacketizer.go +++ b/av/format/rtp/aac_depacketizer.go @@ -10,7 +10,7 @@ import ( ) type aacDepacketizer struct { - audio *codec.AudioMeta + meta *codec.AudioMeta w codec.FrameWriter sizeLength int indexLength int @@ -19,14 +19,14 @@ type aacDepacketizer struct { } // NewAacDepacketizer 实例化 AAC 解包器 -func NewAacDepacketizer(audio *codec.AudioMeta, w codec.FrameWriter) depacketizer { +func NewAacDepacketizer(meta *codec.AudioMeta, w codec.FrameWriter) Depacketizer { fe := &aacDepacketizer{ - audio: audio, + meta: meta, w: w, sizeLength: 13, indexLength: 3, } - fe.syncClock.RTPTimeUnit = 1000.0 / float64(audio.SampleRate) + fe.syncClock.RTPTimeUnit = 1000.0 / float64(meta.SampleRate) return fe } diff --git a/av/format/rtp/demuxer.go b/av/format/rtp/demuxer.go index 83680d4..a1b1714 100644 --- a/av/format/rtp/demuxer.go +++ b/av/format/rtp/demuxer.go @@ -13,8 +13,8 @@ import ( "github.com/cnotch/xlog" ) -// depacketizer 解包器 -type depacketizer interface { +// Depacketizer 解包器 +type Depacketizer interface { Control(p *Packet) error Depacketize(p *Packet) error } @@ -37,15 +37,14 @@ func NewDemuxer(video *codec.VideoMeta, audio *codec.AudioMeta, fw codec.FrameWr logger: logger, } - var videoDepacketizer, audioDepacketizer depacketizer + var videoDepacketizer, audioDepacketizer Depacketizer switch video.Codec { case "H264": videoDepacketizer = NewH264Depacketizer(video, fw) case "H265": videoDepacketizer = NewH265Depacketizer(video, fw) - } - if videoDepacketizer == nil { - return nil, fmt.Errorf("Unsupport video codec type:%s", video.Codec) + default: + return nil, fmt.Errorf("rtp demuxer unsupport video codec type:%s", video.Codec) } fc.depacketizeFuncs[ChannelVideo] = videoDepacketizer.Depacketize diff --git a/av/format/rtp/h264_depacketizer.go b/av/format/rtp/h264_depacketizer.go index 1b1d4a4..021fee8 100644 --- a/av/format/rtp/h264_depacketizer.go +++ b/av/format/rtp/h264_depacketizer.go @@ -13,19 +13,19 @@ import ( type h264Depacketizer struct { fragments []*Packet // 分片包 - video *codec.VideoMeta + meta *codec.VideoMeta w codec.FrameWriter syncClock SyncClock } // NewH264Depacketizer 实例化 H264 帧提取器 -func NewH264Depacketizer(video *codec.VideoMeta, w codec.FrameWriter) depacketizer { +func NewH264Depacketizer(meta *codec.VideoMeta, w codec.FrameWriter) Depacketizer { fe := &h264Depacketizer{ - video: video, + meta: meta, fragments: make([]*Packet, 0, 16), w: w, } - fe.syncClock.RTPTimeUnit = 1000.0 / float64(video.ClockRate) + fe.syncClock.RTPTimeUnit = 1000.0 / float64(meta.ClockRate) return fe } @@ -199,12 +199,12 @@ func (h264dp *h264Depacketizer) writeFrame(frame *codec.Frame) error { nalType := frame.Payload[0] & 0x1f switch nalType { case h264.NalSps: - if len(h264dp.video.Sps) == 0 { - h264dp.video.Sps = frame.Payload + if len(h264dp.meta.Sps) == 0 { + h264dp.meta.Sps = frame.Payload } case h264.NalPps: - if len(h264dp.video.Pps) == 0 { - h264dp.video.Pps = frame.Payload + if len(h264dp.meta.Pps) == 0 { + h264dp.meta.Pps = frame.Payload } case h264.NalFillerData: // ?ignore... return nil diff --git a/av/format/rtp/h265_depacketizer.go b/av/format/rtp/h265_depacketizer.go index 805fae2..08bee2e 100644 --- a/av/format/rtp/h265_depacketizer.go +++ b/av/format/rtp/h265_depacketizer.go @@ -11,19 +11,19 @@ import ( type h265Depacketizer struct { fragments []*Packet // 分片包 - video *codec.VideoMeta + meta *codec.VideoMeta w codec.FrameWriter syncClock SyncClock } // NewH265Depacketizer 实例化 H265 帧提取器 -func NewH265Depacketizer(video *codec.VideoMeta, w codec.FrameWriter) depacketizer { +func NewH265Depacketizer(meta *codec.VideoMeta, w codec.FrameWriter) Depacketizer { fe := &h265Depacketizer{ - video: video, + meta: meta, fragments: make([]*Packet, 0, 16), w: w, } - fe.syncClock.RTPTimeUnit = 1000.0 / float64(video.ClockRate) + fe.syncClock.RTPTimeUnit = 1000.0 / float64(meta.ClockRate) return fe } @@ -178,16 +178,16 @@ func (h265dp *h265Depacketizer) writeFrame(frame *codec.Frame) error { nalType := (frame.Payload[0] >> 1) & 0x3f switch nalType { case hevc.NalVps: - if len(h265dp.video.Vps) == 0 { - h265dp.video.Vps = frame.Payload + if len(h265dp.meta.Vps) == 0 { + h265dp.meta.Vps = frame.Payload } case hevc.NalSps: - if len(h265dp.video.Sps) == 0 { - h265dp.video.Sps = frame.Payload + if len(h265dp.meta.Sps) == 0 { + h265dp.meta.Sps = frame.Payload } case hevc.NalPps: - if len(h265dp.video.Pps) == 0 { - h265dp.video.Pps = frame.Payload + if len(h265dp.meta.Pps) == 0 { + h265dp.meta.Pps = frame.Payload } } return h265dp.w.WriteFrame(frame) diff --git a/media/stream.go b/media/stream.go index 2758ff7..758252d 100755 --- a/media/stream.go +++ b/media/stream.go @@ -105,7 +105,7 @@ func (s *Stream) prepareOtherStream() { s.flvCache = emptyCache{} s.flvMuxer = emptyFlvMuxer{} - // prepare rtp.Packet -> av.Frame + // prepare rtp.Packet -> codec.Frame var err error if s.rtpDemuxer, err = rtp.NewDemuxer(&s.Video, &s.Audio, s, s.logger.With(xlog.Fields(xlog.F("extra", "rtp2frame")))); err != nil { @@ -113,14 +113,15 @@ func (s *Stream) prepareOtherStream() { return } - // prepare av.Frame -> flv.Tag - if s.Video.Codec == "H264" { + // prepare codec.Frame -> flv.Tag + var flvMuxer *flv.Muxer + if flvMuxer, err = flv.NewMuxer(&s.Video, &s.Audio, + s, s.logger.With(xlog.Fields(xlog.F("extra", "frame2flv")))); err == nil { s.flvCache = cache.NewFlvCache(config.CacheGop()) - s.flvMuxer = flv.NewMuxerAvcAac(s.Video, s.Audio, - s, s.logger.With(xlog.Fields(xlog.F("extra", "frame2flv")))) + s.flvMuxer = flvMuxer } - // prepare av.Frame -> mpegts.Frame + // prepare codec.Frame -> mpegts.Frame if s.Video.Codec == "H264" { hlsPlaylist := hls.NewPlaylist() sg, err := hls.NewSegmentGenerator(hlsPlaylist, s.path,