mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
Merge branch 'develop' into merge/develop
This commit is contained in:
commit
aee20d7d65
27 changed files with 4336 additions and 1239 deletions
4
trunk/3rdparty/srs-bench/Makefile
vendored
4
trunk/3rdparty/srs-bench/Makefile
vendored
|
@ -5,13 +5,13 @@ default: bench test
|
||||||
clean:
|
clean:
|
||||||
rm -f ./objs/srs_bench ./objs/srs_test
|
rm -f ./objs/srs_bench ./objs/srs_test
|
||||||
|
|
||||||
.format.txt: *.go srs/*.go vnet/*.go
|
.format.txt: *.go srs/*.go vnet/*.go janus/*.go
|
||||||
gofmt -w .
|
gofmt -w .
|
||||||
echo "done" > .format.txt
|
echo "done" > .format.txt
|
||||||
|
|
||||||
bench: ./objs/srs_bench
|
bench: ./objs/srs_bench
|
||||||
|
|
||||||
./objs/srs_bench: .format.txt *.go srs/*.go vnet/*.go Makefile
|
./objs/srs_bench: .format.txt *.go srs/*.go vnet/*.go janus/*.go Makefile
|
||||||
go build -mod=vendor -o objs/srs_bench .
|
go build -mod=vendor -o objs/srs_bench .
|
||||||
|
|
||||||
test: ./objs/srs_test
|
test: ./objs/srs_test
|
||||||
|
|
37
trunk/3rdparty/srs-bench/README.md
vendored
37
trunk/3rdparty/srs-bench/README.md
vendored
|
@ -184,4 +184,41 @@ yum install -y python2-pip &&
|
||||||
pip install lxml && pip install gcovr
|
pip install lxml && pip install gcovr
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Janus
|
||||||
|
|
||||||
|
支持Janus的压测,使用选项`-sfu janus`可以查看帮助:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./objs/srs_bench -sfu janus --help
|
||||||
|
```
|
||||||
|
|
||||||
|
首先需要启动Janus,推荐使用[janus-docker](https://github.com/winlinvip/janus-docker#usage):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip=$(ifconfig en0 inet|grep inet|awk '{print $2}') &&
|
||||||
|
sed -i '' "s/nat_1_1_mapping.*/nat_1_1_mapping=\"$ip\"/g" janus.jcfg &&
|
||||||
|
docker run --rm -it -p 8080:8080 -p 8443:8443 -p 20000-20010:20000-20010/udp \
|
||||||
|
-v $(pwd)/janus.jcfg:/usr/local/etc/janus/janus.jcfg \
|
||||||
|
-v $(pwd)/janus.plugin.videoroom.jcfg:/usr/local/etc/janus/janus.plugin.videoroom.jcfg \
|
||||||
|
registry.cn-hangzhou.aliyuncs.com/ossrs/janus:v1.0.7
|
||||||
|
```
|
||||||
|
|
||||||
|
> 若启动成功,打开页面,可以自动入会:[http://localhost:8080](http://localhost:8080)
|
||||||
|
|
||||||
|
模拟5个推流入会,可以在页面看到入会的流:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -j10 && ./objs/srs_bench -sfu janus \
|
||||||
|
-pr webrtc://localhost:8080/2345/livestream \
|
||||||
|
-sa avatar.ogg -sv avatar.h264 -fps 25 -sn 5
|
||||||
|
```
|
||||||
|
|
||||||
|
模拟5个拉流入会,只拉流不推流:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -j10 && ./objs/srs_bench -sfu janus \
|
||||||
|
-sr webrtc://localhost:8080/2345/livestream \
|
||||||
|
-nn 5
|
||||||
|
```
|
||||||
|
|
||||||
2021.01, Winlin
|
2021.01, Winlin
|
||||||
|
|
10
trunk/3rdparty/srs-bench/auto/sync_vnet.sh
vendored
10
trunk/3rdparty/srs-bench/auto/sync_vnet.sh
vendored
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
FILES=(udpproxy.go udpproxy_test.go)
|
FILES=(udpproxy.go udpproxy_test.go udpproxy_direct.go udpproxy_direct_test.go)
|
||||||
for file in ${FILES[@]}; do
|
for file in ${FILES[@]}; do
|
||||||
echo "cp vnet/udpproxy.go ~/git/transport/vnet/" &&
|
echo "cp vnet/$file ~/git/transport/vnet/" &&
|
||||||
cp vnet/udpproxy.go ~/git/transport/vnet/
|
cp vnet/$file ~/git/transport/vnet/
|
||||||
done
|
done
|
||||||
|
|
||||||
# https://github.com/pion/webrtc/wiki/Contributing#run-all-automated-tests-and-checks-before-submitting
|
# https://github.com/pion/webrtc/wiki/Contributing#run-all-automated-tests-and-checks-before-submitting
|
||||||
|
@ -26,8 +26,8 @@ echo "go test -race ./..." &&
|
||||||
go test -race ./...
|
go test -race ./...
|
||||||
if [[ $? -ne 0 ]]; then echo "fail"; exit -1; fi
|
if [[ $? -ne 0 ]]; then echo "fail"; exit -1; fi
|
||||||
|
|
||||||
echo "golangci-lint run --skip-files conn_map_test.go" &&
|
echo "golangci-lint run --skip-files conn_map_test.go,router_test.go" &&
|
||||||
golangci-lint run --skip-files conn_map_test.go
|
golangci-lint run --skip-files conn_map_test.go,a_test.go
|
||||||
if [[ $? -ne 0 ]]; then echo "fail"; exit -1; fi
|
if [[ $? -ne 0 ]]; then echo "fail"; exit -1; fi
|
||||||
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
|
|
1077
trunk/3rdparty/srs-bench/janus/api.go
vendored
Normal file
1077
trunk/3rdparty/srs-bench/janus/api.go
vendored
Normal file
File diff suppressed because it is too large
Load diff
301
trunk/3rdparty/srs-bench/janus/ingester.go
vendored
Normal file
301
trunk/3rdparty/srs-bench/janus/ingester.go
vendored
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"github.com/pion/interceptor"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
"github.com/pion/sdp/v3"
|
||||||
|
"github.com/pion/webrtc/v3"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media/h264reader"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media/oggreader"
|
||||||
|
)
|
||||||
|
|
||||||
|
type videoIngester struct {
|
||||||
|
sourceVideo string
|
||||||
|
fps int
|
||||||
|
markerInterceptor *rtpInterceptor
|
||||||
|
sVideoTrack *webrtc.TrackLocalStaticSample
|
||||||
|
sVideoSender *webrtc.RTPSender
|
||||||
|
ready context.Context
|
||||||
|
readyCancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVideoIngester(sourceVideo string) *videoIngester {
|
||||||
|
v := &videoIngester{markerInterceptor: &rtpInterceptor{}, sourceVideo: sourceVideo}
|
||||||
|
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *videoIngester) Close() error {
|
||||||
|
v.readyCancel()
|
||||||
|
if v.sVideoSender != nil {
|
||||||
|
_ = v.sVideoSender.Stop()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *videoIngester) AddTrack(pc *webrtc.PeerConnection, fps int) error {
|
||||||
|
v.fps = fps
|
||||||
|
|
||||||
|
mimeType, trackID := "video/H264", "video"
|
||||||
|
if strings.HasSuffix(v.sourceVideo, ".ivf") {
|
||||||
|
mimeType = "video/VP8"
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
v.sVideoTrack, err = webrtc.NewTrackLocalStaticSample(
|
||||||
|
webrtc.RTPCodecCapability{MimeType: mimeType, ClockRate: 90000}, trackID, "pion",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create video track")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.sVideoSender, err = pc.AddTrack(v.sVideoTrack)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Add video track")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *videoIngester) Ingest(ctx context.Context) error {
|
||||||
|
source, sender, track, fps := v.sourceVideo, v.sVideoSender, v.sVideoTrack, v.fps
|
||||||
|
|
||||||
|
f, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Open file %v", source)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// TODO: FIXME: Support ivf for vp8.
|
||||||
|
h264, err := h264reader.NewReader(f)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Open h264 %v", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := sender.GetParameters().Encodings[0]
|
||||||
|
codec := sender.GetParameters().Codecs[0]
|
||||||
|
headers := sender.GetParameters().HeaderExtensions
|
||||||
|
logger.Tf(ctx, "Video %v, tbn=%v, fps=%v, ssrc=%v, pt=%v, header=%v",
|
||||||
|
codec.MimeType, codec.ClockRate, fps, enc.SSRC, codec.PayloadType, headers)
|
||||||
|
|
||||||
|
// OK, we are ready.
|
||||||
|
v.readyCancel()
|
||||||
|
|
||||||
|
clock := newWallClock()
|
||||||
|
sampleDuration := time.Duration(uint64(time.Millisecond) * 1000 / uint64(fps))
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
var sps, pps *h264reader.NAL
|
||||||
|
var oFrames []*h264reader.NAL
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
frame, err := h264.NextNAL()
|
||||||
|
if err == io.EOF {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Read h264")
|
||||||
|
}
|
||||||
|
|
||||||
|
oFrames = append(oFrames, frame)
|
||||||
|
logger.If(ctx, "NALU %v PictureOrderCount=%v, ForbiddenZeroBit=%v, RefIdc=%v, %v bytes",
|
||||||
|
frame.UnitType.String(), frame.PictureOrderCount, frame.ForbiddenZeroBit, frame.RefIdc, len(frame.Data))
|
||||||
|
|
||||||
|
if frame.UnitType == h264reader.NalUnitTypeSPS {
|
||||||
|
sps = frame
|
||||||
|
} else if frame.UnitType == h264reader.NalUnitTypePPS {
|
||||||
|
pps = frame
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var frames []*h264reader.NAL
|
||||||
|
// Package SPS/PPS to STAP-A
|
||||||
|
if sps != nil && pps != nil {
|
||||||
|
stapA := packageAsSTAPA(sps, pps)
|
||||||
|
frames = append(frames, stapA)
|
||||||
|
}
|
||||||
|
// Append other original frames.
|
||||||
|
for _, frame := range oFrames {
|
||||||
|
if frame.UnitType != h264reader.NalUnitTypeSPS && frame.UnitType != h264reader.NalUnitTypePPS {
|
||||||
|
frames = append(frames, frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covert frames to sample(buffers).
|
||||||
|
for i, frame := range frames {
|
||||||
|
sample := media.Sample{Data: frame.Data, Duration: sampleDuration}
|
||||||
|
// Use the sample timestamp for frames.
|
||||||
|
if i != len(frames)-1 {
|
||||||
|
sample.Duration = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// For STAP-A, set marker to false, to make Chrome happy.
|
||||||
|
if ri := v.markerInterceptor; ri.rtpWriter == nil {
|
||||||
|
ri.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
|
// TODO: Should we decode to check whether SPS/PPS?
|
||||||
|
if len(payload) > 0 && payload[0]&0x1f == 24 {
|
||||||
|
header.Marker = false // 24, STAP-A
|
||||||
|
}
|
||||||
|
return ri.nextRTPWriter.Write(header, payload, attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = track.WriteSample(sample); err != nil {
|
||||||
|
return errors.Wrapf(err, "Write sample")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d := clock.Tick(sampleDuration); d > 0 {
|
||||||
|
time.Sleep(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
type audioIngester struct {
|
||||||
|
sourceAudio string
|
||||||
|
audioLevelInterceptor *rtpInterceptor
|
||||||
|
sAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
|
sAudioSender *webrtc.RTPSender
|
||||||
|
ready context.Context
|
||||||
|
readyCancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAudioIngester(sourceAudio string) *audioIngester {
|
||||||
|
v := &audioIngester{audioLevelInterceptor: &rtpInterceptor{}, sourceAudio: sourceAudio}
|
||||||
|
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *audioIngester) Close() error {
|
||||||
|
v.readyCancel() // OK we are closed, also ready.
|
||||||
|
|
||||||
|
if v.sAudioSender != nil {
|
||||||
|
_ = v.sAudioSender.Stop()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *audioIngester) AddTrack(pc *webrtc.PeerConnection) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
mimeType, trackID := "audio/opus", "audio"
|
||||||
|
v.sAudioTrack, err = webrtc.NewTrackLocalStaticSample(
|
||||||
|
webrtc.RTPCodecCapability{MimeType: mimeType, ClockRate: 48000, Channels: 2}, trackID, "pion",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create audio track")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.sAudioSender, err = pc.AddTrack(v.sAudioTrack)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Add audio track")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *audioIngester) Ingest(ctx context.Context) error {
|
||||||
|
source, sender, track := v.sourceAudio, v.sAudioSender, v.sAudioTrack
|
||||||
|
|
||||||
|
f, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Open file %v", source)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
ogg, _, err := oggreader.NewWith(f)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Open ogg %v", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := sender.GetParameters().Encodings[0]
|
||||||
|
codec := sender.GetParameters().Codecs[0]
|
||||||
|
headers := sender.GetParameters().HeaderExtensions
|
||||||
|
logger.Tf(ctx, "Audio %v, tbn=%v, channels=%v, ssrc=%v, pt=%v, header=%v",
|
||||||
|
codec.MimeType, codec.ClockRate, codec.Channels, enc.SSRC, codec.PayloadType, headers)
|
||||||
|
|
||||||
|
// Whether should encode the audio-level in RTP header.
|
||||||
|
var audioLevel *webrtc.RTPHeaderExtensionParameter
|
||||||
|
for _, h := range headers {
|
||||||
|
if h.URI == sdp.AudioLevelURI {
|
||||||
|
audioLevel = &h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, we are ready.
|
||||||
|
v.readyCancel()
|
||||||
|
|
||||||
|
clock := newWallClock()
|
||||||
|
var lastGranule uint64
|
||||||
|
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
pageData, pageHeader, err := ogg.ParseNextPage()
|
||||||
|
if err == io.EOF {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Read ogg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The amount of samples is the difference between the last and current timestamp
|
||||||
|
sampleCount := pageHeader.GranulePosition - lastGranule
|
||||||
|
lastGranule = pageHeader.GranulePosition
|
||||||
|
sampleDuration := time.Duration(uint64(time.Millisecond) * 1000 * sampleCount / uint64(codec.ClockRate))
|
||||||
|
|
||||||
|
// For audio-level, set the extensions if negotiated.
|
||||||
|
if ri := v.audioLevelInterceptor; ri.rtpWriter == nil {
|
||||||
|
ri.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
|
if audioLevel != nil {
|
||||||
|
audioLevelPayload, err := new(rtp.AudioLevelExtension).Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = header.SetExtension(uint8(audioLevel.ID), audioLevelPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ri.nextRTPWriter.Write(header, payload, attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = track.WriteSample(media.Sample{Data: pageData, Duration: sampleDuration}); err != nil {
|
||||||
|
return errors.Wrapf(err, "Write sample")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d := clock.Tick(sampleDuration); d > 0 {
|
||||||
|
time.Sleep(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
158
trunk/3rdparty/srs-bench/janus/interceptor.go
vendored
Normal file
158
trunk/3rdparty/srs-bench/janus/interceptor.go
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/interceptor"
|
||||||
|
"github.com/pion/rtcp"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rtpInterceptorOptionFunc func(i *rtpInterceptor)
|
||||||
|
|
||||||
|
// Common RTP packet interceptor for benchmark.
|
||||||
|
// @remark Should never merge with rtcpInterceptor, because they has the same Write interface.
|
||||||
|
type rtpInterceptor struct {
|
||||||
|
// If rtpReader is nil, use the default next one to read.
|
||||||
|
rtpReader interceptor.RTPReaderFunc
|
||||||
|
nextRTPReader interceptor.RTPReader
|
||||||
|
// If rtpWriter is nil, use the default next one to write.
|
||||||
|
rtpWriter interceptor.RTPWriterFunc
|
||||||
|
nextRTPWriter interceptor.RTPWriter
|
||||||
|
// Other common fields.
|
||||||
|
bypassInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRTPInterceptor(options ...rtpInterceptorOptionFunc) *rtpInterceptor {
|
||||||
|
v := &rtpInterceptor{}
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
||||||
|
v.nextRTPWriter = writer
|
||||||
|
return v // Handle all RTP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) Write(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
|
if v.rtpWriter != nil {
|
||||||
|
return v.rtpWriter(header, payload, attributes)
|
||||||
|
}
|
||||||
|
return v.nextRTPWriter.Write(header, payload, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
||||||
|
v.nextRTPReader = reader
|
||||||
|
return v // Handle all RTP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
|
if v.rtpReader != nil {
|
||||||
|
return v.rtpReader(b, a)
|
||||||
|
}
|
||||||
|
return v.nextRTPReader.Read(b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtpInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
type rtcpInterceptorOptionFunc func(i *rtcpInterceptor)
|
||||||
|
|
||||||
|
// Common RTCP packet interceptor for benchmark.
|
||||||
|
// @remark Should never merge with rtpInterceptor, because they has the same Write interface.
|
||||||
|
type rtcpInterceptor struct {
|
||||||
|
// If rtcpReader is nil, use the default next one to read.
|
||||||
|
rtcpReader interceptor.RTCPReaderFunc
|
||||||
|
nextRTCPReader interceptor.RTCPReader
|
||||||
|
// If rtcpWriter is nil, use the default next one to write.
|
||||||
|
rtcpWriter interceptor.RTCPWriterFunc
|
||||||
|
nextRTCPWriter interceptor.RTCPWriter
|
||||||
|
// Other common fields.
|
||||||
|
bypassInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRTCPInterceptor(options ...rtcpInterceptorOptionFunc) *rtcpInterceptor {
|
||||||
|
v := &rtcpInterceptor{}
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtcpInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
||||||
|
v.nextRTCPReader = reader
|
||||||
|
return v // Handle all RTCP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtcpInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
|
if v.rtcpReader != nil {
|
||||||
|
return v.rtcpReader(b, a)
|
||||||
|
}
|
||||||
|
return v.nextRTCPReader.Read(b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtcpInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
||||||
|
v.nextRTCPWriter = writer
|
||||||
|
return v // Handle all RTCP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *rtcpInterceptor) Write(pkts []rtcp.Packet, attributes interceptor.Attributes) (int, error) {
|
||||||
|
if v.rtcpWriter != nil {
|
||||||
|
return v.rtcpWriter(pkts, attributes)
|
||||||
|
}
|
||||||
|
return v.nextRTCPWriter.Write(pkts, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing.
|
||||||
|
type bypassInterceptor struct {
|
||||||
|
interceptor.Interceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
||||||
|
return reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
||||||
|
return reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *bypassInterceptor) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
198
trunk/3rdparty/srs-bench/janus/janus.go
vendored
Normal file
198
trunk/3rdparty/srs-bench/janus/janus.go
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sr string
|
||||||
|
var pli int
|
||||||
|
|
||||||
|
var pr, sourceAudio, sourceVideo string
|
||||||
|
var fps int
|
||||||
|
|
||||||
|
var audioLevel, videoTWCC bool
|
||||||
|
|
||||||
|
var clients, streams, delay int
|
||||||
|
|
||||||
|
func Parse(ctx context.Context) {
|
||||||
|
fl := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||||
|
|
||||||
|
var sfu string
|
||||||
|
fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or janus")
|
||||||
|
|
||||||
|
fl.StringVar(&sr, "sr", "", "")
|
||||||
|
fl.IntVar(&pli, "pli", 10, "")
|
||||||
|
|
||||||
|
fl.StringVar(&pr, "pr", "", "")
|
||||||
|
fl.StringVar(&sourceAudio, "sa", "", "")
|
||||||
|
fl.StringVar(&sourceVideo, "sv", "", "")
|
||||||
|
fl.IntVar(&fps, "fps", 0, "")
|
||||||
|
|
||||||
|
fl.BoolVar(&audioLevel, "al", true, "")
|
||||||
|
fl.BoolVar(&videoTWCC, "twcc", true, "")
|
||||||
|
|
||||||
|
fl.IntVar(&clients, "nn", 1, "")
|
||||||
|
fl.IntVar(&streams, "sn", 1, "")
|
||||||
|
fl.IntVar(&delay, "delay", 50, "")
|
||||||
|
|
||||||
|
fl.Usage = func() {
|
||||||
|
fmt.Println(fmt.Sprintf("Usage: %v [Options]", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("Options:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sfu The target SFU, srs or janus. Default: srs"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -nn The number of clients to simulate. Default: 1"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sn The number of streams to simulate. Variable: %%d. Default: 1"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -delay The start delay in ms for each client or stream to simulate. Default: 50"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -al [Optional] Whether enable audio-level. Default: true"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -twcc [Optional] Whether enable vdieo-twcc. Default: true"))
|
||||||
|
fmt.Println(fmt.Sprintf("Player or Subscriber:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sr The url to play/subscribe. If sn exceed 1, auto append variable %%d."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -pli [Optional] PLI request interval in seconds. Default: 10"))
|
||||||
|
fmt.Println(fmt.Sprintf("Publisher:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -pr The url to publish. If sn exceed 1, auto append variable %%d."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -fps The fps of .h264 source file."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sa [Optional] The file path to read audio, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sv [Optional] The file path to read video, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个播放,1个推流:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -sr webrtc://localhost:8080/2345/livestream", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -pr webrtc://localhost:8080/2345/livestream -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个流,3个播放,共3个客户端:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -sr webrtc://localhost:8080/2345/livestream -nn 3", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -pr webrtc://localhost:8080/2345/livestream -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,2个流,每个流3个播放,共6个客户端:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -sr webrtc://localhost:8080/2345/livestream_%%d -sn 2 -nn 3", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -pr webrtc://localhost:8080/2345/livestream_%%d -sn 2 -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,2个推流:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sfu janus -pr webrtc://localhost:8080/2345/livestream_%%d -sn 2 -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
}
|
||||||
|
if err := fl.Parse(os.Args[1:]); err == flag.ErrHelp {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
showHelp := (clients <= 0 || streams <= 0)
|
||||||
|
if sr == "" && pr == "" {
|
||||||
|
showHelp = true
|
||||||
|
}
|
||||||
|
if pr != "" && (sourceAudio == "" && sourceVideo == "") {
|
||||||
|
showHelp = true
|
||||||
|
}
|
||||||
|
if showHelp {
|
||||||
|
fl.Usage()
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
summaryDesc := fmt.Sprintf("delay=%v, al=%v, twcc=%v", delay, audioLevel, videoTWCC)
|
||||||
|
if sr != "" {
|
||||||
|
summaryDesc = fmt.Sprintf("%v, play(url=%v, pli=%v)", summaryDesc, sr, pli)
|
||||||
|
}
|
||||||
|
if pr != "" {
|
||||||
|
summaryDesc = fmt.Sprintf("%v, publish(url=%v, sa=%v, sv=%v, fps=%v)",
|
||||||
|
summaryDesc, pr, sourceAudio, sourceVideo, fps)
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Run benchmark with %v", summaryDesc)
|
||||||
|
|
||||||
|
checkFlags := func() error {
|
||||||
|
if sourceVideo != "" && !strings.HasSuffix(sourceVideo, ".h264") {
|
||||||
|
return errors.Errorf("Should be .264, actual %v", sourceVideo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceVideo != "" && strings.HasSuffix(sourceVideo, ".h264") && fps <= 0 {
|
||||||
|
return errors.Errorf("Video fps should >0, actual %v", fps)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := checkFlags(); err != nil {
|
||||||
|
logger.Ef(ctx, "Check faile err %+v", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ctx context.Context) error {
|
||||||
|
// Run tasks.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Run all subscribers or players.
|
||||||
|
for i := 0; sr != "" && i < streams && ctx.Err() == nil; i++ {
|
||||||
|
r_auto := sr
|
||||||
|
if streams > 1 && !strings.Contains(r_auto, "%") {
|
||||||
|
r_auto += "%d"
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 := r_auto
|
||||||
|
if strings.Contains(r2, "%") {
|
||||||
|
r2 = fmt.Sprintf(r2, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; sr != "" && j < clients && ctx.Err() == nil; j++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(sr string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if err := startPlay(ctx, sr, audioLevel, videoTWCC, pli); err != nil {
|
||||||
|
if errors.Cause(err) != context.Canceled {
|
||||||
|
logger.Wf(ctx, "Run err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(r2)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all publishers.
|
||||||
|
for i := 0; pr != "" && i < streams && ctx.Err() == nil; i++ {
|
||||||
|
r_auto := pr
|
||||||
|
if streams > 1 && !strings.Contains(r_auto, "%") {
|
||||||
|
r_auto += "%d"
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 := r_auto
|
||||||
|
if strings.Contains(r2, "%") {
|
||||||
|
r2 = fmt.Sprintf(r2, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(pr string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if err := startPublish(ctx, pr, sourceAudio, sourceVideo, fps, audioLevel, videoTWCC); err != nil {
|
||||||
|
if errors.Cause(err) != context.Canceled {
|
||||||
|
logger.Wf(ctx, "Run err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(r2)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
246
trunk/3rdparty/srs-bench/janus/player.go
vendored
Normal file
246
trunk/3rdparty/srs-bench/janus/player.go
vendored
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"github.com/pion/interceptor"
|
||||||
|
"github.com/pion/rtcp"
|
||||||
|
"github.com/pion/sdp/v3"
|
||||||
|
"github.com/pion/webrtc/v3"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startPlay(ctx context.Context, r string, enableAudioLevel, enableTWCC bool, pli int) error {
|
||||||
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
|
u, err := url.Parse(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Parse url %v", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var room int
|
||||||
|
var display string
|
||||||
|
if us := strings.SplitN(u.Path, "/", 3); len(us) >= 3 {
|
||||||
|
if iv, err := strconv.Atoi(us[1]); err != nil {
|
||||||
|
return errors.Wrapf(err, "parse %v", us[1])
|
||||||
|
} else {
|
||||||
|
room = iv
|
||||||
|
}
|
||||||
|
|
||||||
|
display = strings.Join(us[2:], "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Tf(ctx, "Run play url=%v, room=%v, diplay=%v, audio-level=%v, twcc=%v",
|
||||||
|
r, room, display, enableAudioLevel, enableTWCC)
|
||||||
|
|
||||||
|
// For audio-level.
|
||||||
|
webrtcNewPeerConnection := func(configuration webrtc.Configuration) (*webrtc.PeerConnection, error) {
|
||||||
|
m := &webrtc.MediaEngine{}
|
||||||
|
if err := m.RegisterDefaultCodecs(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range []string{sdp.SDESMidURI, sdp.SDESRTPStreamIDURI, sdp.TransportCCURI} {
|
||||||
|
if extension == sdp.TransportCCURI && !enableTWCC {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/pion/ion/issues/130
|
||||||
|
// https://github.com/pion/ion-sfu/pull/373/files#diff-6f42c5ac6f8192dd03e5a17e9d109e90cb76b1a4a7973be6ce44a89ffd1b5d18R73
|
||||||
|
for _, extension := range []string{sdp.SDESMidURI, sdp.SDESRTPStreamIDURI, sdp.AudioLevelURI} {
|
||||||
|
if extension == sdp.AudioLevelURI && !enableAudioLevel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeAudio); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i := &interceptor.Registry{}
|
||||||
|
if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i))
|
||||||
|
return api.NewPeerConnection(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, err := webrtcNewPeerConnection(webrtc.Configuration{
|
||||||
|
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create PC")
|
||||||
|
}
|
||||||
|
|
||||||
|
var receivers []*webrtc.RTPReceiver
|
||||||
|
defer func() {
|
||||||
|
pc.Close()
|
||||||
|
for _, receiver := range receivers {
|
||||||
|
receiver.Stop()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
pc.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio, webrtc.RTPTransceiverInit{
|
||||||
|
Direction: webrtc.RTPTransceiverDirectionRecvonly,
|
||||||
|
})
|
||||||
|
pc.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RTPTransceiverInit{
|
||||||
|
Direction: webrtc.RTPTransceiverDirectionRecvonly,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Signaling API
|
||||||
|
api := newJanusAPI(fmt.Sprintf("http://%v/janus", u.Host))
|
||||||
|
|
||||||
|
if err := api.Create(ctx); err != nil {
|
||||||
|
return errors.Wrapf(err, "create")
|
||||||
|
}
|
||||||
|
defer api.Close()
|
||||||
|
|
||||||
|
// Discover the publisherInfo to subscribe.
|
||||||
|
publisherInfo, err := api.DiscoverPublisher(ctx, room, display, 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Publisher found, room=%v, display=%v, %v", room, display, publisherInfo)
|
||||||
|
|
||||||
|
subscribeHandle, err := api.AttachPlugin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "attach plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
offer, err := api.JoinAsSubscribe(ctx, subscribeHandle, room, publisherInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "subscribe")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchange offer and generate answer.
|
||||||
|
if err := pc.SetRemoteDescription(webrtc.SessionDescription{
|
||||||
|
Type: webrtc.SDPTypeOffer, SDP: offer,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrapf(err, "Set offer %v", offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
answer, err := pc.CreateAnswer(nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create answer")
|
||||||
|
}
|
||||||
|
if err := pc.SetLocalDescription(answer); err != nil {
|
||||||
|
return errors.Wrapf(err, "Set answer %v", answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send answer to Janus.
|
||||||
|
if err := api.Subscribe(ctx, subscribeHandle, room, answer.SDP); err != nil {
|
||||||
|
return errors.Wrapf(err, "Subscribe with answer %v", answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTrack := func(ctx context.Context, track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) error {
|
||||||
|
// Send a PLI on an interval so that the publisher is pushing a keyframe
|
||||||
|
go func() {
|
||||||
|
if track.Kind() == webrtc.RTPCodecTypeAudio {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if pli <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(time.Duration(pli) * time.Second):
|
||||||
|
_ = pc.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{
|
||||||
|
MediaSSRC: uint32(track.SSRC()),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
receivers = append(receivers, receiver)
|
||||||
|
|
||||||
|
codec := track.Codec()
|
||||||
|
|
||||||
|
trackDesc := fmt.Sprintf("channels=%v", codec.Channels)
|
||||||
|
if track.Kind() == webrtc.RTPCodecTypeVideo {
|
||||||
|
trackDesc = fmt.Sprintf("fmtp=%v", codec.SDPFmtpLine)
|
||||||
|
}
|
||||||
|
if headers := receiver.GetParameters().HeaderExtensions; len(headers) > 0 {
|
||||||
|
trackDesc = fmt.Sprintf("%v, header=%v", trackDesc, headers)
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Got track %v, pt=%v, tbn=%v, %v",
|
||||||
|
codec.MimeType, codec.PayloadType, codec.ClockRate, trackDesc)
|
||||||
|
|
||||||
|
return writeTrackToDisk(ctx, track)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
pc.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
|
||||||
|
err = handleTrack(ctx, track, receiver)
|
||||||
|
if err != nil {
|
||||||
|
codec := track.Codec()
|
||||||
|
err = errors.Wrapf(err, "Handle track %v, pt=%v", codec.MimeType, codec.PayloadType)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||||
|
logger.If(ctx, "ICE state %v", state)
|
||||||
|
|
||||||
|
if state == webrtc.ICEConnectionStateFailed || state == webrtc.ICEConnectionStateClosed {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Wf(ctx, "Close for ICE state %v", state)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTrackToDisk(ctx context.Context, track *webrtc.TrackRemote) error {
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
pkt, _, err := track.ReadRTP()
|
||||||
|
if err != nil {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Wrapf(err, "Read RTP")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.If(ctx, "Got packet ssrc=%v, pt=%v, seq=%v %vB",
|
||||||
|
pkt.SSRC, pkt.PayloadType, pkt.SequenceNumber, len(pkt.Payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
352
trunk/3rdparty/srs-bench/janus/publisher.go
vendored
Normal file
352
trunk/3rdparty/srs-bench/janus/publisher.go
vendored
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"github.com/pion/interceptor"
|
||||||
|
"github.com/pion/sdp/v3"
|
||||||
|
"github.com/pion/webrtc/v3"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps int, enableAudioLevel, enableTWCC bool) error {
|
||||||
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
|
u, err := url.Parse(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Parse url %v", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var room int
|
||||||
|
var display string
|
||||||
|
if us := strings.SplitN(u.Path, "/", 3); len(us) >= 3 {
|
||||||
|
if iv, err := strconv.Atoi(us[1]); err != nil {
|
||||||
|
return errors.Wrapf(err, "parse %v", us[1])
|
||||||
|
} else {
|
||||||
|
room = iv
|
||||||
|
}
|
||||||
|
|
||||||
|
display = strings.Join(us[2:], "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Tf(ctx, "Run publish url=%v, audio=%v, video=%v, fps=%v, audio-level=%v, twcc=%v",
|
||||||
|
r, sourceAudio, sourceVideo, fps, enableAudioLevel, enableTWCC)
|
||||||
|
|
||||||
|
// Filter for SPS/PPS marker.
|
||||||
|
var aIngester *audioIngester
|
||||||
|
var vIngester *videoIngester
|
||||||
|
|
||||||
|
// For audio-level and sps/pps marker.
|
||||||
|
// TODO: FIXME: Should share with player.
|
||||||
|
webrtcNewPeerConnection := func(configuration webrtc.Configuration) (*webrtc.PeerConnection, error) {
|
||||||
|
m := &webrtc.MediaEngine{}
|
||||||
|
if err := m.RegisterDefaultCodecs(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range []string{sdp.SDESMidURI, sdp.SDESRTPStreamIDURI, sdp.TransportCCURI} {
|
||||||
|
if extension == sdp.TransportCCURI && !enableTWCC {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeVideo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/pion/ion/issues/130
|
||||||
|
// https://github.com/pion/ion-sfu/pull/373/files#diff-6f42c5ac6f8192dd03e5a17e9d109e90cb76b1a4a7973be6ce44a89ffd1b5d18R73
|
||||||
|
for _, extension := range []string{sdp.SDESMidURI, sdp.SDESRTPStreamIDURI, sdp.AudioLevelURI} {
|
||||||
|
if extension == sdp.AudioLevelURI && !enableAudioLevel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := m.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeAudio); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := &interceptor.Registry{}
|
||||||
|
if err := webrtc.RegisterDefaultInterceptors(m, registry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceAudio != "" {
|
||||||
|
aIngester = newAudioIngester(sourceAudio)
|
||||||
|
registry.Add(aIngester.audioLevelInterceptor)
|
||||||
|
}
|
||||||
|
if sourceVideo != "" {
|
||||||
|
vIngester = newVideoIngester(sourceVideo)
|
||||||
|
registry.Add(vIngester.markerInterceptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(registry))
|
||||||
|
return api.NewPeerConnection(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, err := webrtcNewPeerConnection(webrtc.Configuration{})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create PC")
|
||||||
|
}
|
||||||
|
|
||||||
|
doClose := func() {
|
||||||
|
if pc != nil {
|
||||||
|
pc.Close()
|
||||||
|
}
|
||||||
|
if vIngester != nil {
|
||||||
|
vIngester.Close()
|
||||||
|
}
|
||||||
|
if aIngester != nil {
|
||||||
|
aIngester.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer doClose()
|
||||||
|
|
||||||
|
if vIngester != nil {
|
||||||
|
if err := vIngester.AddTrack(pc, fps); err != nil {
|
||||||
|
return errors.Wrapf(err, "Add track")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if aIngester != nil {
|
||||||
|
if err := aIngester.AddTrack(pc); err != nil {
|
||||||
|
return errors.Wrapf(err, "Add track")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offer, err := pc.CreateOffer(nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Create Offer")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pc.SetLocalDescription(offer); err != nil {
|
||||||
|
return errors.Wrapf(err, "Set offer %v", offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signaling API
|
||||||
|
api := newJanusAPI(fmt.Sprintf("http://%v/janus", u.Host))
|
||||||
|
|
||||||
|
webrtcUpCtx, webrtcUpCancel := context.WithCancel(ctx)
|
||||||
|
api.onWebrtcUp = func(sender, sessionID uint64) {
|
||||||
|
logger.Tf(ctx, "Event webrtcup: DTLS/SRTP done, from=(sender:%v,session:%v)", sender, sessionID)
|
||||||
|
webrtcUpCancel()
|
||||||
|
}
|
||||||
|
api.onMedia = func(sender, sessionID uint64, mtype string, receiving bool) {
|
||||||
|
logger.Tf(ctx, "Event media: %v receiving=%v, from=(sender:%v,session:%v)", mtype, receiving, sender, sessionID)
|
||||||
|
}
|
||||||
|
api.onSlowLink = func(sender, sessionID uint64, media string, lost uint64, uplink bool) {
|
||||||
|
logger.Tf(ctx, "Event slowlink: %v lost=%v, uplink=%v, from=(sender:%v,session:%v)", media, lost, uplink, sender, sessionID)
|
||||||
|
}
|
||||||
|
api.onPublisher = func(sender, sessionID uint64, publishers []publisherInfo) {
|
||||||
|
logger.Tf(ctx, "Event publisher: %v, from=(sender:%v,session:%v)", publishers, sender, sessionID)
|
||||||
|
}
|
||||||
|
api.onUnPublished = func(sender, sessionID, id uint64) {
|
||||||
|
logger.Tf(ctx, "Event unpublish: %v, from=(sender:%v,session:%v)", id, sender, sessionID)
|
||||||
|
}
|
||||||
|
api.onLeave = func(sender, sessionID, id uint64) {
|
||||||
|
logger.Tf(ctx, "Event leave: %v, from=(sender:%v,session:%v)", id, sender, sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := api.Create(ctx); err != nil {
|
||||||
|
return errors.Wrapf(err, "create")
|
||||||
|
}
|
||||||
|
defer api.Close()
|
||||||
|
|
||||||
|
publishHandleID, err := api.AttachPlugin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "attach plugin")
|
||||||
|
}
|
||||||
|
defer api.DetachPlugin(ctx, publishHandleID)
|
||||||
|
|
||||||
|
if err := api.JoinAsPublisher(ctx, publishHandleID, room, display); err != nil {
|
||||||
|
return errors.Wrapf(err, "join as publisher")
|
||||||
|
}
|
||||||
|
|
||||||
|
answer, err := api.Publish(ctx, publishHandleID, offer.SDP)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "join as publisher")
|
||||||
|
}
|
||||||
|
defer api.UnPublish(ctx, publishHandleID)
|
||||||
|
|
||||||
|
// Setup the offer-answer
|
||||||
|
if err := pc.SetRemoteDescription(webrtc.SessionDescription{
|
||||||
|
Type: webrtc.SDPTypeAnswer, SDP: answer,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrapf(err, "Set answer %v", answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Tf(ctx, "State signaling=%v, ice=%v, conn=%v", pc.SignalingState(), pc.ICEConnectionState(), pc.ConnectionState())
|
||||||
|
|
||||||
|
// ICE state management.
|
||||||
|
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||||
|
logger.Tf(ctx, "ICE state %v", state)
|
||||||
|
})
|
||||||
|
|
||||||
|
pc.OnSignalingStateChange(func(state webrtc.SignalingState) {
|
||||||
|
logger.Tf(ctx, "Signaling state %v", state)
|
||||||
|
})
|
||||||
|
|
||||||
|
if aIngester != nil {
|
||||||
|
aIngester.sAudioSender.Transport().OnStateChange(func(state webrtc.DTLSTransportState) {
|
||||||
|
logger.Tf(ctx, "DTLS state %v", state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
pcDoneCtx, pcDoneCancel := context.WithCancel(context.Background())
|
||||||
|
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
|
||||||
|
logger.Tf(ctx, "PC state %v", state)
|
||||||
|
|
||||||
|
if state == webrtc.PeerConnectionStateConnected {
|
||||||
|
pcDoneCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == webrtc.PeerConnectionStateFailed || state == webrtc.PeerConnectionStateClosed {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Wf(ctx, "Close for PC state %v", state)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// OK, DTLS/SRTP ok.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
case <-webrtcUpCtx.Done():
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for event from context or tracks.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-ctx.Done()
|
||||||
|
doClose() // Interrupt the RTCP read.
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if aIngester == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-pcDoneCtx.Done():
|
||||||
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read audio packets")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1500)
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
if _, _, err := aIngester.sAudioSender.Read(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if aIngester == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-pcDoneCtx.Done():
|
||||||
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest audio %v", sourceAudio)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read audio and send out.
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
if err := aIngester.Ingest(ctx); err != nil {
|
||||||
|
if errors.Cause(err) == io.EOF {
|
||||||
|
logger.Tf(ctx, "EOF, restart ingest audio %v", sourceAudio)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Wf(ctx, "Ignore audio err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if vIngester == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-pcDoneCtx.Done():
|
||||||
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read video packets")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1500)
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
if _, _, err := vIngester.sVideoSender.Read(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if vIngester == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-pcDoneCtx.Done():
|
||||||
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest video %v", sourceVideo)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
if err := vIngester.Ingest(ctx); err != nil {
|
||||||
|
if errors.Cause(err) == io.EOF {
|
||||||
|
logger.Tf(ctx, "EOF, restart ingest video %v", sourceVideo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Wf(ctx, "Ignore video err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
1176
trunk/3rdparty/srs-bench/janus/util.go
vendored
Normal file
1176
trunk/3rdparty/srs-bench/janus/util.go
vendored
Normal file
File diff suppressed because it is too large
Load diff
48
trunk/3rdparty/srs-bench/janus/util2.go
vendored
Normal file
48
trunk/3rdparty/srs-bench/janus/util2.go
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// 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 janus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTransactionID() string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
for i := 0; i < 12; i++ {
|
||||||
|
sb.WriteByte(byte('a') + byte(rand.Int()%26))
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeJSON(s string) string {
|
||||||
|
var o map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(s), &o); err != nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, err := json.Marshal(o); err != nil {
|
||||||
|
return s
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
251
trunk/3rdparty/srs-bench/main.go
vendored
251
trunk/3rdparty/srs-bench/main.go
vendored
|
@ -24,133 +24,35 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ossrs/go-oryx-lib/errors"
|
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"github.com/ossrs/srs-bench/janus"
|
||||||
"github.com/ossrs/srs-bench/srs"
|
"github.com/ossrs/srs-bench/srs"
|
||||||
"net"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var sr, dumpAudio, dumpVideo string
|
var sfu string
|
||||||
var pli int
|
fl := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||||
flag.StringVar(&sr, "sr", "", "")
|
fl.SetOutput(ioutil.Discard)
|
||||||
flag.StringVar(&dumpAudio, "da", "", "")
|
fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or janus")
|
||||||
flag.StringVar(&dumpVideo, "dv", "", "")
|
_ = fl.Parse(os.Args[1:])
|
||||||
flag.IntVar(&pli, "pli", 10, "")
|
|
||||||
|
|
||||||
var pr, sourceAudio, sourceVideo string
|
|
||||||
var fps int
|
|
||||||
flag.StringVar(&pr, "pr", "", "")
|
|
||||||
flag.StringVar(&sourceAudio, "sa", "", "")
|
|
||||||
flag.StringVar(&sourceVideo, "sv", "", "")
|
|
||||||
flag.IntVar(&fps, "fps", 0, "")
|
|
||||||
|
|
||||||
var audioLevel, videoTWCC bool
|
|
||||||
flag.BoolVar(&audioLevel, "al", true, "")
|
|
||||||
flag.BoolVar(&videoTWCC, "twcc", true, "")
|
|
||||||
|
|
||||||
var clients, streams, delay int
|
|
||||||
flag.IntVar(&clients, "nn", 1, "")
|
|
||||||
flag.IntVar(&streams, "sn", 1, "")
|
|
||||||
flag.IntVar(&delay, "delay", 50, "")
|
|
||||||
|
|
||||||
var statListen string
|
|
||||||
flag.StringVar(&statListen, "stat", "", "")
|
|
||||||
|
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Println(fmt.Sprintf("Usage: %v [Options]", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("Options:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -nn The number of clients to simulate. Default: 1"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -sn The number of streams to simulate. Variable: %%d. Default: 1"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -delay The start delay in ms for each client or stream to simulate. Default: 50"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -al [Optional] Whether enable audio-level. Default: true"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -twcc [Optional] Whether enable vdieo-twcc. Default: true"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -stat [Optional] The stat server API listen port."))
|
|
||||||
fmt.Println(fmt.Sprintf("Player or Subscriber:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -sr The url to play/subscribe. If sn exceed 1, auto append variable %%d."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -da [Optional] The file path to dump audio, ignore if empty."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -dv [Optional] The file path to dump video, ignore if empty."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -pli [Optional] PLI request interval in seconds. Default: 10"))
|
|
||||||
fmt.Println(fmt.Sprintf("Publisher:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" -pr The url to publish. If sn exceed 1, auto append variable %%d."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -fps The fps of .h264 source file."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -sa [Optional] The file path to read audio, ignore if empty."))
|
|
||||||
fmt.Println(fmt.Sprintf(" -sv [Optional] The file path to read video, ignore if empty."))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,1个播放,1个推流:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream -sa a.ogg -sv v.h264 -fps 25", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,1个流,3个播放,共3个客户端:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream -nn 3", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream -sa a.ogg -sv v.h264 -fps 25", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,2个流,每个流3个播放,共6个客户端:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream_%%d -sn 2 -nn 3", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream_%%d -sn 2 -sa a.ogg -sv v.h264 -fps 25", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,2个推流:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream_%%d -sn 2 -sa a.ogg -sv v.h264 -fps 25", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,1个录制:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream -da a.ogg -dv v.h264", os.Args[0]))
|
|
||||||
fmt.Println(fmt.Sprintf("\n例如,1个明文播放:"))
|
|
||||||
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream?encrypt=false", os.Args[0]))
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
showHelp := (clients <= 0 || streams <= 0)
|
|
||||||
if sr == "" && pr == "" {
|
|
||||||
showHelp = true
|
|
||||||
}
|
|
||||||
if pr != "" && (sourceAudio == "" && sourceVideo == "") {
|
|
||||||
showHelp = true
|
|
||||||
}
|
|
||||||
if showHelp {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if statListen != "" && !strings.Contains(statListen, ":") {
|
|
||||||
statListen = ":" + statListen
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
summaryDesc := fmt.Sprintf("clients=%v, delay=%v, al=%v, twcc=%v, stat=%v", clients, delay, audioLevel, videoTWCC, statListen)
|
if sfu == "srs" {
|
||||||
if sr != "" {
|
srs.Parse(ctx)
|
||||||
summaryDesc = fmt.Sprintf("%v, play(url=%v, da=%v, dv=%v, pli=%v)", summaryDesc, sr, dumpAudio, dumpVideo, pli)
|
} else if sfu == "janus" {
|
||||||
}
|
janus.Parse(ctx)
|
||||||
if pr != "" {
|
} else {
|
||||||
summaryDesc = fmt.Sprintf("%v, publish(url=%v, sa=%v, sv=%v, fps=%v)",
|
fmt.Println(fmt.Sprintf("Usage: %v [Options]", os.Args[0]))
|
||||||
summaryDesc, pr, sourceAudio, sourceVideo, fps)
|
fmt.Println(fmt.Sprintf("Options:"))
|
||||||
}
|
fmt.Println(fmt.Sprintf(" -sfu The target SFU, srs or janus. Default: srs"))
|
||||||
logger.Tf(ctx, "Start benchmark with %v", summaryDesc)
|
|
||||||
|
|
||||||
checkFlag := func() error {
|
|
||||||
if dumpVideo != "" && !strings.HasSuffix(dumpVideo, ".h264") && !strings.HasSuffix(dumpVideo, ".ivf") {
|
|
||||||
return errors.Errorf("Should be .ivf or .264, actual %v", dumpVideo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sourceVideo != "" && !strings.HasSuffix(sourceVideo, ".h264") {
|
|
||||||
return errors.Errorf("Should be .264, actual %v", sourceVideo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sourceVideo != "" && strings.HasSuffix(sourceVideo, ".h264") && fps <= 0 {
|
|
||||||
return errors.Errorf("Video fps should >0, actual %v", fps)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := checkFlag(); err != nil {
|
|
||||||
logger.Ef(ctx, "Check faile err %+v", err)
|
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
// Process all signals.
|
|
||||||
go func() {
|
go func() {
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
|
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
@ -160,124 +62,17 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Run tasks.
|
var err error
|
||||||
var wg sync.WaitGroup
|
if sfu == "srs" {
|
||||||
|
err = srs.Run(ctx)
|
||||||
// Start STAT API server.
|
} else if sfu == "janus" {
|
||||||
wg.Add(1)
|
err = janus.Run(ctx)
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
if statListen == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var lc net.ListenConfig
|
|
||||||
ln, err := lc.Listen(ctx, "tcp", statListen)
|
|
||||||
if err != nil {
|
|
||||||
logger.Ef(ctx, "stat listen err+%v", err)
|
|
||||||
cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
srs.HandleStat(ctx, mux, statListen)
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Handler: mux,
|
|
||||||
BaseContext: func(listener net.Listener) context.Context {
|
|
||||||
return ctx
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
srv.Shutdown(ctx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
logger.Tf(ctx, "Stat listen at %v", statListen)
|
|
||||||
if err := srv.Serve(ln); err != nil {
|
|
||||||
if ctx.Err() == nil {
|
|
||||||
logger.Ef(ctx, "stat serve err+%v", err)
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Start all subscribers or players.
|
|
||||||
for i := 0; sr != "" && i < streams && ctx.Err() == nil; i++ {
|
|
||||||
r_auto := sr
|
|
||||||
if streams > 1 && !strings.Contains(r_auto, "%") {
|
|
||||||
r_auto += "%d"
|
|
||||||
}
|
|
||||||
|
|
||||||
r2 := r_auto
|
|
||||||
if strings.Contains(r2, "%") {
|
|
||||||
r2 = fmt.Sprintf(r2, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := 0; sr != "" && j < clients && ctx.Err() == nil; j++ {
|
|
||||||
// Dump audio or video only for the first client.
|
|
||||||
da, dv := dumpAudio, dumpVideo
|
|
||||||
if i > 0 {
|
|
||||||
da, dv = "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
srs.StatRTC.Subscribers.Expect++
|
|
||||||
srs.StatRTC.Subscribers.Alive++
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func(sr, da, dv string) {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
|
||||||
srs.StatRTC.Subscribers.Alive--
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := srs.StartPlay(ctx, sr, da, dv, audioLevel, videoTWCC, pli); err != nil {
|
|
||||||
if errors.Cause(err) != context.Canceled {
|
|
||||||
logger.Wf(ctx, "Run err %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(r2, da, dv)
|
|
||||||
|
|
||||||
time.Sleep(time.Duration(delay) * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start all publishers.
|
if err != nil {
|
||||||
for i := 0; pr != "" && i < streams && ctx.Err() == nil; i++ {
|
logger.Wf(ctx, "srs err %+v", err)
|
||||||
r_auto := pr
|
return
|
||||||
if streams > 1 && !strings.Contains(r_auto, "%") {
|
|
||||||
r_auto += "%d"
|
|
||||||
}
|
|
||||||
|
|
||||||
r2 := r_auto
|
|
||||||
if strings.Contains(r2, "%") {
|
|
||||||
r2 = fmt.Sprintf(r2, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
srs.StatRTC.Publishers.Expect++
|
|
||||||
srs.StatRTC.Publishers.Alive++
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func(pr string) {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
|
||||||
srs.StatRTC.Publishers.Alive--
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := srs.StartPublish(ctx, pr, sourceAudio, sourceVideo, fps, audioLevel, videoTWCC); err != nil {
|
|
||||||
if errors.Cause(err) != context.Canceled {
|
|
||||||
logger.Wf(ctx, "Run err %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(r2)
|
|
||||||
|
|
||||||
time.Sleep(time.Duration(delay) * time.Millisecond)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
logger.Tf(ctx, "Done")
|
logger.Tf(ctx, "Done")
|
||||||
}
|
}
|
||||||
|
|
12
trunk/3rdparty/srs-bench/srs/ingester.go
vendored
12
trunk/3rdparty/srs-bench/srs/ingester.go
vendored
|
@ -41,15 +41,15 @@ import (
|
||||||
type videoIngester struct {
|
type videoIngester struct {
|
||||||
sourceVideo string
|
sourceVideo string
|
||||||
fps int
|
fps int
|
||||||
markerInterceptor *RTPInterceptor
|
markerInterceptor *rtpInterceptor
|
||||||
sVideoTrack *webrtc.TrackLocalStaticSample
|
sVideoTrack *webrtc.TrackLocalStaticSample
|
||||||
sVideoSender *webrtc.RTPSender
|
sVideoSender *webrtc.RTPSender
|
||||||
ready context.Context
|
ready context.Context
|
||||||
readyCancel context.CancelFunc
|
readyCancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVideoIngester(sourceVideo string) *videoIngester {
|
func newVideoIngester(sourceVideo string) *videoIngester {
|
||||||
v := &videoIngester{markerInterceptor: &RTPInterceptor{}, sourceVideo: sourceVideo}
|
v := &videoIngester{markerInterceptor: &rtpInterceptor{}, sourceVideo: sourceVideo}
|
||||||
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -183,15 +183,15 @@ func (v *videoIngester) Ingest(ctx context.Context) error {
|
||||||
|
|
||||||
type audioIngester struct {
|
type audioIngester struct {
|
||||||
sourceAudio string
|
sourceAudio string
|
||||||
audioLevelInterceptor *RTPInterceptor
|
audioLevelInterceptor *rtpInterceptor
|
||||||
sAudioTrack *webrtc.TrackLocalStaticSample
|
sAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
sAudioSender *webrtc.RTPSender
|
sAudioSender *webrtc.RTPSender
|
||||||
ready context.Context
|
ready context.Context
|
||||||
readyCancel context.CancelFunc
|
readyCancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAudioIngester(sourceAudio string) *audioIngester {
|
func newAudioIngester(sourceAudio string) *audioIngester {
|
||||||
v := &audioIngester{audioLevelInterceptor: &RTPInterceptor{}, sourceAudio: sourceAudio}
|
v := &audioIngester{audioLevelInterceptor: &rtpInterceptor{}, sourceAudio: sourceAudio}
|
||||||
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
v.ready, v.readyCancel = context.WithCancel(context.Background())
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
60
trunk/3rdparty/srs-bench/srs/interceptor.go
vendored
60
trunk/3rdparty/srs-bench/srs/interceptor.go
vendored
|
@ -26,11 +26,11 @@ import (
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RTPInterceptorOptionFunc func(i *RTPInterceptor)
|
type rtpInterceptorOptionFunc func(i *rtpInterceptor)
|
||||||
|
|
||||||
// Common RTP packet interceptor for benchmark.
|
// Common RTP packet interceptor for benchmark.
|
||||||
// @remark Should never merge with RTCPInterceptor, because they has the same Write interface.
|
// @remark Should never merge with rtcpInterceptor, because they has the same Write interface.
|
||||||
type RTPInterceptor struct {
|
type rtpInterceptor struct {
|
||||||
// If rtpReader is nil, use the default next one to read.
|
// If rtpReader is nil, use the default next one to read.
|
||||||
rtpReader interceptor.RTPReaderFunc
|
rtpReader interceptor.RTPReaderFunc
|
||||||
nextRTPReader interceptor.RTPReader
|
nextRTPReader interceptor.RTPReader
|
||||||
|
@ -38,52 +38,52 @@ type RTPInterceptor struct {
|
||||||
rtpWriter interceptor.RTPWriterFunc
|
rtpWriter interceptor.RTPWriterFunc
|
||||||
nextRTPWriter interceptor.RTPWriter
|
nextRTPWriter interceptor.RTPWriter
|
||||||
// Other common fields.
|
// Other common fields.
|
||||||
BypassInterceptor
|
bypassInterceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTPInterceptor(options ...RTPInterceptorOptionFunc) *RTPInterceptor {
|
func newRTPInterceptor(options ...rtpInterceptorOptionFunc) *rtpInterceptor {
|
||||||
v := &RTPInterceptor{}
|
v := &rtpInterceptor{}
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
func (v *rtpInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
||||||
v.nextRTPWriter = writer
|
v.nextRTPWriter = writer
|
||||||
return v // Handle all RTP
|
return v // Handle all RTP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) Write(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
func (v *rtpInterceptor) Write(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
if v.rtpWriter != nil {
|
if v.rtpWriter != nil {
|
||||||
return v.rtpWriter(header, payload, attributes)
|
return v.rtpWriter(header, payload, attributes)
|
||||||
}
|
}
|
||||||
return v.nextRTPWriter.Write(header, payload, attributes)
|
return v.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
func (v *rtpInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
func (v *rtpInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
||||||
v.nextRTPReader = reader
|
v.nextRTPReader = reader
|
||||||
return v // Handle all RTP
|
return v // Handle all RTP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
func (v *rtpInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if v.rtpReader != nil {
|
if v.rtpReader != nil {
|
||||||
return v.rtpReader(b, a)
|
return v.rtpReader(b, a)
|
||||||
}
|
}
|
||||||
return v.nextRTPReader.Read(b, a)
|
return v.nextRTPReader.Read(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTPInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
func (v *rtpInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RTCPInterceptorOptionFunc func(i *RTCPInterceptor)
|
type rtcpInterceptorOptionFunc func(i *rtcpInterceptor)
|
||||||
|
|
||||||
// Common RTCP packet interceptor for benchmark.
|
// Common RTCP packet interceptor for benchmark.
|
||||||
// @remark Should never merge with RTPInterceptor, because they has the same Write interface.
|
// @remark Should never merge with rtpInterceptor, because they has the same Write interface.
|
||||||
type RTCPInterceptor struct {
|
type rtcpInterceptor struct {
|
||||||
// If rtcpReader is nil, use the default next one to read.
|
// If rtcpReader is nil, use the default next one to read.
|
||||||
rtcpReader interceptor.RTCPReaderFunc
|
rtcpReader interceptor.RTCPReaderFunc
|
||||||
nextRTCPReader interceptor.RTCPReader
|
nextRTCPReader interceptor.RTCPReader
|
||||||
|
@ -91,35 +91,35 @@ type RTCPInterceptor struct {
|
||||||
rtcpWriter interceptor.RTCPWriterFunc
|
rtcpWriter interceptor.RTCPWriterFunc
|
||||||
nextRTCPWriter interceptor.RTCPWriter
|
nextRTCPWriter interceptor.RTCPWriter
|
||||||
// Other common fields.
|
// Other common fields.
|
||||||
BypassInterceptor
|
bypassInterceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRTCPInterceptor(options ...RTCPInterceptorOptionFunc) *RTCPInterceptor {
|
func newRTCPInterceptor(options ...rtcpInterceptorOptionFunc) *rtcpInterceptor {
|
||||||
v := &RTCPInterceptor{}
|
v := &rtcpInterceptor{}
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
opt(v)
|
opt(v)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCPInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
func (v *rtcpInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
||||||
v.nextRTCPReader = reader
|
v.nextRTCPReader = reader
|
||||||
return v // Handle all RTCP
|
return v // Handle all RTCP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCPInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
func (v *rtcpInterceptor) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if v.rtcpReader != nil {
|
if v.rtcpReader != nil {
|
||||||
return v.rtcpReader(b, a)
|
return v.rtcpReader(b, a)
|
||||||
}
|
}
|
||||||
return v.nextRTCPReader.Read(b, a)
|
return v.nextRTCPReader.Read(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCPInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
func (v *rtcpInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
||||||
v.nextRTCPWriter = writer
|
v.nextRTCPWriter = writer
|
||||||
return v // Handle all RTCP
|
return v // Handle all RTCP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RTCPInterceptor) Write(pkts []rtcp.Packet, attributes interceptor.Attributes) (int, error) {
|
func (v *rtcpInterceptor) Write(pkts []rtcp.Packet, attributes interceptor.Attributes) (int, error) {
|
||||||
if v.rtcpWriter != nil {
|
if v.rtcpWriter != nil {
|
||||||
return v.rtcpWriter(pkts, attributes)
|
return v.rtcpWriter(pkts, attributes)
|
||||||
}
|
}
|
||||||
|
@ -127,32 +127,32 @@ func (v *RTCPInterceptor) Write(pkts []rtcp.Packet, attributes interceptor.Attri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
type BypassInterceptor struct {
|
type bypassInterceptor struct {
|
||||||
interceptor.Interceptor
|
interceptor.Interceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
func (v *bypassInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
|
||||||
return reader
|
return reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
func (v *bypassInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
|
||||||
return writer
|
return writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
func (v *bypassInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
|
||||||
return writer
|
return writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
func (v *bypassInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
func (v *bypassInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
||||||
return reader
|
return reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
func (v *bypassInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BypassInterceptor) Close() error {
|
func (v *bypassInterceptor) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
6
trunk/3rdparty/srs-bench/srs/player.go
vendored
6
trunk/3rdparty/srs-bench/srs/player.go
vendored
|
@ -40,10 +40,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// @see https://github.com/pion/webrtc/blob/master/examples/save-to-disk/main.go
|
// @see https://github.com/pion/webrtc/blob/master/examples/save-to-disk/main.go
|
||||||
func StartPlay(ctx context.Context, r, dumpAudio, dumpVideo string, enableAudioLevel, enableTWCC bool, pli int) error {
|
func startPlay(ctx context.Context, r, dumpAudio, dumpVideo string, enableAudioLevel, enableTWCC bool, pli int) error {
|
||||||
ctx = logger.WithContext(ctx)
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
logger.Tf(ctx, "Start play url=%v, audio=%v, video=%v, audio-level=%v, twcc=%v",
|
logger.Tf(ctx, "Run play url=%v, audio=%v, video=%v, audio-level=%v, twcc=%v",
|
||||||
r, dumpAudio, dumpVideo, enableAudioLevel, enableTWCC)
|
r, dumpAudio, dumpVideo, enableAudioLevel, enableTWCC)
|
||||||
|
|
||||||
// For audio-level.
|
// For audio-level.
|
||||||
|
@ -257,7 +257,7 @@ func StartPlay(ctx context.Context, r, dumpAudio, dumpVideo string, enableAudioL
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
StatRTC.PeerConnection = pc.GetStats()
|
gStatRTC.PeerConnection = pc.GetStats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
20
trunk/3rdparty/srs-bench/srs/publisher.go
vendored
20
trunk/3rdparty/srs-bench/srs/publisher.go
vendored
|
@ -34,10 +34,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// @see https://github.com/pion/webrtc/blob/master/examples/play-from-disk/main.go
|
// @see https://github.com/pion/webrtc/blob/master/examples/play-from-disk/main.go
|
||||||
func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps int, enableAudioLevel, enableTWCC bool) error {
|
func startPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps int, enableAudioLevel, enableTWCC bool) error {
|
||||||
ctx = logger.WithContext(ctx)
|
ctx = logger.WithContext(ctx)
|
||||||
|
|
||||||
logger.Tf(ctx, "Start publish url=%v, audio=%v, video=%v, fps=%v, audio-level=%v, twcc=%v",
|
logger.Tf(ctx, "Run publish url=%v, audio=%v, video=%v, fps=%v, audio-level=%v, twcc=%v",
|
||||||
r, sourceAudio, sourceVideo, fps, enableAudioLevel, enableTWCC)
|
r, sourceAudio, sourceVideo, fps, enableAudioLevel, enableTWCC)
|
||||||
|
|
||||||
// Filter for SPS/PPS marker.
|
// Filter for SPS/PPS marker.
|
||||||
|
@ -78,11 +78,11 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceAudio != "" {
|
if sourceAudio != "" {
|
||||||
aIngester = NewAudioIngester(sourceAudio)
|
aIngester = newAudioIngester(sourceAudio)
|
||||||
registry.Add(aIngester.audioLevelInterceptor)
|
registry.Add(aIngester.audioLevelInterceptor)
|
||||||
}
|
}
|
||||||
if sourceVideo != "" {
|
if sourceVideo != "" {
|
||||||
vIngester = NewVideoIngester(sourceVideo)
|
vIngester = newVideoIngester(sourceVideo)
|
||||||
registry.Add(vIngester.markerInterceptor)
|
registry.Add(vIngester.markerInterceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
pcDone, pcDoneCancel := context.WithCancel(context.Background())
|
pcDoneCtx, pcDoneCancel := context.WithCancel(context.Background())
|
||||||
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
|
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
|
||||||
logger.Tf(ctx, "PC state %v", state)
|
logger.Tf(ctx, "PC state %v", state)
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case <-pcDone.Done():
|
case <-pcDoneCtx.Done():
|
||||||
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read audio packets")
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read audio packets")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case <-pcDone.Done():
|
case <-pcDoneCtx.Done():
|
||||||
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest audio %v", sourceAudio)
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest audio %v", sourceAudio)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case <-pcDone.Done():
|
case <-pcDoneCtx.Done():
|
||||||
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read video packets")
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start read video packets")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case <-pcDone.Done():
|
case <-pcDoneCtx.Done():
|
||||||
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest video %v", sourceVideo)
|
logger.Tf(ctx, "PC(ICE+DTLS+SRTP) done, start ingest video %v", sourceVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ func StartPublish(ctx context.Context, r, sourceAudio, sourceVideo string, fps i
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
StatRTC.PeerConnection = pc.GetStats()
|
gStatRTC.PeerConnection = pc.GetStats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
340
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
340
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
|
@ -58,6 +58,56 @@ func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Basic use scenario, publish a stream.
|
||||||
|
func TestRtcBasic_PublishOnly(t *testing.T) {
|
||||||
|
if err := filterTestError(func() error {
|
||||||
|
streamSuffix := fmt.Sprintf("publish-only-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
|
p.streamSuffix = streamSuffix
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
||||||
|
if err := p.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
|
var nnRTCP, nnRTP int64
|
||||||
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
|
nnRTP++
|
||||||
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
|
cancel() // Send enough packets, done.
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "publish write %v RTP read %v RTCP packets", nnRTP, nnRTCP)
|
||||||
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}, func(api *testWebRTCAPI) {
|
||||||
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
|
chunk, parsed := newChunkMessageType(c)
|
||||||
|
if !parsed {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Chunk %v, ok=%v %v bytes", chunk, ok, len(c.UserData()))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Run(ctx, cancel)
|
||||||
|
}()); err != nil {
|
||||||
|
t.Errorf("err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Basic use scenario, publish a stream, then play it.
|
// Basic use scenario, publish a stream, then play it.
|
||||||
func TestRtcBasic_PublishPlay(t *testing.T) {
|
func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
ctx := logger.WithContext(context.Background())
|
ctx := logger.WithContext(context.Background())
|
||||||
|
@ -83,8 +133,8 @@ func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
defer wg.Wait()
|
defer wg.Wait()
|
||||||
|
|
||||||
// The event notify.
|
// The event notify.
|
||||||
var thePublisher *TestPublisher
|
var thePublisher *testPublisher
|
||||||
var thePlayer *TestPlayer
|
var thePlayer *testPlayer
|
||||||
|
|
||||||
mainReady, mainReadyCancel := context.WithCancel(context.Background())
|
mainReady, mainReadyCancel := context.WithCancel(context.Background())
|
||||||
publishReady, publishReadyCancel := context.WithCancel(context.Background())
|
publishReady, publishReadyCancel := context.WithCancel(context.Background())
|
||||||
|
@ -99,13 +149,13 @@ func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
streamSuffix := fmt.Sprintf("basic-publish-play-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("basic-publish-play-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
|
||||||
// Initialize player with private api.
|
// Initialize player with private api.
|
||||||
if thePlayer, err = NewTestPlayer(CreateApiForPlayer, func(play *TestPlayer) error {
|
if thePlayer, err = newTestPlayer(createApiForPlayer, func(play *testPlayer) error {
|
||||||
play.streamSuffix = streamSuffix
|
play.streamSuffix = streamSuffix
|
||||||
resources = append(resources, play)
|
resources = append(resources, play)
|
||||||
|
|
||||||
var nnPlayWriteRTCP, nnPlayReadRTCP, nnPlayWriteRTP, nnPlayReadRTP uint64
|
var nnPlayWriteRTCP, nnPlayReadRTCP, nnPlayWriteRTP, nnPlayReadRTP uint64
|
||||||
return play.Setup(*srsVnetClientIP, func(api *TestWebRTCAPI) {
|
return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
api.registry.Add(NewRTPInterceptor(func(i *RTPInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnPlayReadRTP++; nnPlayReadRTP >= uint64(*srsPlayOKPackets) {
|
if nnPlayReadRTP++; nnPlayReadRTP >= uint64(*srsPlayOKPackets) {
|
||||||
cancel() // Completed.
|
cancel() // Completed.
|
||||||
|
@ -115,7 +165,7 @@ func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
return i.nextRTPReader.Read(payload, attributes)
|
return i.nextRTPReader.Read(payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
||||||
nnPlayReadRTCP++
|
nnPlayReadRTCP++
|
||||||
|
@ -133,14 +183,14 @@ func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize publisher with private api.
|
// Initialize publisher with private api.
|
||||||
if thePublisher, err = NewTestPublisher(CreateApiForPublisher, func(pub *TestPublisher) error {
|
if thePublisher, err = newTestPublisher(createApiForPublisher, func(pub *testPublisher) error {
|
||||||
pub.streamSuffix = streamSuffix
|
pub.streamSuffix = streamSuffix
|
||||||
pub.iceReadyCancel = publishReadyCancel
|
pub.iceReadyCancel = publishReadyCancel
|
||||||
resources = append(resources, pub)
|
resources = append(resources, pub)
|
||||||
|
|
||||||
var nnPubWriteRTCP, nnPubReadRTCP, nnPubWriteRTP, nnPubReadRTP uint64
|
var nnPubWriteRTCP, nnPubReadRTCP, nnPubWriteRTP, nnPubReadRTP uint64
|
||||||
return pub.Setup(*srsVnetClientIP, func(api *TestWebRTCAPI) {
|
return pub.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
api.registry.Add(NewRTPInterceptor(func(i *RTPInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
i.rtpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
nn, attr, err := i.nextRTPReader.Read(buf, attributes)
|
nn, attr, err := i.nextRTPReader.Read(buf, attributes)
|
||||||
nnPubReadRTP++
|
nnPubReadRTP++
|
||||||
|
@ -154,7 +204,7 @@ func TestRtcBasic_PublishPlay(t *testing.T) {
|
||||||
return nn, err
|
return nn, err
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
||||||
nnPubReadRTCP++
|
nnPubReadRTCP++
|
||||||
|
@ -237,8 +287,8 @@ func TestRtcBasic_Republish(t *testing.T) {
|
||||||
defer wg.Wait()
|
defer wg.Wait()
|
||||||
|
|
||||||
// The event notify.
|
// The event notify.
|
||||||
var thePublisher, theRepublisher *TestPublisher
|
var thePublisher, theRepublisher *testPublisher
|
||||||
var thePlayer *TestPlayer
|
var thePlayer *testPlayer
|
||||||
|
|
||||||
mainReady, mainReadyCancel := context.WithCancel(context.Background())
|
mainReady, mainReadyCancel := context.WithCancel(context.Background())
|
||||||
publishReady, publishReadyCancel := context.WithCancel(context.Background())
|
publishReady, publishReadyCancel := context.WithCancel(context.Background())
|
||||||
|
@ -254,13 +304,13 @@ func TestRtcBasic_Republish(t *testing.T) {
|
||||||
streamSuffix := fmt.Sprintf("basic-publish-play-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("basic-publish-play-%v-%v", os.Getpid(), rand.Int())
|
||||||
|
|
||||||
// Initialize player with private api.
|
// Initialize player with private api.
|
||||||
if thePlayer, err = NewTestPlayer(CreateApiForPlayer, func(play *TestPlayer) error {
|
if thePlayer, err = newTestPlayer(createApiForPlayer, func(play *testPlayer) error {
|
||||||
play.streamSuffix = streamSuffix
|
play.streamSuffix = streamSuffix
|
||||||
resources = append(resources, play)
|
resources = append(resources, play)
|
||||||
|
|
||||||
var nnPlayReadRTP uint64
|
var nnPlayReadRTP uint64
|
||||||
return play.Setup(*srsVnetClientIP, func(api *TestWebRTCAPI) {
|
return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
api.registry.Add(NewRTPInterceptor(func(i *RTPInterceptor) {
|
api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) {
|
||||||
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
select {
|
select {
|
||||||
case <-republishReady.Done():
|
case <-republishReady.Done():
|
||||||
|
@ -280,14 +330,14 @@ func TestRtcBasic_Republish(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize publisher with private api.
|
// Initialize publisher with private api.
|
||||||
if thePublisher, err = NewTestPublisher(CreateApiForPublisher, func(pub *TestPublisher) error {
|
if thePublisher, err = newTestPublisher(createApiForPublisher, func(pub *testPublisher) error {
|
||||||
pub.streamSuffix = streamSuffix
|
pub.streamSuffix = streamSuffix
|
||||||
pub.iceReadyCancel = publishReadyCancel
|
pub.iceReadyCancel = publishReadyCancel
|
||||||
resources = append(resources, pub)
|
resources = append(resources, pub)
|
||||||
|
|
||||||
var nnPubReadRTCP uint64
|
var nnPubReadRTCP uint64
|
||||||
return pub.Setup(*srsVnetClientIP, func(api *TestWebRTCAPI) {
|
return pub.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) {
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
nn, attr, err := i.nextRTCPReader.Read(buf, attributes)
|
||||||
if nnPubReadRTCP++; nnPubReadRTCP > 0 && pub.cancel != nil {
|
if nnPubReadRTCP++; nnPubReadRTCP > 0 && pub.cancel != nil {
|
||||||
|
@ -303,7 +353,7 @@ func TestRtcBasic_Republish(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize re-publisher with private api.
|
// Initialize re-publisher with private api.
|
||||||
if theRepublisher, err = NewTestPublisher(CreateApiForPublisher, func(pub *TestPublisher) error {
|
if theRepublisher, err = newTestPublisher(createApiForPublisher, func(pub *testPublisher) error {
|
||||||
pub.streamSuffix = streamSuffix
|
pub.streamSuffix = streamSuffix
|
||||||
pub.iceReadyCancel = republishReadyCancel
|
pub.iceReadyCancel = republishReadyCancel
|
||||||
resources = append(resources, pub)
|
resources = append(resources, pub)
|
||||||
|
@ -369,7 +419,7 @@ func TestRtcBasic_Republish(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientActive_Default(t *testing.T) {
|
func TestRtcDTLS_ClientActive_Default(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -380,15 +430,15 @@ func TestRtcDTLS_ClientActive_Default(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -397,9 +447,9 @@ func TestRtcDTLS_ClientActive_Default(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -424,7 +474,7 @@ func TestRtcDTLS_ClientActive_Default(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_Default(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_Default(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -435,15 +485,15 @@ func TestRtcDTLS_ClientPassive_Default(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -452,9 +502,9 @@ func TestRtcDTLS_ClientPassive_Default(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -476,7 +526,7 @@ func TestRtcDTLS_ClientPassive_Default(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientActive_Duplicated_Alert(t *testing.T) {
|
func TestRtcDTLS_ClientActive_Duplicated_Alert(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -487,15 +537,15 @@ func TestRtcDTLS_ClientActive_Duplicated_Alert(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -504,15 +554,15 @@ func TestRtcDTLS_ClientActive_Duplicated_Alert(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS {
|
if !parsed || chunk.chunk != chunkTypeDTLS {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the alert to server, ignore error.
|
// Copy the alert to server, ignore error.
|
||||||
if chunk.content == DTLSContentTypeAlert {
|
if chunk.content == dtlsContentTypeAlert {
|
||||||
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
||||||
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
||||||
}
|
}
|
||||||
|
@ -535,7 +585,7 @@ func TestRtcDTLS_ClientActive_Duplicated_Alert(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_Duplicated_Alert(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_Duplicated_Alert(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(func() error {
|
||||||
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
streamSuffix := fmt.Sprintf("dtls-active-no-arq-%v-%v", os.Getpid(), rand.Int())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -546,15 +596,15 @@ func TestRtcDTLS_ClientPassive_Duplicated_Alert(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -563,15 +613,15 @@ func TestRtcDTLS_ClientPassive_Duplicated_Alert(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS {
|
if !parsed || chunk.chunk != chunkTypeDTLS {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the alert to server, ignore error.
|
// Copy the alert to server, ignore error.
|
||||||
if chunk.content == DTLSContentTypeAlert {
|
if chunk.content == dtlsContentTypeAlert {
|
||||||
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
||||||
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
_, _ = api.proxy.Deliver(c.SourceAddr(), c.DestinationAddr(), c.UserData())
|
||||||
}
|
}
|
||||||
|
@ -601,7 +651,7 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -612,15 +662,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -629,17 +679,17 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.T
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnClientHello, nnMaxDrop := 0, 1
|
nnClientHello, nnMaxDrop := 0, 1
|
||||||
var lastClientHello *DTLSRecord
|
var lastClientHello *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake || chunk.handshake != DTLSHandshakeTypeClientHello {
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake || chunk.handshake != dtlsHandshakeTypeClientHello {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -679,7 +729,7 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -690,15 +740,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -707,17 +757,17 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ClientHello(t *testing.
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnClientHello, nnMaxDrop := 0, 1
|
nnClientHello, nnMaxDrop := 0, 1
|
||||||
var lastClientHello *DTLSRecord
|
var lastClientHello *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake || chunk.handshake != DTLSHandshakeTypeClientHello {
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake || chunk.handshake != dtlsHandshakeTypeClientHello {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -756,7 +806,7 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -767,15 +817,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -784,23 +834,23 @@ func TestRtcDTLS_ClientActive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.T
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnServerHello, nnMaxDrop := 0, 1
|
nnServerHello, nnMaxDrop := 0, 1
|
||||||
var lastClientHello, lastServerHello *DTLSRecord
|
var lastClientHello, lastServerHello *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake ||
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake ||
|
||||||
(chunk.handshake != DTLSHandshakeTypeClientHello && chunk.handshake != DTLSHandshakeTypeServerHello) {
|
(chunk.handshake != dtlsHandshakeTypeClientHello && chunk.handshake != dtlsHandshakeTypeServerHello) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk.handshake == DTLSHandshakeTypeClientHello {
|
if chunk.handshake == dtlsHandshakeTypeClientHello {
|
||||||
if lastClientHello != nil && record.Equals(lastClientHello) {
|
if lastClientHello != nil && record.Equals(lastClientHello) {
|
||||||
r0 = errors.Errorf("dup record %v", record)
|
r0 = errors.Errorf("dup record %v", record)
|
||||||
}
|
}
|
||||||
|
@ -844,7 +894,7 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -855,15 +905,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -872,23 +922,23 @@ func TestRtcDTLS_ClientPassive_ARQ_ClientHello_ByDropped_ServerHello(t *testing.
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnServerHello, nnMaxDrop := 0, 1
|
nnServerHello, nnMaxDrop := 0, 1
|
||||||
var lastClientHello, lastServerHello *DTLSRecord
|
var lastClientHello, lastServerHello *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake ||
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake ||
|
||||||
(chunk.handshake != DTLSHandshakeTypeClientHello && chunk.handshake != DTLSHandshakeTypeServerHello) {
|
(chunk.handshake != dtlsHandshakeTypeClientHello && chunk.handshake != dtlsHandshakeTypeServerHello) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk.handshake == DTLSHandshakeTypeClientHello {
|
if chunk.handshake == dtlsHandshakeTypeClientHello {
|
||||||
if lastClientHello != nil && record.Equals(lastClientHello) {
|
if lastClientHello != nil && record.Equals(lastClientHello) {
|
||||||
r0 = errors.Errorf("dup record %v", record)
|
r0 = errors.Errorf("dup record %v", record)
|
||||||
}
|
}
|
||||||
|
@ -929,7 +979,7 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -940,15 +990,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -957,17 +1007,17 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_Certificate(t *testing.T
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnCertificate, nnMaxDrop := 0, 1
|
nnCertificate, nnMaxDrop := 0, 1
|
||||||
var lastCertificate *DTLSRecord
|
var lastCertificate *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake || chunk.handshake != DTLSHandshakeTypeCertificate {
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake || chunk.handshake != dtlsHandshakeTypeCertificate {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +1056,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1017,15 +1067,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -1034,17 +1084,17 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_Certificate(t *testing.
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnCertificate, nnMaxDrop := 0, 1
|
nnCertificate, nnMaxDrop := 0, 1
|
||||||
var lastCertificate *DTLSRecord
|
var lastCertificate *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || chunk.chunk != ChunkTypeDTLS || chunk.content != DTLSContentTypeHandshake || chunk.handshake != DTLSHandshakeTypeCertificate {
|
if !parsed || chunk.chunk != chunkTypeDTLS || chunk.content != dtlsContentTypeHandshake || chunk.handshake != dtlsHandshakeTypeCertificate {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1083,7 +1133,7 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *test
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupActive
|
p.onOffer = testUtilSetupActive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1094,15 +1144,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -1111,17 +1161,17 @@ func TestRtcDTLS_ClientActive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *test
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnCertificate, nnMaxDrop := 0, 1
|
nnCertificate, nnMaxDrop := 0, 1
|
||||||
var lastChangeCipherSepc, lastCertifidate *DTLSRecord
|
var lastChangeCipherSepc, lastCertifidate *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || (!chunk.IsChangeCipherSpec() && !chunk.IsCertificate()) {
|
if !parsed || (!chunk.IsChangeCipherSpec() && !chunk.IsCertificate()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1169,7 +1219,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *tes
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1180,15 +1230,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -1197,17 +1247,17 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *tes
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnCertificate, nnMaxDrop := 0, 1
|
nnCertificate, nnMaxDrop := 0, 1
|
||||||
var lastChangeCipherSepc, lastCertifidate *DTLSRecord
|
var lastChangeCipherSepc, lastCertifidate *dtlsRecord
|
||||||
|
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed || (!chunk.IsChangeCipherSpec() && !chunk.IsCertificate()) {
|
if !parsed || (!chunk.IsChangeCipherSpec() && !chunk.IsCertificate()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := NewDTLSRecord(c.UserData())
|
record, err := newDTLSRecord(c.UserData())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1246,7 +1296,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_ByDropped_ChangeCipherSpec(t *tes
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ClientHello(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ClientHello(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1257,10 +1307,10 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ClientHello(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
nnDrop, dropAll := 0, false
|
nnDrop, dropAll := 0, false
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1299,7 +1349,7 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ClientHello(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ServerHello(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ServerHello(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1310,10 +1360,10 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ServerHello(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
nnDrop, dropAll := 0, false
|
nnDrop, dropAll := 0, false
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1402,7 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ServerHello(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_Certificate(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_Certificate(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1363,10 +1413,10 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_Certificate(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
nnDrop, dropAll := 0, false
|
nnDrop, dropAll := 0, false
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1405,7 +1455,7 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_Certificate(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ChangeCipherSpec(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ChangeCipherSpec(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1416,10 +1466,10 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ChangeCipherSpec(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
nnDrop, dropAll := 0, false
|
nnDrop, dropAll := 0, false
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1459,7 +1509,7 @@ func TestRtcDTLS_ClientPassive_ARQ_DropAllAfter_ChangeCipherSpec(t *testing.T) {
|
||||||
func TestRtcDTLS_ClientPassive_ARQ_VeryBadNetwork(t *testing.T) {
|
func TestRtcDTLS_ClientPassive_ARQ_VeryBadNetwork(t *testing.T) {
|
||||||
if err := filterTestError(func() error {
|
if err := filterTestError(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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1470,15 +1520,15 @@ func TestRtcDTLS_ClientPassive_ARQ_VeryBadNetwork(t *testing.T) {
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -1487,10 +1537,10 @@ func TestRtcDTLS_ClientPassive_ARQ_VeryBadNetwork(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnDropClientHello, nnDropCertificate := 0, 0
|
nnDropClientHello, nnDropCertificate := 0, 0
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1537,7 +1587,7 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
||||||
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())
|
||||||
p, err := NewTestPublisher(CreateApiForPublisher, func(p *TestPublisher) error {
|
p, err := newTestPublisher(createApiForPublisher, func(p *testPublisher) error {
|
||||||
p.streamSuffix = streamSuffix
|
p.streamSuffix = streamSuffix
|
||||||
p.onOffer = testUtilSetupPassive
|
p.onOffer = testUtilSetupPassive
|
||||||
return nil
|
return nil
|
||||||
|
@ -1548,15 +1598,15 @@ 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)
|
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) {
|
||||||
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
i.rtpWriter = func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||||
nnRTP++
|
nnRTP++
|
||||||
return i.nextRTPWriter.Write(header, payload, attributes)
|
return i.nextRTPWriter.Write(header, payload, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
api.registry.Add(NewRTCPInterceptor(func(i *RTCPInterceptor) {
|
api.registry.Add(newRTCPInterceptor(func(i *rtcpInterceptor) {
|
||||||
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
i.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
if nnRTCP++; nnRTCP >= int64(*srsPublishOKPackets) && nnRTP >= int64(*srsPublishOKPackets) {
|
||||||
cancel() // Send enough packets, done.
|
cancel() // Send enough packets, done.
|
||||||
|
@ -1565,11 +1615,11 @@ func TestRtcDTLS_ClientPassive_ARQ_Certificate_After_ClientHello(t *testing.T) {
|
||||||
return i.nextRTCPReader.Read(buf, attributes)
|
return i.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}, func(api *TestWebRTCAPI) {
|
}, func(api *testWebRTCAPI) {
|
||||||
nnDropClientHello, nnDropCertificate := 0, 0
|
nnDropClientHello, nnDropCertificate := 0, 0
|
||||||
var firstCertificate time.Time
|
var firstCertificate time.Time
|
||||||
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
api.router.AddChunkFilter(func(c vnet.Chunk) (ok bool) {
|
||||||
chunk, parsed := NewChunkMessageType(c)
|
chunk, parsed := newChunkMessageType(c)
|
||||||
if !parsed {
|
if !parsed {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
282
trunk/3rdparty/srs-bench/srs/srs.go
vendored
Normal file
282
trunk/3rdparty/srs-bench/srs/srs.go
vendored
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
|
"github.com/ossrs/go-oryx-lib/logger"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sr, dumpAudio, dumpVideo string
|
||||||
|
var pli int
|
||||||
|
|
||||||
|
var pr, sourceAudio, sourceVideo string
|
||||||
|
var fps int
|
||||||
|
|
||||||
|
var audioLevel, videoTWCC bool
|
||||||
|
|
||||||
|
var clients, streams, delay int
|
||||||
|
|
||||||
|
var statListen string
|
||||||
|
|
||||||
|
func Parse(ctx context.Context) {
|
||||||
|
fl := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||||
|
|
||||||
|
var sfu string
|
||||||
|
fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or janus")
|
||||||
|
|
||||||
|
fl.StringVar(&sr, "sr", "", "")
|
||||||
|
fl.StringVar(&dumpAudio, "da", "", "")
|
||||||
|
fl.StringVar(&dumpVideo, "dv", "", "")
|
||||||
|
fl.IntVar(&pli, "pli", 10, "")
|
||||||
|
|
||||||
|
fl.StringVar(&pr, "pr", "", "")
|
||||||
|
fl.StringVar(&sourceAudio, "sa", "", "")
|
||||||
|
fl.StringVar(&sourceVideo, "sv", "", "")
|
||||||
|
fl.IntVar(&fps, "fps", 0, "")
|
||||||
|
|
||||||
|
fl.BoolVar(&audioLevel, "al", true, "")
|
||||||
|
fl.BoolVar(&videoTWCC, "twcc", true, "")
|
||||||
|
|
||||||
|
fl.IntVar(&clients, "nn", 1, "")
|
||||||
|
fl.IntVar(&streams, "sn", 1, "")
|
||||||
|
fl.IntVar(&delay, "delay", 50, "")
|
||||||
|
|
||||||
|
fl.StringVar(&statListen, "stat", "", "")
|
||||||
|
|
||||||
|
fl.Usage = func() {
|
||||||
|
fmt.Println(fmt.Sprintf("Usage: %v [Options]", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("Options:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sfu The target SFU, srs or janus. Default: srs"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -nn The number of clients to simulate. Default: 1"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sn The number of streams to simulate. Variable: %%d. Default: 1"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -delay The start delay in ms for each client or stream to simulate. Default: 50"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -al [Optional] Whether enable audio-level. Default: true"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -twcc [Optional] Whether enable vdieo-twcc. Default: true"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -stat [Optional] The stat server API listen port."))
|
||||||
|
fmt.Println(fmt.Sprintf("Player or Subscriber:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sr The url to play/subscribe. If sn exceed 1, auto append variable %%d."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -da [Optional] The file path to dump audio, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -dv [Optional] The file path to dump video, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -pli [Optional] PLI request interval in seconds. Default: 10"))
|
||||||
|
fmt.Println(fmt.Sprintf("Publisher:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" -pr The url to publish. If sn exceed 1, auto append variable %%d."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -fps The fps of .h264 source file."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sa [Optional] The file path to read audio, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf(" -sv [Optional] The file path to read video, ignore if empty."))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个播放,1个推流:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个流,3个播放,共3个客户端:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream -nn 3", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,2个流,每个流3个播放,共6个客户端:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream_%%d -sn 2 -nn 3", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream_%%d -sn 2 -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,2个推流:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -pr webrtc://localhost/live/livestream_%%d -sn 2 -sa avatar.ogg -sv avatar.h264 -fps 25", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个录制:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream -da avatar.ogg -dv avatar.h264", os.Args[0]))
|
||||||
|
fmt.Println(fmt.Sprintf("\n例如,1个明文播放:"))
|
||||||
|
fmt.Println(fmt.Sprintf(" %v -sr webrtc://localhost/live/livestream?encrypt=false", os.Args[0]))
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
_ = fl.Parse(os.Args[1:])
|
||||||
|
|
||||||
|
showHelp := (clients <= 0 || streams <= 0)
|
||||||
|
if sr == "" && pr == "" {
|
||||||
|
showHelp = true
|
||||||
|
}
|
||||||
|
if pr != "" && (sourceAudio == "" && sourceVideo == "") {
|
||||||
|
showHelp = true
|
||||||
|
}
|
||||||
|
if showHelp {
|
||||||
|
fl.Usage()
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statListen != "" && !strings.Contains(statListen, ":") {
|
||||||
|
statListen = ":" + statListen
|
||||||
|
}
|
||||||
|
|
||||||
|
summaryDesc := fmt.Sprintf("clients=%v, delay=%v, al=%v, twcc=%v, stat=%v", clients, delay, audioLevel, videoTWCC, statListen)
|
||||||
|
if sr != "" {
|
||||||
|
summaryDesc = fmt.Sprintf("%v, play(url=%v, da=%v, dv=%v, pli=%v)", summaryDesc, sr, dumpAudio, dumpVideo, pli)
|
||||||
|
}
|
||||||
|
if pr != "" {
|
||||||
|
summaryDesc = fmt.Sprintf("%v, publish(url=%v, sa=%v, sv=%v, fps=%v)",
|
||||||
|
summaryDesc, pr, sourceAudio, sourceVideo, fps)
|
||||||
|
}
|
||||||
|
logger.Tf(ctx, "Run benchmark with %v", summaryDesc)
|
||||||
|
|
||||||
|
checkFlags := func() error {
|
||||||
|
if dumpVideo != "" && !strings.HasSuffix(dumpVideo, ".h264") && !strings.HasSuffix(dumpVideo, ".ivf") {
|
||||||
|
return errors.Errorf("Should be .ivf or .264, actual %v", dumpVideo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceVideo != "" && !strings.HasSuffix(sourceVideo, ".h264") {
|
||||||
|
return errors.Errorf("Should be .264, actual %v", sourceVideo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceVideo != "" && strings.HasSuffix(sourceVideo, ".h264") && fps <= 0 {
|
||||||
|
return errors.Errorf("Video fps should >0, actual %v", fps)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := checkFlags(); err != nil {
|
||||||
|
logger.Ef(ctx, "Check faile err %+v", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ctx context.Context) error {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
// Run tasks.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Run STAT API server.
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if statListen == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var lc net.ListenConfig
|
||||||
|
ln, err := lc.Listen(ctx, "tcp", statListen)
|
||||||
|
if err != nil {
|
||||||
|
logger.Ef(ctx, "stat listen err+%v", err)
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
handleStat(ctx, mux, statListen)
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Handler: mux,
|
||||||
|
BaseContext: func(listener net.Listener) context.Context {
|
||||||
|
return ctx
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
logger.Tf(ctx, "Stat listen at %v", statListen)
|
||||||
|
if err := srv.Serve(ln); err != nil {
|
||||||
|
if ctx.Err() == nil {
|
||||||
|
logger.Ef(ctx, "stat serve err+%v", err)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run all subscribers or players.
|
||||||
|
for i := 0; sr != "" && i < streams && ctx.Err() == nil; i++ {
|
||||||
|
r_auto := sr
|
||||||
|
if streams > 1 && !strings.Contains(r_auto, "%") {
|
||||||
|
r_auto += "%d"
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 := r_auto
|
||||||
|
if strings.Contains(r2, "%") {
|
||||||
|
r2 = fmt.Sprintf(r2, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; sr != "" && j < clients && ctx.Err() == nil; j++ {
|
||||||
|
// Dump audio or video only for the first client.
|
||||||
|
da, dv := dumpAudio, dumpVideo
|
||||||
|
if i > 0 {
|
||||||
|
da, dv = "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
gStatRTC.Subscribers.Expect++
|
||||||
|
gStatRTC.Subscribers.Alive++
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(sr, da, dv string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() {
|
||||||
|
gStatRTC.Subscribers.Alive--
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := startPlay(ctx, sr, da, dv, audioLevel, videoTWCC, pli); err != nil {
|
||||||
|
if errors.Cause(err) != context.Canceled {
|
||||||
|
logger.Wf(ctx, "Run err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(r2, da, dv)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all publishers.
|
||||||
|
for i := 0; pr != "" && i < streams && ctx.Err() == nil; i++ {
|
||||||
|
r_auto := pr
|
||||||
|
if streams > 1 && !strings.Contains(r_auto, "%") {
|
||||||
|
r_auto += "%d"
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 := r_auto
|
||||||
|
if strings.Contains(r2, "%") {
|
||||||
|
r2 = fmt.Sprintf(r2, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
gStatRTC.Publishers.Expect++
|
||||||
|
gStatRTC.Publishers.Alive++
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(pr string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() {
|
||||||
|
gStatRTC.Publishers.Alive--
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := startPublish(ctx, pr, sourceAudio, sourceVideo, fps, audioLevel, videoTWCC); err != nil {
|
||||||
|
if errors.Cause(err) != context.Canceled {
|
||||||
|
logger.Wf(ctx, "Run err %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(r2)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
6
trunk/3rdparty/srs-bench/srs/stat.go
vendored
6
trunk/3rdparty/srs-bench/srs/stat.go
vendored
|
@ -41,9 +41,9 @@ type statRTC struct {
|
||||||
PeerConnection interface{} `json:"random-pc"`
|
PeerConnection interface{} `json:"random-pc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var StatRTC statRTC
|
var gStatRTC statRTC
|
||||||
|
|
||||||
func HandleStat(ctx context.Context, mux *http.ServeMux, l string) {
|
func handleStat(ctx context.Context, mux *http.ServeMux, l string) {
|
||||||
if strings.HasPrefix(l, ":") {
|
if strings.HasPrefix(l, ":") {
|
||||||
l = "127.0.0.1" + l
|
l = "127.0.0.1" + l
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func HandleStat(ctx context.Context, mux *http.ServeMux, l string) {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
}{
|
}{
|
||||||
0, &StatRTC,
|
0, &gStatRTC,
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(res)
|
b, err := json.Marshal(res)
|
||||||
|
|
214
trunk/3rdparty/srs-bench/srs/util.go
vendored
214
trunk/3rdparty/srs-bench/srs/util.go
vendored
|
@ -376,97 +376,97 @@ func srsIsRTCP(b []byte) bool {
|
||||||
return (len(b) >= 12) && (b[0]&0x80) != 0 && (b[1] >= 192 && b[1] <= 223)
|
return (len(b) >= 12) && (b[0]&0x80) != 0 && (b[1] >= 192 && b[1] <= 223)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChunkType int
|
type chunkType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ChunkTypeICE ChunkType = iota + 1
|
chunkTypeICE chunkType = iota + 1
|
||||||
ChunkTypeDTLS
|
chunkTypeDTLS
|
||||||
ChunkTypeRTP
|
chunkTypeRTP
|
||||||
ChunkTypeRTCP
|
chunkTypeRTCP
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v ChunkType) String() string {
|
func (v chunkType) String() string {
|
||||||
switch v {
|
switch v {
|
||||||
case ChunkTypeICE:
|
case chunkTypeICE:
|
||||||
return "ICE"
|
return "ICE"
|
||||||
case ChunkTypeDTLS:
|
case chunkTypeDTLS:
|
||||||
return "DTLS"
|
return "DTLS"
|
||||||
case ChunkTypeRTP:
|
case chunkTypeRTP:
|
||||||
return "RTP"
|
return "RTP"
|
||||||
case ChunkTypeRTCP:
|
case chunkTypeRTCP:
|
||||||
return "RTCP"
|
return "RTCP"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DTLSContentType int
|
type dtlsContentType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DTLSContentTypeHandshake DTLSContentType = 22
|
dtlsContentTypeHandshake dtlsContentType = 22
|
||||||
DTLSContentTypeChangeCipherSpec DTLSContentType = 20
|
dtlsContentTypeChangeCipherSpec dtlsContentType = 20
|
||||||
DTLSContentTypeAlert DTLSContentType = 21
|
dtlsContentTypeAlert dtlsContentType = 21
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v DTLSContentType) String() string {
|
func (v dtlsContentType) String() string {
|
||||||
switch v {
|
switch v {
|
||||||
case DTLSContentTypeHandshake:
|
case dtlsContentTypeHandshake:
|
||||||
return "Handshake"
|
return "Handshake"
|
||||||
case DTLSContentTypeChangeCipherSpec:
|
case dtlsContentTypeChangeCipherSpec:
|
||||||
return "ChangeCipherSpec"
|
return "ChangeCipherSpec"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DTLSHandshakeType int
|
type dtlsHandshakeType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DTLSHandshakeTypeClientHello DTLSHandshakeType = 1
|
dtlsHandshakeTypeClientHello dtlsHandshakeType = 1
|
||||||
DTLSHandshakeTypeServerHello DTLSHandshakeType = 2
|
dtlsHandshakeTypeServerHello dtlsHandshakeType = 2
|
||||||
DTLSHandshakeTypeCertificate DTLSHandshakeType = 11
|
dtlsHandshakeTypeCertificate dtlsHandshakeType = 11
|
||||||
DTLSHandshakeTypeServerKeyExchange DTLSHandshakeType = 12
|
dtlsHandshakeTypeServerKeyExchange dtlsHandshakeType = 12
|
||||||
DTLSHandshakeTypeCertificateRequest DTLSHandshakeType = 13
|
dtlsHandshakeTypeCertificateRequest dtlsHandshakeType = 13
|
||||||
DTLSHandshakeTypeServerDone DTLSHandshakeType = 14
|
dtlsHandshakeTypeServerDone dtlsHandshakeType = 14
|
||||||
DTLSHandshakeTypeCertificateVerify DTLSHandshakeType = 15
|
dtlsHandshakeTypeCertificateVerify dtlsHandshakeType = 15
|
||||||
DTLSHandshakeTypeClientKeyExchange DTLSHandshakeType = 16
|
dtlsHandshakeTypeClientKeyExchange dtlsHandshakeType = 16
|
||||||
DTLSHandshakeTypeFinished DTLSHandshakeType = 20
|
dtlsHandshakeTypeFinished dtlsHandshakeType = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v DTLSHandshakeType) String() string {
|
func (v dtlsHandshakeType) String() string {
|
||||||
switch v {
|
switch v {
|
||||||
case DTLSHandshakeTypeClientHello:
|
case dtlsHandshakeTypeClientHello:
|
||||||
return "ClientHello"
|
return "ClientHello"
|
||||||
case DTLSHandshakeTypeServerHello:
|
case dtlsHandshakeTypeServerHello:
|
||||||
return "ServerHello"
|
return "ServerHello"
|
||||||
case DTLSHandshakeTypeCertificate:
|
case dtlsHandshakeTypeCertificate:
|
||||||
return "Certificate"
|
return "Certificate"
|
||||||
case DTLSHandshakeTypeServerKeyExchange:
|
case dtlsHandshakeTypeServerKeyExchange:
|
||||||
return "ServerKeyExchange"
|
return "ServerKeyExchange"
|
||||||
case DTLSHandshakeTypeCertificateRequest:
|
case dtlsHandshakeTypeCertificateRequest:
|
||||||
return "CertificateRequest"
|
return "CertificateRequest"
|
||||||
case DTLSHandshakeTypeServerDone:
|
case dtlsHandshakeTypeServerDone:
|
||||||
return "ServerDone"
|
return "ServerDone"
|
||||||
case DTLSHandshakeTypeCertificateVerify:
|
case dtlsHandshakeTypeCertificateVerify:
|
||||||
return "CertificateVerify"
|
return "CertificateVerify"
|
||||||
case DTLSHandshakeTypeClientKeyExchange:
|
case dtlsHandshakeTypeClientKeyExchange:
|
||||||
return "ClientKeyExchange"
|
return "ClientKeyExchange"
|
||||||
case DTLSHandshakeTypeFinished:
|
case dtlsHandshakeTypeFinished:
|
||||||
return "Finished"
|
return "Finished"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChunkMessageType struct {
|
type chunkMessageType struct {
|
||||||
chunk ChunkType
|
chunk chunkType
|
||||||
content DTLSContentType
|
content dtlsContentType
|
||||||
handshake DTLSHandshakeType
|
handshake dtlsHandshakeType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) String() string {
|
func (v *chunkMessageType) String() string {
|
||||||
if v.chunk == ChunkTypeDTLS {
|
if v.chunk == chunkTypeDTLS {
|
||||||
if v.content == DTLSContentTypeHandshake {
|
if v.content == dtlsContentTypeHandshake {
|
||||||
return fmt.Sprintf("%v-%v-%v", v.chunk, v.content, v.handshake)
|
return fmt.Sprintf("%v-%v-%v", v.chunk, v.content, v.handshake)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%v-%v", v.chunk, v.content)
|
return fmt.Sprintf("%v-%v", v.chunk, v.content)
|
||||||
|
@ -475,26 +475,26 @@ func (v *ChunkMessageType) String() string {
|
||||||
return fmt.Sprintf("%v", v.chunk)
|
return fmt.Sprintf("%v", v.chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChunkMessageType(c vnet.Chunk) (*ChunkMessageType, bool) {
|
func newChunkMessageType(c vnet.Chunk) (*chunkMessageType, bool) {
|
||||||
b := c.UserData()
|
b := c.UserData()
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
v := &ChunkMessageType{}
|
v := &chunkMessageType{}
|
||||||
|
|
||||||
if srsIsRTPOrRTCP(b) {
|
if srsIsRTPOrRTCP(b) {
|
||||||
if srsIsRTCP(b) {
|
if srsIsRTCP(b) {
|
||||||
v.chunk = ChunkTypeRTCP
|
v.chunk = chunkTypeRTCP
|
||||||
} else {
|
} else {
|
||||||
v.chunk = ChunkTypeRTP
|
v.chunk = chunkTypeRTP
|
||||||
}
|
}
|
||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
if srsIsStun(b) {
|
if srsIsStun(b) {
|
||||||
v.chunk = ChunkTypeICE
|
v.chunk = chunkTypeICE
|
||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,40 +502,40 @@ func NewChunkMessageType(c vnet.Chunk) (*ChunkMessageType, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
v.chunk, v.content = ChunkTypeDTLS, DTLSContentType(b[0])
|
v.chunk, v.content = chunkTypeDTLS, dtlsContentType(b[0])
|
||||||
if v.content != DTLSContentTypeHandshake {
|
if v.content != dtlsContentTypeHandshake {
|
||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) < 14 {
|
if len(b) < 14 {
|
||||||
return v, false
|
return v, false
|
||||||
}
|
}
|
||||||
v.handshake = DTLSHandshakeType(b[13])
|
v.handshake = dtlsHandshakeType(b[13])
|
||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) IsHandshake() bool {
|
func (v *chunkMessageType) IsHandshake() bool {
|
||||||
return v.chunk == ChunkTypeDTLS && v.content == DTLSContentTypeHandshake
|
return v.chunk == chunkTypeDTLS && v.content == dtlsContentTypeHandshake
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) IsClientHello() bool {
|
func (v *chunkMessageType) IsClientHello() bool {
|
||||||
return v.chunk == ChunkTypeDTLS && v.content == DTLSContentTypeHandshake && v.handshake == DTLSHandshakeTypeClientHello
|
return v.chunk == chunkTypeDTLS && v.content == dtlsContentTypeHandshake && v.handshake == dtlsHandshakeTypeClientHello
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) IsServerHello() bool {
|
func (v *chunkMessageType) IsServerHello() bool {
|
||||||
return v.chunk == ChunkTypeDTLS && v.content == DTLSContentTypeHandshake && v.handshake == DTLSHandshakeTypeServerHello
|
return v.chunk == chunkTypeDTLS && v.content == dtlsContentTypeHandshake && v.handshake == dtlsHandshakeTypeServerHello
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) IsCertificate() bool {
|
func (v *chunkMessageType) IsCertificate() bool {
|
||||||
return v.chunk == ChunkTypeDTLS && v.content == DTLSContentTypeHandshake && v.handshake == DTLSHandshakeTypeCertificate
|
return v.chunk == chunkTypeDTLS && v.content == dtlsContentTypeHandshake && v.handshake == dtlsHandshakeTypeCertificate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ChunkMessageType) IsChangeCipherSpec() bool {
|
func (v *chunkMessageType) IsChangeCipherSpec() bool {
|
||||||
return v.chunk == ChunkTypeDTLS && v.content == DTLSContentTypeChangeCipherSpec
|
return v.chunk == chunkTypeDTLS && v.content == dtlsContentTypeChangeCipherSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
type DTLSRecord struct {
|
type dtlsRecord struct {
|
||||||
ContentType DTLSContentType
|
ContentType dtlsContentType
|
||||||
Version uint16
|
Version uint16
|
||||||
Epoch uint16
|
Epoch uint16
|
||||||
SequenceNumber uint64
|
SequenceNumber uint64
|
||||||
|
@ -543,25 +543,25 @@ type DTLSRecord struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDTLSRecord(b []byte) (*DTLSRecord, error) {
|
func newDTLSRecord(b []byte) (*dtlsRecord, error) {
|
||||||
v := &DTLSRecord{}
|
v := &dtlsRecord{}
|
||||||
return v, v.Unmarshal(b)
|
return v, v.Unmarshal(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DTLSRecord) String() string {
|
func (v *dtlsRecord) String() string {
|
||||||
return fmt.Sprintf("epoch=%v, sequence=%v", v.Epoch, v.SequenceNumber)
|
return fmt.Sprintf("epoch=%v, sequence=%v", v.Epoch, v.SequenceNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DTLSRecord) Equals(p *DTLSRecord) bool {
|
func (v *dtlsRecord) Equals(p *dtlsRecord) bool {
|
||||||
return v.Epoch == p.Epoch && v.SequenceNumber == p.SequenceNumber
|
return v.Epoch == p.Epoch && v.SequenceNumber == p.SequenceNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DTLSRecord) Unmarshal(b []byte) error {
|
func (v *dtlsRecord) Unmarshal(b []byte) error {
|
||||||
if len(b) < 13 {
|
if len(b) < 13 {
|
||||||
return errors.Errorf("requires 13B only %v", len(b))
|
return errors.Errorf("requires 13B only %v", len(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
v.ContentType = DTLSContentType(b[0])
|
v.ContentType = dtlsContentType(b[0])
|
||||||
v.Version = uint16(b[1])<<8 | uint16(b[2])
|
v.Version = uint16(b[1])<<8 | uint16(b[2])
|
||||||
v.Epoch = uint16(b[3])<<8 | uint16(b[4])
|
v.Epoch = uint16(b[3])<<8 | uint16(b[4])
|
||||||
v.SequenceNumber = uint64(b[5])<<40 | uint64(b[6])<<32 | uint64(b[7])<<24 | uint64(b[8])<<16 | uint64(b[9])<<8 | uint64(b[10])
|
v.SequenceNumber = uint64(b[5])<<40 | uint64(b[6])<<32 | uint64(b[7])<<24 | uint64(b[8])<<16 | uint64(b[9])<<8 | uint64(b[10])
|
||||||
|
@ -570,11 +570,11 @@ func (v *DTLSRecord) Unmarshal(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestWebRTCAPIOptionFunc func(api *TestWebRTCAPI)
|
type testWebRTCAPIOptionFunc func(api *testWebRTCAPI)
|
||||||
|
|
||||||
type TestWebRTCAPI struct {
|
type testWebRTCAPI struct {
|
||||||
// The options to setup the api.
|
// The options to setup the api.
|
||||||
options []TestWebRTCAPIOptionFunc
|
options []testWebRTCAPIOptionFunc
|
||||||
// The api and settings.
|
// The api and settings.
|
||||||
api *webrtc.API
|
api *webrtc.API
|
||||||
mediaEngine *webrtc.MediaEngine
|
mediaEngine *webrtc.MediaEngine
|
||||||
|
@ -588,8 +588,8 @@ type TestWebRTCAPI struct {
|
||||||
proxy *vnet_proxy.UDPProxy
|
proxy *vnet_proxy.UDPProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestWebRTCAPI(options ...TestWebRTCAPIOptionFunc) (*TestWebRTCAPI, error) {
|
func newTestWebRTCAPI(options ...testWebRTCAPIOptionFunc) (*testWebRTCAPI, error) {
|
||||||
v := &TestWebRTCAPI{}
|
v := &testWebRTCAPI{}
|
||||||
|
|
||||||
v.mediaEngine = &webrtc.MediaEngine{}
|
v.mediaEngine = &webrtc.MediaEngine{}
|
||||||
if err := v.mediaEngine.RegisterDefaultCodecs(); err != nil {
|
if err := v.mediaEngine.RegisterDefaultCodecs(); err != nil {
|
||||||
|
@ -610,7 +610,7 @@ func NewTestWebRTCAPI(options ...TestWebRTCAPIOptionFunc) (*TestWebRTCAPI, error
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestWebRTCAPI) Close() error {
|
func (v *testWebRTCAPI) Close() error {
|
||||||
if v.proxy != nil {
|
if v.proxy != nil {
|
||||||
_ = v.proxy.Close()
|
_ = v.proxy.Close()
|
||||||
}
|
}
|
||||||
|
@ -622,7 +622,7 @@ func (v *TestWebRTCAPI) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestWebRTCAPI) Setup(vnetClientIP string, options ...TestWebRTCAPIOptionFunc) error {
|
func (v *testWebRTCAPI) Setup(vnetClientIP string, options ...testWebRTCAPIOptionFunc) error {
|
||||||
// Setting engine for https://github.com/pion/transport/tree/master/vnet
|
// Setting engine for https://github.com/pion/transport/tree/master/vnet
|
||||||
setupVnet := func(vnetClientIP string) (err error) {
|
setupVnet := func(vnetClientIP string) (err error) {
|
||||||
// We create a private router for a api, however, it's possible to share the
|
// We create a private router for a api, however, it's possible to share the
|
||||||
|
@ -674,23 +674,23 @@ func (v *TestWebRTCAPI) Setup(vnetClientIP string, options ...TestWebRTCAPIOptio
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestWebRTCAPI) NewPeerConnection(configuration webrtc.Configuration) (*webrtc.PeerConnection, error) {
|
func (v *testWebRTCAPI) NewPeerConnection(configuration webrtc.Configuration) (*webrtc.PeerConnection, error) {
|
||||||
return v.api.NewPeerConnection(configuration)
|
return v.api.NewPeerConnection(configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestPlayerOptionFunc func(p *TestPlayer) error
|
type testPlayerOptionFunc func(p *testPlayer) error
|
||||||
|
|
||||||
type TestPlayer struct {
|
type testPlayer struct {
|
||||||
pc *webrtc.PeerConnection
|
pc *webrtc.PeerConnection
|
||||||
receivers []*webrtc.RTPReceiver
|
receivers []*webrtc.RTPReceiver
|
||||||
// We should dispose it.
|
// We should dispose it.
|
||||||
api *TestWebRTCAPI
|
api *testWebRTCAPI
|
||||||
// Optional suffix for stream url.
|
// Optional suffix for stream url.
|
||||||
streamSuffix string
|
streamSuffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateApiForPlayer(play *TestPlayer) error {
|
func createApiForPlayer(play *testPlayer) error {
|
||||||
api, err := NewTestWebRTCAPI()
|
api, err := newTestWebRTCAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -699,8 +699,8 @@ func CreateApiForPlayer(play *TestPlayer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestPlayer(options ...TestPlayerOptionFunc) (*TestPlayer, error) {
|
func newTestPlayer(options ...testPlayerOptionFunc) (*testPlayer, error) {
|
||||||
v := &TestPlayer{}
|
v := &testPlayer{}
|
||||||
|
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
if err := opt(v); err != nil {
|
if err := opt(v); err != nil {
|
||||||
|
@ -711,11 +711,11 @@ func NewTestPlayer(options ...TestPlayerOptionFunc) (*TestPlayer, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPlayer) Setup(vnetClientIP string, options ...TestWebRTCAPIOptionFunc) error {
|
func (v *testPlayer) Setup(vnetClientIP string, options ...testWebRTCAPIOptionFunc) error {
|
||||||
return v.api.Setup(vnetClientIP, options...)
|
return v.api.Setup(vnetClientIP, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPlayer) Close() error {
|
func (v *testPlayer) Close() error {
|
||||||
if v.pc != nil {
|
if v.pc != nil {
|
||||||
_ = v.pc.Close()
|
_ = v.pc.Close()
|
||||||
}
|
}
|
||||||
|
@ -731,13 +731,13 @@ func (v *TestPlayer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPlayer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
func (v *testPlayer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
r := fmt.Sprintf("%v://%v%v", srsSchema, *srsServer, *srsStream)
|
r := fmt.Sprintf("%v://%v%v", srsSchema, *srsServer, *srsStream)
|
||||||
if v.streamSuffix != "" {
|
if v.streamSuffix != "" {
|
||||||
r = fmt.Sprintf("%v-%v", r, v.streamSuffix)
|
r = fmt.Sprintf("%v-%v", r, v.streamSuffix)
|
||||||
}
|
}
|
||||||
pli := time.Duration(*srsPlayPLI) * time.Millisecond
|
pli := time.Duration(*srsPlayPLI) * time.Millisecond
|
||||||
logger.Tf(ctx, "Start play url=%v", r)
|
logger.Tf(ctx, "Run play url=%v", r)
|
||||||
|
|
||||||
pc, err := v.api.NewPeerConnection(webrtc.Configuration{})
|
pc, err := v.api.NewPeerConnection(webrtc.Configuration{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -770,7 +770,7 @@ func (v *TestPlayer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
return errors.Wrapf(err, "Api request offer=%v", offer.SDP)
|
return errors.Wrapf(err, "Api request offer=%v", offer.SDP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a proxy for real server and vnet.
|
// Run a proxy for real server and vnet.
|
||||||
if address, err := parseAddressOfCandidate(answer); err != nil {
|
if address, err := parseAddressOfCandidate(answer); err != nil {
|
||||||
return errors.Wrapf(err, "parse address of %v", answer)
|
return errors.Wrapf(err, "parse address of %v", answer)
|
||||||
} else if err := v.api.proxy.Proxy(v.api.network, address); err != nil {
|
} else if err := v.api.proxy.Proxy(v.api.network, address); err != nil {
|
||||||
|
@ -834,9 +834,9 @@ func (v *TestPlayer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestPublisherOptionFunc func(p *TestPublisher) error
|
type testPublisherOptionFunc func(p *testPublisher) error
|
||||||
|
|
||||||
type TestPublisher struct {
|
type testPublisher 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
|
iceReadyCancel context.CancelFunc
|
||||||
|
@ -845,15 +845,15 @@ type TestPublisher struct {
|
||||||
vIngester *videoIngester
|
vIngester *videoIngester
|
||||||
pc *webrtc.PeerConnection
|
pc *webrtc.PeerConnection
|
||||||
// We should dispose it.
|
// We should dispose it.
|
||||||
api *TestWebRTCAPI
|
api *testWebRTCAPI
|
||||||
// Optional suffix for stream url.
|
// Optional suffix for stream url.
|
||||||
streamSuffix string
|
streamSuffix string
|
||||||
// To cancel the publisher, pass by Run.
|
// To cancel the publisher, pass by Run.
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateApiForPublisher(pub *TestPublisher) error {
|
func createApiForPublisher(pub *testPublisher) error {
|
||||||
api, err := NewTestWebRTCAPI()
|
api, err := newTestWebRTCAPI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -862,10 +862,10 @@ func CreateApiForPublisher(pub *TestPublisher) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestPublisher(options ...TestPublisherOptionFunc) (*TestPublisher, error) {
|
func newTestPublisher(options ...testPublisherOptionFunc) (*testPublisher, error) {
|
||||||
sourceVideo, sourceAudio := *srsPublishVideo, *srsPublishAudio
|
sourceVideo, sourceAudio := *srsPublishVideo, *srsPublishAudio
|
||||||
|
|
||||||
v := &TestPublisher{}
|
v := &testPublisher{}
|
||||||
|
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
if err := opt(v); err != nil {
|
if err := opt(v); err != nil {
|
||||||
|
@ -875,17 +875,17 @@ func NewTestPublisher(options ...TestPublisherOptionFunc) (*TestPublisher, error
|
||||||
|
|
||||||
// Create ingesters.
|
// Create ingesters.
|
||||||
if sourceAudio != "" {
|
if sourceAudio != "" {
|
||||||
v.aIngester = NewAudioIngester(sourceAudio)
|
v.aIngester = newAudioIngester(sourceAudio)
|
||||||
}
|
}
|
||||||
if sourceVideo != "" {
|
if sourceVideo != "" {
|
||||||
v.vIngester = NewVideoIngester(sourceVideo)
|
v.vIngester = newVideoIngester(sourceVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the interceptors for packets.
|
// Setup the interceptors for packets.
|
||||||
api := v.api
|
api := v.api
|
||||||
api.options = append(api.options, func(api *TestWebRTCAPI) {
|
api.options = append(api.options, func(api *testWebRTCAPI) {
|
||||||
// Filter for RTCP packets.
|
// Filter for RTCP packets.
|
||||||
rtcpInterceptor := &RTCPInterceptor{}
|
rtcpInterceptor := &rtcpInterceptor{}
|
||||||
rtcpInterceptor.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
rtcpInterceptor.rtcpReader = func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||||
return rtcpInterceptor.nextRTCPReader.Read(buf, attributes)
|
return rtcpInterceptor.nextRTCPReader.Read(buf, attributes)
|
||||||
}
|
}
|
||||||
|
@ -906,11 +906,11 @@ func NewTestPublisher(options ...TestPublisherOptionFunc) (*TestPublisher, error
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPublisher) Setup(vnetClientIP string, options ...TestWebRTCAPIOptionFunc) error {
|
func (v *testPublisher) Setup(vnetClientIP string, options ...testWebRTCAPIOptionFunc) error {
|
||||||
return v.api.Setup(vnetClientIP, options...)
|
return v.api.Setup(vnetClientIP, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPublisher) Close() error {
|
func (v *testPublisher) Close() error {
|
||||||
if v.vIngester != nil {
|
if v.vIngester != nil {
|
||||||
_ = v.vIngester.Close()
|
_ = v.vIngester.Close()
|
||||||
}
|
}
|
||||||
|
@ -930,12 +930,12 @@ func (v *TestPublisher) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPublisher) SetStreamSuffix(suffix string) *TestPublisher {
|
func (v *testPublisher) SetStreamSuffix(suffix string) *testPublisher {
|
||||||
v.streamSuffix = suffix
|
v.streamSuffix = suffix
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TestPublisher) Run(ctx context.Context, cancel context.CancelFunc) error {
|
func (v *testPublisher) Run(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
// Save the cancel.
|
// Save the cancel.
|
||||||
v.cancel = cancel
|
v.cancel = cancel
|
||||||
|
|
||||||
|
@ -945,7 +945,7 @@ func (v *TestPublisher) Run(ctx context.Context, cancel context.CancelFunc) erro
|
||||||
}
|
}
|
||||||
sourceVideo, sourceAudio, fps := *srsPublishVideo, *srsPublishAudio, *srsPublishVideoFps
|
sourceVideo, sourceAudio, fps := *srsPublishVideo, *srsPublishAudio, *srsPublishVideoFps
|
||||||
|
|
||||||
logger.Tf(ctx, "Start publish url=%v, audio=%v, video=%v, fps=%v",
|
logger.Tf(ctx, "Run publish url=%v, audio=%v, video=%v, fps=%v",
|
||||||
r, sourceAudio, sourceVideo, fps)
|
r, sourceAudio, sourceVideo, fps)
|
||||||
|
|
||||||
pc, err := v.api.NewPeerConnection(webrtc.Configuration{})
|
pc, err := v.api.NewPeerConnection(webrtc.Configuration{})
|
||||||
|
@ -986,7 +986,7 @@ func (v *TestPublisher) Run(ctx context.Context, cancel context.CancelFunc) erro
|
||||||
return errors.Wrapf(err, "Api request offer=%v", offer.SDP)
|
return errors.Wrapf(err, "Api request offer=%v", offer.SDP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a proxy for real server and vnet.
|
// Run a proxy for real server and vnet.
|
||||||
if address, err := parseAddressOfCandidate(answerSDP); err != nil {
|
if address, err := parseAddressOfCandidate(answerSDP); err != nil {
|
||||||
return errors.Wrapf(err, "parse address of %v", answerSDP)
|
return errors.Wrapf(err, "parse address of %v", answerSDP)
|
||||||
} else if err := v.api.proxy.Proxy(v.api.network, address); err != nil {
|
} else if err := v.api.proxy.Proxy(v.api.network, address); err != nil {
|
||||||
|
|
30
trunk/3rdparty/srs-bench/vnet/udpproxy_direct.go
vendored
30
trunk/3rdparty/srs-bench/vnet/udpproxy_direct.go
vendored
|
@ -1,32 +1,14 @@
|
||||||
// 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 vnet
|
package vnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deliver directly send packet to vnet or real-server.
|
||||||
|
// For example, we can use this API to simulate the REPLAY ATTACK.
|
||||||
func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) {
|
func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) {
|
||||||
v.workers.Range(func(key, value interface{}) bool {
|
v.workers.Range(func(key, value interface{}) bool {
|
||||||
if nn, err := value.(*aUDPProxyWorker).Deliver(sourceAddr, destAddr, b); err != nil {
|
if nn, err = value.(*aUDPProxyWorker).Deliver(sourceAddr, destAddr, b); err != nil {
|
||||||
return false // Fail, abort.
|
return false // Fail, abort.
|
||||||
} else if nn == len(b) {
|
} else if nn == len(b) {
|
||||||
return false // Done.
|
return false // Done.
|
||||||
|
@ -43,12 +25,12 @@ func (v *aUDPProxyWorker) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn i
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support deliver packet from real server to vnet.
|
// nolint:godox // TODO: Support deliver packet from real server to vnet.
|
||||||
// If packet is from vent, proxy to real server.
|
// If packet is from vnet, proxy to real server.
|
||||||
var realSocket *net.UDPConn
|
var realSocket *net.UDPConn
|
||||||
if value, ok := v.endpoints.Load(addr.String()); !ok {
|
if value, ok := v.endpoints.Load(addr.String()); !ok {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
} else {
|
} else { // nolint:golint
|
||||||
realSocket = value.(*net.UDPConn)
|
realSocket = value.(*net.UDPConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,22 @@
|
||||||
// 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 vnet
|
package vnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pion/logging"
|
|
||||||
"github.com/pion/transport/vnet"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// vnet client:
|
// The vnet client:
|
||||||
// 10.0.0.11:5787
|
// 10.0.0.11:5787
|
||||||
// proxy to real server:
|
// which proxy to real server:
|
||||||
// 192.168.1.10:8000
|
// 192.168.1.10:8000
|
||||||
|
// We should get a reply if directly deliver to proxy.
|
||||||
func TestUDPProxyDirectDeliver(t *testing.T) {
|
func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
@ -57,7 +39,7 @@ func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case <-time.After(time.Duration(*testTimeout) * time.Millisecond):
|
case <-time.After(time.Duration(*testTimeout) * time.Millisecond):
|
||||||
r2 = fmt.Errorf("timeout")
|
r2 = fmt.Errorf("timeout") // nolint:goerr113
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -86,7 +68,7 @@ func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
doVnetProxy := func() error {
|
doVnetProxy := func() error {
|
||||||
router, err := vnet.NewRouter(&vnet.RouterConfig{
|
router, err := NewRouter(&RouterConfig{
|
||||||
CIDR: "0.0.0.0/0",
|
CIDR: "0.0.0.0/0",
|
||||||
LoggerFactory: logging.NewDefaultLoggerFactory(),
|
LoggerFactory: logging.NewDefaultLoggerFactory(),
|
||||||
})
|
})
|
||||||
|
@ -94,23 +76,23 @@ func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
clientNetwork := vnet.NewNet(&vnet.NetConfig{
|
clientNetwork := NewNet(&NetConfig{
|
||||||
StaticIP: "10.0.0.11",
|
StaticIP: "10.0.0.11",
|
||||||
})
|
})
|
||||||
if err = router.AddNet(clientNetwork); err != nil {
|
if err = router.AddNet(clientNetwork); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := router.Start(); err != nil {
|
if err = router.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer router.Stop()
|
defer router.Stop() // nolint:errcheck
|
||||||
|
|
||||||
proxy, err := NewProxy(router)
|
proxy, err := NewProxy(router)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer proxy.Close()
|
defer proxy.Close() // nolint:errcheck
|
||||||
|
|
||||||
// For utest, mock the target real server.
|
// For utest, mock the target real server.
|
||||||
proxy.mockRealServerAddr = mockServer.realServerAddr
|
proxy.mockRealServerAddr = mockServer.realServerAddr
|
||||||
|
@ -122,7 +104,7 @@ func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := proxy.Proxy(clientNetwork, serverAddr); err != nil {
|
if err = proxy.Proxy(clientNetwork, serverAddr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,41 +119,41 @@ func TestUDPProxyDirectDeliver(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
selfKillCancel()
|
selfKillCancel()
|
||||||
client.Close()
|
_ = client.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Write by vnet client.
|
// Write by vnet client.
|
||||||
if _, err := client.WriteTo([]byte("Hello"), serverAddr); err != nil {
|
if _, err = client.WriteTo([]byte("Hello"), serverAddr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 1500)
|
buf := make([]byte, 1500)
|
||||||
if n, addr, err := client.ReadFrom(buf); err != nil {
|
if n, addr, err := client.ReadFrom(buf); err != nil { // nolint:gocritic,govet
|
||||||
if selfKill.Err() == context.Canceled {
|
if errors.Is(selfKill.Err(), context.Canceled) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else if n != 5 || addr == nil {
|
} else if n != 5 || addr == nil {
|
||||||
return fmt.Errorf("n=%v, addr=%v", n, addr)
|
return fmt.Errorf("n=%v, addr=%v", n, addr) // nolint:goerr113
|
||||||
} else if string(buf[:n]) != "Hello" {
|
} else if string(buf[:n]) != "Hello" { // nolint:goconst
|
||||||
return fmt.Errorf("data %v", buf[:n])
|
return fmt.Errorf("data %v", buf[:n]) // nolint:goerr113
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directly write, simulate the ARQ packet.
|
// Directly write, simulate the ARQ packet.
|
||||||
// We should got the echo packet also.
|
// We should got the echo packet also.
|
||||||
if _, err := proxy.Deliver(client.LocalAddr(), serverAddr, []byte("Hello")); err != nil {
|
if _, err = proxy.Deliver(client.LocalAddr(), serverAddr, []byte("Hello")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, addr, err := client.ReadFrom(buf); err != nil {
|
if n, addr, err := client.ReadFrom(buf); err != nil { // nolint:gocritic,govet
|
||||||
if selfKill.Err() == context.Canceled {
|
if errors.Is(selfKill.Err(), context.Canceled) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else if n != 5 || addr == nil {
|
} else if n != 5 || addr == nil {
|
||||||
return fmt.Errorf("n=%v, addr=%v", n, addr)
|
return fmt.Errorf("n=%v, addr=%v", n, addr) // nolint:goerr113
|
||||||
} else if string(buf[:n]) != "Hello" {
|
} else if string(buf[:n]) != "Hello" {
|
||||||
return fmt.Errorf("data %v", buf[:n])
|
return fmt.Errorf("data %v", buf[:n]) // nolint:goerr113
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
13
trunk/configure
vendored
13
trunk/configure
vendored
|
@ -462,7 +462,7 @@ mv ${SRS_WORKDIR}/${SRS_MAKEFILE} ${SRS_WORKDIR}/${SRS_MAKEFILE}.bk
|
||||||
# generate phony header
|
# generate phony header
|
||||||
cat << END > ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
cat << END > ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
||||||
.PHONY: default _default install help clean destroy server srs_ingest_hls utest _prepare_dir $__mphonys
|
.PHONY: default _default install help clean destroy server srs_ingest_hls utest _prepare_dir $__mphonys
|
||||||
.PHONY: clean_srs clean_modules clean_openssl clean_nginx clean_cherrypy clean_srtp2 clean_opus clean_ffmpeg clean_st
|
.PHONY: clean_srs clean_modules clean_openssl clean_srtp2 clean_opus clean_ffmpeg clean_st
|
||||||
.PHONY: st ffmpeg
|
.PHONY: st ffmpeg
|
||||||
|
|
||||||
# install prefix.
|
# install prefix.
|
||||||
|
@ -505,7 +505,6 @@ doclean:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest $__mcleanups)
|
(cd ${SRS_OBJS_DIR} && rm -rf srs srs_utest $__mcleanups)
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf src/* include lib)
|
(cd ${SRS_OBJS_DIR} && rm -rf src/* include lib)
|
||||||
(mkdir -p ${SRS_OBJS_DIR}/utest && cd ${SRS_OBJS_DIR}/utest && rm -rf *.o *.a)
|
(mkdir -p ${SRS_OBJS_DIR}/utest && cd ${SRS_OBJS_DIR}/utest && rm -rf *.o *.a)
|
||||||
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
|
||||||
|
|
||||||
clean: clean_srs clean_modules
|
clean: clean_srs clean_modules
|
||||||
|
|
||||||
|
@ -541,12 +540,6 @@ clean_st:
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf st-srs)
|
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM} && rm -rf st-srs)
|
||||||
@echo "Please rebuild ST by: ./configure"
|
@echo "Please rebuild ST by: ./configure"
|
||||||
|
|
||||||
clean_nginx:
|
|
||||||
(cd ${SRS_OBJS_DIR} && rm -rf nginx)
|
|
||||||
|
|
||||||
clean_cherrypy:
|
|
||||||
(cd research/api-server/static-dir && rm -rf crossdomain.xml forward live players)
|
|
||||||
|
|
||||||
st:
|
st:
|
||||||
(cd ${SRS_OBJS_DIR} && rm -f srs srs_utest)
|
(cd ${SRS_OBJS_DIR} && rm -f srs srs_utest)
|
||||||
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM}/st-srs && \$(MAKE) clean && \$(MAKE) ${_ST_MAKE} EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}")
|
(cd ${SRS_OBJS_DIR}/${SRS_PLATFORM}/st-srs && \$(MAKE) clean && \$(MAKE) ${_ST_MAKE} EXTRA_CFLAGS="${_ST_EXTRA_CFLAGS}")
|
||||||
|
@ -587,7 +580,11 @@ install:
|
||||||
@mkdir -p \$(__REAL_INSTALL)
|
@mkdir -p \$(__REAL_INSTALL)
|
||||||
@echo "Now make the http root dir"
|
@echo "Now make the http root dir"
|
||||||
@mkdir -p \$(__REAL_INSTALL)/objs/nginx/html
|
@mkdir -p \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
|
@cp -f research/api-server/static-dir/index.html \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
@cp -f research/api-server/static-dir/crossdomain.xml \$(__REAL_INSTALL)/objs/nginx/html
|
@cp -f research/api-server/static-dir/crossdomain.xml \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
|
@cp -f research/api-server/static-dir/favicon.ico \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
|
@cp -Rf research/players \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
|
@cp -Rf research/console \$(__REAL_INSTALL)/objs/nginx/html
|
||||||
@echo "Now copy binary files"
|
@echo "Now copy binary files"
|
||||||
@mkdir -p \$(__REAL_INSTALL)/objs
|
@mkdir -p \$(__REAL_INSTALL)/objs
|
||||||
@cp -f objs/srs \$(__REAL_INSTALL)/objs
|
@cp -f objs/srs \$(__REAL_INSTALL)/objs
|
||||||
|
|
|
@ -153,18 +153,6 @@ ok_msg "start install srs"
|
||||||
ret=$?; if [[ 0 -ne ${ret} ]]; then failed_msg "install srs failed"; exit $ret; fi
|
ret=$?; if [[ 0 -ne ${ret} ]]; then failed_msg "install srs failed"; exit $ret; fi
|
||||||
ok_msg "install srs success"
|
ok_msg "install srs success"
|
||||||
|
|
||||||
# Copy srs-console
|
|
||||||
HTTP_HOME="${package_dir}/${INSTALL}/objs/nginx/html/"
|
|
||||||
(
|
|
||||||
cp $work_dir/research/api-server/static-dir/index.html ${HTTP_HOME} &&
|
|
||||||
cp $work_dir/research/api-server/static-dir/favicon.ico ${HTTP_HOME} &&
|
|
||||||
cp $work_dir/research/api-server/static-dir/crossdomain.xml ${HTTP_HOME} &&
|
|
||||||
cp -R $work_dir/research/players ${HTTP_HOME} &&
|
|
||||||
cp -R $work_dir/research/console ${HTTP_HOME}
|
|
||||||
) >> $log 2>&1
|
|
||||||
ret=$?; if [[ 0 -ne ${ret} ]]; then failed_msg "copy utilities failed"; exit $ret; fi
|
|
||||||
ok_msg "copy utilities success"
|
|
||||||
|
|
||||||
# copy extra files to package.
|
# copy extra files to package.
|
||||||
ok_msg "start copy extra files to package"
|
ok_msg "start copy extra files to package"
|
||||||
(
|
(
|
||||||
|
|
|
@ -165,6 +165,33 @@ bool SrsErrorPithyPrint::can_print(int error_code, uint32_t* pnn)
|
||||||
return new_stage || stage->can_print();
|
return new_stage || stage->can_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrsAlonePithyPrint::SrsAlonePithyPrint() : info_(0)
|
||||||
|
{
|
||||||
|
//stage work for one print
|
||||||
|
info_.nb_clients = 1;
|
||||||
|
|
||||||
|
previous_tick_ = srs_get_system_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsAlonePithyPrint::~SrsAlonePithyPrint()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsAlonePithyPrint::elapse()
|
||||||
|
{
|
||||||
|
srs_utime_t diff = srs_get_system_time() - previous_tick_;
|
||||||
|
previous_tick_ = srs_get_system_time();
|
||||||
|
|
||||||
|
diff = srs_max(0, diff);
|
||||||
|
|
||||||
|
info_.elapse(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsAlonePithyPrint::can_print()
|
||||||
|
{
|
||||||
|
return info_.can_print();
|
||||||
|
}
|
||||||
|
|
||||||
// The global stage manager for pithy print, multiple stages.
|
// The global stage manager for pithy print, multiple stages.
|
||||||
static SrsStageManager* _srs_stages = new SrsStageManager();
|
static SrsStageManager* _srs_stages = new SrsStageManager();
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,20 @@ public:
|
||||||
bool can_print(int err, uint32_t* pnn = NULL);
|
bool can_print(int err, uint32_t* pnn = NULL);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An standalone pithy print, without shared stages.
|
||||||
|
class SrsAlonePithyPrint
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SrsStageInfo info_;
|
||||||
|
srs_utime_t previous_tick_;
|
||||||
|
public:
|
||||||
|
SrsAlonePithyPrint();
|
||||||
|
virtual ~SrsAlonePithyPrint();
|
||||||
|
public:
|
||||||
|
virtual void elapse();
|
||||||
|
virtual bool can_print();
|
||||||
|
};
|
||||||
|
|
||||||
// The stage is used for a collection of object to do print,
|
// The stage is used for a collection of object to do print,
|
||||||
// the print time in a stage is constant and not changed,
|
// the print time in a stage is constant and not changed,
|
||||||
// that is, we always got one message to print every specified time.
|
// that is, we always got one message to print every specified time.
|
||||||
|
|
|
@ -563,619 +563,6 @@ VOID TEST(KernelRTCTest, StringDumpHexTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern SSL_CTX* srs_build_dtls_ctx(SrsDtlsVersion version, std::string role);
|
|
||||||
|
|
||||||
class MockDtls
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SSL_CTX* dtls_ctx;
|
|
||||||
SSL* dtls;
|
|
||||||
BIO* bio_in;
|
|
||||||
BIO* bio_out;
|
|
||||||
ISrsDtlsCallback* callback_;
|
|
||||||
bool handshake_done_for_us;
|
|
||||||
SrsDtlsRole role_;
|
|
||||||
SrsDtlsVersion version_;
|
|
||||||
public:
|
|
||||||
MockDtls(ISrsDtlsCallback* callback);
|
|
||||||
virtual ~MockDtls();
|
|
||||||
srs_error_t initialize(std::string role, std::string version);
|
|
||||||
srs_error_t start_active_handshake();
|
|
||||||
srs_error_t on_dtls(char* data, int nb_data);
|
|
||||||
srs_error_t do_handshake();
|
|
||||||
};
|
|
||||||
|
|
||||||
MockDtls::MockDtls(ISrsDtlsCallback* callback)
|
|
||||||
{
|
|
||||||
dtls_ctx = NULL;
|
|
||||||
dtls = NULL;
|
|
||||||
|
|
||||||
callback_ = callback;
|
|
||||||
handshake_done_for_us = false;
|
|
||||||
|
|
||||||
role_ = SrsDtlsRoleServer;
|
|
||||||
version_ = SrsDtlsVersionAuto;
|
|
||||||
}
|
|
||||||
|
|
||||||
MockDtls::~MockDtls()
|
|
||||||
{
|
|
||||||
if (dtls_ctx) {
|
|
||||||
SSL_CTX_free(dtls_ctx);
|
|
||||||
dtls_ctx = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dtls) {
|
|
||||||
SSL_free(dtls);
|
|
||||||
dtls = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtls::initialize(std::string role, std::string version)
|
|
||||||
{
|
|
||||||
role_ = SrsDtlsRoleServer;
|
|
||||||
if (role == "active") {
|
|
||||||
role_ = SrsDtlsRoleClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version == "dtls1.0") {
|
|
||||||
version_ = SrsDtlsVersion1_0;
|
|
||||||
} else if (version == "dtls1.2") {
|
|
||||||
version_ = SrsDtlsVersion1_2;
|
|
||||||
} else {
|
|
||||||
version_ = SrsDtlsVersionAuto;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtls_ctx = srs_build_dtls_ctx(version_, role);
|
|
||||||
dtls = SSL_new(dtls_ctx);
|
|
||||||
srs_assert(dtls);
|
|
||||||
|
|
||||||
if (role_ == SrsDtlsRoleClient) {
|
|
||||||
SSL_set_connect_state(dtls);
|
|
||||||
SSL_set_max_send_fragment(dtls, kRtpPacketSize);
|
|
||||||
} else {
|
|
||||||
SSL_set_accept_state(dtls);
|
|
||||||
}
|
|
||||||
|
|
||||||
bio_in = BIO_new(BIO_s_mem());
|
|
||||||
srs_assert(bio_in);
|
|
||||||
|
|
||||||
bio_out = BIO_new(BIO_s_mem());
|
|
||||||
srs_assert(bio_out);
|
|
||||||
|
|
||||||
SSL_set_bio(dtls, bio_in, bio_out);
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtls::start_active_handshake()
|
|
||||||
{
|
|
||||||
if (role_ == SrsDtlsRoleClient) {
|
|
||||||
return do_handshake();
|
|
||||||
}
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtls::on_dtls(char* data, int nb_data)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
srs_assert(BIO_reset(bio_in) == 1);
|
|
||||||
srs_assert(BIO_reset(bio_out) == 1);
|
|
||||||
srs_assert(BIO_write(bio_in, data, nb_data) > 0);
|
|
||||||
|
|
||||||
if ((err = do_handshake()) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "do handshake");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (BIO_ctrl_pending(bio_in) > 0) {
|
|
||||||
char buf[8092];
|
|
||||||
int nb = SSL_read(dtls, buf, sizeof(buf));
|
|
||||||
if (nb <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = callback_->on_dtls_application_data(buf, nb)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "on DTLS data, size=%u", nb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtls::do_handshake()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
int r0 = SSL_do_handshake(dtls);
|
|
||||||
int r1 = SSL_get_error(dtls, r0);
|
|
||||||
if (r0 < 0 && (r1 != SSL_ERROR_NONE && r1 != SSL_ERROR_WANT_READ && r1 != SSL_ERROR_WANT_WRITE)) {
|
|
||||||
return srs_error_new(ERROR_RTC_DTLS, "handshake r0=%d, r1=%d", r0, r1);
|
|
||||||
}
|
|
||||||
if (r1 == SSL_ERROR_NONE) {
|
|
||||||
handshake_done_for_us = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* data = NULL;
|
|
||||||
int size = BIO_get_mem_data(bio_out, &data);
|
|
||||||
|
|
||||||
if (size > 0 && (err = callback_->write_dtls_data(data, size)) != srs_success) {
|
|
||||||
return srs_error_wrap(err, "dtls send size=%u", size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handshake_done_for_us) {
|
|
||||||
return callback_->on_dtls_handshake_done();
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockDtlsCallback : virtual public ISrsDtlsCallback, virtual public ISrsCoroutineHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SrsDtls* peer;
|
|
||||||
MockDtls* peer2;
|
|
||||||
SrsCoroutine* trd;
|
|
||||||
srs_error_t r0;
|
|
||||||
bool done;
|
|
||||||
std::vector<SrsSample> samples;
|
|
||||||
public:
|
|
||||||
int nn_client_hello_lost;
|
|
||||||
int nn_server_hello_lost;
|
|
||||||
int nn_certificate_lost;
|
|
||||||
int nn_new_session_lost;
|
|
||||||
int nn_change_cipher_lost;
|
|
||||||
public:
|
|
||||||
// client -> server
|
|
||||||
int nn_client_hello;
|
|
||||||
// server -> client
|
|
||||||
int nn_server_hello;
|
|
||||||
// client -> server
|
|
||||||
int nn_certificate;
|
|
||||||
// server -> client
|
|
||||||
int nn_new_session;
|
|
||||||
int nn_change_cipher;
|
|
||||||
public:
|
|
||||||
MockDtlsCallback();
|
|
||||||
virtual ~MockDtlsCallback();
|
|
||||||
virtual srs_error_t on_dtls_handshake_done();
|
|
||||||
virtual srs_error_t on_dtls_application_data(const char* data, const int len);
|
|
||||||
virtual srs_error_t write_dtls_data(void* data, int size);
|
|
||||||
virtual srs_error_t on_dtls_alert(std::string type, std::string desc);
|
|
||||||
virtual srs_error_t cycle();
|
|
||||||
};
|
|
||||||
|
|
||||||
MockDtlsCallback::MockDtlsCallback()
|
|
||||||
{
|
|
||||||
peer = NULL;
|
|
||||||
peer2 = NULL;
|
|
||||||
r0 = srs_success;
|
|
||||||
done = false;
|
|
||||||
trd = new SrsSTCoroutine("mock", this);
|
|
||||||
srs_assert(trd->start() == srs_success);
|
|
||||||
|
|
||||||
nn_client_hello_lost = 0;
|
|
||||||
nn_server_hello_lost = 0;
|
|
||||||
nn_certificate_lost = 0;
|
|
||||||
nn_new_session_lost = 0;
|
|
||||||
nn_change_cipher_lost = 0;
|
|
||||||
|
|
||||||
nn_client_hello = 0;
|
|
||||||
nn_server_hello = 0;
|
|
||||||
nn_certificate = 0;
|
|
||||||
nn_new_session = 0;
|
|
||||||
nn_change_cipher = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MockDtlsCallback::~MockDtlsCallback()
|
|
||||||
{
|
|
||||||
srs_freep(trd);
|
|
||||||
srs_freep(r0);
|
|
||||||
for (vector<SrsSample>::iterator it = samples.begin(); it != samples.end(); ++it) {
|
|
||||||
delete[] it->bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtlsCallback::on_dtls_handshake_done()
|
|
||||||
{
|
|
||||||
done = true;
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtlsCallback::on_dtls_application_data(const char* data, const int len)
|
|
||||||
{
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtlsCallback::write_dtls_data(void* data, int size)
|
|
||||||
{
|
|
||||||
int nn_lost = 0;
|
|
||||||
if (true) {
|
|
||||||
uint8_t content_type = 0;
|
|
||||||
if (size >= 1) {
|
|
||||||
content_type = (uint8_t)((uint8_t*)data)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t handshake_type = 0;
|
|
||||||
if (size >= 14) {
|
|
||||||
handshake_type = (uint8_t)((uint8_t*)data)[13];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content_type == 22) {
|
|
||||||
if (handshake_type == 1) {
|
|
||||||
nn_lost = nn_client_hello_lost--;
|
|
||||||
nn_client_hello++;
|
|
||||||
} else if (handshake_type == 2) {
|
|
||||||
nn_lost = nn_server_hello_lost--;
|
|
||||||
nn_server_hello++;
|
|
||||||
} else if (handshake_type == 11) {
|
|
||||||
nn_lost = nn_certificate_lost--;
|
|
||||||
nn_certificate++;
|
|
||||||
} else if (handshake_type == 4) {
|
|
||||||
nn_lost = nn_new_session_lost--;
|
|
||||||
nn_new_session++;
|
|
||||||
}
|
|
||||||
} else if (content_type == 20) {
|
|
||||||
nn_lost = nn_change_cipher_lost--;
|
|
||||||
nn_change_cipher++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate to drop packet.
|
|
||||||
if (nn_lost > 0) {
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out it.
|
|
||||||
char* cp = new char[size];
|
|
||||||
memcpy(cp, data, size);
|
|
||||||
|
|
||||||
samples.push_back(SrsSample((char*)cp, size));
|
|
||||||
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtlsCallback::on_dtls_alert(std::string type, std::string desc)
|
|
||||||
{
|
|
||||||
return srs_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_error_t MockDtlsCallback::cycle()
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
while (err == srs_success) {
|
|
||||||
if ((err = trd->pull()) != srs_success) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (samples.empty()) {
|
|
||||||
srs_usleep(0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsSample p = samples.at(0);
|
|
||||||
samples.erase(samples.begin());
|
|
||||||
|
|
||||||
if (peer) {
|
|
||||||
err = peer->on_dtls((char*)p.bytes, p.size);
|
|
||||||
} else if (peer2) {
|
|
||||||
err = peer2->on_dtls((char*)p.bytes, p.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_freepa(p.bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy it for utest to check it.
|
|
||||||
r0 = srs_error_copy(err);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for mock io to done, try to switch to coroutine many times.
|
|
||||||
void mock_wait_dtls_io_done(SrsDtlsImpl* client_impl, int count = 100, int interval = 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (client_impl) {
|
|
||||||
dynamic_cast<SrsDtlsClientImpl*>(client_impl)->reset_timer_ = true;
|
|
||||||
}
|
|
||||||
srs_usleep(interval * SRS_UTIME_MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To avoid the crash when peer or peer2 is freed before io.
|
|
||||||
class MockBridgeDtlsIO
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
MockDtlsCallback* io_;
|
|
||||||
public:
|
|
||||||
MockBridgeDtlsIO(MockDtlsCallback* io, SrsDtls* peer, MockDtls* peer2) {
|
|
||||||
io_ = io;
|
|
||||||
io->peer = peer;
|
|
||||||
io->peer2 = peer2;
|
|
||||||
}
|
|
||||||
virtual ~MockBridgeDtlsIO() {
|
|
||||||
io_->peer = NULL;
|
|
||||||
io_->peer2 = NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
VOID TEST(KernelRTCTest, DTLSClientARQTest)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// No ARQ, check the number of packets.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
cio.peer = &server; sio.peer = &client;
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(1, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(1, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(1, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientHello lost, client retransmit the ClientHello.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
// Lost 2 packets, total packets should be 3.
|
|
||||||
// Note that only one server hello.
|
|
||||||
cio.nn_client_hello_lost = 1;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(2, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(1, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(1, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(1, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Certificate lost, client retransmit the Certificate.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
// Lost 2 packets, total packets should be 3.
|
|
||||||
// Note that only one server NewSessionTicket.
|
|
||||||
cio.nn_certificate_lost = 1;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(2, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(2, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(0, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID TEST(KernelRTCTest, DTLSServerARQTest)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
// No ARQ, check the number of packets.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
cio.peer = &server; sio.peer = &client;
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(1, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(1, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(1, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerHello lost, client retransmit the ClientHello.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
// Lost 2 packets, total packets should be 3.
|
|
||||||
sio.nn_server_hello_lost = 1;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(2, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(2, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(1, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(1, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSessionTicket lost, client retransmit the Certificate.
|
|
||||||
if (true) {
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, &client, NULL);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", "dtls1.0"));
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", "dtls1.0"));
|
|
||||||
|
|
||||||
// Lost 2 packets, total packets should be 3.
|
|
||||||
sio.nn_new_session_lost = 1;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake());
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
EXPECT_TRUE(sio.r0 == srs_success);
|
|
||||||
EXPECT_TRUE(cio.r0 == srs_success);
|
|
||||||
|
|
||||||
EXPECT_TRUE(cio.done);
|
|
||||||
EXPECT_TRUE(sio.done);
|
|
||||||
|
|
||||||
EXPECT_EQ(1, cio.nn_client_hello);
|
|
||||||
EXPECT_EQ(1, sio.nn_server_hello);
|
|
||||||
EXPECT_EQ(2, cio.nn_certificate);
|
|
||||||
EXPECT_EQ(2, sio.nn_new_session);
|
|
||||||
EXPECT_EQ(0, sio.nn_change_cipher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DTLSFlowCase
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
|
|
||||||
string ClientVersion;
|
|
||||||
string ServerVersion;
|
|
||||||
|
|
||||||
bool ClientDone;
|
|
||||||
bool ServerDone;
|
|
||||||
|
|
||||||
bool ClientError;
|
|
||||||
bool ServerError;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<< (std::ostream& stream, const DTLSFlowCase& c)
|
|
||||||
{
|
|
||||||
stream << "Case #" << c.id
|
|
||||||
<< ", client(" << c.ClientVersion << ",done=" << c.ClientDone << ",err=" << c.ClientError << ")"
|
|
||||||
<< ", server(" << c.ServerVersion << ",done=" << c.ServerDone << ",err=" << c.ServerError << ")";
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID TEST(KernelRTCTest, DTLSClientFlowTest)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
DTLSFlowCase cases[] = {
|
|
||||||
// OK, Client, Server: DTLS v1.0
|
|
||||||
{0, "dtls1.0", "dtls1.0", true, true, false, false},
|
|
||||||
// OK, Client, Server: DTLS v1.2
|
|
||||||
{1, "dtls1.2", "dtls1.2", true, true, false, false},
|
|
||||||
// OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2).
|
|
||||||
{2, "dtls1.0", "auto", true, true, false, false},
|
|
||||||
// OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2).
|
|
||||||
{3, "dtls1.2", "auto", true, true, false, false},
|
|
||||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
|
||||||
{4, "auto", "dtls1.0", true, true, false, false},
|
|
||||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
|
||||||
{5, "auto", "dtls1.2", true, true, false, false},
|
|
||||||
// OK?, Client: DTLS v1.0, Server: DTLS v1.2
|
|
||||||
{6, "dtls1.0", "dtls1.2", true, true, false, false},
|
|
||||||
// OK?, Client: DTLS v1.2, Server: DTLS v1.0
|
|
||||||
{7, "dtls1.2", "dtls1.0", true, true, false, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSFlowCase)); i++) {
|
|
||||||
DTLSFlowCase c = cases[i];
|
|
||||||
|
|
||||||
MockDtlsCallback cio; SrsDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; MockDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, NULL, &server); MockBridgeDtlsIO b1(&sio, &client, NULL);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c;
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c;
|
|
||||||
mock_wait_dtls_io_done(client.impl, 15, 5);
|
|
||||||
|
|
||||||
// Note that the cio error is generated from server, vice versa.
|
|
||||||
EXPECT_EQ(c.ClientDone, cio.done) << c;
|
|
||||||
EXPECT_EQ(c.ServerDone, sio.done) << c;
|
|
||||||
|
|
||||||
EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c;
|
|
||||||
EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID TEST(KernelRTCTest, DTLSServerFlowTest)
|
|
||||||
{
|
|
||||||
srs_error_t err = srs_success;
|
|
||||||
|
|
||||||
DTLSFlowCase cases[] = {
|
|
||||||
// OK, Client, Server: DTLS v1.0
|
|
||||||
{0, "dtls1.0", "dtls1.0", true, true, false, false},
|
|
||||||
// OK, Client, Server: DTLS v1.2
|
|
||||||
{1, "dtls1.2", "dtls1.2", true, true, false, false},
|
|
||||||
// OK, Client: DTLS v1.0, Server: DTLS auto(v1.0 or v1.2).
|
|
||||||
{2, "dtls1.0", "auto", true, true, false, false},
|
|
||||||
// OK, Client: DTLS v1.2, Server: DTLS auto(v1.0 or v1.2).
|
|
||||||
{3, "dtls1.2", "auto", true, true, false, false},
|
|
||||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
|
||||||
{4, "auto", "dtls1.0", true, true, false, false},
|
|
||||||
// OK, Client: DTLS auto(v1.0 or v1.2), Server: DTLS v1.0
|
|
||||||
{5, "auto", "dtls1.2", true, true, false, false},
|
|
||||||
// OK?, Client: DTLS v1.0, Server: DTLS v1.2
|
|
||||||
{6, "dtls1.0", "dtls1.2", true, true, false, false},
|
|
||||||
// OK?, Client: DTLS v1.2, Server: DTLS v1.0
|
|
||||||
{7, "dtls1.2", "dtls1.0", true, true, false, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)(sizeof(cases) / sizeof(DTLSFlowCase)); i++) {
|
|
||||||
DTLSFlowCase c = cases[i];
|
|
||||||
|
|
||||||
MockDtlsCallback cio; MockDtls client(&cio);
|
|
||||||
MockDtlsCallback sio; SrsDtls server(&sio);
|
|
||||||
MockBridgeDtlsIO b0(&cio, &server, NULL); MockBridgeDtlsIO b1(&sio, NULL, &client);
|
|
||||||
HELPER_EXPECT_SUCCESS(client.initialize("active", c.ClientVersion)) << c;
|
|
||||||
HELPER_EXPECT_SUCCESS(server.initialize("passive", c.ServerVersion)) << c;
|
|
||||||
|
|
||||||
HELPER_EXPECT_SUCCESS(client.start_active_handshake()) << c;
|
|
||||||
mock_wait_dtls_io_done(NULL, 15, 5);
|
|
||||||
|
|
||||||
// Note that the cio error is generated from server, vice versa.
|
|
||||||
EXPECT_EQ(c.ClientDone, cio.done) << c;
|
|
||||||
EXPECT_EQ(c.ServerDone, sio.done) << c;
|
|
||||||
|
|
||||||
EXPECT_EQ(c.ClientError, sio.r0 != srs_success) << c;
|
|
||||||
EXPECT_EQ(c.ServerError, cio.r0 != srs_success) << c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID TEST(KernelRTCTest, SequenceCompare)
|
VOID TEST(KernelRTCTest, SequenceCompare)
|
||||||
{
|
{
|
||||||
if (true) {
|
if (true) {
|
||||||
|
|
Loading…
Reference in a new issue