mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Squash: Merge SRS 4.0, regression test for RTMP.
This commit is contained in:
parent
a81aa2edc5
commit
b874d9c9ba
32 changed files with 9977 additions and 131 deletions
BIN
trunk/3rdparty/srs-bench/avatar.flv
vendored
Normal file
BIN
trunk/3rdparty/srs-bench/avatar.flv
vendored
Normal file
Binary file not shown.
3112
trunk/3rdparty/srs-bench/avatar.log
vendored
Normal file
3112
trunk/3rdparty/srs-bench/avatar.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
trunk/3rdparty/srs-bench/bbb.flv
vendored
Normal file
BIN
trunk/3rdparty/srs-bench/bbb.flv
vendored
Normal file
Binary file not shown.
2773
trunk/3rdparty/srs-bench/bbb.log
vendored
Normal file
2773
trunk/3rdparty/srs-bench/bbb.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
73
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
73
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
|
@ -45,23 +45,6 @@ import (
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
if err := prepareTest(); err != nil {
|
|
||||||
logger.Ef(nil, "Prepare test fail, err %+v", err)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable the logger during all tests.
|
|
||||||
if *srsLog == false {
|
|
||||||
olw := logger.Switch(ioutil.Discard)
|
|
||||||
defer func() {
|
|
||||||
logger.Switch(olw)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for https://github.com/ossrs/srs/pull/2483
|
// Test for https://github.com/ossrs/srs/pull/2483
|
||||||
func TestPR2483_RtcStatApi_PublisherOnly(t *testing.T) {
|
func TestPR2483_RtcStatApi_PublisherOnly(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(func() error {
|
||||||
|
@ -876,6 +859,9 @@ func TestRtcDTLS_ClientPassive_Duplicated_Alert(t *testing.T) {
|
||||||
//
|
//
|
||||||
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
||||||
func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T) {
|
func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -889,7 +875,6 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -938,7 +923,7 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -954,6 +939,9 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T
|
||||||
// openssl will create a new ClientHello with increased sequence. It's ok, but waste a lots of duplicated ClientHello
|
// openssl will create a new ClientHello with increased sequence. It's ok, but waste a lots of duplicated ClientHello
|
||||||
// packets, so we fail the test, requires the epoch+sequence never dup, even for ARQ.
|
// packets, so we fail the test, requires the epoch+sequence never dup, even for ARQ.
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-passive-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-passive-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -967,7 +955,6 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1016,7 +1003,7 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1018,9 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.
|
||||||
//
|
//
|
||||||
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
||||||
func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T) {
|
func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0, r1 error
|
var r0, r1 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1044,7 +1034,6 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1102,7 +1091,7 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0, r1); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1119,6 +1108,9 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T
|
||||||
// openssl will create a new ClientHello with increased sequence. It's ok, but waste a lots of duplicated ClientHello
|
// openssl will create a new ClientHello with increased sequence. It's ok, but waste a lots of duplicated ClientHello
|
||||||
// packets, so we fail the test, requires the epoch+sequence never dup, even for ARQ.
|
// packets, so we fail the test, requires the epoch+sequence never dup, even for ARQ.
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0, r1 error
|
var r0, r1 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-passive-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-passive-arq-client-hello-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1132,7 +1124,6 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1190,7 +1181,7 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0, r1); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1204,6 +1195,9 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.
|
||||||
//
|
//
|
||||||
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
||||||
func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T) {
|
func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1217,7 +1211,6 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1266,7 +1259,7 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1281,6 +1274,9 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T
|
||||||
// @remark If retransmit the Certificate, with the same epoch+sequence, peer will drop the message. It's ok right now, but
|
// @remark If retransmit the Certificate, with the same epoch+sequence, peer will drop the message. It's ok right now, but
|
||||||
// wast some packets, so we check the epoch+sequence which should never dup, even for ARQ.
|
// wast some packets, so we check the epoch+sequence which should never dup, even for ARQ.
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-passive-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-passive-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1294,7 +1290,6 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1343,7 +1338,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1358,6 +1353,9 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.
|
||||||
//
|
//
|
||||||
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
// @remark The pion is active, so it can be consider a benchmark for DTLS server.
|
||||||
func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *testing.T) {
|
func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0, r1 error
|
var r0, r1 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1371,7 +1369,6 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *test
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1428,7 +1425,7 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *test
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0, r1); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1444,6 +1441,9 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *test
|
||||||
// @remark If retransmit the Certificate, with the same epoch+sequence, peer will drop the message, and never generate the
|
// @remark If retransmit the Certificate, with the same epoch+sequence, peer will drop the message, and never generate the
|
||||||
// ChangeCipherSpec, which will cause DTLS fail.
|
// ChangeCipherSpec, which will cause DTLS fail.
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0, r1 error
|
var r0, r1 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-passive-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-passive-arq-certificate-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1457,7 +1457,6 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *tes
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1514,7 +1513,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *tes
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0, r1); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1812,6 +1811,9 @@ func TestRtcDTLS_ClientPassive_ARQ_VeryBadNetwork(t *testing.T) {
|
||||||
// If we retransmit 2 ClientHello packets, consumed 150ms, server might wait at 200ms.
|
// If we retransmit 2 ClientHello packets, consumed 150ms, server might wait at 200ms.
|
||||||
// Then we retransmit the Certificate, server reset the timer and retransmit it in 50ms, not 200ms.
|
// Then we retransmit the Certificate, server reset the timer and retransmit it in 50ms, not 200ms.
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
err := func() error {
|
err := func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-passive-no-arq-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-passive-no-arq-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
@ -1825,7 +1827,6 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
|
||||||
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
var nnRTCP, nnRTP int64
|
var nnRTCP, nnRTP int64
|
||||||
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
@ -1890,7 +1891,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
||||||
|
|
||||||
return p.Run(ctx, cancel)
|
return p.Run(ctx, cancel)
|
||||||
}()
|
}()
|
||||||
if err := filterTestError(err, r0); err != nil {
|
if err := filterTestError(ctx.Err(), err, r0); err != nil {
|
||||||
t.Errorf("err %+v", err)
|
t.Errorf("err %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1939,7 +1940,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)
|
||||||
|
|
||||||
|
|
395
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
Normal file
395
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
Normal file
|
@ -0,0 +1,395 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Winlin
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
package srs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ossrs/go-oryx-lib/avc"
|
||||||
|
"github.com/ossrs/go-oryx-lib/flv"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"github.com/ossrs/go-oryx-lib/rtmp"
|
||||||
|
"github.com/pion/interceptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRtmpPublishPlay(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
|
var r0, r1 error
|
||||||
|
err := func() error {
|
||||||
|
publisher := NewRTMPPublisher()
|
||||||
|
defer publisher.Close()
|
||||||
|
|
||||||
|
player := NewRTMPPlayer()
|
||||||
|
defer player.Close()
|
||||||
|
|
||||||
|
// Connect to RTMP URL.
|
||||||
|
streamSuffix := fmt.Sprintf("rtmp-regression-%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
|
||||||
|
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 {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r1 = player.Consume(ctx); r1 != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
|
t.Errorf("err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRtmpPublish_RtcPlay(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
|
var r0, r1 error
|
||||||
|
err := func() (err error) {
|
||||||
|
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
rtmpUrl := fmt.Sprintf("%v://%v%v-%v", srsSchema, *srsServer, *srsStream, streamSuffix)
|
||||||
|
|
||||||
|
// Publisher connect to a RTMP stream.
|
||||||
|
publisher := NewRTMPPublisher()
|
||||||
|
defer publisher.Close()
|
||||||
|
|
||||||
|
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the RTC player.
|
||||||
|
var thePlayer *testPlayer
|
||||||
|
if thePlayer, err = newTestPlayer(registerMiniCodecs, func(play *testPlayer) error {
|
||||||
|
play.streamSuffix = streamSuffix
|
||||||
|
var nnPlayReadRTP uint64
|
||||||
|
return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
|
nn, attr, err := i.nextRTPReader.Read(payload, attributes)
|
||||||
|
if err != nil {
|
||||||
|
return nn, attr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nnPlayReadRTP++; nnPlayReadRTP >= uint64(*srsPlayOKPackets) {
|
||||||
|
cancel() // Completed.
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Play RECV RTP #%v %vB", nnPlayReadRTP, nn)
|
||||||
|
return nn, attr, err
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer thePlayer.Close()
|
||||||
|
|
||||||
|
// Run publisher and players.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
|
var playerIceReady context.Context
|
||||||
|
playerIceReady, thePlayer.iceReadyCancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if r1 = thePlayer.Run(logger.WithContext(ctx), cancel); r1 != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "player done")
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Wait for player ready.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-playerIceReady.Done():
|
||||||
|
}
|
||||||
|
|
||||||
|
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "publisher done")
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||||
|
t.Errorf("err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRtmpPublish_MultipleSequences(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
|
var r0, r1, r2 error
|
||||||
|
err := func() error {
|
||||||
|
publisher := NewRTMPPublisher()
|
||||||
|
defer publisher.Close()
|
||||||
|
|
||||||
|
player := NewRTMPPlayer()
|
||||||
|
defer player.Close()
|
||||||
|
|
||||||
|
// Connect to RTMP URL.
|
||||||
|
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(ctx.Err(), err, r0, r1, r2); err != nil {
|
||||||
|
t.Errorf("err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRtmpPublish_MultipleSequences_RtcPlay(t *testing.T) {
|
||||||
|
ctx := logger.WithContext(context.Background())
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
|
||||||
|
var r0, r1, r2 error
|
||||||
|
err := func() (err error) {
|
||||||
|
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
rtmpUrl := fmt.Sprintf("%v://%v%v-%v", srsSchema, *srsServer, *srsStream, streamSuffix)
|
||||||
|
|
||||||
|
// Publisher connect to a RTMP stream.
|
||||||
|
publisher := NewRTMPPublisher()
|
||||||
|
defer publisher.Close()
|
||||||
|
|
||||||
|
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the RTC player.
|
||||||
|
var thePlayer *testPlayer
|
||||||
|
if thePlayer, err = newTestPlayer(registerMiniCodecs, func(play *testPlayer) error {
|
||||||
|
play.streamSuffix = streamSuffix
|
||||||
|
var nnSpsPps uint64
|
||||||
|
var previousSpsPps []byte
|
||||||
|
return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
|
nn, attr, err := i.nextRTPReader.Read(payload, attributes)
|
||||||
|
if err != nil {
|
||||||
|
return nn, attr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
annexb, nalus, err := DemuxRtpSpsPps(payload[:nn])
|
||||||
|
if err != nil || len(nalus) == 0 ||
|
||||||
|
(nalus[0].NALUType != avc.NALUTypeSPS && nalus[0].NALUType != avc.NALUTypePPS) ||
|
||||||
|
bytes.Equal(annexb, previousSpsPps) {
|
||||||
|
return nn, attr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
previousSpsPps = annexb
|
||||||
|
if nnSpsPps++; nnSpsPps >= 2 {
|
||||||
|
cancel() // Completed.
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Play RECV SPS/PPS #%v %vB %v", nnSpsPps, nn, nalus[0].NALUType)
|
||||||
|
return nn, attr, err
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer thePlayer.Close()
|
||||||
|
|
||||||
|
// Run publisher and players.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
|
var playerIceReady context.Context
|
||||||
|
playerIceReady, thePlayer.iceReadyCancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if r1 = thePlayer.Run(logger.WithContext(ctx), cancel); r1 != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "player done")
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Wait for player ready.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-playerIceReady.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()
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "publisher done")
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err := filterTestError(ctx.Err(), err, r0, r1, r2); err != nil {
|
||||||
|
t.Errorf("err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
45
trunk/3rdparty/srs-bench/srs/srs_test.go
vendored
Normal file
45
trunk/3rdparty/srs-bench/srs/srs_test.go
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Winlin
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
package srs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if err := prepareTest(); err != nil {
|
||||||
|
logger.Ef(nil, "Prepare test fail, err %+v", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable the logger during all tests.
|
||||||
|
if *srsLog == false {
|
||||||
|
olw := logger.Switch(ioutil.Discard)
|
||||||
|
defer func() {
|
||||||
|
logger.Switch(olw)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
462
trunk/3rdparty/srs-bench/srs/util.go
vendored
462
trunk/3rdparty/srs-bench/srs/util.go
vendored
|
@ -25,7 +25,14 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"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"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
"github.com/pion/rtp/codecs"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -62,6 +69,8 @@ var srsStream *string
|
||||||
var srsLiveStream *string
|
var srsLiveStream *string
|
||||||
var srsPublishAudio *string
|
var srsPublishAudio *string
|
||||||
var srsPublishVideo *string
|
var srsPublishVideo *string
|
||||||
|
var srsPublishAvatar *string
|
||||||
|
var srsPublishBBB *string
|
||||||
var srsVnetClientIP *string
|
var srsVnetClientIP *string
|
||||||
|
|
||||||
func prepareTest() error {
|
func prepareTest() error {
|
||||||
|
@ -78,6 +87,8 @@ func prepareTest() error {
|
||||||
srsPublishOKPackets = flag.Int("srs-publish-ok-packets", 3, "If send N RTP, recv N RTCP packets, it's ok, or fail")
|
srsPublishOKPackets = flag.Int("srs-publish-ok-packets", 3, "If send N RTP, recv N RTCP packets, it's ok, or fail")
|
||||||
srsPublishAudio = flag.String("srs-publish-audio", "avatar.ogg", "The audio file for publisher.")
|
srsPublishAudio = flag.String("srs-publish-audio", "avatar.ogg", "The audio file for publisher.")
|
||||||
srsPublishVideo = flag.String("srs-publish-video", "avatar.h264", "The video file for publisher.")
|
srsPublishVideo = flag.String("srs-publish-video", "avatar.h264", "The video file for publisher.")
|
||||||
|
srsPublishAvatar = flag.String("srs-publish-avatar", "avatar.flv", "The avatar file for publisher.")
|
||||||
|
srsPublishBBB = flag.String("srs-publish-bbb", "bbb.flv", "The bbb file for publisher.")
|
||||||
srsPublishVideoFps = flag.Int("srs-publish-video-fps", 25, "The video fps for publisher.")
|
srsPublishVideoFps = flag.Int("srs-publish-video-fps", 25, "The video fps for publisher.")
|
||||||
srsVnetClientIP = flag.String("srs-vnet-client-ip", "192.168.168.168", "The client ip in pion/vnet.")
|
srsVnetClientIP = flag.String("srs-vnet-client-ip", "192.168.168.168", "The client ip in pion/vnet.")
|
||||||
srsDTLSDropPackets = flag.Int("srs-dtls-drop-packets", 5, "If dropped N packets, it's ok, or fail")
|
srsDTLSDropPackets = flag.Int("srs-dtls-drop-packets", 5, "If dropped N packets, it's ok, or fail")
|
||||||
|
@ -122,6 +133,14 @@ func prepareTest() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *srsPublishAvatar, err = tryOpenFile(*srsPublishAvatar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *srsPublishBBB, err = tryOpenFile(*srsPublishBBB); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if *srsPublishAudio, err = tryOpenFile(*srsPublishAudio); err != nil {
|
if *srsPublishAudio, err = tryOpenFile(*srsPublishAudio); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -734,6 +753,7 @@ type testPlayerOptionFunc func(p *testPlayer) error
|
||||||
type testPlayer struct {
|
type testPlayer struct {
|
||||||
onOffer func(s *webrtc.SessionDescription) error
|
onOffer func(s *webrtc.SessionDescription) error
|
||||||
onAnswer func(s *webrtc.SessionDescription) error
|
onAnswer func(s *webrtc.SessionDescription) error
|
||||||
|
iceReadyCancel context.CancelFunc
|
||||||
pc *webrtc.PeerConnection
|
pc *webrtc.PeerConnection
|
||||||
receivers []*webrtc.RTPReceiver
|
receivers []*webrtc.RTPReceiver
|
||||||
// We should dispose it.
|
// We should dispose it.
|
||||||
|
@ -893,8 +913,20 @@ func (v *testPlayer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||||
if state == webrtc.ICEConnectionStateFailed || state == webrtc.ICEConnectionStateClosed {
|
logger.Tf(ctx, "ICE state %v", state)
|
||||||
err = errors.Errorf("Close for ICE state %v", state)
|
})
|
||||||
|
|
||||||
|
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
|
||||||
|
logger.Tf(ctx, "PC state %v", state)
|
||||||
|
|
||||||
|
if state == webrtc.PeerConnectionStateConnected {
|
||||||
|
if v.iceReadyCancel != nil {
|
||||||
|
v.iceReadyCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == webrtc.PeerConnectionStateFailed || state == webrtc.PeerConnectionStateClosed {
|
||||||
|
err = errors.Errorf("Close for PC state %v", state)
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1240,3 +1272,429 @@ func (v *testPublisher) Run(ctx context.Context, cancel context.CancelFunc) erro
|
||||||
}
|
}
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RTMPClient struct {
|
||||||
|
rtmpUrl string
|
||||||
|
|
||||||
|
rtmpTcUrl string
|
||||||
|
rtmpStream string
|
||||||
|
rtmpUrlObject *url.URL
|
||||||
|
|
||||||
|
streamID int
|
||||||
|
|
||||||
|
conn *net.TCPConn
|
||||||
|
proto *rtmp.Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPClient) Close() error {
|
||||||
|
if v.conn != nil {
|
||||||
|
v.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPClient) connect(rtmpUrl string) error {
|
||||||
|
v.rtmpUrl = rtmpUrl
|
||||||
|
|
||||||
|
if index := strings.LastIndex(rtmpUrl, "/"); index <= 0 {
|
||||||
|
return fmt.Errorf("invalid url %v, index=%v", rtmpUrl, index)
|
||||||
|
} else {
|
||||||
|
v.rtmpTcUrl = rtmpUrl[0:index]
|
||||||
|
v.rtmpStream = rtmpUrl[index+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse RTMP url.
|
||||||
|
rtmpUrlObject, err := url.Parse(rtmpUrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.rtmpUrlObject = rtmpUrlObject
|
||||||
|
|
||||||
|
port := rtmpUrlObject.Port()
|
||||||
|
if port == "" {
|
||||||
|
port = "1935"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to TCP server.
|
||||||
|
rtmpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%v:%v", rtmpUrlObject.Hostname(), port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := net.DialTCP("tcp4", nil, rtmpAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.conn = c
|
||||||
|
|
||||||
|
// RTMP Handshake with server.
|
||||||
|
hs := rtmp.NewHandshake(rand.New(rand.NewSource(time.Now().UnixNano())))
|
||||||
|
if err := hs.WriteC0S0(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := hs.WriteC1S1(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := hs.ReadC0S0(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s1, err := hs.ReadC1S1(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := hs.ReadC2S2(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hs.WriteC2S2(c, s1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to RTMP tcUrl.
|
||||||
|
p := rtmp.NewProtocol(v.conn)
|
||||||
|
|
||||||
|
pkt := rtmp.NewConnectAppPacket()
|
||||||
|
pkt.CommandObject.Set("tcUrl", amf0.NewString(v.rtmpTcUrl))
|
||||||
|
if err = p.WritePacket(pkt, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := rtmp.NewConnectAppResPacket(pkt.TransactionID)
|
||||||
|
if _, err := p.ExpectPacket(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.proto = p
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPClient) Publish(ctx context.Context, rtmpUrl string) error {
|
||||||
|
if err := v.connect(rtmpUrl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p := v.proto
|
||||||
|
|
||||||
|
// Create RTMP stream.
|
||||||
|
if true {
|
||||||
|
pkt := rtmp.NewCreateStreamPacket()
|
||||||
|
if err := p.WritePacket(pkt, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := rtmp.NewCreateStreamResPacket(pkt.TransactionID)
|
||||||
|
if _, err := p.ExpectPacket(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.streamID = int(res.StreamID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish RTMP stream.
|
||||||
|
if true {
|
||||||
|
pkt := rtmp.NewPublishPacket()
|
||||||
|
pkt.StreamName = *amf0.NewString(v.rtmpStream)
|
||||||
|
if err := p.WritePacket(pkt, v.streamID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := rtmp.NewCallPacket()
|
||||||
|
if _, err := p.ExpectPacket(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPClient) Play(ctx context.Context, rtmpUrl string) error {
|
||||||
|
if err := v.connect(rtmpUrl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p := v.proto
|
||||||
|
|
||||||
|
// Create RTMP stream.
|
||||||
|
if true {
|
||||||
|
pkt := rtmp.NewCreateStreamPacket()
|
||||||
|
if err := p.WritePacket(pkt, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := rtmp.NewCreateStreamResPacket(pkt.TransactionID)
|
||||||
|
if _, err := p.ExpectPacket(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.streamID = int(res.StreamID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play RTMP stream.
|
||||||
|
if true {
|
||||||
|
pkt := rtmp.NewPlayPacket()
|
||||||
|
pkt.StreamName = *amf0.NewString(v.rtmpStream)
|
||||||
|
if err := p.WritePacket(pkt, v.streamID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := rtmp.NewCallPacket()
|
||||||
|
if _, err := p.ExpectPacket(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTMPPublisher struct {
|
||||||
|
client *RTMPClient
|
||||||
|
// Whether auto close transport when ingest done.
|
||||||
|
closeTransportWhenIngestDone bool
|
||||||
|
|
||||||
|
onSendPacket func(m *rtmp.Message) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRTMPPublisher() *RTMPPublisher {
|
||||||
|
v := &RTMPPublisher{
|
||||||
|
client: &RTMPClient{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, set to on.
|
||||||
|
v.closeTransportWhenIngestDone = true
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPublisher) Close() error {
|
||||||
|
return v.client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPublisher) Publish(ctx context.Context, rtmpUrl string) error {
|
||||||
|
return v.client.Publish(ctx, rtmpUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error {
|
||||||
|
// If ctx is cancelled, close the RTMP transport.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-ctx.Done()
|
||||||
|
if v.closeTransportWhenIngestDone {
|
||||||
|
v.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Consume all packets.
|
||||||
|
err := v.ingest(flvInput)
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ctx.Err() == context.Canceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPublisher) ingest(flvInput string) error {
|
||||||
|
p := v.client
|
||||||
|
|
||||||
|
fs, err := os.Open(flvInput)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fs.Close()
|
||||||
|
|
||||||
|
demuxer, err := flv.NewDemuxer(fs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, _, err = demuxer.ReadHeader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
tagType, tagSize, timestamp, err := demuxer.ReadTagHeader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := demuxer.ReadTag(tagSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagType != flv.TagTypeVideo && tagType != flv.TagTypeAudio {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
m := rtmp.NewStreamMessage(p.streamID)
|
||||||
|
m.MessageType = rtmp.MessageType(tagType)
|
||||||
|
m.Timestamp = uint64(timestamp)
|
||||||
|
m.Payload = tag
|
||||||
|
if err = p.proto.WriteMessage(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.onSendPacket != nil {
|
||||||
|
if err = v.onSendPacket(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RTMPPlayer struct {
|
||||||
|
// Transport.
|
||||||
|
client *RTMPClient
|
||||||
|
// FLV packager.
|
||||||
|
videoPackager flv.VideoPackager
|
||||||
|
|
||||||
|
onRecvPacket func(m *rtmp.Message, a *flv.AudioFrame, v *flv.VideoFrame) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRTMPPlayer() *RTMPPlayer {
|
||||||
|
return &RTMPPlayer{
|
||||||
|
client: &RTMPClient{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPlayer) Close() error {
|
||||||
|
return v.client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPlayer) Consume(ctx context.Context) error {
|
||||||
|
// If ctx is cancelled, close the RTMP transport.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer wg.Wait()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-ctx.Done()
|
||||||
|
v.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Consume all packets.
|
||||||
|
err := v.consume()
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ctx.Err() == context.Canceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *RTMPPlayer) consume() error {
|
||||||
|
for {
|
||||||
|
res, err := v.client.proto.ExpectMessage(rtmp.MessageTypeVideo, rtmp.MessageTypeAudio)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.onRecvPacket != 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DemuxRtpSpsPps(payload []byte) ([]byte, []*avc.NALU, error) {
|
||||||
|
// Parse RTP packet.
|
||||||
|
pkt := rtp.Packet{}
|
||||||
|
if err := pkt.Unmarshal(payload); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode H264 packet.
|
||||||
|
h264Packet := codecs.H264Packet{}
|
||||||
|
annexb, err := h264Packet.Unmarshal(pkt.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return annexb, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore if not STAP-A
|
||||||
|
if !bytes.HasPrefix(annexb, []byte{0x00, 0x00, 0x00, 0x01}) {
|
||||||
|
return annexb, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse to NALUs
|
||||||
|
rawNalus := bytes.Split(annexb, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
|
||||||
|
nalus := []*avc.NALU{}
|
||||||
|
for _, rawNalu := range rawNalus {
|
||||||
|
if len(rawNalu) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nalu := avc.NewNALU()
|
||||||
|
if err := nalu.UnmarshalBinary(rawNalu); err != nil {
|
||||||
|
return annexb, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nalus = append(nalus, nalu)
|
||||||
|
}
|
||||||
|
|
||||||
|
return annexb, nalus, nil
|
||||||
|
}
|
||||||
|
|
738
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/amf0/amf0.go
generated
vendored
Normal file
738
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/amf0/amf0.go
generated
vendored
Normal file
|
@ -0,0 +1,738 @@
|
||||||
|
// 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 amf0 package support AMF0 codec.
|
||||||
|
package amf0
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
oe "github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Please read @doc amf0_spec_121207.pdf, @page 4, @section 2.1 Types Overview
|
||||||
|
type marker uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
markerNumber marker = iota // 0
|
||||||
|
markerBoolean // 1
|
||||||
|
markerString // 2
|
||||||
|
markerObject // 3
|
||||||
|
markerMovieClip // 4
|
||||||
|
markerNull // 5
|
||||||
|
markerUndefined // 6
|
||||||
|
markerReference // 7
|
||||||
|
markerEcmaArray // 8
|
||||||
|
markerObjectEnd // 9
|
||||||
|
markerStrictArray // 10
|
||||||
|
markerDate // 11
|
||||||
|
markerLongString // 12
|
||||||
|
markerUnsupported // 13
|
||||||
|
markerRecordSet // 14
|
||||||
|
markerXmlDocument // 15
|
||||||
|
markerTypedObject // 16
|
||||||
|
markerAvmPlusObject // 17
|
||||||
|
|
||||||
|
markerForbidden marker = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v marker) String() string {
|
||||||
|
switch v {
|
||||||
|
case markerNumber:
|
||||||
|
return "Number"
|
||||||
|
case markerBoolean:
|
||||||
|
return "Boolean"
|
||||||
|
case markerString:
|
||||||
|
return "String"
|
||||||
|
case markerObject:
|
||||||
|
return "Object"
|
||||||
|
case markerNull:
|
||||||
|
return "Null"
|
||||||
|
case markerUndefined:
|
||||||
|
return "Undefined"
|
||||||
|
case markerReference:
|
||||||
|
return "Reference"
|
||||||
|
case markerEcmaArray:
|
||||||
|
return "EcmaArray"
|
||||||
|
case markerObjectEnd:
|
||||||
|
return "ObjectEnd"
|
||||||
|
case markerStrictArray:
|
||||||
|
return "StrictArray"
|
||||||
|
case markerDate:
|
||||||
|
return "Date"
|
||||||
|
case markerLongString:
|
||||||
|
return "LongString"
|
||||||
|
case markerUnsupported:
|
||||||
|
return "Unsupported"
|
||||||
|
case markerXmlDocument:
|
||||||
|
return "XmlDocument"
|
||||||
|
case markerTypedObject:
|
||||||
|
return "TypedObject"
|
||||||
|
case markerAvmPlusObject:
|
||||||
|
return "AvmPlusObject"
|
||||||
|
case markerMovieClip:
|
||||||
|
return "MovieClip"
|
||||||
|
case markerRecordSet:
|
||||||
|
return "RecordSet"
|
||||||
|
default:
|
||||||
|
return "Forbidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For utest to mock it.
|
||||||
|
type buffer interface {
|
||||||
|
Bytes() []byte
|
||||||
|
WriteByte(c byte) error
|
||||||
|
Write(p []byte) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var createBuffer = func() buffer {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All AMF0 things.
|
||||||
|
type Amf0 interface {
|
||||||
|
// Binary marshaler and unmarshaler.
|
||||||
|
encoding.BinaryUnmarshaler
|
||||||
|
encoding.BinaryMarshaler
|
||||||
|
// Get the size of bytes to marshal this object.
|
||||||
|
Size() int
|
||||||
|
|
||||||
|
// Get the Marker of any AMF0 stuff.
|
||||||
|
amf0Marker() marker
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery the amf0 object from the bytes b.
|
||||||
|
func Discovery(p []byte) (a Amf0, err error) {
|
||||||
|
if len(p) < 1 {
|
||||||
|
return nil, oe.Errorf("require 1 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
m := marker(p[0])
|
||||||
|
|
||||||
|
switch m {
|
||||||
|
case markerNumber:
|
||||||
|
return NewNumber(0), nil
|
||||||
|
case markerBoolean:
|
||||||
|
return NewBoolean(false), nil
|
||||||
|
case markerString:
|
||||||
|
return NewString(""), nil
|
||||||
|
case markerObject:
|
||||||
|
return NewObject(), nil
|
||||||
|
case markerNull:
|
||||||
|
return NewNull(), nil
|
||||||
|
case markerUndefined:
|
||||||
|
return NewUndefined(), nil
|
||||||
|
case markerReference:
|
||||||
|
case markerEcmaArray:
|
||||||
|
return NewEcmaArray(), nil
|
||||||
|
case markerObjectEnd:
|
||||||
|
return &objectEOF{}, nil
|
||||||
|
case markerStrictArray:
|
||||||
|
return NewStrictArray(), nil
|
||||||
|
case markerDate, markerLongString, markerUnsupported, markerXmlDocument,
|
||||||
|
markerTypedObject, markerAvmPlusObject, markerForbidden, markerMovieClip,
|
||||||
|
markerRecordSet:
|
||||||
|
return nil, oe.Errorf("Marker %v is not supported", m)
|
||||||
|
}
|
||||||
|
return nil, oe.Errorf("Marker %v is invalid", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The UTF8 string, please read @doc amf0_spec_121207.pdf, @page 3, @section 1.3.1 Strings and UTF-8
|
||||||
|
type amf0UTF8 string
|
||||||
|
|
||||||
|
func (v *amf0UTF8) Size() int {
|
||||||
|
return 2 + len(string(*v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *amf0UTF8) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 2 {
|
||||||
|
return oe.Errorf("require 2 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
size := uint16(p[0])<<8 | uint16(p[1])
|
||||||
|
|
||||||
|
if p = data[2:]; len(p) < int(size) {
|
||||||
|
return oe.Errorf("require %v bytes only %v", int(size), len(p))
|
||||||
|
}
|
||||||
|
*v = amf0UTF8(string(p[:size]))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *amf0UTF8) MarshalBinary() (data []byte, err error) {
|
||||||
|
data = make([]byte, v.Size())
|
||||||
|
|
||||||
|
size := uint16(len(string(*v)))
|
||||||
|
data[0] = byte(size >> 8)
|
||||||
|
data[1] = byte(size)
|
||||||
|
|
||||||
|
if size > 0 {
|
||||||
|
copy(data[2:], []byte(*v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number object, please read @doc amf0_spec_121207.pdf, @page 5, @section 2.2 Number Type
|
||||||
|
type Number float64
|
||||||
|
|
||||||
|
func NewNumber(f float64) *Number {
|
||||||
|
v := Number(f)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Number) amf0Marker() marker {
|
||||||
|
return markerNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Number) Size() int {
|
||||||
|
return 1 + 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Number) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 9 {
|
||||||
|
return oe.Errorf("require 9 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerNumber {
|
||||||
|
return oe.Errorf("Number marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := binary.BigEndian.Uint64(p[1:])
|
||||||
|
*v = Number(math.Float64frombits(f))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Number) MarshalBinary() (data []byte, err error) {
|
||||||
|
data = make([]byte, 9)
|
||||||
|
data[0] = byte(markerNumber)
|
||||||
|
f := math.Float64bits(float64(*v))
|
||||||
|
binary.BigEndian.PutUint64(data[1:], f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The string objet, please read @doc amf0_spec_121207.pdf, @page 5, @section 2.4 String Type
|
||||||
|
type String string
|
||||||
|
|
||||||
|
func NewString(s string) *String {
|
||||||
|
v := String(s)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *String) amf0Marker() marker {
|
||||||
|
return markerString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *String) Size() int {
|
||||||
|
u := amf0UTF8(*v)
|
||||||
|
return 1 + u.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *String) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 1 {
|
||||||
|
return oe.Errorf("require 1 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerString {
|
||||||
|
return oe.Errorf("String marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sv amf0UTF8
|
||||||
|
if err = sv.UnmarshalBinary(p[1:]); err != nil {
|
||||||
|
return oe.WithMessage(err, "utf8")
|
||||||
|
}
|
||||||
|
*v = String(string(sv))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *String) MarshalBinary() (data []byte, err error) {
|
||||||
|
u := amf0UTF8(*v)
|
||||||
|
|
||||||
|
var pb []byte
|
||||||
|
if pb, err = u.MarshalBinary(); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "utf8")
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append([]byte{byte(markerString)}, pb...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 object end type, please read @doc amf0_spec_121207.pdf, @page 5, @section 2.11 Object End Type
|
||||||
|
type objectEOF struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectEOF) amf0Marker() marker {
|
||||||
|
return markerObjectEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectEOF) Size() int {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectEOF) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
p := data
|
||||||
|
|
||||||
|
if len(p) < 3 {
|
||||||
|
return oe.Errorf("require 3 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p[0] != 0 || p[1] != 0 || p[2] != 9 {
|
||||||
|
return oe.Errorf("EOF marker %v is illegal", p[0:3])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectEOF) MarshalBinary() (data []byte, err error) {
|
||||||
|
return []byte{0, 0, 9}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use array for object and ecma array, to keep the original order.
|
||||||
|
type property struct {
|
||||||
|
key amf0UTF8
|
||||||
|
value Amf0
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object-like AMF0 structure, like object and ecma array and strict array.
|
||||||
|
type objectBase struct {
|
||||||
|
properties []*property
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectBase) Size() int {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
var size int
|
||||||
|
|
||||||
|
for _, p := range v.properties {
|
||||||
|
key, value := p.key, p.value
|
||||||
|
size += key.Size() + value.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectBase) Get(key string) Amf0 {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
for _, p := range v.properties {
|
||||||
|
if string(p.key) == key {
|
||||||
|
return p.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectBase) Set(key string, value Amf0) *objectBase {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
prop := &property{key: amf0UTF8(key), value: value}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
for i, p := range v.properties {
|
||||||
|
if string(p.key) == key {
|
||||||
|
v.properties[i] = prop
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
v.properties = append(v.properties, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectBase) unmarshal(p []byte, eof bool, maxElems int) (err error) {
|
||||||
|
// if no eof, elems specified by maxElems.
|
||||||
|
if !eof && maxElems < 0 {
|
||||||
|
return oe.Errorf("maxElems=%v without eof", maxElems)
|
||||||
|
}
|
||||||
|
// if eof, maxElems must be -1.
|
||||||
|
if eof && maxElems != -1 {
|
||||||
|
return oe.Errorf("maxElems=%v with eof", maxElems)
|
||||||
|
}
|
||||||
|
|
||||||
|
readOne := func() (amf0UTF8, Amf0, error) {
|
||||||
|
var u amf0UTF8
|
||||||
|
if err = u.UnmarshalBinary(p); err != nil {
|
||||||
|
return "", nil, oe.WithMessage(err, "prop name")
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p[u.Size():]
|
||||||
|
var a Amf0
|
||||||
|
if a, err = Discovery(p); err != nil {
|
||||||
|
return "", nil, oe.WithMessage(err, fmt.Sprintf("discover prop %v", string(u)))
|
||||||
|
}
|
||||||
|
return u, a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pushOne := func(u amf0UTF8, a Amf0) error {
|
||||||
|
// For object property, consume the whole bytes.
|
||||||
|
if err = a.UnmarshalBinary(p); err != nil {
|
||||||
|
return oe.WithMessage(err, fmt.Sprintf("unmarshal prop %v", string(u)))
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(string(u), a)
|
||||||
|
p = p[a.Size():]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for eof {
|
||||||
|
u, a, err := readOne()
|
||||||
|
if err != nil {
|
||||||
|
return oe.WithMessage(err, "read")
|
||||||
|
}
|
||||||
|
|
||||||
|
// For object EOF, we should only consume total 3bytes.
|
||||||
|
if u.Size() == 2 && a.amf0Marker() == markerObjectEnd {
|
||||||
|
// 2 bytes is consumed by u(name), the a(eof) should only consume 1 byte.
|
||||||
|
p = p[1:]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pushOne(u, a); err != nil {
|
||||||
|
return oe.WithMessage(err, "push")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(v.properties) < maxElems {
|
||||||
|
u, a, err := readOne()
|
||||||
|
if err != nil {
|
||||||
|
return oe.WithMessage(err, "read")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pushOne(u, a); err != nil {
|
||||||
|
return oe.WithMessage(err, "push")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectBase) marshal(b buffer) (err error) {
|
||||||
|
v.lock.Lock()
|
||||||
|
defer v.lock.Unlock()
|
||||||
|
|
||||||
|
var pb []byte
|
||||||
|
for _, p := range v.properties {
|
||||||
|
key, value := p.key, p.value
|
||||||
|
|
||||||
|
if pb, err = key.MarshalBinary(); err != nil {
|
||||||
|
return oe.WithMessage(err, fmt.Sprintf("marshal %v", string(key)))
|
||||||
|
}
|
||||||
|
if _, err = b.Write(pb); err != nil {
|
||||||
|
return oe.Wrapf(err, "write %v", string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pb, err = value.MarshalBinary(); err != nil {
|
||||||
|
return oe.WithMessage(err, fmt.Sprintf("marshal value for %v", string(key)))
|
||||||
|
}
|
||||||
|
if _, err = b.Write(pb); err != nil {
|
||||||
|
return oe.Wrapf(err, "marshal value for %v", string(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 object, please read @doc amf0_spec_121207.pdf, @page 5, @section 2.5 Object Type
|
||||||
|
type Object struct {
|
||||||
|
objectBase
|
||||||
|
eof objectEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewObject() *Object {
|
||||||
|
v := &Object{}
|
||||||
|
v.properties = []*property{}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Object) amf0Marker() marker {
|
||||||
|
return markerObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Object) Size() int {
|
||||||
|
return int(1) + v.eof.Size() + v.objectBase.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Object) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 1 {
|
||||||
|
return oe.Errorf("require 1 byte only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerObject {
|
||||||
|
return oe.Errorf("Object marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
p = p[1:]
|
||||||
|
|
||||||
|
if err = v.unmarshal(p, true, -1); err != nil {
|
||||||
|
return oe.WithMessage(err, "unmarshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Object) MarshalBinary() (data []byte, err error) {
|
||||||
|
b := createBuffer()
|
||||||
|
|
||||||
|
if err = b.WriteByte(byte(markerObject)); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = v.marshal(b); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pb []byte
|
||||||
|
if pb, err = v.eof.MarshalBinary(); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "marshal")
|
||||||
|
}
|
||||||
|
if _, err = b.Write(pb); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 ecma array, please read @doc amf0_spec_121207.pdf, @page 6, @section 2.10 ECMA Array Type
|
||||||
|
type EcmaArray struct {
|
||||||
|
objectBase
|
||||||
|
count uint32
|
||||||
|
eof objectEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEcmaArray() *EcmaArray {
|
||||||
|
v := &EcmaArray{}
|
||||||
|
v.properties = []*property{}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *EcmaArray) amf0Marker() marker {
|
||||||
|
return markerEcmaArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *EcmaArray) Size() int {
|
||||||
|
return int(1) + 4 + v.eof.Size() + v.objectBase.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *EcmaArray) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 5 {
|
||||||
|
return oe.Errorf("require 5 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerEcmaArray {
|
||||||
|
return oe.Errorf("EcmaArray marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
v.count = binary.BigEndian.Uint32(p[1:])
|
||||||
|
p = p[5:]
|
||||||
|
|
||||||
|
if err = v.unmarshal(p, true, -1); err != nil {
|
||||||
|
return oe.WithMessage(err, "unmarshal")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *EcmaArray) MarshalBinary() (data []byte, err error) {
|
||||||
|
b := createBuffer()
|
||||||
|
|
||||||
|
if err = b.WriteByte(byte(markerEcmaArray)); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = binary.Write(b, binary.BigEndian, v.count); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = v.marshal(b); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
var pb []byte
|
||||||
|
if pb, err = v.eof.MarshalBinary(); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "marshal")
|
||||||
|
}
|
||||||
|
if _, err = b.Write(pb); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 strict array, please read @doc amf0_spec_121207.pdf, @page 7, @section 2.12 Strict Array Type
|
||||||
|
type StrictArray struct {
|
||||||
|
objectBase
|
||||||
|
count uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStrictArray() *StrictArray {
|
||||||
|
v := &StrictArray{}
|
||||||
|
v.properties = []*property{}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StrictArray) amf0Marker() marker {
|
||||||
|
return markerStrictArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StrictArray) Size() int {
|
||||||
|
return int(1) + 4 + v.objectBase.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StrictArray) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 5 {
|
||||||
|
return oe.Errorf("require 5 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerStrictArray {
|
||||||
|
return oe.Errorf("StrictArray marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
v.count = binary.BigEndian.Uint32(p[1:])
|
||||||
|
p = p[5:]
|
||||||
|
|
||||||
|
if int(v.count) <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = v.unmarshal(p, false, int(v.count)); err != nil {
|
||||||
|
return oe.WithMessage(err, "unmarshal")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StrictArray) MarshalBinary() (data []byte, err error) {
|
||||||
|
b := createBuffer()
|
||||||
|
|
||||||
|
if err = b.WriteByte(byte(markerStrictArray)); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = binary.Write(b, binary.BigEndian, v.count); err != nil {
|
||||||
|
return nil, oe.Wrap(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = v.marshal(b); err != nil {
|
||||||
|
return nil, oe.WithMessage(err, "marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The single marker object, for all AMF0 which only has the marker, like null and undefined.
|
||||||
|
type singleMarkerObject struct {
|
||||||
|
target marker
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSingleMarkerObject(m marker) singleMarkerObject {
|
||||||
|
return singleMarkerObject{target: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *singleMarkerObject) amf0Marker() marker {
|
||||||
|
return v.target
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *singleMarkerObject) Size() int {
|
||||||
|
return int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *singleMarkerObject) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 1 {
|
||||||
|
return oe.Errorf("require 1 byte only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != v.target {
|
||||||
|
return oe.Errorf("%v marker %v is illegal", v.target, m)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *singleMarkerObject) MarshalBinary() (data []byte, err error) {
|
||||||
|
return []byte{byte(v.target)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 null, please read @doc amf0_spec_121207.pdf, @page 6, @section 2.7 null Type
|
||||||
|
type null struct {
|
||||||
|
singleMarkerObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNull() *null {
|
||||||
|
v := null{}
|
||||||
|
v.singleMarkerObject = newSingleMarkerObject(markerNull)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 undefined, please read @doc amf0_spec_121207.pdf, @page 6, @section 2.8 undefined Type
|
||||||
|
type undefined struct {
|
||||||
|
singleMarkerObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUndefined() Amf0 {
|
||||||
|
v := undefined{}
|
||||||
|
v.singleMarkerObject = newSingleMarkerObject(markerUndefined)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AMF0 boolean, please read @doc amf0_spec_121207.pdf, @page 5, @section 2.3 Boolean Type
|
||||||
|
type Boolean bool
|
||||||
|
|
||||||
|
func NewBoolean(b bool) Amf0 {
|
||||||
|
v := Boolean(b)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Boolean) amf0Marker() marker {
|
||||||
|
return markerBoolean
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Boolean) Size() int {
|
||||||
|
return int(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Boolean) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
var p []byte
|
||||||
|
if p = data; len(p) < 2 {
|
||||||
|
return oe.Errorf("require 2 bytes only %v", len(p))
|
||||||
|
}
|
||||||
|
if m := marker(p[0]); m != markerBoolean {
|
||||||
|
return oe.Errorf("BOOL marker %v is illegal", m)
|
||||||
|
}
|
||||||
|
if p[1] == 0 {
|
||||||
|
*v = false
|
||||||
|
} else {
|
||||||
|
*v = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Boolean) MarshalBinary() (data []byte, err error) {
|
||||||
|
var b byte
|
||||||
|
if *v {
|
||||||
|
b = 1
|
||||||
|
}
|
||||||
|
return []byte{byte(markerBoolean), b}, nil
|
||||||
|
}
|
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
|
||||||
|
}
|
1756
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/rtmp/rtmp.go
generated
vendored
Normal file
1756
trunk/3rdparty/srs-bench/vendor/github.com/ossrs/go-oryx-lib/rtmp/rtmp.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
3
trunk/3rdparty/srs-bench/vendor/modules.txt
vendored
3
trunk/3rdparty/srs-bench/vendor/modules.txt
vendored
|
@ -3,9 +3,12 @@ github.com/google/uuid
|
||||||
# github.com/ossrs/go-oryx-lib v0.0.8
|
# github.com/ossrs/go-oryx-lib v0.0.8
|
||||||
## 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/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
|
||||||
|
github.com/ossrs/go-oryx-lib/rtmp
|
||||||
# github.com/pion/datachannel v1.4.21
|
# github.com/pion/datachannel v1.4.21
|
||||||
github.com/pion/datachannel
|
github.com/pion/datachannel
|
||||||
# github.com/pion/dtls/v2 v2.0.8
|
# github.com/pion/dtls/v2 v2.0.8
|
||||||
|
|
|
@ -25,7 +25,7 @@ rtc_server {
|
||||||
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
||||||
#
|
#
|
||||||
# The * means retrieving server IP automatically, from all network interfaces,
|
# The * means retrieving server IP automatically, from all network interfaces,
|
||||||
# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,17 @@ rtc_server {
|
||||||
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
||||||
#
|
#
|
||||||
# The * means retrieving server IP automatically, from all network interfaces,
|
# The * means retrieving server IP automatically, from all network interfaces,
|
||||||
# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp on;
|
||||||
}
|
}
|
||||||
http_remux {
|
http_remux {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
|
|
@ -17,6 +17,7 @@ http_server {
|
||||||
rtc_server {
|
rtc_server {
|
||||||
enabled on;
|
enabled on;
|
||||||
listen 8000;
|
listen 8000;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
|
@ -29,5 +30,9 @@ vhost __defaultVhost__ {
|
||||||
}
|
}
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc off;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,7 +388,7 @@ rtc_server {
|
||||||
# And by multiple ENV variables:
|
# And by multiple ENV variables:
|
||||||
# $CANDIDATE $EIP # TODO: Implements it.
|
# $CANDIDATE $EIP # TODO: Implements it.
|
||||||
# @remark For Firefox, the candidate MUST be IP, MUST NOT be DNS name.
|
# @remark For Firefox, the candidate MUST be IP, MUST NOT be DNS name.
|
||||||
# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
# default: *
|
# default: *
|
||||||
candidate *;
|
candidate *;
|
||||||
# The IP family filter for auto discover candidate, it can be:
|
# The IP family filter for auto discover candidate, it can be:
|
||||||
|
@ -459,19 +459,16 @@ vhost rtc.vhost.srs.com {
|
||||||
# default: 0
|
# default: 0
|
||||||
drop_for_pt 0;
|
drop_for_pt 0;
|
||||||
###############################################################
|
###############################################################
|
||||||
# For transmuxing RTMP to RTC, the strategy for bframe.
|
# Whether enable transmuxing RTMP to RTC.
|
||||||
# keep Keep bframe, which may make browser with playing problems.
|
# If enabled, transcode aac to opus.
|
||||||
# discard Discard bframe, maybe cause browser with little problems.
|
# default: off
|
||||||
# default: discard
|
rtmp_to_rtc off;
|
||||||
bframe discard;
|
# Whether keep B-frame, which is normal feature in live streaming,
|
||||||
# For transmuxing RTMP to RTC, the strategy for aac audio.
|
# but usually disabled in RTC.
|
||||||
# transcode Transcode aac to opus.
|
# default: off
|
||||||
# discard Discard aac audio packet.
|
keep_bframe off;
|
||||||
# default: transcode
|
|
||||||
aac transcode;
|
|
||||||
###############################################################
|
###############################################################
|
||||||
# For transmuxing RTC to RTMP.
|
# Whether enable transmuxing RTC to RTMP.
|
||||||
# Whether trans-mux RTC to RTMP streaming.
|
|
||||||
# Default: off
|
# Default: off
|
||||||
rtc_to_rtmp off;
|
rtc_to_rtmp off;
|
||||||
# The PLI interval in seconds, for RTC to RTMP.
|
# The PLI interval in seconds, for RTC to RTMP.
|
||||||
|
@ -889,6 +886,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ rtc_server {
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
bframe discard;
|
keep_bframe off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,20 +20,15 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
bframe discard;
|
rtmp_to_rtc on;
|
||||||
|
keep_bframe off;
|
||||||
rtc_to_rtmp on;
|
rtc_to_rtmp on;
|
||||||
}
|
}
|
||||||
play {
|
play {
|
||||||
|
|
|
@ -21,19 +21,18 @@ stats {
|
||||||
}
|
}
|
||||||
rtc_server {
|
rtc_server {
|
||||||
enabled on;
|
enabled on;
|
||||||
# Listen at udp://8000
|
|
||||||
listen 8000;
|
listen 8000;
|
||||||
#
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
# 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/wiki/v4_CN_RTCWiki#config-candidate
|
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc off;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp off;
|
||||||
}
|
}
|
||||||
http_remux {
|
http_remux {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
|
|
@ -19,19 +19,17 @@ stats {
|
||||||
}
|
}
|
||||||
rtc_server {
|
rtc_server {
|
||||||
enabled on;
|
enabled on;
|
||||||
# Listen at udp://8000
|
|
||||||
listen 8000;
|
listen 8000;
|
||||||
#
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
# 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/wiki/v4_CN_RTCWiki#config-candidate
|
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
rtc_to_rtmp on;
|
rtc_to_rtmp on;
|
||||||
}
|
}
|
||||||
http_remux {
|
http_remux {
|
||||||
|
|
40
trunk/conf/rtmp2rtc.conf
Normal file
40
trunk/conf/rtmp2rtc.conf
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
listen 1935;
|
||||||
|
max_connections 1000;
|
||||||
|
daemon off;
|
||||||
|
srs_log_tank console;
|
||||||
|
|
||||||
|
http_server {
|
||||||
|
enabled on;
|
||||||
|
listen 8080;
|
||||||
|
dir ./objs/nginx/html;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_api {
|
||||||
|
enabled on;
|
||||||
|
listen 1985;
|
||||||
|
}
|
||||||
|
stats {
|
||||||
|
network 0;
|
||||||
|
}
|
||||||
|
rtc_server {
|
||||||
|
enabled on;
|
||||||
|
listen 8000;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
|
candidate $CANDIDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
vhost __defaultVhost__ {
|
||||||
|
rtc {
|
||||||
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp on;
|
||||||
|
}
|
||||||
|
http_remux {
|
||||||
|
enabled on;
|
||||||
|
mount [vhost]/[app]/[stream].flv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ http_server {
|
||||||
rtc_server {
|
rtc_server {
|
||||||
enabled on;
|
enabled on;
|
||||||
listen 8000;
|
listen 8000;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
|
@ -30,5 +31,9 @@ vhost __defaultVhost__ {
|
||||||
}
|
}
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc off;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ rtc_server {
|
||||||
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
||||||
#
|
#
|
||||||
# The * means retrieving server IP automatically, from all network interfaces,
|
# The * means retrieving server IP automatically, from all network interfaces,
|
||||||
# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate
|
||||||
candidate $CANDIDATE;
|
candidate $CANDIDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,10 @@ rtc_server {
|
||||||
vhost __defaultVhost__ {
|
vhost __defaultVhost__ {
|
||||||
rtc {
|
rtc {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc
|
||||||
|
rtmp_to_rtc off;
|
||||||
|
# @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp
|
||||||
|
rtc_to_rtmp off;
|
||||||
}
|
}
|
||||||
http_remux {
|
http_remux {
|
||||||
enabled on;
|
enabled on;
|
||||||
|
|
|
@ -27,6 +27,10 @@ The changelog for SRS.
|
||||||
|
|
||||||
## SRS 4.0 Changelog
|
## SRS 4.0 Changelog
|
||||||
|
|
||||||
|
* v4.0, 2021-10-11, Fix [#1641](https://github.com/ossrs/srs/issues/1641), HLS/RTC picture corrupt for SPS/PPS lost. v4.0.175
|
||||||
|
* v4.0, 2021-10-11, RTC: Refine config, aac to rtmp_to_rtc, bframe to keep_bframe. v4.0.174
|
||||||
|
* v4.0, 2021-10-10, For [#1641](https://github.com/ossrs/srs/issues/1641), Support RTMP publish and play regression test. v4.0.173
|
||||||
|
* v4.0, 2021-10-10, RTC: Change rtc.aac to discard by default. v4.0.172
|
||||||
* v4.0, 2021-10-10, Fix [#2304](https://github.com/ossrs/srs/issues/2304) Remove Push RTSP feature. v4.0.171
|
* v4.0, 2021-10-10, Fix [#2304](https://github.com/ossrs/srs/issues/2304) Remove Push RTSP feature. v4.0.171
|
||||||
* v4.0, 2021-10-10, Fix [#2653](https://github.com/ossrs/srs/issues/2653) Remove HTTP RAW API. v4.0.170
|
* v4.0, 2021-10-10, Fix [#2653](https://github.com/ossrs/srs/issues/2653) Remove HTTP RAW API. v4.0.170
|
||||||
* v4.0, 2021-10-08, Merge [#2654](https://github.com/ossrs/srs/pull/2654): Parse width and width from SPS/PPS. v4.0.169
|
* v4.0, 2021-10-08, Merge [#2654](https://github.com/ossrs/srs/pull/2654): Parse width and width from SPS/PPS. v4.0.169
|
||||||
|
|
|
@ -522,6 +522,32 @@ srs_error_t srs_config_transform_vhost(SrsConfDirective* root)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SRS3.0, change the forward.
|
||||||
|
// SRS1/2:
|
||||||
|
// vhost { rtc { aac; } }
|
||||||
|
// SRS3+:
|
||||||
|
// vhost { rtc { rtmp_to_rtc; } }
|
||||||
|
if (n == "rtc") {
|
||||||
|
SrsConfDirective* aac = conf->get("aac");
|
||||||
|
if (aac) {
|
||||||
|
string v = aac->arg0() == "transcode" ? "on" : "off";
|
||||||
|
conf->get_or_create("rtmp_to_rtc")->set_arg0(v);
|
||||||
|
conf->remove(aac); srs_freep(aac);
|
||||||
|
srs_warn("transform: vhost.rtc.aac to vhost.rtc.rtmp_to_rtc %s", v.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsConfDirective* bframe = conf->get("bframe");
|
||||||
|
if (bframe) {
|
||||||
|
string v = bframe->arg0() == "keep" ? "on" : "off";
|
||||||
|
conf->get_or_create("keep_bframe")->set_arg0(v);
|
||||||
|
conf->remove(bframe); srs_freep(bframe);
|
||||||
|
srs_warn("transform: vhost.rtc.bframe to vhost.rtc.keep_bframe %s", v.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2782,7 +2808,7 @@ srs_error_t SrsConfig::check_normal_config()
|
||||||
if (m != "enabled" && m != "nack" && m != "twcc" && m != "nack_no_copy"
|
if (m != "enabled" && m != "nack" && m != "twcc" && m != "nack_no_copy"
|
||||||
&& m != "bframe" && m != "aac" && m != "stun_timeout" && m != "stun_strict_check"
|
&& m != "bframe" && m != "aac" && m != "stun_timeout" && m != "stun_strict_check"
|
||||||
&& m != "dtls_role" && m != "dtls_version" && m != "drop_for_pt" && m != "rtc_to_rtmp"
|
&& m != "dtls_role" && m != "dtls_version" && m != "drop_for_pt" && m != "rtc_to_rtmp"
|
||||||
&& m != "pli_for_rtmp") {
|
&& m != "pli_for_rtmp" && m != "rtmp_to_rtc" && m != "keep_bframe") {
|
||||||
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.rtc.%s of %s", m.c_str(), vhost->arg0().c_str());
|
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.rtc.%s of %s", m.c_str(), vhost->arg0().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3641,25 +3667,7 @@ bool SrsConfig::get_rtc_enabled(string vhost)
|
||||||
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_bframe_discard(string vhost)
|
bool SrsConfig::get_rtc_keep_bframe(string vhost)
|
||||||
{
|
|
||||||
static bool DEFAULT = true;
|
|
||||||
|
|
||||||
SrsConfDirective* conf = get_rtc(vhost);
|
|
||||||
|
|
||||||
if (!conf) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
conf = conf->get("bframe");
|
|
||||||
if (!conf || conf->arg0().empty()) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf->arg0() != "keep";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SrsConfig::get_rtc_aac_discard(string vhost)
|
|
||||||
{
|
{
|
||||||
static bool DEFAULT = false;
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
@ -3669,12 +3677,30 @@ bool SrsConfig::get_rtc_aac_discard(string vhost)
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = conf->get("aac");
|
conf = conf->get("keep_bframe");
|
||||||
if (!conf || conf->arg0().empty()) {
|
if (!conf || conf->arg0().empty()) {
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf->arg0() == "discard";
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsConfig::get_rtc_from_rtmp(string vhost)
|
||||||
|
{
|
||||||
|
static bool DEFAULT = false;
|
||||||
|
|
||||||
|
SrsConfDirective* conf = get_rtc(vhost);
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = conf->get("rtmp_to_rtc");
|
||||||
|
if (!conf || conf->arg0().empty()) {
|
||||||
|
return DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRS_CONF_PERFER_FALSE(conf->arg0());
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_utime_t SrsConfig::get_rtc_stun_timeout(string vhost)
|
srs_utime_t SrsConfig::get_rtc_stun_timeout(string vhost)
|
||||||
|
|
|
@ -481,8 +481,8 @@ private:
|
||||||
public:
|
public:
|
||||||
SrsConfDirective* get_rtc(std::string vhost);
|
SrsConfDirective* get_rtc(std::string vhost);
|
||||||
bool get_rtc_enabled(std::string vhost);
|
bool get_rtc_enabled(std::string vhost);
|
||||||
bool get_rtc_bframe_discard(std::string vhost);
|
bool get_rtc_keep_bframe(std::string vhost);
|
||||||
bool get_rtc_aac_discard(std::string vhost);
|
bool get_rtc_from_rtmp(std::string vhost);
|
||||||
srs_utime_t get_rtc_stun_timeout(std::string vhost);
|
srs_utime_t get_rtc_stun_timeout(std::string vhost);
|
||||||
bool get_rtc_stun_strict_check(std::string vhost);
|
bool get_rtc_stun_strict_check(std::string vhost);
|
||||||
std::string get_rtc_dtls_role(std::string vhost);
|
std::string get_rtc_dtls_role(std::string vhost);
|
||||||
|
|
|
@ -707,8 +707,8 @@ SrsRtcFromRtmpBridger::SrsRtcFromRtmpBridger(SrsRtcSource* source)
|
||||||
source_ = source;
|
source_ = source;
|
||||||
format = new SrsRtmpFormat();
|
format = new SrsRtmpFormat();
|
||||||
codec_ = new SrsAudioTranscoder();
|
codec_ = new SrsAudioTranscoder();
|
||||||
discard_aac = false;
|
rtmp_to_rtc = false;
|
||||||
discard_bframe = false;
|
keep_bframe = false;
|
||||||
merge_nalus = false;
|
merge_nalus = false;
|
||||||
meta = new SrsMetaCache();
|
meta = new SrsMetaCache();
|
||||||
audio_sequence = 0;
|
audio_sequence = 0;
|
||||||
|
@ -743,22 +743,24 @@ srs_error_t SrsRtcFromRtmpBridger::initialize(SrsRequest* r)
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
req = r;
|
req = r;
|
||||||
|
rtmp_to_rtc = _srs_config->get_rtc_from_rtmp(req->vhost);
|
||||||
|
|
||||||
|
if (rtmp_to_rtc) {
|
||||||
if ((err = format->initialize()) != srs_success) {
|
if ((err = format->initialize()) != srs_success) {
|
||||||
return srs_error_wrap(err, "format initialize");
|
return srs_error_wrap(err, "format initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
int bitrate = 48000; // The output bitrate in bps.
|
int bitrate = 48000; // The output bitrate in bps.
|
||||||
if ((err = codec_->initialize(SrsAudioCodecIdAAC, SrsAudioCodecIdOpus, kAudioChannel, kAudioSamplerate, bitrate)) != srs_success) {
|
if ((err = codec_->initialize(SrsAudioCodecIdAAC, SrsAudioCodecIdOpus, kAudioChannel, kAudioSamplerate,
|
||||||
|
bitrate)) != srs_success) {
|
||||||
return srs_error_wrap(err, "init codec");
|
return srs_error_wrap(err, "init codec");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: FIXME: Support reload.
|
keep_bframe = _srs_config->get_rtc_keep_bframe(req->vhost);
|
||||||
discard_aac = _srs_config->get_rtc_aac_discard(req->vhost);
|
|
||||||
discard_bframe = _srs_config->get_rtc_bframe_discard(req->vhost);
|
|
||||||
merge_nalus = _srs_config->get_rtc_server_merge_nalus();
|
merge_nalus = _srs_config->get_rtc_server_merge_nalus();
|
||||||
srs_trace("RTC bridge from RTMP, discard_aac=%d, discard_bframe=%d, merge_nalus=%d",
|
srs_trace("RTC bridge from RTMP, rtmp2rtc=%d, keep_bframe=%d, merge_nalus=%d",
|
||||||
discard_aac, discard_bframe, merge_nalus);
|
rtmp_to_rtc, keep_bframe, merge_nalus);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -767,6 +769,10 @@ srs_error_t SrsRtcFromRtmpBridger::on_publish()
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (!rtmp_to_rtc) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: FIXME: Should sync with bridger?
|
// TODO: FIXME: Should sync with bridger?
|
||||||
if ((err = source_->on_publish()) != srs_success) {
|
if ((err = source_->on_publish()) != srs_success) {
|
||||||
return srs_error_wrap(err, "source publish");
|
return srs_error_wrap(err, "source publish");
|
||||||
|
@ -781,6 +787,10 @@ srs_error_t SrsRtcFromRtmpBridger::on_publish()
|
||||||
|
|
||||||
void SrsRtcFromRtmpBridger::on_unpublish()
|
void SrsRtcFromRtmpBridger::on_unpublish()
|
||||||
{
|
{
|
||||||
|
if (!rtmp_to_rtc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the metadata cache, to make VLC happy when disable/enable stream.
|
// Reset the metadata cache, to make VLC happy when disable/enable stream.
|
||||||
// @see https://github.com/ossrs/srs/issues/1630#issuecomment-597979448
|
// @see https://github.com/ossrs/srs/issues/1630#issuecomment-597979448
|
||||||
meta->update_previous_vsh();
|
meta->update_previous_vsh();
|
||||||
|
@ -795,6 +805,10 @@ srs_error_t SrsRtcFromRtmpBridger::on_audio(SrsSharedPtrMessage* msg)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (!rtmp_to_rtc) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: FIXME: Support parsing OPUS for RTC.
|
// TODO: FIXME: Support parsing OPUS for RTC.
|
||||||
if ((err = format->on_audio(msg)) != srs_success) {
|
if ((err = format->on_audio(msg)) != srs_success) {
|
||||||
return srs_error_wrap(err, "format consume audio");
|
return srs_error_wrap(err, "format consume audio");
|
||||||
|
@ -813,7 +827,7 @@ srs_error_t SrsRtcFromRtmpBridger::on_audio(SrsSharedPtrMessage* msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When drop aac audio packet, never transcode.
|
// When drop aac audio packet, never transcode.
|
||||||
if (discard_aac && acodec == SrsAudioCodecIdAAC) {
|
if (acodec != SrsAudioCodecIdAAC) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -905,6 +919,10 @@ srs_error_t SrsRtcFromRtmpBridger::on_video(SrsSharedPtrMessage* msg)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
if (!rtmp_to_rtc) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// cache the sequence header if h264
|
// cache the sequence header if h264
|
||||||
bool is_sequence_header = SrsFlvVideo::sh(msg->payload, msg->size);
|
bool is_sequence_header = SrsFlvVideo::sh(msg->payload, msg->size);
|
||||||
if (is_sequence_header && (err = meta->update_vsh(msg)) != srs_success) {
|
if (is_sequence_header && (err = meta->update_vsh(msg)) != srs_success) {
|
||||||
|
@ -993,7 +1011,7 @@ srs_error_t SrsRtcFromRtmpBridger::filter(SrsSharedPtrMessage* msg, SrsFormat* f
|
||||||
|
|
||||||
// Because RTC does not support B-frame, so we will drop them.
|
// Because RTC does not support B-frame, so we will drop them.
|
||||||
// TODO: Drop B-frame in better way, which not cause picture corruption.
|
// TODO: Drop B-frame in better way, which not cause picture corruption.
|
||||||
if (discard_bframe) {
|
if (!keep_bframe) {
|
||||||
if ((err = sample->parse_bframe()) != srs_success) {
|
if ((err = sample->parse_bframe()) != srs_success) {
|
||||||
return srs_error_wrap(err, "parse bframe");
|
return srs_error_wrap(err, "parse bframe");
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,9 +254,9 @@ private:
|
||||||
// The metadata cache.
|
// The metadata cache.
|
||||||
SrsMetaCache* meta;
|
SrsMetaCache* meta;
|
||||||
private:
|
private:
|
||||||
bool discard_aac;
|
bool rtmp_to_rtc;
|
||||||
SrsAudioTranscoder* codec_;
|
SrsAudioTranscoder* codec_;
|
||||||
bool discard_bframe;
|
bool keep_bframe;
|
||||||
bool merge_nalus;
|
bool merge_nalus;
|
||||||
uint16_t audio_sequence;
|
uint16_t audio_sequence;
|
||||||
uint16_t video_sequence;
|
uint16_t video_sequence;
|
||||||
|
|
|
@ -863,6 +863,7 @@ srs_error_t SrsRtmpConn::do_publishing(SrsLiveSource* source, SrsPublishRecvThre
|
||||||
SrsAutoFree(SrsPithyPrint, pprint);
|
SrsAutoFree(SrsPithyPrint, pprint);
|
||||||
|
|
||||||
// start isolate recv thread.
|
// start isolate recv thread.
|
||||||
|
// TODO: FIXME: Pass the callback here.
|
||||||
if ((err = rtrd->start()) != srs_success) {
|
if ((err = rtrd->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "rtmp: receive thread");
|
return srs_error_wrap(err, "rtmp: receive thread");
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 4
|
#define VERSION_MAJOR 4
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 171
|
#define VERSION_REVISION 175
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -818,10 +818,6 @@ srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream)
|
||||||
if (!stream->require(sequenceParameterSetLength)) {
|
if (!stream->require(sequenceParameterSetLength)) {
|
||||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "decode SPS data");
|
return srs_error_new(ERROR_HLS_DECODE_ERROR, "decode SPS data");
|
||||||
}
|
}
|
||||||
if (vcodec->sequenceParameterSetNALUnit.size() > 0) {
|
|
||||||
stream->skip(sequenceParameterSetLength);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sequenceParameterSetLength > 0) {
|
if (sequenceParameterSetLength > 0) {
|
||||||
vcodec->sequenceParameterSetNALUnit.resize(sequenceParameterSetLength);
|
vcodec->sequenceParameterSetNALUnit.resize(sequenceParameterSetLength);
|
||||||
stream->read_bytes(&vcodec->sequenceParameterSetNALUnit[0], sequenceParameterSetLength);
|
stream->read_bytes(&vcodec->sequenceParameterSetNALUnit[0], sequenceParameterSetLength);
|
||||||
|
@ -846,10 +842,6 @@ srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream)
|
||||||
if (!stream->require(pictureParameterSetLength)) {
|
if (!stream->require(pictureParameterSetLength)) {
|
||||||
return srs_error_new(ERROR_HLS_DECODE_ERROR, "decode PPS data");
|
return srs_error_new(ERROR_HLS_DECODE_ERROR, "decode PPS data");
|
||||||
}
|
}
|
||||||
if (vcodec->pictureParameterSetNALUnit.size() > 0) {
|
|
||||||
stream->skip(pictureParameterSetLength);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pictureParameterSetLength > 0) {
|
if (pictureParameterSetLength > 0) {
|
||||||
vcodec->pictureParameterSetNALUnit.resize(pictureParameterSetLength);
|
vcodec->pictureParameterSetNALUnit.resize(pictureParameterSetLength);
|
||||||
stream->read_bytes(&vcodec->pictureParameterSetNALUnit[0], pictureParameterSetLength);
|
stream->read_bytes(&vcodec->pictureParameterSetNALUnit[0], pictureParameterSetLength);
|
||||||
|
|
|
@ -3575,8 +3575,8 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5)
|
||||||
EXPECT_FALSE(conf.get_http_api_crossdomain());
|
EXPECT_FALSE(conf.get_http_api_crossdomain());
|
||||||
EXPECT_TRUE(conf.get_raw_api());
|
EXPECT_TRUE(conf.get_raw_api());
|
||||||
EXPECT_TRUE(conf.get_raw_api_allow_reload());
|
EXPECT_TRUE(conf.get_raw_api_allow_reload());
|
||||||
EXPECT_TRUE(conf.get_raw_api_allow_query());
|
EXPECT_FALSE(conf.get_raw_api_allow_query()); // Always disabled
|
||||||
EXPECT_TRUE(conf.get_raw_api_allow_update());
|
EXPECT_FALSE(conf.get_raw_api_allow_update()); // Always disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue