From f8b41214972bef6accc995b3517cded580f2e095 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 10 Oct 2021 19:39:39 +0800 Subject: [PATCH] Test: Add Multiple Sequence test for RTMP --- trunk/3rdparty/srs-bench/srs/rtc_test.go | 2 +- trunk/3rdparty/srs-bench/srs/rtmp_test.go | 98 +++- trunk/3rdparty/srs-bench/srs/util.go | 74 ++- .../github.com/ossrs/go-oryx-lib/avc/avc.go | 476 ++++++++++++++++++ trunk/3rdparty/srs-bench/vendor/modules.txt | 1 + trunk/conf/full.conf | 1 + trunk/conf/regression-test.conf | 7 +- 7 files changed, 647 insertions(+), 12 deletions(-) create mode 100644 trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go diff --git a/trunk/3rdparty/srs-bench/srs/rtc_test.go b/trunk/3rdparty/srs-bench/srs/rtc_test.go index 4d0b62407..506aca650 100644 --- a/trunk/3rdparty/srs-bench/srs/rtc_test.go +++ b/trunk/3rdparty/srs-bench/srs/rtc_test.go @@ -1922,7 +1922,7 @@ func TestRTCServerVersion(t *testing.T) { } } -func TestRtcPublishFlvPlay(t *testing.T) { +func TestRtcPublish_FlvPlay(t *testing.T) { ctx := logger.WithContext(context.Background()) ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond) diff --git a/trunk/3rdparty/srs-bench/srs/rtmp_test.go b/trunk/3rdparty/srs-bench/srs/rtmp_test.go index 7d41e506a..a9bc8c015 100644 --- a/trunk/3rdparty/srs-bench/srs/rtmp_test.go +++ b/trunk/3rdparty/srs-bench/srs/rtmp_test.go @@ -23,6 +23,8 @@ package srs import ( "context" "fmt" + "github.com/ossrs/go-oryx-lib/avc" + "github.com/ossrs/go-oryx-lib/flv" "math/rand" "os" "sync" @@ -63,7 +65,7 @@ func TestRtmpPublishPlay(t *testing.T) { go func() { defer wg.Done() var nnPackets int - player.onRecvPacket = func(m *rtmp.Message) error { + player.onRecvPacket = func(m *rtmp.Message, a *flv.AudioFrame, v *flv.VideoFrame) error { logger.Tf(ctx, "got %v packet, %v %vms %vB", nnPackets, m.MessageType, m.Timestamp, len(m.Payload)) if nnPackets += 1; nnPackets > 50 { @@ -94,3 +96,97 @@ func TestRtmpPublishPlay(t *testing.T) { t.Errorf("err %+v", err) } } + +func TestRtmpPublish_MultipleSequences(t *testing.T) { + var r0, r1, r2 error + err := func() error { + publisher := NewRTMPPublisher() + defer publisher.Close() + + player := NewRTMPPlayer() + defer player.Close() + + // Connect to RTMP URL. + ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond) + streamSuffix := fmt.Sprintf("rtmp-multi-spspps-%v-%v", os.Getpid(), rand.Int()) + rtmpUrl := fmt.Sprintf("rtmp://%v/live/%v", *srsServer, streamSuffix) + + if err := publisher.Publish(ctx, rtmpUrl); err != nil { + return err + } + + if err := player.Play(ctx, rtmpUrl); err != nil { + return err + } + + // Check packets. + var wg sync.WaitGroup + defer wg.Wait() + + wg.Add(1) + go func() { + defer wg.Done() + var nnPackets int + var previousAvccr *avc.AVCDecoderConfigurationRecord + player.onRecvPacket = func(m *rtmp.Message, a *flv.AudioFrame, v *flv.VideoFrame) error { + if m.MessageType == rtmp.MessageTypeAudio || v.FrameType != flv.VideoFrameTypeKeyframe || + v.Trait != flv.VideoFrameTraitSequenceHeader { + return nil + } + + avccr := avc.NewAVCDecoderConfigurationRecord() + if err := avccr.UnmarshalBinary(v.Raw); err != nil { + return err + } + + // Ingore the duplicated sps/pps. + if IsAvccrEquals(previousAvccr, avccr) { + return nil + } + previousAvccr = avccr + + logger.Tf(ctx, "got %v sps/pps, %v %vms %vB, sps=%v, pps=%v, %v, %v", + nnPackets, m.MessageType, m.Timestamp, len(m.Payload), len(avccr.SequenceParameterSetNALUnits), + len(avccr.PictureParameterSetNALUnits), avccr.AVCProfileIndication, avccr.AVCLevelIndication) + if nnPackets++; nnPackets >=2 { + cancel() + } + return nil + } + if r1 = player.Consume(ctx); r1 != nil { + cancel() + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + var nnPackets int + ctxAvatar, cancelAvatar := context.WithCancel(ctx) + publisher.onSendPacket = func(m *rtmp.Message) error { + if m.MessageType == rtmp.MessageTypeVideo { + nnPackets++ + } + if nnPackets > 10 { + cancelAvatar() + } + return nil + } + + publisher.closeTransportWhenIngestDone = false + if r0 = publisher.Ingest(ctxAvatar, *srsPublishBBB); r0 != nil { + cancel() + } + + publisher.closeTransportWhenIngestDone = true + if r2 = publisher.Ingest(ctx, *srsPublishAvatar); r2 != nil { + cancel() + } + }() + + return nil + }() + if err := filterTestError(err, r0, r1, r2); err != nil { + t.Errorf("err %+v", err) + } +} diff --git a/trunk/3rdparty/srs-bench/srs/util.go b/trunk/3rdparty/srs-bench/srs/util.go index 045b8cc97..d6877b55e 100644 --- a/trunk/3rdparty/srs-bench/srs/util.go +++ b/trunk/3rdparty/srs-bench/srs/util.go @@ -26,6 +26,7 @@ import ( "flag" "fmt" "github.com/ossrs/go-oryx-lib/amf0" + "github.com/ossrs/go-oryx-lib/avc" "github.com/ossrs/go-oryx-lib/flv" "github.com/ossrs/go-oryx-lib/rtmp" "io" @@ -1429,14 +1430,21 @@ func (v *RTMPClient) Play(ctx context.Context, rtmpUrl string) error { type RTMPPublisher struct { client *RTMPClient + // Whether auto close transport when ingest done. + closeTransportWhenIngestDone bool onSendPacket func(m *rtmp.Message) error } func NewRTMPPublisher() *RTMPPublisher { - return &RTMPPublisher{ + v := &RTMPPublisher{ client: &RTMPClient{}, } + + // By default, set to on. + v.closeTransportWhenIngestDone = true + + return v } func (v *RTMPPublisher) Close() error { @@ -1456,7 +1464,9 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error { go func() { defer wg.Done() <-ctx.Done() - v.Close() + if v.closeTransportWhenIngestDone { + v.Close() + } }() // Consume all packets. @@ -1522,9 +1532,12 @@ func (v *RTMPPublisher) ingest(flvInput string) error { } type RTMPPlayer struct { + // Transport. client *RTMPClient + // FLV packager. + videoPackager flv.VideoPackager - onRecvPacket func(m *rtmp.Message) error + onRecvPacket func(m *rtmp.Message, a *flv.AudioFrame, v *flv.VideoFrame) error } func NewRTMPPlayer() *RTMPPlayer { @@ -1538,6 +1551,11 @@ func (v *RTMPPlayer) Close() error { } func (v *RTMPPlayer) Play(ctx context.Context, rtmpUrl string) error { + var err error + if v.videoPackager, err = flv.NewVideoPackager(); err != nil { + return err + } + return v.client.Play(ctx, rtmpUrl) } @@ -1572,9 +1590,57 @@ func (v *RTMPPlayer) consume() error { } if v.onRecvPacket != nil { - if err := v.onRecvPacket(res); err != nil { + var audioFrame *flv.AudioFrame + var videoFrame *flv.VideoFrame + if res.MessageType == rtmp.MessageTypeVideo { + if videoFrame, err = v.videoPackager.Decode(res.Payload); err != nil { + return err + } + } + + if err := v.onRecvPacket(res, audioFrame, videoFrame); err != nil { return err } } } } + +func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool { + if a == nil || b == nil { + return false + } + + if a.AVCLevelIndication != b.AVCLevelIndication || + a.AVCProfileIndication != b.AVCProfileIndication || + a.LengthSizeMinusOne != b.LengthSizeMinusOne || + len(a.SequenceParameterSetNALUnits) != len(b.SequenceParameterSetNALUnits) || + len(a.PictureParameterSetNALUnits) != len(b.PictureParameterSetNALUnits) { + return false + } + + for i := 0; i < len(a.SequenceParameterSetNALUnits); i++ { + if !IsNALUEquals(a.SequenceParameterSetNALUnits[i], b.SequenceParameterSetNALUnits[i]) { + return false + } + } + + for i := 0; i < len(a.PictureParameterSetNALUnits); i++ { + if !IsNALUEquals(a.PictureParameterSetNALUnits[i], b.PictureParameterSetNALUnits[i]) { + return false + } + } + + return true +} + +func IsNALUEquals(a, b *avc.NALU) bool { + if a == nil || b == nil { + return false + } + + if a.NALUType != b.NALUType || a.NALRefIDC != b.NALRefIDC { + return false + } + + return bytes.Equal(a.Data, b.Data) +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go b/trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go new file mode 100644 index 000000000..4e3193912 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go @@ -0,0 +1,476 @@ +// The MIT License (MIT) +// +// Copyright (c) 2013-2017 Oryx(ossrs) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// The oryx AVC package includes some utilites. +// The NALU(Netowrk Abstraction Layer Unit) is suitable for transmission over network. +// We could package NALUs by AnnexB, IBMF or RTP according to different scenarios. +// @note AnnexB is designed for bit-oriented stream, such as MPEG-TS/HLS, please +// read ISO_IEC_14496-10-AVC-2003.pdf at page 211, AnnexB Byte stream Format. +// @note IBMF is designed for file storage, such as MP4/FLV, please read +// ISO_IEC_14496-15-AVC-format-2012.pdf at page 16, 5.2.4.1 AVC decoder +// configuration record. +// @note RTP payload for H.264, defined in RFC6184 https://tools.ietf.org/html/rfc6184 +// it directly uses and extends the NAL header. +package avc + +import ( + "bytes" + "fmt" + "github.com/ossrs/go-oryx-lib/errors" +) + +// @doc ISO_IEC_14496-10-AVC-2003.pdf at page 44, 7.3.1 NAL unit syntax +type NALRefIDC uint8 + +// @doc ISO_IEC_14496-10-AVC-2003.pdf at page 44, 7.3.1 NAL unit syntax +type NALUType uint8 + +const ( + NALUTypeNonIDR NALUType = 1 // Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( ) + NALUTypeDataPartitionA NALUType = 2 // Coded slice data partition A slice_data_partition_a_layer_rbsp( ) + NALUTypeDataPartitionB NALUType = 3 // Coded slice data partition B slice_data_partition_b_layer_rbsp( ) + NALUTypeDataPartitionC NALUType = 4 // Coded slice data partition C slice_data_partition_c_layer_rbsp( ) + NALUTypeIDR NALUType = 5 // Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( ) + NALUTypeSEI NALUType = 6 // Supplemental enhancement information (SEI) sei_rbsp( ) + NALUTypeSPS NALUType = 7 // Sequence parameter set seq_parameter_set_rbsp( ) + NALUTypePPS NALUType = 8 // Picture parameter set pic_parameter_set_rbsp( ) + NALUTypeAccessUnitDelimiter NALUType = 9 // Access unit delimiter access_unit_delimiter_rbsp( ) + NALUTypeEOSequence NALUType = 10 // End of sequence end_of_seq_rbsp( ) + NALUTypeEOStream NALUType = 11 // End of stream end_of_stream_rbsp( ) + NALUTypeFilterData NALUType = 12 // Filler data filler_data_rbsp( ) + NALUTypeSPSExt NALUType = 13 // Sequence parameter set extension seq_parameter_set_extension_rbsp( ) + NALUTypePrefixNALU NALUType = 14 // Prefix NAL unit prefix_nal_unit_rbsp( ) + NALUTypeSubsetSPS NALUType = 15 // Subset sequence parameter set subset_seq_parameter_set_rbsp( ) + NALUTypeLayerWithoutPartition NALUType = 19 // Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( ) + NALUTypeCodedSliceExt NALUType = 20 // Coded slice extension slice_layer_extension_rbsp( ) +) + +func (v NALUType) String() string { + switch v { + case NALUTypeNonIDR: + return "NonIDR" + case NALUTypeDataPartitionA: + return "DataPartitionA" + case NALUTypeDataPartitionB: + return "DataPartitionB" + case NALUTypeDataPartitionC: + return "DataPartitionC" + case NALUTypeIDR: + return "IDR" + case NALUTypeSEI: + return "SEI" + case NALUTypeSPS: + return "SPS" + case NALUTypePPS: + return "PPS" + case NALUTypeAccessUnitDelimiter: + return "AccessUnitDelimiter" + case NALUTypeEOSequence: + return "EOSequence" + case NALUTypeEOStream: + return "EOStream" + case NALUTypeFilterData: + return "FilterData" + case NALUTypeSPSExt: + return "SPSExt" + case NALUTypePrefixNALU: + return "PrefixNALU" + case NALUTypeSubsetSPS: + return "SubsetSPS" + case NALUTypeLayerWithoutPartition: + return "LayerWithoutPartition" + case NALUTypeCodedSliceExt: + return "CodedSliceExt" + default: + return "Forbidden" + return fmt.Sprintf("NALU/%v", uint8(v)) + } +} + +// @doc ISO_IEC_14496-10-AVC-2003.pdf at page 60, 7.4.1 NAL unit semantics +type NALUHeader struct { + // The 2-bits nal_ref_idc. + NALRefIDC NALRefIDC + // The 5-bits nal_unit_type. + NALUType NALUType +} + +func NewNALUHeader() *NALUHeader { + return &NALUHeader{} +} + +func (v *NALUHeader) String() string { + return fmt.Sprintf("%v, NRI=%v", v.NALUType, v.NALRefIDC) +} + +func (v *NALUHeader) Size() int { + return 1 +} + +func (v *NALUHeader) UnmarshalBinary(data []byte) error { + if len(data) < 1 { + return errors.New("empty NALU") + } + v.NALRefIDC = NALRefIDC(uint8(data[0]>>5) & 0x03) + v.NALUType = NALUType(uint8(data[0]) & 0x1f) + return nil +} + +func (v *NALUHeader) MarshalBinary() ([]byte, error) { + return []byte{ + byte(v.NALRefIDC)<<5 | byte(v.NALUType), + }, nil +} + +// @doc ISO_IEC_14496-10-AVC-2003.pdf at page 60, 7.4.1 NAL unit semantics +type NALU struct { + *NALUHeader + Data []byte +} + +func NewNALU() *NALU { + return &NALU{NALUHeader: NewNALUHeader()} +} + +func (v *NALU) String() string { + return fmt.Sprintf("%v, size=%vB", v.NALUHeader, len(v.Data)) +} + +func (v *NALU) Size() int { + return 1 + len(v.Data) +} + +func (v *NALU) UnmarshalBinary(data []byte) error { + if err := v.NALUHeader.UnmarshalBinary(data); err != nil { + return errors.WithMessage(err, "unmarshal") + } + + v.Data = data[1:] + return nil +} + +func (v *NALU) MarshalBinary() ([]byte, error) { + b, err := v.NALUHeader.MarshalBinary() + if err != nil { + return nil, errors.WithMessage(err, "marshal") + } + + if len(v.Data) == 0 { + return b, nil + } + return append(b, v.Data...), nil +} + +// @doc Annex A Profiles and levels, ISO_IEC_14496-10-AVC-2003.pdf, page 205. +// @note that it's uint8 in IBMF, but extended in other specs, so we use uint16. +type AVCProfile uint16 + +const ( + // @see ffmpeg, libavcodec/avcodec.h:2713 + AVCProfileBaseline AVCProfile = 66 + AVCProfileConstrainedBaseline AVCProfile = 578 + AVCProfileMain AVCProfile = 77 + AVCProfileExtended AVCProfile = 88 + AVCProfileHigh AVCProfile = 100 + AVCProfileHigh10 AVCProfile = 110 + AVCProfileHigh10Intra AVCProfile = 2158 + AVCProfileHigh422 AVCProfile = 122 + AVCProfileHigh422Intra AVCProfile = 2170 + AVCProfileHigh444 AVCProfile = 144 + AVCProfileHigh444Predictive AVCProfile = 244 + AVCProfileHigh444Intra AVCProfile = 2192 +) + +func (v AVCProfile) String() string { + switch v { + case AVCProfileBaseline: + return "Baseline" + case AVCProfileConstrainedBaseline: + return "ConstrainedBaseline" + case AVCProfileMain: + return "Main" + case AVCProfileExtended: + return "Extended" + case AVCProfileHigh: + return "High" + case AVCProfileHigh10: + return "High10" + case AVCProfileHigh10Intra: + return "High10Intra" + case AVCProfileHigh422: + return "High422" + case AVCProfileHigh422Intra: + return "High422Intra" + case AVCProfileHigh444: + return "High444" + case AVCProfileHigh444Predictive: + return "High444Predictive" + case AVCProfileHigh444Intra: + return "High444Intra" + default: + return "Forbidden" + } +} + +// @doc Annex A Profiles and levels, ISO_IEC_14496-10-AVC-2003.pdf, page 207. +type AVCLevel uint8 + +const ( + AVCLevel_1 = 10 + AVCLevel_11 = 11 + AVCLevel_12 = 12 + AVCLevel_13 = 13 + AVCLevel_2 = 20 + AVCLevel_21 = 21 + AVCLevel_22 = 22 + AVCLevel_3 = 30 + AVCLevel_31 = 31 + AVCLevel_32 = 32 + AVCLevel_4 = 40 + AVCLevel_41 = 41 + AVCLevel_5 = 50 + AVCLevel_51 = 51 +) + +func (v AVCLevel) String() string { + switch v { + case AVCLevel_1: + return "Level_1" + case AVCLevel_11: + return "Level_11" + case AVCLevel_12: + return "Level_12" + case AVCLevel_13: + return "Level_13" + case AVCLevel_2: + return "Level_2" + case AVCLevel_21: + return "Level_21" + case AVCLevel_22: + return "Level_22" + case AVCLevel_3: + return "Level_3" + case AVCLevel_31: + return "Level_31" + case AVCLevel_32: + return "Level_32" + case AVCLevel_4: + return "Level_4" + case AVCLevel_41: + return "Level_41" + case AVCLevel_5: + return "Level_5" + case AVCLevel_51: + return "Level_51" + default: + return "Forbidden" + } +} + +// @doc ISO_IEC_14496-15-AVC-format-2012.pdf at page 16, 5.2.4.1.1 Syntax +type AVCDecoderConfigurationRecord struct { + // It contains the profile code as defined in ISO/IEC 14496-10. + configurationVersion uint8 + // It is a byte defined exactly the same as the byte which occurs between the + // profile_IDC and level_IDC in a sequence parameter set (SPS), as defined in + // ISO/IEC 14496-10. + // @remark It's 8 bits. + AVCProfileIndication AVCProfile + // It contains the level code as defined in ISO/IEC 14496-10. + profileCompatibility uint8 + // It indicates the length in bytes of the NALUnitLength field in an AVC video sample + // or AVC parameter set sample of the associated stream minus one. + AVCLevelIndication AVCLevel + // It indicates the length in bytes of the NALUnitLength field in an AVC video sample + // or AVC parameter set sample of the associated stream minus one. + LengthSizeMinusOne uint8 + // It contains a SPS NAL unit, as specified in ISO/IEC 14496-10. SPSs shall occur in + // order of ascending parameter set identifier with gaps being allowed. + SequenceParameterSetNALUnits []*NALU + // It contains a PPS NAL unit, as specified in ISO/IEC 14496-10. PPSs shall occur in + // order of ascending parameter set identifier with gaps being allowed. + PictureParameterSetNALUnits []*NALU + // @remark We ignore the sequenceParameterSetExtNALUnit. +} + +func NewAVCDecoderConfigurationRecord() *AVCDecoderConfigurationRecord { + v := &AVCDecoderConfigurationRecord{} + v.configurationVersion = 0x01 + return v +} + +func (v *AVCDecoderConfigurationRecord) MarshalBinary() ([]byte, error) { + var buf bytes.Buffer + buf.WriteByte(byte(v.configurationVersion)) + buf.WriteByte(byte(v.AVCProfileIndication)) + buf.WriteByte(byte(v.profileCompatibility)) + buf.WriteByte(byte(v.AVCLevelIndication)) + buf.WriteByte(byte(v.LengthSizeMinusOne)) + + // numOfSequenceParameterSets + buf.WriteByte(byte(len(v.SequenceParameterSetNALUnits))) + for _, sps := range v.SequenceParameterSetNALUnits { + b, err := sps.MarshalBinary() + if err != nil { + return nil, errors.WithMessage(err, "sps") + } + + sequenceParameterSetLength := uint16(len(b)) + buf.WriteByte(byte(sequenceParameterSetLength >> 8)) + buf.WriteByte(byte(sequenceParameterSetLength)) + buf.Write(b) + } + + // numOfPictureParameterSets + buf.WriteByte(byte(len(v.PictureParameterSetNALUnits))) + for _, pps := range v.PictureParameterSetNALUnits { + b, err := pps.MarshalBinary() + if err != nil { + return nil, errors.WithMessage(err, "pps") + } + + pictureParameterSetLength := uint16(len(b)) + buf.WriteByte(byte(pictureParameterSetLength >> 8)) + buf.WriteByte(byte(pictureParameterSetLength)) + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (v *AVCDecoderConfigurationRecord) UnmarshalBinary(data []byte) error { + b := data + if len(b) < 6 { + return errors.Errorf("requires 6+ only %v bytes", len(b)) + } + + v.configurationVersion = uint8(b[0]) + v.AVCProfileIndication = AVCProfile(uint8(b[1])) + v.profileCompatibility = uint8(b[2]) + v.AVCLevelIndication = AVCLevel(uint8(b[3])) + v.LengthSizeMinusOne = uint8(b[4]) & 0x03 + b = b[5:] + + numOfSequenceParameterSets := uint8(b[0]) & 0x1f + b = b[1:] + for i := 0; i < int(numOfSequenceParameterSets); i++ { + if len(b) < 2 { + return errors.Errorf("requires 2+ only %v bytes", len(b)) + } + sequenceParameterSetLength := int(uint16(b[0])<<8 | uint16(b[1])) + b = b[2:] + + if len(b) < sequenceParameterSetLength { + return errors.Errorf("requires %v only %v bytes", sequenceParameterSetLength, len(b)) + } + sps := NewNALU() + if err := sps.UnmarshalBinary(b[:sequenceParameterSetLength]); err != nil { + return errors.WithMessage(err, "unmarshal") + } + b = b[sequenceParameterSetLength:] + + v.SequenceParameterSetNALUnits = append(v.SequenceParameterSetNALUnits, sps) + } + + if len(b) < 1 { + return errors.New("no PPS length") + } + numOfPictureParameterSets := uint8(b[0]) + b = b[1:] + for i := 0; i < int(numOfPictureParameterSets); i++ { + if len(b) < 2 { + return errors.Errorf("requiers 2+ only %v bytes", len(b)) + } + + pictureParameterSetLength := int(uint16(b[0])<<8 | uint16(b[1])) + b = b[2:] + + if len(b) < pictureParameterSetLength { + return errors.Errorf("requires %v only %v bytes", pictureParameterSetLength, len(b)) + } + pps := NewNALU() + if err := pps.UnmarshalBinary(b[:pictureParameterSetLength]); err != nil { + return errors.WithMessage(err, "unmarshal") + } + b = b[pictureParameterSetLength:] + + v.PictureParameterSetNALUnits = append(v.PictureParameterSetNALUnits, pps) + } + return nil +} + +// @doc ISO_IEC_14496-15-AVC-format-2012.pdf at page 20, 5.3.4.2 Sample format +type AVCSample struct { + lengthSizeMinusOne uint8 + NALUs []*NALU +} + +func NewAVCSample(lengthSizeMinusOne uint8) *AVCSample { + return &AVCSample{lengthSizeMinusOne: lengthSizeMinusOne} +} + +func (v *AVCSample) MarshalBinary() ([]byte, error) { + sizeOfNALU := int(v.lengthSizeMinusOne) + 1 + + var buf bytes.Buffer + for _, nalu := range v.NALUs { + b, err := nalu.MarshalBinary() + if err != nil { + return nil, errors.WithMessage(err, "write") + } + + length := uint64(len(b)) + for i := 0; i < sizeOfNALU; i++ { + buf.WriteByte(byte(length >> uint8(8*(sizeOfNALU-1-i)))) + } + buf.Write(b) + } + + return buf.Bytes(), nil +} + +func (v *AVCSample) UnmarshalBinary(data []byte) error { + sizeOfNALU := int(v.lengthSizeMinusOne) + 1 + for b := data; len(b) > 0; { + if len(b) < sizeOfNALU { + return errors.Errorf("requires %v+ only %v bytes", sizeOfNALU, len(b)) + } + + var length uint64 + for i := 0; i < sizeOfNALU; i++ { + length |= uint64(b[i]) << uint8(8*(sizeOfNALU-1-i)) + } + b = b[sizeOfNALU:] + + if len(b) < int(length) { + return errors.Errorf("requires %v only %v bytes", length, len(b)) + } + + nalu := NewNALU() + if err := nalu.UnmarshalBinary(b[:length]); err != nil { + return errors.WithMessage(err, "unmarshal") + } + b = b[length:] + + v.NALUs = append(v.NALUs, nalu) + } + return nil +} diff --git a/trunk/3rdparty/srs-bench/vendor/modules.txt b/trunk/3rdparty/srs-bench/vendor/modules.txt index de81c4a55..22a1c9937 100644 --- a/trunk/3rdparty/srs-bench/vendor/modules.txt +++ b/trunk/3rdparty/srs-bench/vendor/modules.txt @@ -4,6 +4,7 @@ github.com/google/uuid ## explicit github.com/ossrs/go-oryx-lib/aac github.com/ossrs/go-oryx-lib/amf0 +github.com/ossrs/go-oryx-lib/avc github.com/ossrs/go-oryx-lib/errors github.com/ossrs/go-oryx-lib/flv github.com/ossrs/go-oryx-lib/logger diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 145c670b8..b31994153 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -877,6 +877,7 @@ vhost publish.srs.com { # whether parse the sps when publish stream. # we can got the resolution of video for stat api. # but we may failed to cause publish failed. + # @remark If disabled, HLS might never update the sps/pps, it depends on this. # default: on parse_sps on; } diff --git a/trunk/conf/regression-test.conf b/trunk/conf/regression-test.conf index 2490ed295..0fb6ee2ff 100644 --- a/trunk/conf/regression-test.conf +++ b/trunk/conf/regression-test.conf @@ -20,13 +20,7 @@ stats { } rtc_server { enabled on; - # Listen at udp://8000 listen 8000; - # - # The $CANDIDATE means fetch from env, if not configed, use * as default. - # - # The * means retrieving server IP automatically, from all network interfaces, - # @see https://github.com/ossrs/srs/issues/307#issuecomment-599028124 candidate $CANDIDATE; } @@ -34,6 +28,7 @@ vhost __defaultVhost__ { rtc { enabled on; bframe discard; + aac transcode; rtc_to_rtmp on; } play {