refactoring flv muxer

This commit is contained in:
notch
2021-01-14 11:51:48 +08:00
parent 21ed16382c
commit fd86f38297
10 changed files with 462 additions and 397 deletions

View File

@@ -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)
}

View File

@@ -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)}

View File

@@ -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)
}

222
av/format/flv/muxer.go Normal file
View File

@@ -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 }
// 网络播放时 PTSPresentation Time Stamp的延时
// 影响视频 Tag 的 CTS 和音频的 DTSDecoding 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 panicr = %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)
}

View File

@@ -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"
)
// 网络播放时 PTSPresentation Time Stamp的延时
// 影响视频 Tag 的 CTS 和音频的 DTSDecoding 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 panicr = %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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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,