mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Test: Add Multiple Sequence test for RTMP
This commit is contained in:
parent
29b98c49cc
commit
f8b4121497
7 changed files with 647 additions and 12 deletions
2
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
2
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
|
@ -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 := logger.WithContext(context.Background())
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
|
|
98
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
98
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
|
@ -23,6 +23,8 @@ package srs
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ossrs/go-oryx-lib/avc"
|
||||||
|
"github.com/ossrs/go-oryx-lib/flv"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -63,7 +65,7 @@ func TestRtmpPublishPlay(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
var nnPackets int
|
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",
|
logger.Tf(ctx, "got %v packet, %v %vms %vB",
|
||||||
nnPackets, m.MessageType, m.Timestamp, len(m.Payload))
|
nnPackets, m.MessageType, m.Timestamp, len(m.Payload))
|
||||||
if nnPackets += 1; nnPackets > 50 {
|
if nnPackets += 1; nnPackets > 50 {
|
||||||
|
@ -94,3 +96,97 @@ func TestRtmpPublishPlay(t *testing.T) {
|
||||||
t.Errorf("err %+v", err)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
72
trunk/3rdparty/srs-bench/srs/util.go
vendored
72
trunk/3rdparty/srs-bench/srs/util.go
vendored
|
@ -26,6 +26,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ossrs/go-oryx-lib/amf0"
|
"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/flv"
|
||||||
"github.com/ossrs/go-oryx-lib/rtmp"
|
"github.com/ossrs/go-oryx-lib/rtmp"
|
||||||
"io"
|
"io"
|
||||||
|
@ -1429,14 +1430,21 @@ func (v *RTMPClient) Play(ctx context.Context, rtmpUrl string) error {
|
||||||
|
|
||||||
type RTMPPublisher struct {
|
type RTMPPublisher struct {
|
||||||
client *RTMPClient
|
client *RTMPClient
|
||||||
|
// Whether auto close transport when ingest done.
|
||||||
|
closeTransportWhenIngestDone bool
|
||||||
|
|
||||||
onSendPacket func(m *rtmp.Message) error
|
onSendPacket func(m *rtmp.Message) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTMPPublisher() *RTMPPublisher {
|
func NewRTMPPublisher() *RTMPPublisher {
|
||||||
return &RTMPPublisher{
|
v := &RTMPPublisher{
|
||||||
client: &RTMPClient{},
|
client: &RTMPClient{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// By default, set to on.
|
||||||
|
v.closeTransportWhenIngestDone = true
|
||||||
|
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPPublisher) Close() error {
|
func (v *RTMPPublisher) Close() error {
|
||||||
|
@ -1456,7 +1464,9 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
if v.closeTransportWhenIngestDone {
|
||||||
v.Close()
|
v.Close()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Consume all packets.
|
// Consume all packets.
|
||||||
|
@ -1522,9 +1532,12 @@ func (v *RTMPPublisher) ingest(flvInput string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RTMPPlayer struct {
|
type RTMPPlayer struct {
|
||||||
|
// Transport.
|
||||||
client *RTMPClient
|
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 {
|
func NewRTMPPlayer() *RTMPPlayer {
|
||||||
|
@ -1538,6 +1551,11 @@ func (v *RTMPPlayer) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTMPPlayer) Play(ctx context.Context, rtmpUrl string) 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)
|
return v.client.Play(ctx, rtmpUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1572,9 +1590,57 @@ func (v *RTMPPlayer) consume() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.onRecvPacket != nil {
|
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
|
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)
|
||||||
|
}
|
||||||
|
|
476
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go
generated
vendored
Normal file
476
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/avc/avc.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
1
trunk/3rdparty/srs-bench/vendor/modules.txt
vendored
1
trunk/3rdparty/srs-bench/vendor/modules.txt
vendored
|
@ -4,6 +4,7 @@ github.com/google/uuid
|
||||||
## explicit
|
## explicit
|
||||||
github.com/ossrs/go-oryx-lib/aac
|
github.com/ossrs/go-oryx-lib/aac
|
||||||
github.com/ossrs/go-oryx-lib/amf0
|
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/errors
|
||||||
github.com/ossrs/go-oryx-lib/flv
|
github.com/ossrs/go-oryx-lib/flv
|
||||||
github.com/ossrs/go-oryx-lib/logger
|
github.com/ossrs/go-oryx-lib/logger
|
||||||
|
|
|
@ -877,6 +877,7 @@ vhost publish.srs.com {
|
||||||
# whether parse the sps when publish stream.
|
# whether parse the sps when publish stream.
|
||||||
# we can got the resolution of video for stat api.
|
# we can got the resolution of video for stat api.
|
||||||
# but we may failed to cause publish failed.
|
# but we may failed to cause publish failed.
|
||||||
|
# @remark If disabled, HLS might never update the sps/pps, it depends on this.
|
||||||
# default: on
|
# default: on
|
||||||
parse_sps on;
|
parse_sps on;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,7 @@ stats {
|
||||||
}
|
}
|
||||||
rtc_server {
|
rtc_server {
|
||||||
enabled on;
|
enabled on;
|
||||||
# Listen at udp://8000
|
|
||||||
listen 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;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +28,7 @@ vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
bframe discard;
|
bframe discard;
|
||||||
|
aac transcode;
|
||||||
rtc_to_rtmp on;
|
rtc_to_rtmp on;
|
||||||
}
|
}
|
||||||
play {
|
play {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue