diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1bf0452d69..b34cff63af 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,8 +15,8 @@ jobs:
# Build for CentOS 7
- name: Build on CentOS7, baseline
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos7-baseline .
- - name: Build on CentOS7, with SRT
- run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos7-srt .
+ - name: Build on CentOS7, with all features
+ run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos7-all .
- name: Build on CentOS7, without WebRTC
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos7-no-webrtc .
- name: Build on CentOS7, without ASM
@@ -35,8 +35,8 @@ jobs:
# Build for CentOS 6
- name: Build on CentOS6, baseline
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos6-baseline .
- - name: Build on CentOS6, with SRT
- run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos6-srt .
+ - name: Build on CentOS6, with all features
+ run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target centos6-all .
build-ubuntu16:
name: build-ubuntu16
@@ -49,8 +49,8 @@ jobs:
# Build for Ubuntu16
- name: Build on Ubuntu16, baseline
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu16-baseline .
- - name: Build on Ubuntu16, with SRT
- run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu16-srt .
+ - name: Build on Ubuntu16, with all features
+ run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu16-all .
build-ubuntu18:
name: build-ubuntu18
@@ -63,8 +63,8 @@ jobs:
# Build for Ubuntu18
- name: Build on Ubuntu18, baseline
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu18-baseline .
- - name: Build on Ubuntu18, with SRT
- run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu18-srt .
+ - name: Build on Ubuntu18, with all features
+ run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu18-all .
build-ubuntu20:
name: build-ubuntu20
@@ -77,8 +77,8 @@ jobs:
# Build for Ubuntu20
- name: Build on Ubuntu20, baseline
run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu20-baseline .
- - name: Build on Ubuntu20, with SRT
- run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu20-srt .
+ - name: Build on Ubuntu20, with all features
+ run: DOCKER_BUILDKIT=1 docker build -f trunk/Dockerfile.builds --target ubuntu20-all .
build-cross-arm:
name: build-cross-arm
diff --git a/trunk/3rdparty/srs-bench/README.md b/trunk/3rdparty/srs-bench/README.md
index 9fb5ff9ca5..7a26f5c946 100644
--- a/trunk/3rdparty/srs-bench/README.md
+++ b/trunk/3rdparty/srs-bench/README.md
@@ -112,7 +112,7 @@ fi
然后运行回归测试用例,如果只跑一次,可以直接运行:
```bash
-go test ./srs -mod=vendor -v
+go test ./srs -mod=vendor -v -count=1
```
也可以用make编译出重复使用的二进制:
@@ -137,7 +137,7 @@ PASS
可以给回归测试传参数,这样可以测试不同的序列,比如:
```bash
-go test ./srs -mod=vendor -v -srs-server=127.0.0.1
+go test ./srs -mod=vendor -v -count=1 -srs-server=127.0.0.1
# Or
make && ./objs/srs_test -test.v -srs-server=127.0.0.1
```
@@ -151,8 +151,8 @@ make && ./objs/srs_test -test.v -srs-log -test.run TestRtcBasic_PublishPlay
支持的参数如下:
* `-srs-server`,RTC服务器地址。默认值:`127.0.0.1`
-* `-srs-stream`,RTC流地址。默认值:`/rtc/regression`
-* `-srs-timeout`,每个Case的超时时间,毫秒。默认值:`3000`,即3秒。
+* `-srs-stream`,RTC流地址,一般会加上随机的后缀。默认值:`/rtc/regression`
+* `-srs-timeout`,每个Case的超时时间,毫秒。默认值:`5000`,即5秒。
* `-srs-publish-audio`,推流时,使用的音频文件。默认值:`avatar.ogg`
* `-srs-publish-video`,推流时,使用的视频文件。默认值:`avatar.h264`
* `-srs-publish-video-fps`,推流时,视频文件的FPS。默认值:`25`
@@ -189,7 +189,7 @@ pip install lxml && pip install gcovr
支持Janus的压测,使用选项`-sfu janus`可以查看帮助:
```bash
-./objs/srs_bench -sfu janus --help
+make && ./objs/srs_bench -sfu janus --help
```
首先需要启动Janus,推荐使用[janus-docker](https://github.com/winlinvip/janus-docker#usage):
@@ -221,4 +221,31 @@ make -j10 && ./objs/srs_bench -sfu janus \
-nn 5
```
+## GB28181
+
+支持GB28181的压测,使用选项`-sfu gb28181`可以查看帮助:
+
+```bash
+make && ./objs/srs_bench -sfu gb28181 --help
+```
+
+运行回归测试用例,更多命令请参考[Regression Test](#regression-test):
+
+```bash
+go test ./gb28181 -mod=vendor -v -count=1
+```
+
+支持的参数如下:
+
+* `-srs-sip`,SIP服务器地址。默认值:`tcp://127.0.0.1:5060`
+* `-srs-stream`,GB的user,即流名称,一般会加上随机的后缀。默认值:`3402000000`
+* `-srs-timeout`,每个Case的超时时间,毫秒。默认值:`11000`,即11秒。
+* `-srs-publish-audio`,推流时,使用的音频文件。默认值:`avatar.aac`
+* `-srs-publish-video`,推流时,使用的视频文件。默认值:`avatar.h264`
+* `-srs-publish-video-fps`,推流时,视频文件的FPS。默认值:`25`
+
+其他不常用参数:
+
+* `-srs-log`,是否开启详细日志。默认值:`false`
+
2021.01, Winlin
diff --git a/trunk/3rdparty/srs-bench/avatar.aac b/trunk/3rdparty/srs-bench/avatar.aac
new file mode 100644
index 0000000000..d448808fa4
Binary files /dev/null and b/trunk/3rdparty/srs-bench/avatar.aac differ
diff --git a/trunk/3rdparty/srs-bench/gb28181/gb28181.go b/trunk/3rdparty/srs-bench/gb28181/gb28181.go
new file mode 100644
index 0000000000..38122e786f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/gb28181.go
@@ -0,0 +1,145 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "github.com/ossrs/go-oryx-lib/logger"
+ "io"
+ "os"
+ "strings"
+ "time"
+)
+
+type gbMainConfig struct {
+ sipConfig SIPConfig
+ psConfig PSConfig
+}
+
+func Parse(ctx context.Context) interface{} {
+ fl := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
+
+ var sfu string
+ fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or gb28181 or janus")
+
+ c := &gbMainConfig{}
+ fl.StringVar(&c.sipConfig.addr, "pr", "", "")
+ fl.StringVar(&c.sipConfig.user, "user", "", "")
+ fl.StringVar(&c.sipConfig.server, "server", "", "")
+ fl.StringVar(&c.sipConfig.domain, "domain", "", "")
+ fl.IntVar(&c.sipConfig.random, "random", 0, "")
+
+ fl.StringVar(&c.psConfig.video, "sv", "", "")
+ fl.StringVar(&c.psConfig.audio, "sa", "", "")
+ fl.IntVar(&c.psConfig.fps, "fps", 0, "")
+
+ 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 gb28181 or janus. Default: srs"))
+ fmt.Println(fmt.Sprintf("SIP:"))
+ fmt.Println(fmt.Sprintf(" -user The SIP username, ID of device."))
+ fmt.Println(fmt.Sprintf(" -random Append N number to user as random device ID, like 1320000001."))
+ fmt.Println(fmt.Sprintf(" -server The SIP server ID, ID of server."))
+ fmt.Println(fmt.Sprintf(" -domain The SIP domain, domain of server and device."))
+ fmt.Println(fmt.Sprintf("Publisher:"))
+ fmt.Println(fmt.Sprintf(" -pr The SIP server address, format is tcp://ip:port over TCP."))
+ fmt.Println(fmt.Sprintf(" -fps [Optional] 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个推流:"))
+ fmt.Println(fmt.Sprintf(" %v -sfu gb28181 -pr tcp://127.0.0.1:5060 -user 34020000001320000001 -server 34020000002000000001 -domain 3402000000", os.Args[0]))
+ fmt.Println(fmt.Sprintf(" %v -sfu gb28181 -pr tcp://127.0.0.1:5060 -user 3402000000 -random 10 -server 34020000002000000001 -domain 3402000000", os.Args[0]))
+ fmt.Println(fmt.Sprintf(" %v -sfu gb28181 -pr tcp://127.0.0.1:5060 -user 3402000000 -random 10 -server 34020000002000000001 -domain 3402000000 -sa avatar.aac -sv avatar.h264 -fps 25", os.Args[0]))
+ fmt.Println(fmt.Sprintf(" %v -sfu gb28181 -pr tcp://127.0.0.1:5060 -user livestream -server srs -domain ossrs.io -sa avatar.aac -sv avatar.h264 -fps 25", os.Args[0]))
+ fmt.Println()
+ }
+ if err := fl.Parse(os.Args[1:]); err == flag.ErrHelp {
+ os.Exit(0)
+ }
+
+ showHelp := c.sipConfig.String() == ""
+ if showHelp {
+ fl.Usage()
+ os.Exit(-1)
+ }
+
+ summaryDesc := ""
+ if c.sipConfig.addr != "" {
+ pubString := strings.Join([]string{c.sipConfig.String(), c.psConfig.String()}, ",")
+ summaryDesc = fmt.Sprintf("%v, publish(%v)", summaryDesc, pubString)
+ }
+ logger.Tf(ctx, "Run benchmark with %v", summaryDesc)
+
+ return c
+}
+
+func Run(ctx context.Context, r0 interface{}) (err error) {
+ conf := r0.(*gbMainConfig)
+ ctx, cancel := context.WithCancel(ctx)
+
+ session := NewGBSession(&GBSessionConfig{
+ regTimeout: 3 * time.Hour, inviteTimeout: 3 * time.Hour,
+ }, &conf.sipConfig)
+ defer session.Close()
+
+ if err := session.Connect(ctx); err != nil {
+ return errors.Wrapf(err, "connect %v", conf.sipConfig)
+ }
+
+ if err := session.Register(ctx); err != nil {
+ return errors.Wrapf(err, "register %v", conf.sipConfig)
+ }
+
+ if err := session.Invite(ctx); err != nil {
+ return errors.Wrapf(err, "invite %v", conf.sipConfig)
+ }
+
+ if conf.psConfig.video == "" || conf.psConfig.audio == "" {
+ cancel()
+ return nil
+ }
+
+ ingester := NewPSIngester(&IngesterConfig{
+ psConfig: conf.psConfig,
+ ssrc: uint32(session.out.ssrc),
+ clockRate: session.out.clockRate,
+ payloadType: uint8(session.out.payloadType),
+ })
+ defer ingester.Close()
+
+ if ingester.conf.serverAddr, err = utilBuildMediaAddr(session.sip.conf.addr, session.out.mediaPort); err != nil {
+ return err
+ }
+
+ if err := ingester.Ingest(ctx); err != nil {
+ if errors.Cause(err) == io.EOF {
+ logger.Tf(ctx, "EOF, video=%v, audio=%v", conf.psConfig.video, conf.psConfig.audio)
+ return nil
+ }
+ return errors.Wrap(err, "ingest")
+ }
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/gb28181_test.go b/trunk/3rdparty/srs-bench/gb28181/gb28181_test.go
new file mode 100644
index 0000000000..43139aa5b7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/gb28181_test.go
@@ -0,0 +1,45 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "github.com/ossrs/go-oryx-lib/logger"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ if err := prepareTest(); err != nil {
+ logger.Ef(nil, "Prepare test fail, err %+v", err)
+ os.Exit(-1)
+ }
+
+ // Disable the logger during all tests.
+ if *srsLog == false {
+ olw := logger.Switch(ioutil.Discard)
+ defer func() {
+ logger.Switch(olw)
+ }()
+ }
+
+ os.Exit(m.Run())
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/gb_test.go b/trunk/3rdparty/srs-bench/gb28181/gb_test.go
new file mode 100644
index 0000000000..b4dc6d39b6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/gb_test.go
@@ -0,0 +1,495 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "context"
+ "fmt"
+ "github.com/ghettovoice/gosip/sip"
+ "github.com/ossrs/go-oryx-lib/logger"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "testing"
+ "time"
+)
+
+func TestGbPublishRegularly(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ err := func() error {
+ t := NewGBTestPublisher()
+ defer t.Close()
+
+ var nnPackets int
+ t.ingester.onSendPacket = func(pack *PSPackStream) error {
+ if nnPackets += 1; nnPackets > 10 {
+ cancel()
+ }
+ return nil
+ }
+
+ if err := t.Run(ctx); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+ if err := filterTestError(ctx.Err(), err); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionHandshake(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ err := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+
+ // Use fast heartbeat for utest.
+ t.session.heartbeatInterval = 100 * time.Millisecond
+
+ if err := t.Run(ctx); err != nil {
+ return err
+ }
+
+ var nn int
+ t.session.onMessageHeartbeat = func(req, res sip.Message) error {
+ if nn++; nn >= 3 {
+ t.session.cancel()
+ }
+ return nil
+ }
+
+ <-t.session.heartbeatCtx.Done()
+ return t.session.heartbeatCtx.Err()
+ }()
+ if err := filterTestError(ctx.Err(), err); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionHandshakeDropRegisterOk(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ r0 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+
+ conf = t.session.sip.conf
+
+ ctx, cancel2 := context.WithCancel(ctx)
+ t.session.onRegisterDone = func(req, res sip.Message) error {
+ cancel2()
+ return nil
+ }
+
+ return t.Run(ctx)
+ }()
+
+ // Use the same session for SIP.
+ r1 := func() error {
+ session := NewGBTestSession()
+ session.session.sip.conf = conf
+ defer session.Close()
+ return session.Run(ctx)
+ }()
+ if err := filterTestError(ctx.Err(), r0, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionHandshakeDropInviteRequest(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ r0 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ // Drop the invite request, to simulate the device crash or disconnect when got this message.
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.session.onInviteRequest = func(req sip.Message) error {
+ cancel2()
+ return nil
+ }
+
+ return t.Run(ctx2)
+ }()
+
+ // When device restart session when inviting, server should re-invite when got register message.
+ r1 := func() error {
+ t := NewGBTestSession()
+ t.session.sip.conf = conf
+ defer t.Close()
+ return t.Run(ctx)
+ }()
+ if err := filterTestError(ctx.Err(), r0, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionHandshakeDropInvite200Ack(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ r0 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ // Drop the invite ok ACK, to simulate the device crash or disconnect when got this message.
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.session.onInviteOkAck = func(req, res sip.Message) error {
+ cancel2()
+ return nil
+ }
+
+ return t.Run(ctx2)
+ }()
+
+ // When device restart session when 200 ack of invite, server should be stable state and waiting for media, then
+ //there should be a media timeout and re-invite.
+ r1 := func() error {
+ t := NewGBTestSession()
+ t.session.sip.conf = conf
+ defer t.Close()
+ return t.Run(ctx)
+ }()
+ if err := filterTestError(ctx.Err(), r0, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbPublishMediaDisconnect(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ r0 := func() error {
+ t := NewGBTestPublisher()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ var nnPackets int
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.ingester.onSendPacket = func(pack *PSPackStream) error {
+ if nnPackets += 1; nnPackets > 200 {
+ cancel2()
+ }
+ return nil
+ }
+
+ if err := t.Run(ctx2); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+
+ r1 := func() error {
+ t := NewGBTestSession()
+ t.session.sip.conf = conf
+ defer t.Close()
+ return t.Run(ctx)
+ }()
+
+ if err := filterTestError(ctx.Err(), r0, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionBye(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ err := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+
+ // Use fast heartbeat for utest.
+ t.session.heartbeatInterval = 100 * time.Millisecond
+
+ if err := t.Run(ctx); err != nil {
+ return err
+ }
+
+ var nn int
+ t.session.onMessageHeartbeat = func(req, res sip.Message) error {
+ if nn++; nn == 3 {
+ return t.session.Bye(ctx)
+ }
+ return nil
+ }
+
+ reconnectTimeout := time.Duration(*srsMediaTimeout+*srsReinviteTimeout+1000) * time.Millisecond
+ ctx2, cancel2 := context.WithTimeout(ctx, reconnectTimeout)
+ defer cancel2()
+
+ req, err := t.session.sip.Wait(ctx2, sip.INVITE)
+ if req != nil {
+ return fmt.Errorf("should not invite after bye")
+ }
+ if errors.Cause(err) == context.DeadlineExceeded {
+ return nil
+ }
+
+ return err
+ }()
+ if err := filterTestError(ctx.Err(), err); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbSessionUnregister(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ err := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+
+ // Use fast heartbeat for utest.
+ t.session.heartbeatInterval = 100 * time.Millisecond
+
+ if err := t.Run(ctx); err != nil {
+ return err
+ }
+
+ var nn int
+ t.session.onMessageHeartbeat = func(req, res sip.Message) error {
+ if nn++; nn == 3 {
+ return t.session.UnRegister(ctx)
+ }
+ return nil
+ }
+
+ reconnectTimeout := time.Duration(*srsMediaTimeout+*srsReinviteTimeout+1000) * time.Millisecond
+ ctx2, cancel2 := context.WithTimeout(ctx, reconnectTimeout)
+ defer cancel2()
+
+ req, err := t.session.sip.Wait(ctx2, sip.INVITE)
+ if req != nil {
+ return fmt.Errorf("should not invite after bye")
+ }
+ if errors.Cause(err) == context.DeadlineExceeded {
+ return nil
+ }
+
+ return err
+ }()
+ if err := filterTestError(ctx.Err(), err); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbPublishReinvite(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ err := func() error {
+ t := NewGBTestPublisher()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ var nnPackets int
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.ingester.onSendPacket = func(pack *PSPackStream) error {
+ if nnPackets += 1; nnPackets == 3 {
+ cancel2()
+ }
+ return nil
+ }
+
+ if err := t.Run(ctx2); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+
+ r1 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+ t.session.sip.conf = conf
+
+ // Only register the device, bind to session.
+ if err := t.session.Connect(ctx); err != nil {
+ return err
+ }
+ if err := t.session.Register(ctx); err != nil {
+ return err
+ }
+
+ // We should get reinvite when reconnect to SRS.
+ req, err := t.session.sip.Wait(ctx, sip.INVITE)
+ if req == nil {
+ return fmt.Errorf("should reinvite after disconnect")
+ }
+
+ return err
+ }()
+ if err := filterTestError(ctx.Err(), err, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbPublishBye(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ err := func() error {
+ t := NewGBTestPublisher()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ var nnPackets int
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.ingester.onSendPacket = func(pack *PSPackStream) error {
+ if nnPackets += 1; nnPackets == 10 {
+ if err := t.session.Bye(ctx2); err != nil {
+ return err
+ }
+ cancel2()
+ }
+ return nil
+ }
+
+ if err := t.Run(ctx2); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+
+ r1 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+ t.session.sip.conf = conf
+
+ // Only register the device, bind to session.
+ if err := t.session.Connect(ctx); err != nil {
+ return err
+ }
+ if err := t.session.Register(ctx); err != nil {
+ return err
+ }
+
+ // We should not get reinvite when reconnect to SRS.
+ reconnectTimeout := time.Duration(*srsMediaTimeout+*srsReinviteTimeout+1000) * time.Millisecond
+ ctx2, cancel2 := context.WithTimeout(ctx, reconnectTimeout)
+ defer cancel2()
+
+ req, err := t.session.sip.Wait(ctx2, sip.INVITE)
+ if req != nil {
+ return fmt.Errorf("should not invite after bye")
+ }
+ if errors.Cause(err) == context.DeadlineExceeded {
+ return nil
+ }
+
+ return err
+ }()
+ if err := filterTestError(ctx.Err(), err, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
+
+func TestGbPublishUnregister(t *testing.T) {
+ ctx := logger.WithContext(context.Background())
+ ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
+ defer cancel()
+
+ var conf *SIPConfig
+ err := func() error {
+ t := NewGBTestPublisher()
+ defer t.Close()
+ conf = t.session.sip.conf
+
+ var nnPackets int
+ ctx2, cancel2 := context.WithCancel(ctx)
+ t.ingester.onSendPacket = func(pack *PSPackStream) error {
+ if nnPackets += 1; nnPackets == 10 {
+ if err := t.session.UnRegister(ctx2); err != nil {
+ return err
+ }
+ cancel2()
+ }
+ return nil
+ }
+
+ if err := t.Run(ctx2); err != nil {
+ return err
+ }
+
+ return nil
+ }()
+
+ r1 := func() error {
+ t := NewGBTestSession()
+ defer t.Close()
+ t.session.sip.conf = conf
+
+ // Only register the device, bind to session.
+ if err := t.session.Connect(ctx); err != nil {
+ return err
+ }
+ if err := t.session.Register(ctx); err != nil {
+ return err
+ }
+
+ // We should not get reinvite when reconnect to SRS.
+ reconnectTimeout := time.Duration(*srsMediaTimeout+*srsReinviteTimeout+1000) * time.Millisecond
+ ctx2, cancel2 := context.WithTimeout(ctx, reconnectTimeout)
+ defer cancel2()
+
+ req, err := t.session.sip.Wait(ctx2, sip.INVITE)
+ if req != nil {
+ return fmt.Errorf("should not invite after bye")
+ }
+ if errors.Cause(err) == context.DeadlineExceeded {
+ return nil
+ }
+
+ return err
+ }()
+ if err := filterTestError(ctx.Err(), err, r1); err != nil {
+ t.Errorf("err %+v", err)
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/ingester.go b/trunk/3rdparty/srs-bench/gb28181/ingester.go
new file mode 100644
index 0000000000..5ba4960121
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/ingester.go
@@ -0,0 +1,418 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "context"
+ "github.com/ghettovoice/gosip/sip"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "github.com/ossrs/go-oryx-lib/logger"
+ "github.com/pion/webrtc/v3/pkg/media/h264reader"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type GBSessionConfig struct {
+ regTimeout time.Duration
+ inviteTimeout time.Duration
+}
+
+type GBSessionOutput struct {
+ ssrc int64
+ mediaPort int64
+ clockRate uint64
+ payloadType uint8
+}
+
+type GBSession struct {
+ // GB config.
+ conf *GBSessionConfig
+ // The output of session.
+ out *GBSessionOutput
+ // The SIP session object.
+ sip *SIPSession
+ // Callback when REGISTER done.
+ onRegisterDone func(req, res sip.Message) error
+ // Callback when got INVITE request.
+ onInviteRequest func(req sip.Message) error
+ // Callback when got INVITE 200 OK ACK request.
+ onInviteOkAck func(req, res sip.Message) error
+ // Callback when got MESSAGE response.
+ onMessageHeartbeat func(req, res sip.Message) error
+ // For heartbeat coroutines.
+ heartbeatInterval time.Duration
+ heartbeatCtx context.Context
+ cancel context.CancelFunc
+ // WaitGroup for coroutines.
+ wg sync.WaitGroup
+}
+
+func NewGBSession(c *GBSessionConfig, sc *SIPConfig) *GBSession {
+ return &GBSession{
+ sip: NewSIPSession(sc),
+ conf: c,
+ out: &GBSessionOutput{
+ clockRate: uint64(90000),
+ payloadType: uint8(96),
+ },
+ heartbeatInterval: 1 * time.Second,
+ }
+}
+
+func (v *GBSession) Close() error {
+ if v.cancel != nil {
+ v.cancel()
+ }
+ v.sip.Close()
+ v.wg.Wait()
+ return nil
+}
+
+func (v *GBSession) Connect(ctx context.Context) error {
+ client := v.sip
+
+ if err := client.Connect(ctx); err != nil {
+ return errors.Wrap(err, "connect")
+ }
+
+ return ctx.Err()
+}
+
+func (v *GBSession) Register(ctx context.Context) error {
+ client := v.sip
+
+ for ctx.Err() == nil {
+ ctx, regCancel := context.WithTimeout(ctx, v.conf.regTimeout)
+ defer regCancel()
+
+ regReq, regRes, err := client.Register(ctx)
+ if err != nil {
+ return errors.Wrap(err, "register")
+ }
+ logger.Tf(ctx, "Register id=%v, response=%v", regReq.MessageID(), regRes.MessageID())
+
+ if v.onRegisterDone != nil {
+ if err = v.onRegisterDone(regReq, regRes); err != nil {
+ return errors.Wrap(err, "callback")
+ }
+ }
+
+ break
+ }
+
+ return ctx.Err()
+}
+
+func (v *GBSession) Invite(ctx context.Context) error {
+ client := v.sip
+
+ for ctx.Err() == nil {
+ ctx, inviteCancel := context.WithTimeout(ctx, v.conf.inviteTimeout)
+ defer inviteCancel()
+
+ inviteReq, err := client.Wait(ctx, sip.INVITE)
+ if err != nil {
+ return errors.Wrap(err, "wait")
+ }
+ logger.Tf(ctx, "Got INVITE request, Call-ID=%v", sipGetCallID(inviteReq))
+
+ if v.onInviteRequest != nil {
+ if err = v.onInviteRequest(inviteReq); err != nil {
+ return errors.Wrap(err, "callback")
+ }
+ }
+
+ if err = client.Trying(ctx, inviteReq); err != nil {
+ return errors.Wrapf(err, "trying invite is %v", inviteReq.String())
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ inviteRes, err := client.InviteResponse(ctx, inviteReq)
+ if err != nil {
+ return errors.Wrapf(err, "response invite is %v", inviteReq.String())
+ }
+
+ offer := inviteReq.Body()
+ ssrcStr := strings.Split(strings.Split(offer, "y=")[1], "\r\n")[0]
+ if v.out.ssrc, err = strconv.ParseInt(ssrcStr, 10, 64); err != nil {
+ return errors.Wrapf(err, "parse ssrc=%v, sdp %v", ssrcStr, offer)
+ }
+ mediaPortStr := strings.Split(strings.Split(offer, "m=video")[1], " ")[1]
+ if v.out.mediaPort, err = strconv.ParseInt(mediaPortStr, 10, 64); err != nil {
+ return errors.Wrapf(err, "parse media port=%v, sdp %v", mediaPortStr, offer)
+ }
+ logger.Tf(ctx, "Invite id=%v, response=%v, y=%v, ssrc=%v, mediaPort=%v",
+ inviteReq.MessageID(), inviteRes.MessageID(), ssrcStr, v.out.ssrc, v.out.mediaPort,
+ )
+
+ if v.onInviteOkAck != nil {
+ if err = v.onInviteOkAck(inviteReq, inviteRes); err != nil {
+ return errors.Wrap(err, "callback")
+ }
+ }
+
+ break
+ }
+
+ // Start goroutine for heartbeat every 1s.
+ v.heartbeatCtx, v.cancel = context.WithCancel(ctx)
+ go func(ctx context.Context) {
+ v.wg.Add(1)
+ defer v.wg.Done()
+
+ for ctx.Err() == nil {
+ req, res, err := client.Message(ctx)
+ if err != nil {
+ v.cancel()
+ logger.Ef(ctx, "heartbeat err %+v", err)
+ return
+ }
+
+ if v.onMessageHeartbeat != nil {
+ if err = v.onMessageHeartbeat(req, res); err != nil {
+ v.cancel()
+ logger.Ef(ctx, "callback err %+v", err)
+ return
+ }
+ }
+
+ select {
+ case <-ctx.Done():
+ return
+ case <-time.After(v.heartbeatInterval):
+ }
+ }
+ }(v.heartbeatCtx)
+
+ return ctx.Err()
+}
+
+func (v *GBSession) Bye(ctx context.Context) error {
+ client := v.sip
+
+ for ctx.Err() == nil {
+ ctx, regCancel := context.WithTimeout(ctx, v.conf.regTimeout)
+ defer regCancel()
+
+ regReq, regRes, err := client.Bye(ctx)
+ if err != nil {
+ return errors.Wrap(err, "bye")
+ }
+ logger.Tf(ctx, "Bye id=%v, response=%v", regReq.MessageID(), regRes.MessageID())
+
+ break
+ }
+
+ return ctx.Err()
+}
+
+func (v *GBSession) UnRegister(ctx context.Context) error {
+ client := v.sip
+
+ for ctx.Err() == nil {
+ ctx, regCancel := context.WithTimeout(ctx, v.conf.regTimeout)
+ defer regCancel()
+
+ regReq, regRes, err := client.UnRegister(ctx)
+ if err != nil {
+ return errors.Wrap(err, "UnRegister")
+ }
+ logger.Tf(ctx, "UnRegister id=%v, response=%v", regReq.MessageID(), regRes.MessageID())
+
+ break
+ }
+
+ return ctx.Err()
+}
+
+type IngesterConfig struct {
+ psConfig PSConfig
+ ssrc uint32
+ serverAddr string
+ clockRate uint64
+ payloadType uint8
+}
+
+type PSIngester struct {
+ conf *IngesterConfig
+ onSendPacket func(pack *PSPackStream) error
+ cancel context.CancelFunc
+}
+
+func NewPSIngester(c *IngesterConfig) *PSIngester {
+ return &PSIngester{conf: c}
+}
+
+func (v *PSIngester) Close() error {
+ if v.cancel != nil {
+ v.cancel()
+ }
+ return nil
+}
+
+func (v *PSIngester) Ingest(ctx context.Context) error {
+ ctx, v.cancel = context.WithCancel(ctx)
+
+ ps := NewPSClient(uint32(v.conf.ssrc), v.conf.serverAddr)
+ if err := ps.Connect(ctx); err != nil {
+ return errors.Wrapf(err, "connect media=%v", v.conf.serverAddr)
+ }
+ defer ps.Close()
+
+ videoFile, err := os.Open(v.conf.psConfig.video)
+ if err != nil {
+ return errors.Wrapf(err, "Open file %v", v.conf.psConfig.video)
+ }
+ defer videoFile.Close()
+
+ f, err := os.Open(v.conf.psConfig.audio)
+ if err != nil {
+ return errors.Wrapf(err, "Open file %v", v.conf.psConfig.audio)
+ }
+ defer f.Close()
+
+ h264, err := h264reader.NewReader(videoFile)
+ if err != nil {
+ return errors.Wrapf(err, "Open h264 %v", v.conf.psConfig.video)
+ }
+
+ audio, err := NewAACReader(f)
+ if err != nil {
+ return errors.Wrapf(err, "Open ogg %v", v.conf.psConfig.audio)
+ }
+
+ // Scale the video samples to 1024 according to AAC, that is 1 video frame means 1024 samples.
+ audioSampleRate := audio.codec.ASC().SampleRate.ToHz()
+ videoSampleRate := 1024 * 1000 / v.conf.psConfig.fps
+ logger.Tf(ctx, "PS: Media stream, tbn=%v, ssrc=%v, pt=%v, Video(%v, fps=%v, rate=%v), Audio(%v, rate=%v, channels=%v)",
+ v.conf.clockRate, v.conf.ssrc, v.conf.payloadType, v.conf.psConfig.video, v.conf.psConfig.fps, videoSampleRate,
+ v.conf.psConfig.audio, audioSampleRate, audio.codec.ASC().Channels)
+
+ lastPrint := time.Now()
+ var aacSamples, avcSamples uint64
+ var audioDTS, videoDTS uint64
+ defer func() {
+ logger.Tf(ctx, "Consume Video(samples=%v, dts=%v, ts=%.2f) and Audio(samples=%v, dts=%v, ts=%.2f)",
+ avcSamples, videoDTS, float64(videoDTS)/90.0, aacSamples, audioDTS, float64(audioDTS)/90.0,
+ )
+ }()
+
+ clock := newWallClock()
+ var pack *PSPackStream
+ for ctx.Err() == nil {
+ if pack == nil {
+ pack = NewPSPackStream(v.conf.payloadType)
+ }
+
+ // One pack should only contains one video frame.
+ if !pack.hasVideo {
+ var sps, pps *h264reader.NAL
+ var videoFrames []*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")
+ }
+
+ videoFrames = append(videoFrames, 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
+ }
+ }
+
+ // We convert the video sample rate to be based over 1024, that is 1024 samples means one video frame.
+ avcSamples += 1024
+ videoDTS = uint64(v.conf.clockRate*avcSamples) / uint64(videoSampleRate)
+
+ if sps != nil || pps != nil {
+ err = pack.WriteHeader(videoDTS)
+ } else {
+ err = pack.WritePackHeader(videoDTS)
+ }
+ if err != nil {
+ return errors.Wrap(err, "pack header")
+ }
+
+ for _, frame := range videoFrames {
+ if err = pack.WriteVideo(frame.Data, videoDTS); err != nil {
+ return errors.Wrapf(err, "write video %v", len(frame.Data))
+ }
+ }
+ }
+
+ // Always read and consume one audio frame each time.
+ if true {
+ audioFrame, err := audio.NextADTSFrame()
+ if err != nil {
+ return errors.Wrap(err, "Read AAC")
+ }
+
+ // Each AAC frame contains 1024 samples, DTS = total-samples / sample-rate
+ aacSamples += 1024
+ audioDTS = uint64(v.conf.clockRate*aacSamples) / uint64(audioSampleRate)
+ if time.Now().Sub(lastPrint) > 3*time.Second {
+ lastPrint = time.Now()
+ logger.Tf(ctx, "Consume Video(samples=%v, dts=%v, ts=%.2f) and Audio(samples=%v, dts=%v, ts=%.2f)",
+ avcSamples, videoDTS, float64(videoDTS)/90.0, aacSamples, audioDTS, float64(audioDTS)/90.0,
+ )
+ }
+
+ if err = pack.WriteAudio(audioFrame, audioDTS); err != nil {
+ return errors.Wrapf(err, "write audio %v", len(audioFrame))
+ }
+ }
+
+ // Send pack when got video and enough audio frames.
+ if pack.hasVideo && videoDTS < audioDTS {
+ if err := ps.WritePacksOverRTP(pack.packets); err != nil {
+ return errors.Wrap(err, "write")
+ }
+ if v.onSendPacket != nil {
+ if err := v.onSendPacket(pack); err != nil {
+ return errors.Wrap(err, "callback")
+ }
+ }
+ pack = nil // Reset pack.
+ }
+
+ // One audio frame(1024 samples), the duration is 1024/audioSampleRate in seconds.
+ sampleDuration := time.Duration(uint64(time.Second) * 1024 / uint64(audioSampleRate))
+ if d := clock.Tick(sampleDuration); d > 0 {
+ time.Sleep(d)
+ }
+ }
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/ps.go b/trunk/3rdparty/srs-bench/gb28181/ps.go
new file mode 100644
index 0000000000..c2d9fd76b3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/ps.go
@@ -0,0 +1,281 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "context"
+ "fmt"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "github.com/pion/rtp"
+ "github.com/yapingcat/gomedia/codec"
+ "github.com/yapingcat/gomedia/mpeg2"
+ "math"
+ "net"
+ "net/url"
+ "strings"
+)
+
+type PSConfig struct {
+ // The video source file.
+ video string
+ // The fps for h264 file.
+ fps int
+ // The audio source file.
+ audio string
+}
+
+func (v *PSConfig) String() string {
+ sb := []string{}
+ if v.video != "" {
+ sb = append(sb, fmt.Sprintf("video=%v", v.video))
+ }
+ if v.fps > 0 {
+ sb = append(sb, fmt.Sprintf("fps=%v", v.fps))
+ }
+ if v.audio != "" {
+ sb = append(sb, fmt.Sprintf("audio=%v", v.audio))
+ }
+ return strings.Join(sb, ",")
+}
+
+type PSClient struct {
+ // SSRC from SDP.
+ ssrc uint32
+ // The server IP address and port to connect to.
+ serverAddr string
+ // Inner state, sequence number.
+ seq uint16
+ // Inner state, media TCP connection
+ conn *net.TCPConn
+}
+
+func NewPSClient(ssrc uint32, serverAddr string) *PSClient {
+ return &PSClient{ssrc: ssrc, serverAddr: serverAddr}
+}
+
+func (v *PSClient) Close() error {
+ if v.conn != nil {
+ v.conn.Close()
+ }
+ return nil
+}
+
+func (v *PSClient) Connect(ctx context.Context) error {
+ if u, err := url.Parse(v.serverAddr); err != nil {
+ return errors.Wrapf(err, "parse addr=%v", v.serverAddr)
+ } else if addr, err := net.ResolveTCPAddr(u.Scheme, u.Host); err != nil {
+ return errors.Wrapf(err, "parse addr=%v, scheme=%v, host=%v", v.serverAddr, u.Scheme, u.Host)
+ } else if v.conn, err = net.DialTCP(u.Scheme, nil, addr); err != nil {
+ return errors.Wrapf(err, "connect addr=%v as %v", v.serverAddr, addr.String())
+ }
+
+ return nil
+}
+
+func (v *PSClient) WritePacksOverRTP(packs []*PSPacket) error {
+ for _, pack := range packs {
+ for _, payload := range pack.ps {
+ v.seq++
+ p := rtp.Packet{Header: rtp.Header{
+ Version: 2, PayloadType: uint8(pack.pt), SequenceNumber: v.seq,
+ Timestamp: uint32(pack.ts), SSRC: uint32(v.ssrc),
+ }, Payload: payload}
+
+ b, err := p.Marshal()
+ if err != nil {
+ return errors.Wrapf(err, "rtp marshal")
+ }
+
+ if _, err = v.conn.Write([]byte{uint8(len(b) >> 8), uint8(len(b))}); err != nil {
+ return errors.Wrapf(err, "write length=%v", len(b))
+ }
+
+ if _, err = v.conn.Write(b); err != nil {
+ return errors.Wrapf(err, "write payload %v bytes", len(b))
+ }
+ }
+ }
+
+ return nil
+}
+
+type PSPacketType int
+
+const (
+ PSPacketTypePackHeader PSPacketType = iota
+ PSPacketTypeSystemHeader
+ PSPacketTypeProgramStramMap
+ PSPacketTypeVideo
+ PSPacketTypeAudio
+)
+
+type PSPacket struct {
+ t PSPacketType
+ ts uint64
+ pt uint8
+ ps [][]byte
+}
+
+func NewPSPacket(t PSPacketType, p []byte, ts uint64, pt uint8) *PSPacket {
+ v := &PSPacket{t: t, ts: ts, pt: pt}
+ if p != nil {
+ v.ps = append(v.ps, p)
+ }
+ return v
+}
+
+func (v *PSPacket) Append(p []byte) *PSPacket {
+ v.ps = append(v.ps, p)
+ return v
+}
+
+type PSPackStream struct {
+ // The RTP paload type.
+ pt uint8
+ // Split a big media frame to small PES packets.
+ ideaPesLength int
+ // The generated bytes of PS stream data.
+ packets []*PSPacket
+ // Whether has video packet.
+ hasVideo bool
+}
+
+func NewPSPackStream(pt uint8) *PSPackStream {
+ return &PSPackStream{ideaPesLength: 1400, pt: pt}
+}
+
+func (v *PSPackStream) WriteHeader(dts uint64) error {
+ if err := v.WritePackHeader(dts); err != nil {
+ return err
+ }
+ if err := v.WriteSystemHeader(dts); err != nil {
+ return err
+ }
+ if err := v.WriteProgramStreamMap(dts); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (v *PSPackStream) WritePackHeader(dts uint64) error {
+ w := codec.NewBitStreamWriter(1500)
+
+ pack := &mpeg2.PSPackHeader{
+ System_clock_reference_base: dts,
+ Program_mux_rate: 159953,
+ Pack_stuffing_length: 6,
+ }
+
+ pack.Encode(w)
+
+ v.packets = append(v.packets, NewPSPacket(PSPacketTypePackHeader, w.Bits(), dts, v.pt))
+ return nil
+}
+
+func (v *PSPackStream) WriteSystemHeader(dts uint64) error {
+ w := codec.NewBitStreamWriter(1500)
+
+ system := &mpeg2.System_header{
+ Rate_bound: 159953,
+ Video_bound: 1,
+ Audio_bound: 1,
+ Streams: []*mpeg2.Elementary_Stream{
+ // SrsTsPESStreamIdVideoCommon = 0xe0
+ &mpeg2.Elementary_Stream{Stream_id: uint8(0xe0), P_STD_buffer_bound_scale: 1, P_STD_buffer_size_bound: 128},
+ // SrsTsPESStreamIdAudioCommon = 0xc0
+ &mpeg2.Elementary_Stream{Stream_id: uint8(0xc0), P_STD_buffer_bound_scale: 0, P_STD_buffer_size_bound: 8},
+ // SrsTsPESStreamIdPrivateStream1 = 0xbd
+ &mpeg2.Elementary_Stream{Stream_id: uint8(0xbd), P_STD_buffer_bound_scale: 1, P_STD_buffer_size_bound: 128},
+ // SrsTsPESStreamIdPrivateStream2 = 0xbf
+ &mpeg2.Elementary_Stream{Stream_id: uint8(0xbf), P_STD_buffer_bound_scale: 1, P_STD_buffer_size_bound: 128},
+ },
+ }
+
+ system.Encode(w)
+
+ v.packets = append(v.packets, NewPSPacket(PSPacketTypeSystemHeader, w.Bits(), dts, v.pt))
+ return nil
+}
+
+func (v *PSPackStream) WriteProgramStreamMap(dts uint64) error {
+ w := codec.NewBitStreamWriter(1500)
+
+ psm := &mpeg2.Program_stream_map{
+ Stream_map: []*mpeg2.Elementary_stream_elem{
+ // SrsTsPESStreamIdVideoCommon = 0xe0
+ mpeg2.NewElementary_stream_elem(uint8(mpeg2.PS_STREAM_H264), 0xe0),
+ // SrsTsPESStreamIdAudioCommon = 0xc0
+ mpeg2.NewElementary_stream_elem(uint8(mpeg2.PS_STREAM_AAC), 0xc0),
+ },
+ }
+
+ psm.Encode(w)
+
+ v.packets = append(v.packets, NewPSPacket(PSPacketTypeProgramStramMap, w.Bits(), dts, v.pt))
+ return nil
+}
+
+// The nalu is raw data without ANNEXB header.
+func (v *PSPackStream) WriteVideo(nalu []byte, dts uint64) error {
+ // Mux frame payload in AnnexB format. Always fresh NALU header for frame, see srs_avc_insert_aud.
+ annexb := append([]byte{0, 0, 0, 1}, nalu...)
+
+ video := NewPSPacket(PSPacketTypeVideo, nil, dts, v.pt)
+
+ for i := 0; i < len(annexb); i += v.ideaPesLength {
+ payloadLength := int(math.Min(float64(v.ideaPesLength), float64(len(annexb)-i)))
+ bb := annexb[i : i+payloadLength]
+
+ w := codec.NewBitStreamWriter(65535)
+
+ pes := &mpeg2.PesPacket{
+ Stream_id: uint8(0xe0), // SrsTsPESStreamIdVideoCommon = 0xe0
+ PTS_DTS_flags: uint8(0x03), Dts: dts, Pts: dts, // Both DTS and PTS.
+ Pes_payload: bb,
+ }
+ utilUpdatePesPacketLength(pes)
+
+ pes.Encode(w)
+
+ video.Append(w.Bits())
+ }
+
+ v.hasVideo = true
+ v.packets = append(v.packets, video)
+ return nil
+}
+
+// Write AAC ADTS frame.
+func (v *PSPackStream) WriteAudio(adts []byte, dts uint64) error {
+ w := codec.NewBitStreamWriter(65535)
+
+ pes := &mpeg2.PesPacket{
+ Stream_id: uint8(0xc0), // SrsTsPESStreamIdAudioCommon = 0xc0
+ PTS_DTS_flags: uint8(0x03), Dts: dts, Pts: dts, // Both DTS and PTS.
+ Pes_payload: adts,
+ }
+ utilUpdatePesPacketLength(pes)
+
+ pes.Encode(w)
+
+ v.packets = append(v.packets, NewPSPacket(PSPacketTypeAudio, w.Bits(), dts, v.pt))
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/sip.go b/trunk/3rdparty/srs-bench/gb28181/sip.go
new file mode 100644
index 0000000000..8043ffd8b3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/sip.go
@@ -0,0 +1,561 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "context"
+ "fmt"
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+ "github.com/ghettovoice/gosip/transport"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "github.com/ossrs/go-oryx-lib/logger"
+ "math/rand"
+ "net/url"
+ "strings"
+ "sync"
+ "time"
+)
+
+type SIPConfig struct {
+ // The server address, for example: tcp://127.0.0.1:5060k
+ addr string
+ // The SIP domain, for example: ossrs.io or 3402000000
+ domain string
+ // The SIP device ID, for example: camera or 34020000001320000001
+ user string
+ // The N number of random device ID, for example, 10 means 1320000001
+ random int
+ // The SIP server ID, for example: srs or 34020000002000000001
+ server string
+ // The cached device id.
+ deviceID string
+}
+
+// The global cache to avoid conflict of deviceID.
+// Note that it's not coroutine safe, but it should be OK for utest.
+var deviceIDCache map[string]bool
+
+func init() {
+ deviceIDCache = make(map[string]bool)
+}
+
+func (v *SIPConfig) DeviceID() string {
+ for v.deviceID == "" {
+ // Generate a random ID.
+ var rid string
+ for len(rid) < v.random {
+ rid += fmt.Sprintf("%v", rand.Uint64())
+ }
+ deviceID := fmt.Sprintf("%v%v", v.user, rid[:v.random])
+
+ // Ignore if exists.
+ if _, ok := deviceIDCache[deviceID]; !ok {
+ v.deviceID = deviceID
+ deviceIDCache[deviceID] = true
+ }
+ }
+ return v.deviceID
+}
+
+func (v *SIPConfig) String() string {
+ sb := []string{}
+ if v.addr != "" {
+ sb = append(sb, fmt.Sprintf("addr=%v", v.addr))
+ }
+ if v.domain != "" {
+ sb = append(sb, fmt.Sprintf("domain=%v", v.domain))
+ }
+ if v.user != "" {
+ sb = append(sb, fmt.Sprintf("user=%v", v.user))
+ sb = append(sb, fmt.Sprintf("deviceID=%v", v.DeviceID()))
+ }
+ if v.random > 0 {
+ sb = append(sb, fmt.Sprintf("random=%v", v.random))
+ }
+ if v.server != "" {
+ sb = append(sb, fmt.Sprintf("server=%v", v.server))
+ }
+ return strings.Join(sb, ",")
+}
+
+type SIPSession struct {
+ conf *SIPConfig
+ rb *sip.RequestBuilder
+ requests chan sip.Request
+ responses chan sip.Response
+ wg sync.WaitGroup
+ ctx context.Context
+ cancel context.CancelFunc
+ client *SIPClient
+ seq uint
+}
+
+func NewSIPSession(c *SIPConfig) *SIPSession {
+ return &SIPSession{
+ conf: c, client: NewSIPClient(), rb: sip.NewRequestBuilder(),
+ requests: make(chan sip.Request, 1024), responses: make(chan sip.Response, 1024),
+ seq: 100,
+ }
+}
+
+func (v *SIPSession) Close() error {
+ if v.cancel != nil {
+ v.cancel()
+ }
+ v.client.Close()
+ v.wg.Wait()
+ return nil
+}
+
+func (v *SIPSession) Connect(ctx context.Context) error {
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
+
+ ctx, cancel := context.WithCancel(ctx)
+ v.ctx, v.cancel = ctx, cancel
+
+ if err := v.client.Connect(ctx, v.conf.addr); err != nil {
+ return errors.Wrapf(err, "connect with sipConfig %v", v.conf.String())
+ }
+
+ // Dispatch requests and responses.
+ go func() {
+ v.wg.Add(1)
+ defer v.wg.Done()
+
+ for {
+ select {
+ case <-v.ctx.Done():
+ return
+ case msg := <-v.client.incoming:
+ if req, ok := msg.(sip.Request); ok {
+ select {
+ case v.requests <- req:
+ case <-v.ctx.Done():
+ return
+ }
+ } else if res, ok := msg.(sip.Response); ok {
+ select {
+ case v.responses <- res:
+ case <-v.ctx.Done():
+ return
+ }
+ } else {
+ logger.Wf(ctx, "Drop message %v", msg.String())
+ }
+ }
+ }
+ }()
+
+ return nil
+}
+
+func (v *SIPSession) Register(ctx context.Context) (sip.Message, sip.Message, error) {
+ return v.doRegister(ctx, 3600)
+}
+
+func (v *SIPSession) UnRegister(ctx context.Context) (sip.Message, sip.Message, error) {
+ return v.doRegister(ctx, 0)
+}
+
+func (v *SIPSession) doRegister(ctx context.Context, expires int) (sip.Message, sip.Message, error) {
+ if ctx.Err() != nil {
+ return nil, nil, ctx.Err()
+ }
+
+ sipPort := sip.Port(5060)
+ sipCallID := sip.CallID(fmt.Sprintf("%v", rand.Uint64()))
+ sipBranch := fmt.Sprintf("z9hG4bK_%v", rand.Uint32())
+ sipTag := fmt.Sprintf("%v", rand.Uint32())
+ sipMaxForwards := sip.MaxForwards(70)
+ sipExpires := sip.Expires(uint32(expires))
+ sipPIP := "192.168.3.99"
+ v.seq++
+
+ rb := v.rb
+ rb.SetTransport("TCP")
+ rb.SetMethod(sip.REGISTER)
+ rb.AddVia(&sip.ViaHop{
+ ProtocolName: "SIP", ProtocolVersion: "2.0", Transport: "TCP", Host: sipPIP, Port: &sipPort,
+ Params: sip.NewParams().Add("branch", sip.String{Str: sipBranch}),
+ })
+ rb.SetFrom(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: v.conf.domain},
+ Params: sip.NewParams().Add("tag", sip.String{Str: sipTag}),
+ })
+ rb.SetTo(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: v.conf.domain},
+ })
+ rb.SetCallID(&sipCallID)
+ rb.SetSeqNo(v.seq)
+ rb.SetRecipient(&sip.SipUri{FUser: sip.String{v.conf.server}, FHost: v.conf.domain})
+ rb.SetContact(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: sipPIP, FPort: &sipPort},
+ })
+ rb.SetMaxForwards(&sipMaxForwards)
+ rb.SetExpires(&sipExpires)
+ req, err := rb.Build()
+ if err != nil {
+ return req, nil, errors.Wrap(err, "build request")
+ }
+
+ if err = v.client.Send(req); err != nil {
+ return req, nil, errors.Wrapf(err, "send request %v", req.String())
+ }
+
+ callID := sipGetCallID(req)
+ if callID == "" {
+ return req, nil, errors.Errorf("Invalid SIP Call-ID register %v", req.String())
+ }
+ logger.Tf(ctx, "Send REGISTER request, Call-ID=%v, Expires=%v", callID, expires)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, nil, ctx.Err()
+ case <-v.ctx.Done():
+ return nil, nil, v.ctx.Err()
+ case msg := <-v.responses:
+ if tv := sipGetCallID(msg); tv == callID {
+ return req, msg, nil
+ } else {
+ logger.Wf(v.ctx, "Not callID=%v, msg=%v, drop message %v", callID, tv, msg.String())
+ }
+ }
+ }
+}
+
+func (v *SIPSession) Trying(ctx context.Context, invite sip.Message) error {
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
+
+ req, ok := invite.(sip.Request)
+ if !ok {
+ return errors.Errorf("Invalid SIP request invite %v", invite.String())
+ }
+
+ res := sip.NewResponseFromRequest("", req, sip.StatusCode(100), "Trying", "")
+ if err := v.client.Send(res); err != nil {
+ return errors.Wrapf(err, "send response %v", res.String())
+ }
+
+ return nil
+}
+
+func (v *SIPSession) InviteResponse(ctx context.Context, invite sip.Message) (sip.Message, error) {
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
+ req, ok := invite.(sip.Request)
+ if !ok {
+ return nil, errors.Errorf("Invalid SIP request invite %v", invite.String())
+ }
+
+ callID := sipGetCallID(invite)
+ if callID == "" {
+ return nil, errors.Errorf("Invalid SIP Call-ID invite %v", invite.String())
+ }
+
+ res := sip.NewResponseFromRequest("", req, sip.StatusCode(200), "OK", "")
+ if err := v.client.Send(res); err != nil {
+ return nil, errors.Wrapf(err, "send response %v", res.String())
+ }
+ logger.Tf(ctx, "Send INVITE response, Call-ID=%v", callID)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-v.ctx.Done():
+ return nil, v.ctx.Err()
+ case msg := <-v.requests:
+ // Must be an ACK message.
+ if !msg.IsAck() {
+ return msg, errors.Errorf("invalid ACK message %v", msg.String())
+ }
+
+ // Check CALL-ID of ACK, should be equal to 200 OK.
+ if tv := sipGetCallID(msg); tv == callID {
+ return msg, nil
+ } else {
+ logger.Wf(v.ctx, "Not callID=%v, msg=%v, drop message %v", callID, tv, msg.String())
+ }
+ }
+ }
+}
+
+func (v *SIPSession) Message(ctx context.Context) (sip.Message, sip.Message, error) {
+ if ctx.Err() != nil {
+ return nil, nil, ctx.Err()
+ }
+
+ sipPort := sip.Port(5060)
+ sipCallID := sip.CallID(fmt.Sprintf("%v", rand.Uint64()))
+ sipBranch := fmt.Sprintf("z9hG4bK_%v", rand.Uint32())
+ sipTag := fmt.Sprintf("%v", rand.Uint32())
+ sipMaxForwards := sip.MaxForwards(70)
+ sipExpires := sip.Expires(3600)
+ sipPIP := "192.168.3.99"
+ v.seq++
+
+ rb := v.rb
+ rb.SetTransport("TCP")
+ rb.SetMethod(sip.MESSAGE)
+ rb.AddVia(&sip.ViaHop{
+ ProtocolName: "SIP", ProtocolVersion: "2.0", Transport: "TCP", Host: sipPIP, Port: &sipPort,
+ Params: sip.NewParams().Add("branch", sip.String{Str: sipBranch}),
+ })
+ rb.SetFrom(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: v.conf.domain},
+ Params: sip.NewParams().Add("tag", sip.String{Str: sipTag}),
+ })
+ rb.SetTo(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.server}, FHost: v.conf.domain},
+ })
+ rb.SetCallID(&sipCallID)
+ rb.SetSeqNo(v.seq)
+ rb.SetRecipient(&sip.SipUri{FUser: sip.String{v.conf.server}, FHost: v.conf.domain})
+ rb.SetContact(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: sipPIP, FPort: &sipPort},
+ })
+ rb.SetMaxForwards(&sipMaxForwards)
+ rb.SetExpires(&sipExpires)
+
+ v.seq++
+ rb.SetBody(strings.Join([]string{
+ ``,
+ "",
+ "Keepalive",
+ fmt.Sprintf("%v", v.seq),
+ fmt.Sprintf("%v", v.conf.DeviceID()),
+ "OK",
+ "\n",
+ }, "\n"))
+
+ req, err := rb.Build()
+ if err != nil {
+ return req, nil, errors.Wrap(err, "build request")
+ }
+
+ if err = v.client.Send(req); err != nil {
+ return req, nil, errors.Wrapf(err, "send request %v", req.String())
+ }
+
+ callID := sipGetCallID(req)
+ if callID == "" {
+ return req, nil, errors.Errorf("Invalid SIP Call-ID message %v", req.String())
+ }
+ logger.Tf(ctx, "Send MESSAGE request, Call-ID=%v", callID)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, nil, ctx.Err()
+ case <-v.ctx.Done():
+ return nil, nil, v.ctx.Err()
+ case msg := <-v.responses:
+ if tv := sipGetCallID(msg); tv == callID {
+ return req, msg, nil
+ } else {
+ logger.Wf(v.ctx, "Not callID=%v, msg=%v, drop message %v", callID, tv, msg.String())
+ }
+ }
+ }
+}
+
+func (v *SIPSession) Bye(ctx context.Context) (sip.Message, sip.Message, error) {
+ if ctx.Err() != nil {
+ return nil, nil, ctx.Err()
+ }
+
+ sipPort := sip.Port(5060)
+ sipCallID := sip.CallID(fmt.Sprintf("%v", rand.Uint64()))
+ sipBranch := fmt.Sprintf("z9hG4bK_%v", rand.Uint32())
+ sipTag := fmt.Sprintf("%v", rand.Uint32())
+ sipMaxForwards := sip.MaxForwards(70)
+ sipExpires := sip.Expires(3600)
+ sipPIP := "192.168.3.99"
+ v.seq++
+
+ rb := v.rb
+ rb.SetTransport("TCP")
+ rb.SetMethod(sip.BYE)
+ rb.AddVia(&sip.ViaHop{
+ ProtocolName: "SIP", ProtocolVersion: "2.0", Transport: "TCP", Host: sipPIP, Port: &sipPort,
+ Params: sip.NewParams().Add("branch", sip.String{Str: sipBranch}),
+ })
+ rb.SetFrom(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: v.conf.domain},
+ Params: sip.NewParams().Add("tag", sip.String{Str: sipTag}),
+ })
+ rb.SetTo(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.server}, FHost: v.conf.domain},
+ })
+ rb.SetCallID(&sipCallID)
+ rb.SetSeqNo(v.seq)
+ rb.SetRecipient(&sip.SipUri{FUser: sip.String{v.conf.server}, FHost: v.conf.domain})
+ rb.SetContact(&sip.Address{
+ Uri: &sip.SipUri{FUser: sip.String{v.conf.DeviceID()}, FHost: sipPIP, FPort: &sipPort},
+ })
+ rb.SetMaxForwards(&sipMaxForwards)
+ rb.SetExpires(&sipExpires)
+
+ req, err := rb.Build()
+ if err != nil {
+ return req, nil, errors.Wrap(err, "build request")
+ }
+
+ if err = v.client.Send(req); err != nil {
+ return req, nil, errors.Wrapf(err, "send request %v", req.String())
+ }
+
+ callID := sipGetCallID(req)
+ if callID == "" {
+ return req, nil, errors.Errorf("Invalid SIP Call-ID bye %v", req.String())
+ }
+ logger.Tf(ctx, "Send BYE request, Call-ID=%v", callID)
+
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, nil, ctx.Err()
+ case <-v.ctx.Done():
+ return nil, nil, v.ctx.Err()
+ case msg := <-v.responses:
+ if tv := sipGetCallID(msg); tv == callID {
+ return req, msg, nil
+ } else {
+ logger.Wf(v.ctx, "Not callID=%v, msg=%v, drop message %v", callID, tv, msg.String())
+ }
+ }
+ }
+}
+
+func (v *SIPSession) Wait(ctx context.Context, method sip.RequestMethod) (sip.Message, error) {
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-v.ctx.Done():
+ return nil, v.ctx.Err()
+ case msg := <-v.requests:
+ if r, ok := msg.(sip.Request); ok && r.Method() == method {
+ return msg, nil
+ } else {
+ logger.Wf(v.ctx, "Not method=%v, drop message %v", method, msg.String())
+ }
+ }
+ }
+}
+
+type SIPClient struct {
+ ctx context.Context
+ cancel context.CancelFunc
+ incoming chan sip.Message
+ target *transport.Target
+ protocol transport.Protocol
+ cleanupTimeout time.Duration
+}
+
+func NewSIPClient() *SIPClient {
+ return &SIPClient{
+ cleanupTimeout: 5 * time.Second,
+ }
+}
+
+func (v *SIPClient) Close() error {
+ if v.cancel != nil {
+ v.cancel()
+ }
+
+ // Wait for protocol stack to cleanup.
+ if v.protocol != nil {
+ select {
+ case <-time.After(v.cleanupTimeout):
+ logger.E(v.ctx, "Wait for protocol cleanup timeout")
+ case <-v.protocol.Done():
+ logger.T(v.ctx, "SIP protocol stack done")
+ }
+ }
+
+ return nil
+}
+
+func (v *SIPClient) Connect(ctx context.Context, addr string) error {
+ prURL, err := url.Parse(addr)
+ if err != nil {
+ return errors.Wrapf(err, "parse addr=%v", addr)
+ }
+
+ if prURL.Scheme != "tcp" && prURL.Scheme != "tcp4" {
+ return errors.Errorf("invalid scheme=%v of addr=%v", prURL.Scheme, addr)
+ }
+
+ target, err := transport.NewTargetFromAddr(prURL.Host)
+ if err != nil {
+ return errors.Wrapf(err, "create target to %v", prURL.Host)
+ }
+ v.target = target
+
+ incoming := make(chan sip.Message, 1024)
+ errs := make(chan error, 1)
+ cancels := make(chan struct{}, 1)
+ protocol := transport.NewTcpProtocol(incoming, errs, cancels, nil, log.NewDefaultLogrusLogger())
+ v.protocol = protocol
+ v.incoming = incoming
+
+ // Convert protocol stack errs to context signal.
+ ctx, cancel := context.WithCancel(ctx)
+ v.cancel = cancel
+ v.ctx = ctx
+
+ go func() {
+ select {
+ case <-ctx.Done():
+ return
+ case r0 := <-errs:
+ logger.Ef(ctx, "SIP stack err %+v", r0)
+ cancel()
+ }
+ }()
+
+ // Covert context signal to cancels for protocol stack.
+ go func() {
+ <-ctx.Done()
+ close(cancels)
+ logger.Tf(ctx, "Notify SIP stack to cancel")
+ }()
+
+ return nil
+}
+
+func (v *SIPClient) Send(msg sip.Message) error {
+ logger.Tf(v.ctx, "Send msg %v", msg.String())
+ return v.protocol.Send(v.target, msg)
+}
diff --git a/trunk/3rdparty/srs-bench/gb28181/util.go b/trunk/3rdparty/srs-bench/gb28181/util.go
new file mode 100644
index 0000000000..42bf37cb73
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/gb28181/util.go
@@ -0,0 +1,361 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2022 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 gb28181
+
+import (
+ "bufio"
+ "context"
+ "flag"
+ "fmt"
+ "github.com/ghettovoice/gosip/sip"
+ "github.com/ossrs/go-oryx-lib/aac"
+ "github.com/ossrs/go-oryx-lib/errors"
+ "github.com/yapingcat/gomedia/mpeg2"
+ "io"
+ "net"
+ "net/url"
+ "os"
+ "path"
+ "strings"
+ "time"
+)
+
+var srsLog *bool
+
+var srsTimeout *int
+var srsPublishVideoFps *int
+
+var srsSipAddr *string
+var srsSipUser *string
+var srsSipRandomID *int
+var srsSipDomain *string
+var srsSipSvrID *string
+
+var srsMediaTimeout *int
+var srsReinviteTimeout *int
+var srsPublishAudio *string
+var srsPublishVideo *string
+
+func prepareTest() (err error) {
+ srsSipAddr = flag.String("srs-sip", "tcp://127.0.0.1:5060", "The SRS GB server to connect to")
+ srsSipUser = flag.String("srs-stream", "3402000000", "The GB user/stream to publish")
+ srsSipRandomID = flag.Int("srs-random", 10, "The GB user/stream random suffix to publish")
+ srsSipDomain = flag.String("srs-domain", "3402000000", "The GB SIP domain")
+ srsSipSvrID = flag.String("srs-server", "34020000002000000001", "The GB server ID for SIP")
+ srsLog = flag.Bool("srs-log", false, "Whether enable the detail log")
+ srsTimeout = flag.Int("srs-timeout", 11000, "For each case, the timeout in ms")
+ srsMediaTimeout = flag.Int("srs-media-timeout", 2100, "PS media disconnect timeout in ms")
+ srsReinviteTimeout = flag.Int("srs-reinvite-timeout", 1200, "When disconnect, SIP re-invite timeout in ms")
+ srsPublishAudio = flag.String("srs-publish-audio", "avatar.aac", "The audio file for publisher.")
+ srsPublishVideo = flag.String("srs-publish-video", "avatar.h264", "The video file for publisher.")
+ srsPublishVideoFps = flag.Int("srs-publish-video-fps", 25, "The video fps for publisher.")
+
+ // Should parse it first.
+ flag.Parse()
+
+ // Check file.
+ tryOpenFile := func(filename string) (string, error) {
+ if filename == "" {
+ return filename, nil
+ }
+
+ f, err := os.Open(filename)
+ if err != nil {
+ nfilename := path.Join("../", filename)
+ f2, err := os.Open(nfilename)
+ if err != nil {
+ return filename, errors.Wrapf(err, "No video file at %v or %v", filename, nfilename)
+ }
+ defer f2.Close()
+
+ return nfilename, nil
+ }
+ defer f.Close()
+
+ return filename, nil
+ }
+
+ if *srsPublishVideo, err = tryOpenFile(*srsPublishVideo); err != nil {
+ return err
+ }
+
+ if *srsPublishAudio, err = tryOpenFile(*srsPublishAudio); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type GBTestSession struct {
+ session *GBSession
+}
+
+func NewGBTestSession() *GBTestSession {
+ sipConfig := SIPConfig{
+ addr: *srsSipAddr,
+ domain: *srsSipDomain,
+ user: *srsSipUser,
+ random: *srsSipRandomID,
+ server: *srsSipSvrID,
+ }
+ return &GBTestSession{
+ session: NewGBSession(&GBSessionConfig{
+ regTimeout: time.Duration(*srsTimeout) * 5 * time.Minute,
+ inviteTimeout: time.Duration(*srsTimeout) * 5 * time.Minute,
+ }, &sipConfig),
+ }
+}
+
+func (v *GBTestSession) Close() error {
+ v.session.Close()
+ return nil
+}
+
+func (v *GBTestSession) Run(ctx context.Context) (err error) {
+ if err = v.session.Connect(ctx); err != nil {
+ return errors.Wrap(err, "connect")
+ }
+ if err = v.session.Register(ctx); err != nil {
+ return errors.Wrap(err, "register")
+ }
+ if err = v.session.Invite(ctx); err != nil {
+ return errors.Wrap(err, "invite")
+ }
+
+ return nil
+}
+
+type GBTestPublisher struct {
+ session *GBSession
+ ingester *PSIngester
+}
+
+func NewGBTestPublisher() *GBTestPublisher {
+ sipConfig := SIPConfig{
+ addr: *srsSipAddr,
+ domain: *srsSipDomain,
+ user: *srsSipUser,
+ random: *srsSipRandomID,
+ server: *srsSipSvrID,
+ }
+ psConfig := PSConfig{
+ video: *srsPublishVideo,
+ fps: *srsPublishVideoFps,
+ audio: *srsPublishAudio,
+ }
+ return &GBTestPublisher{
+ session: NewGBSession(&GBSessionConfig{
+ regTimeout: time.Duration(*srsTimeout) * 5 * time.Minute,
+ inviteTimeout: time.Duration(*srsTimeout) * 5 * time.Minute,
+ }, &sipConfig),
+ ingester: NewPSIngester(&IngesterConfig{
+ psConfig: psConfig,
+ }),
+ }
+}
+
+func (v *GBTestPublisher) Close() error {
+ v.ingester.Close()
+ v.session.Close()
+ return nil
+}
+
+func (v *GBTestPublisher) Run(ctx context.Context) (err error) {
+ if err = v.session.Connect(ctx); err != nil {
+ return errors.Wrap(err, "connect")
+ }
+ if err = v.session.Register(ctx); err != nil {
+ return errors.Wrap(err, "register")
+ }
+ if err = v.session.Invite(ctx); err != nil {
+ return errors.Wrap(err, "invite")
+ }
+
+ serverAddr, err := utilBuildMediaAddr(v.session.sip.conf.addr, v.session.out.mediaPort)
+ if err != nil {
+ return errors.Wrap(err, "parse")
+ }
+ v.ingester.conf.serverAddr = serverAddr
+
+ v.ingester.conf.ssrc = uint32(v.session.out.ssrc)
+ v.ingester.conf.clockRate = v.session.out.clockRate
+ v.ingester.conf.payloadType = uint8(v.session.out.payloadType)
+
+ if err := v.ingester.Ingest(ctx); err != nil {
+ return errors.Wrap(err, "ingest")
+ }
+
+ return nil
+}
+
+// Filter the test error, ignore context.Canceled
+func filterTestError(errs ...error) error {
+ var filteredErrors []error
+
+ for _, err := range errs {
+ if err == nil || errors.Cause(err) == context.Canceled {
+ continue
+ }
+
+ // If url error, server maybe error, do not print the detail log.
+ if r0 := errors.Cause(err); r0 != nil {
+ if r1, ok := r0.(*url.Error); ok {
+ err = r1
+ }
+ }
+
+ filteredErrors = append(filteredErrors, err)
+ }
+
+ if len(filteredErrors) == 0 {
+ return nil
+ }
+ if len(filteredErrors) == 1 {
+ return filteredErrors[0]
+ }
+
+ var descs []string
+ for i, err := range filteredErrors[1:] {
+ descs = append(descs, fmt.Sprintf("err #%d, %+v", i, err))
+ }
+ return errors.Wrapf(filteredErrors[0], "with %v", strings.Join(descs, ","))
+}
+
+type wallClock struct {
+ start time.Time
+ duration time.Duration
+}
+
+func newWallClock() *wallClock {
+ return &wallClock{start: time.Now()}
+}
+
+func (v *wallClock) Tick(d time.Duration) time.Duration {
+ v.duration += d
+
+ wc := time.Now().Sub(v.start)
+ re := v.duration - wc
+ if re > 30*time.Millisecond {
+ return re
+ }
+ return 0
+}
+
+func sipGetCallID(m sip.Message) string {
+ if v, ok := m.CallID(); !ok {
+ return ""
+ } else {
+ return v.Value()
+ }
+}
+
+func utilBuildMediaAddr(addr string, mediaPort int64) (string, error) {
+ if u, err := url.Parse(addr); err != nil {
+ return "", errors.Wrapf(err, "parse %v", addr)
+ } else if addr, err := net.ResolveTCPAddr(u.Scheme, u.Host); err != nil {
+ return "", errors.Wrapf(err, "parse %v scheme=%v, host=%v", addr, u.Scheme, u.Host)
+ } else {
+ return fmt.Sprintf("%v://%v:%v",
+ u.Scheme, addr.IP.String(), mediaPort,
+ ), nil
+ }
+}
+
+// See SrsMpegPES::decode
+func utilUpdatePesPacketLength(pes *mpeg2.PesPacket) {
+ var nb_required int
+ if pes.PTS_DTS_flags == 0x2 {
+ nb_required += 5
+ }
+ if pes.PTS_DTS_flags == 0x3 {
+ nb_required += 10
+ }
+ if pes.ESCR_flag > 0 {
+ nb_required += 6
+ }
+ if pes.ES_rate_flag > 0 {
+ nb_required += 3
+ }
+ if pes.DSM_trick_mode_flag > 0 {
+ nb_required += 1
+ }
+ if pes.Additional_copy_info_flag > 0 {
+ nb_required += 1
+ }
+ if pes.PES_CRC_flag > 0 {
+ nb_required += 2
+ }
+ if pes.PES_extension_flag > 0 {
+ nb_required += 1
+ }
+
+ // Size before PES_header_data_length.
+ const fixed = uint16(3)
+ // Size after PES_header_data_length.
+ pes.PES_header_data_length = uint8(nb_required)
+ // Size after PES_packet_length
+ pes.PES_packet_length = uint16(len(pes.Pes_payload)) + fixed + uint16(pes.PES_header_data_length)
+}
+
+type AACReader struct {
+ codec aac.ADTS
+ r *bufio.Reader
+}
+
+func NewAACReader(f io.Reader) (*AACReader, error) {
+ v := &AACReader{}
+
+ var err error
+ if v.codec, err = aac.NewADTS(); err != nil {
+ return nil, err
+ }
+
+ v.r = bufio.NewReaderSize(f, 4096)
+ b, err := v.r.Peek(7 + 1024)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, _, err = v.codec.Decode(b); err != nil {
+ return nil, err
+ }
+
+ return v, nil
+}
+
+func (v *AACReader) NextADTSFrame() ([]byte, error) {
+ b, err := v.r.Peek(7 + 1024)
+ if err != nil {
+ return nil, err
+ }
+
+ _, left, err := v.codec.Decode(b)
+ if err != nil {
+ return nil, err
+ }
+
+ adts := b[:len(b)-len(left)]
+ if _, err = v.r.Discard(len(adts)); err != nil {
+ return nil, err
+ }
+
+ return adts, nil
+}
diff --git a/trunk/3rdparty/srs-bench/go.mod b/trunk/3rdparty/srs-bench/go.mod
index 766440e09c..cf79ee8c51 100644
--- a/trunk/3rdparty/srs-bench/go.mod
+++ b/trunk/3rdparty/srs-bench/go.mod
@@ -3,6 +3,7 @@ module github.com/ossrs/srs-bench
go 1.15
require (
+ github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83
github.com/ossrs/go-oryx-lib v0.0.9
github.com/pion/interceptor v0.0.10
github.com/pion/logging v0.2.2
@@ -11,4 +12,6 @@ require (
github.com/pion/sdp/v3 v3.0.4
github.com/pion/transport v0.12.2
github.com/pion/webrtc/v3 v3.0.13
+ github.com/yapingcat/gomedia/codec v0.0.0-20220617074658-94762898dc25
+ github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25
)
diff --git a/trunk/3rdparty/srs-bench/go.sum b/trunk/3rdparty/srs-bench/go.sum
index ab60a92a30..9076118b64 100644
--- a/trunk/3rdparty/srs-bench/go.sum
+++ b/trunk/3rdparty/srs-bench/go.sum
@@ -1,33 +1,59 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/discoviking/fsm v0.0.0-20150126104936-f4a273feecca/go.mod h1:W+3LQaEkN8qAwwcw0KC546sUEnX86GIT8CcMLZC4mG0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83 h1:4v14bwSGZH2usyuG9XWZgMbGkVU33ayg0cb68nvKfj0=
+github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83/go.mod h1:yTr3BEYSFe9As6XM7ldyrVgqsPwlnw8Ahc4N28VFM2g=
+github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.1.0-rc.1 h1:VK3aeRXMI8osaS6YCDKNZhU6RKtcP3B2wzqxOogNDz8=
+github.com/gobwas/ws v1.1.0-rc.1/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.5 h1:obHEce3upls1IBn1gTw/o7bCv7OJb6Ib/o7wNO+4eKw=
+github.com/nxadm/tail v1.4.5/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
github.com/ossrs/go-oryx-lib v0.0.9 h1:piZkzit/1hqAcXP31/mvDEDpHVjCmBMmvzF3hN8hUuQ=
github.com/ossrs/go-oryx-lib v0.0.9/go.mod h1:i2tH4TZBzAw5h+HwGrNOKvP/nmZgSQz0OEnLLdzcT/8=
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
@@ -74,13 +100,28 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
+github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 h1:hNna6Fi0eP1f2sMBe/rJicDmaHmoXGe1Ta84FPYHLuE=
+github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5/go.mod h1:f1SCnEOt6sc3fOJfPQDRDzHOtSXuTtnz0ImG9kPRDV0=
+github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
+github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
+github.com/yapingcat/gomedia/codec v0.0.0-20220609081842-9e0c0e8a19a0/go.mod h1:obSECV6X3NPUsLL0olA7DurvQHKMq7J3iBTNQ4bL/vQ=
+github.com/yapingcat/gomedia/codec v0.0.0-20220617074658-94762898dc25 h1:1mq/skGEQGCqxHJPKfontELt/a052Gu236H0bge0Qr0=
+github.com/yapingcat/gomedia/codec v0.0.0-20220617074658-94762898dc25/go.mod h1:obSECV6X3NPUsLL0olA7DurvQHKMq7J3iBTNQ4bL/vQ=
+github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25 h1:51qjqT2jsOESm/jDi0k0AdQX33Sg4vhw8X6eooj7c8A=
+github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25/go.mod h1:bvxj2Oi5Rwj7eHm2OjqgOIs8x2T0j+V068eS/SAyZLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -95,12 +136,15 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -108,12 +152,16 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201214095126-aec9a390925b h1:tv7/y4pd+sR8bcNb2D6o7BNU6zjWm0VjQLac+w7fNNM=
+golang.org/x/sys v0.0.0-20201214095126-aec9a390925b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -124,14 +172,17 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/trunk/3rdparty/srs-bench/janus/janus.go b/trunk/3rdparty/srs-bench/janus/janus.go
index ee98e573bc..59fbe97a7e 100644
--- a/trunk/3rdparty/srs-bench/janus/janus.go
+++ b/trunk/3rdparty/srs-bench/janus/janus.go
@@ -46,7 +46,7 @@ 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(&sfu, "sfu", "srs", "The SFU server, srs or gb28181 or janus")
fl.StringVar(&sr, "sr", "", "")
fl.IntVar(&pli, "pli", 10, "")
@@ -66,7 +66,7 @@ func Parse(ctx context.Context) {
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(" -sfu The target SFU, srs or gb28181 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"))
@@ -77,7 +77,7 @@ func Parse(ctx context.Context) {
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(" -fps [Optional] 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个推流:"))
diff --git a/trunk/3rdparty/srs-bench/main.go b/trunk/3rdparty/srs-bench/main.go
index 8d9978c57d..b936e21d40 100644
--- a/trunk/3rdparty/srs-bench/main.go
+++ b/trunk/3rdparty/srs-bench/main.go
@@ -25,6 +25,7 @@ import (
"flag"
"fmt"
"github.com/ossrs/go-oryx-lib/logger"
+ "github.com/ossrs/srs-bench/gb28181"
"github.com/ossrs/srs-bench/janus"
"github.com/ossrs/srs-bench/srs"
"io/ioutil"
@@ -37,18 +38,21 @@ func main() {
var sfu string
fl := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
fl.SetOutput(ioutil.Discard)
- fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or janus")
+ fl.StringVar(&sfu, "sfu", "srs", "The SFU server, srs or gb28181 or janus")
_ = fl.Parse(os.Args[1:])
ctx := context.Background()
+ var conf interface{}
if sfu == "srs" {
srs.Parse(ctx)
+ } else if sfu == "gb28181" {
+ conf = gb28181.Parse(ctx)
} else if sfu == "janus" {
janus.Parse(ctx)
} else {
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(" -sfu The target SFU, srs or gb28181 or janus. Default: srs"))
os.Exit(-1)
}
@@ -65,6 +69,8 @@ func main() {
var err error
if sfu == "srs" {
err = srs.Run(ctx)
+ } else if sfu == "gb28181" {
+ err = gb28181.Run(ctx, conf)
} else if sfu == "janus" {
err = janus.Run(ctx)
}
diff --git a/trunk/3rdparty/srs-bench/srs/srs.go b/trunk/3rdparty/srs-bench/srs/srs.go
index 91597259a4..c0c80c50b4 100644
--- a/trunk/3rdparty/srs-bench/srs/srs.go
+++ b/trunk/3rdparty/srs-bench/srs/srs.go
@@ -50,7 +50,7 @@ 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(&sfu, "sfu", "srs", "The SFU server, srs or gb28181 or janus")
fl.StringVar(&sr, "sr", "", "")
fl.StringVar(&dumpAudio, "da", "", "")
@@ -74,7 +74,7 @@ func Parse(ctx context.Context) {
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(" -sfu The target SFU, srs or gb28181 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"))
@@ -88,7 +88,7 @@ func Parse(ctx context.Context) {
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(" -fps [Optional] 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个推流:"))
diff --git a/trunk/3rdparty/srs-bench/srs/util.go b/trunk/3rdparty/srs-bench/srs/util.go
index b6543902e5..cf3d5a6e5f 100644
--- a/trunk/3rdparty/srs-bench/srs/util.go
+++ b/trunk/3rdparty/srs-bench/srs/util.go
@@ -73,9 +73,7 @@ var srsPublishAvatar *string
var srsPublishBBB *string
var srsVnetClientIP *string
-func prepareTest() error {
- var err error
-
+func prepareTest() (err error) {
srsHttps = flag.Bool("srs-https", false, "Whther connect to HTTPS-API")
srsServer = flag.String("srs-server", "127.0.0.1", "The RTC server to connect to")
srsStream = flag.String("srs-stream", "/rtc/regression", "The RTC app/stream to play")
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/LICENSE
new file mode 100644
index 0000000000..a4f29bf588
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/LICENSE
@@ -0,0 +1,25 @@
+BSD 2-Clause License
+
+Copyright (c) 2017, The GoSIP authors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/log.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/log.go
new file mode 100644
index 0000000000..e5d41bbcbb
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/log.go
@@ -0,0 +1,85 @@
+package log
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Logger interface used as base logger throughout the library.
+type Logger interface {
+ Print(args ...interface{})
+ Printf(format string, args ...interface{})
+
+ Trace(args ...interface{})
+ Tracef(format string, args ...interface{})
+
+ Debug(args ...interface{})
+ Debugf(format string, args ...interface{})
+
+ Info(args ...interface{})
+ Infof(format string, args ...interface{})
+
+ Warn(args ...interface{})
+ Warnf(format string, args ...interface{})
+
+ Error(args ...interface{})
+ Errorf(format string, args ...interface{})
+
+ Fatal(args ...interface{})
+ Fatalf(format string, args ...interface{})
+
+ Panic(args ...interface{})
+ Panicf(format string, args ...interface{})
+
+ WithPrefix(prefix string) Logger
+ Prefix() string
+
+ WithFields(fields Fields) Logger
+ Fields() Fields
+
+ SetLevel(level Level)
+}
+
+type Loggable interface {
+ Log() Logger
+}
+
+type Fields map[string]interface{}
+
+func (fields Fields) String() string {
+ str := make([]string, 0)
+
+ for k, v := range fields {
+ str = append(str, fmt.Sprintf("%s=%+v", k, v))
+ }
+
+ return strings.Join(str, " ")
+}
+
+func (fields Fields) WithFields(newFields Fields) Fields {
+ allFields := make(Fields)
+
+ for k, v := range fields {
+ allFields[k] = v
+ }
+
+ for k, v := range newFields {
+ allFields[k] = v
+ }
+
+ return allFields
+}
+
+func AddFieldsFrom(logger Logger, values ...interface{}) Logger {
+ for _, value := range values {
+ switch v := value.(type) {
+ case Logger:
+ logger = logger.WithFields(v.Fields())
+ case Loggable:
+ logger = logger.WithFields(v.Log().Fields())
+ case interface{ Fields() Fields }:
+ logger = logger.WithFields(v.Fields())
+ }
+ }
+ return logger
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/logrus.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/logrus.go
new file mode 100644
index 0000000000..356d2dfd61
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/log/logrus.go
@@ -0,0 +1,148 @@
+package log
+
+import (
+ "github.com/sirupsen/logrus"
+ prefixed "github.com/x-cray/logrus-prefixed-formatter"
+)
+
+type LogrusLogger struct {
+ log logrus.Ext1FieldLogger
+ prefix string
+ fields Fields
+}
+
+// Level type
+type Level uint32
+
+// These are the different logging levels. You can set the logging level to log
+// on your instance of logger, obtained with `logrus.New()`.
+const (
+ // PanicLevel level, highest level of severity. Logs and then calls panic with the
+ // message passed to Debug, Info, ...
+ PanicLevel Level = iota
+ // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
+ // logging level is set to Panic.
+ FatalLevel
+ // ErrorLevel level. Logs. Used for errors that should definitely be noted.
+ // Commonly used for hooks to send errors to an error tracking service.
+ ErrorLevel
+ // WarnLevel level. Non-critical entries that deserve eyes.
+ WarnLevel
+ // InfoLevel level. General operational entries about what's going on inside the
+ // application.
+ InfoLevel
+ // DebugLevel level. Usually only enabled when debugging. Very verbose logging.
+ DebugLevel
+ // TraceLevel level. Designates finer-grained informational events than the Debug.
+ TraceLevel
+)
+
+func NewLogrusLogger(logrus logrus.Ext1FieldLogger, prefix string, fields Fields) *LogrusLogger {
+ return &LogrusLogger{
+ log: logrus,
+ prefix: prefix,
+ fields: fields,
+ }
+}
+
+func NewDefaultLogrusLogger() *LogrusLogger {
+ logger := logrus.New()
+ logger.Formatter = &prefixed.TextFormatter{
+ FullTimestamp: true,
+ TimestampFormat: "2006-01-02 15:04:05.000",
+ }
+
+ return NewLogrusLogger(logger, "main", nil)
+}
+
+func (l *LogrusLogger) Print(args ...interface{}) {
+ l.prepareEntry().Print(args...)
+}
+
+func (l *LogrusLogger) Printf(format string, args ...interface{}) {
+ l.prepareEntry().Printf(format, args...)
+}
+
+func (l *LogrusLogger) Trace(args ...interface{}) {
+ l.prepareEntry().Trace(args...)
+}
+
+func (l *LogrusLogger) Tracef(format string, args ...interface{}) {
+ l.prepareEntry().Tracef(format, args...)
+}
+
+func (l *LogrusLogger) Debug(args ...interface{}) {
+ l.prepareEntry().Debug(args...)
+}
+
+func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
+ l.prepareEntry().Debugf(format, args...)
+}
+
+func (l *LogrusLogger) Info(args ...interface{}) {
+ l.prepareEntry().Info(args...)
+}
+
+func (l *LogrusLogger) Infof(format string, args ...interface{}) {
+ l.prepareEntry().Infof(format, args...)
+}
+
+func (l *LogrusLogger) Warn(args ...interface{}) {
+ l.prepareEntry().Warn(args...)
+}
+
+func (l *LogrusLogger) Warnf(format string, args ...interface{}) {
+ l.prepareEntry().Warnf(format, args...)
+}
+
+func (l *LogrusLogger) Error(args ...interface{}) {
+ l.prepareEntry().Error(args...)
+}
+
+func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
+ l.prepareEntry().Errorf(format, args...)
+}
+
+func (l *LogrusLogger) Fatal(args ...interface{}) {
+ l.prepareEntry().Fatal(args...)
+}
+
+func (l *LogrusLogger) Fatalf(format string, args ...interface{}) {
+ l.prepareEntry().Fatalf(format, args...)
+}
+
+func (l *LogrusLogger) Panic(args ...interface{}) {
+ l.prepareEntry().Panic(args...)
+}
+
+func (l *LogrusLogger) Panicf(format string, args ...interface{}) {
+ l.prepareEntry().Panicf(format, args...)
+}
+
+func (l *LogrusLogger) WithPrefix(prefix string) Logger {
+ return NewLogrusLogger(l.log, prefix, l.Fields())
+}
+
+func (l *LogrusLogger) Prefix() string {
+ return l.prefix
+}
+
+func (l *LogrusLogger) WithFields(fields Fields) Logger {
+ return NewLogrusLogger(l.log, l.Prefix(), l.Fields().WithFields(fields))
+}
+
+func (l *LogrusLogger) Fields() Fields {
+ return l.fields
+}
+
+func (l *LogrusLogger) prepareEntry() *logrus.Entry {
+ return l.log.
+ WithFields(logrus.Fields(l.Fields())).
+ WithField("prefix", l.Prefix())
+}
+
+func (l *LogrusLogger) SetLevel(level Level) {
+ if ll, ok := l.log.(*logrus.Logger); ok {
+ ll.SetLevel(logrus.Level(level))
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/auth.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/auth.go
new file mode 100644
index 0000000000..f83ae9ddfe
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/auth.go
@@ -0,0 +1,279 @@
+package sip
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+// currently only Digest and MD5
+type Authorization struct {
+ realm string
+ nonce string
+ algorithm string
+ username string
+ password string
+ uri string
+ response string
+ method string
+ qop string
+ nc string
+ cnonce string
+ other map[string]string
+}
+
+func AuthFromValue(value string) *Authorization {
+ auth := &Authorization{
+ algorithm: "MD5",
+ other: make(map[string]string),
+ }
+
+ re := regexp.MustCompile(`([\w]+)="([^"]+)"`)
+ matches := re.FindAllStringSubmatch(value, -1)
+ for _, match := range matches {
+ switch match[1] {
+ case "realm":
+ auth.realm = match[2]
+ case "algorithm":
+ auth.algorithm = match[2]
+ case "nonce":
+ auth.nonce = match[2]
+ case "username":
+ auth.username = match[2]
+ case "uri":
+ auth.uri = match[2]
+ case "response":
+ auth.response = match[2]
+ case "qop":
+ for _, v := range strings.Split(match[2], ",") {
+ v = strings.Trim(v, " ")
+ if v == "auth" || v == "auth-int" {
+ auth.qop = "auth"
+ break
+ }
+ }
+ case "nc":
+ auth.nc = match[2]
+ case "cnonce":
+ auth.cnonce = match[2]
+ default:
+ auth.other[match[1]] = match[2]
+ }
+ }
+
+ return auth
+}
+
+func (auth *Authorization) Realm() string {
+ return auth.realm
+}
+
+func (auth *Authorization) Nonce() string {
+ return auth.nonce
+}
+
+func (auth *Authorization) Algorithm() string {
+ return auth.algorithm
+}
+
+func (auth *Authorization) Username() string {
+ return auth.username
+}
+
+func (auth *Authorization) SetUsername(username string) *Authorization {
+ auth.username = username
+
+ return auth
+}
+
+func (auth *Authorization) SetPassword(password string) *Authorization {
+ auth.password = password
+
+ return auth
+}
+
+func (auth *Authorization) Uri() string {
+ return auth.uri
+}
+
+func (auth *Authorization) SetUri(uri string) *Authorization {
+ auth.uri = uri
+
+ return auth
+}
+
+func (auth *Authorization) SetMethod(method string) *Authorization {
+ auth.method = method
+
+ return auth
+}
+
+func (auth *Authorization) Response() string {
+ return auth.response
+}
+
+func (auth *Authorization) SetResponse(response string) {
+ auth.response = response
+}
+
+func (auth *Authorization) Qop() string {
+ return auth.qop
+}
+
+func (auth *Authorization) SetQop(qop string) {
+ auth.qop = qop
+}
+
+func (auth *Authorization) Nc() string {
+ return auth.nc
+}
+
+func (auth *Authorization) SetNc(nc string) {
+ auth.nc = nc
+}
+
+func (auth *Authorization) CNonce() string {
+ return auth.cnonce
+}
+
+func (auth *Authorization) SetCNonce(cnonce string) {
+ auth.cnonce = cnonce
+}
+
+func (auth *Authorization) CalcResponse() string {
+ return calcResponse(
+ auth.username,
+ auth.realm,
+ auth.password,
+ auth.method,
+ auth.uri,
+ auth.nonce,
+ auth.qop,
+ auth.cnonce,
+ auth.nc,
+ )
+}
+
+func (auth *Authorization) String() string {
+ if auth == nil {
+ return ""
+ }
+
+ str := fmt.Sprintf(
+ `Digest realm="%s",algorithm=%s,nonce="%s",username="%s",uri="%s",response="%s"`,
+ auth.realm,
+ auth.algorithm,
+ auth.nonce,
+ auth.username,
+ auth.uri,
+ auth.response,
+ )
+ if auth.qop == "auth" {
+ str += fmt.Sprintf(`,qop=%s,nc=%s,cnonce="%s"`, auth.qop, auth.nc, auth.cnonce)
+ }
+
+ return str
+}
+
+// calculates Authorization response https://www.ietf.org/rfc/rfc2617.txt
+func calcResponse(username, realm, password, method, uri, nonce, qop, cnonce, nc string) string {
+ calcA1 := func() string {
+ encoder := md5.New()
+ encoder.Write([]byte(username + ":" + realm + ":" + password))
+
+ return hex.EncodeToString(encoder.Sum(nil))
+ }
+ calcA2 := func() string {
+ encoder := md5.New()
+ encoder.Write([]byte(method + ":" + uri))
+
+ return hex.EncodeToString(encoder.Sum(nil))
+ }
+
+ encoder := md5.New()
+ encoder.Write([]byte(calcA1() + ":" + nonce + ":"))
+ if qop != "" {
+ encoder.Write([]byte(nc + ":" + cnonce + ":" + qop + ":"))
+ }
+ encoder.Write([]byte(calcA2()))
+
+ return hex.EncodeToString(encoder.Sum(nil))
+}
+
+func AuthorizeRequest(request Request, response Response, user, password MaybeString) error {
+ if user == nil {
+ return fmt.Errorf("authorize request: user is nil")
+ }
+
+ var authenticateHeaderName, authorizeHeaderName string
+ if response.StatusCode() == 401 {
+ // on 401 Unauthorized increase request seq num, add Authorization header and send once again
+ authenticateHeaderName = "WWW-Authenticate"
+ authorizeHeaderName = "Authorization"
+ } else {
+ // 407 Proxy authentication
+ authenticateHeaderName = "Proxy-Authenticate"
+ authorizeHeaderName = "Proxy-Authorization"
+ }
+
+ if hdrs := response.GetHeaders(authenticateHeaderName); len(hdrs) > 0 {
+ authenticateHeader := hdrs[0].(*GenericHeader)
+ auth := AuthFromValue(authenticateHeader.Contents).
+ SetMethod(string(request.Method())).
+ SetUri(request.Recipient().String()).
+ SetUsername(user.String())
+ if password != nil {
+ auth.SetPassword(password.String())
+ }
+ if auth.Qop() == "auth" {
+ auth.SetNc("00000001")
+ encoder := md5.New()
+ encoder.Write([]byte(user.String() + request.Recipient().String()))
+ if password != nil {
+ encoder.Write([]byte(password.String()))
+ }
+ auth.SetCNonce(hex.EncodeToString(encoder.Sum(nil)))
+ }
+ auth.SetResponse(auth.CalcResponse())
+
+ if hdrs = request.GetHeaders(authorizeHeaderName); len(hdrs) > 0 {
+ authorizationHeader := hdrs[0].Clone().(*GenericHeader)
+ authorizationHeader.Contents = auth.String()
+ request.ReplaceHeaders(authorizationHeader.Name(), []Header{authorizationHeader})
+ } else {
+ request.AppendHeader(&GenericHeader{
+ HeaderName: authorizeHeaderName,
+ Contents: auth.String(),
+ })
+ }
+ } else {
+ return fmt.Errorf("authorize request: header '%s' not found in response", authenticateHeaderName)
+ }
+
+ if viaHop, ok := request.ViaHop(); ok {
+ viaHop.Params.Add("branch", String{Str: GenerateBranch()})
+ }
+
+ if cseq, ok := request.CSeq(); ok {
+ cseq := cseq.Clone().(*CSeq)
+ cseq.SeqNo++
+ request.ReplaceHeaders(cseq.Name(), []Header{cseq})
+ }
+
+ return nil
+}
+
+type Authorizer interface {
+ AuthorizeRequest(request Request, response Response) error
+}
+
+type DefaultAuthorizer struct {
+ User MaybeString
+ Password MaybeString
+}
+
+func (auth *DefaultAuthorizer) AuthorizeRequest(request Request, response Response) error {
+ return AuthorizeRequest(request, response, auth.User, auth.Password)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/builder.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/builder.go
new file mode 100644
index 0000000000..473afe8b30
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/builder.go
@@ -0,0 +1,336 @@
+package sip
+
+import (
+ "fmt"
+
+ "github.com/ghettovoice/gosip/util"
+)
+
+type RequestBuilder struct {
+ protocol string
+ protocolVersion string
+ transport string
+ host string
+ method RequestMethod
+ cseq *CSeq
+ recipient Uri
+ body string
+ callID *CallID
+ via ViaHeader
+ from *FromHeader
+ to *ToHeader
+ contact *ContactHeader
+ expires *Expires
+ userAgent *UserAgentHeader
+ maxForwards *MaxForwards
+ supported *SupportedHeader
+ require *RequireHeader
+ allow AllowHeader
+ contentType *ContentType
+ accept *Accept
+ route *RouteHeader
+ generic map[string]Header
+}
+
+func NewRequestBuilder() *RequestBuilder {
+ callID := CallID(util.RandString(32))
+ maxForwards := MaxForwards(70)
+ userAgent := UserAgentHeader("GoSIP")
+ rb := &RequestBuilder{
+ protocol: "SIP",
+ protocolVersion: "2.0",
+ transport: "UDP",
+ host: "localhost",
+ cseq: &CSeq{SeqNo: 1},
+ body: "",
+ via: make(ViaHeader, 0),
+ callID: &callID,
+ userAgent: &userAgent,
+ maxForwards: &maxForwards,
+ generic: make(map[string]Header),
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetTransport(transport string) *RequestBuilder {
+ if transport == "" {
+ rb.transport = "UDP"
+ } else {
+ rb.transport = transport
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetHost(host string) *RequestBuilder {
+ if host == "" {
+ rb.host = "localhost"
+ } else {
+ rb.host = host
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetMethod(method RequestMethod) *RequestBuilder {
+ rb.method = method
+ rb.cseq.MethodName = method
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetSeqNo(seqNo uint) *RequestBuilder {
+ rb.cseq.SeqNo = uint32(seqNo)
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetRecipient(uri Uri) *RequestBuilder {
+ rb.recipient = uri.Clone()
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetBody(body string) *RequestBuilder {
+ rb.body = body
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetCallID(callID *CallID) *RequestBuilder {
+ if callID != nil {
+ rb.callID = callID
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) AddVia(via *ViaHop) *RequestBuilder {
+ if via.ProtocolName == "" {
+ via.ProtocolName = rb.protocol
+ }
+ if via.ProtocolVersion == "" {
+ via.ProtocolVersion = rb.protocolVersion
+ }
+ if via.Transport == "" {
+ via.Transport = rb.transport
+ }
+ if via.Host == "" {
+ via.Host = rb.host
+ }
+ if via.Params == nil {
+ via.Params = NewParams()
+ }
+
+ rb.via = append(rb.via, via)
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetFrom(address *Address) *RequestBuilder {
+ if address == nil {
+ rb.from = nil
+ } else {
+ address = address.Clone()
+ if address.Uri.Host() == "" {
+ address.Uri.SetHost(rb.host)
+ }
+ rb.from = &FromHeader{
+ DisplayName: address.DisplayName,
+ Address: address.Uri,
+ Params: address.Params,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetTo(address *Address) *RequestBuilder {
+ if address == nil {
+ rb.to = nil
+ } else {
+ address = address.Clone()
+ if address.Uri.Host() == "" {
+ address.Uri.SetHost(rb.host)
+ }
+ rb.to = &ToHeader{
+ DisplayName: address.DisplayName,
+ Address: address.Uri,
+ Params: address.Params,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetContact(address *Address) *RequestBuilder {
+ if address == nil {
+ rb.contact = nil
+ } else {
+ address = address.Clone()
+ if address.Uri.Host() == "" {
+ address.Uri.SetHost(rb.host)
+ }
+ rb.contact = &ContactHeader{
+ DisplayName: address.DisplayName,
+ Address: address.Uri,
+ Params: address.Params,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetExpires(expires *Expires) *RequestBuilder {
+ rb.expires = expires
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetUserAgent(userAgent *UserAgentHeader) *RequestBuilder {
+ rb.userAgent = userAgent
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetMaxForwards(maxForwards *MaxForwards) *RequestBuilder {
+ rb.maxForwards = maxForwards
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetAllow(methods []RequestMethod) *RequestBuilder {
+ rb.allow = methods
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetSupported(options []string) *RequestBuilder {
+ if len(options) == 0 {
+ rb.supported = nil
+ } else {
+ rb.supported = &SupportedHeader{
+ Options: options,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetRequire(options []string) *RequestBuilder {
+ if len(options) == 0 {
+ rb.require = nil
+ } else {
+ rb.require = &RequireHeader{
+ Options: options,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetContentType(contentType *ContentType) *RequestBuilder {
+ rb.contentType = contentType
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetAccept(accept *Accept) *RequestBuilder {
+ rb.accept = accept
+
+ return rb
+}
+
+func (rb *RequestBuilder) SetRoutes(routes []Uri) *RequestBuilder {
+ if len(routes) == 0 {
+ rb.route = nil
+ } else {
+ rb.route = &RouteHeader{
+ Addresses: routes,
+ }
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) AddHeader(header Header) *RequestBuilder {
+ rb.generic[header.Name()] = header
+
+ return rb
+}
+
+func (rb *RequestBuilder) RemoveHeader(headerName string) *RequestBuilder {
+ if _, ok := rb.generic[headerName]; ok {
+ delete(rb.generic, headerName)
+ }
+
+ return rb
+}
+
+func (rb *RequestBuilder) Build() (Request, error) {
+ if rb.method == "" {
+ return nil, fmt.Errorf("undefined method name")
+ }
+ if rb.recipient == nil {
+ return nil, fmt.Errorf("empty recipient")
+ }
+ if rb.from == nil {
+ return nil, fmt.Errorf("empty 'From' header")
+ }
+ if rb.to == nil {
+ return nil, fmt.Errorf("empty 'From' header")
+ }
+
+ hdrs := make([]Header, 0)
+
+ if rb.route != nil {
+ hdrs = append(hdrs, rb.route)
+ }
+
+ if len(rb.via) != 0 {
+ via := make(ViaHeader, 0)
+ for _, viaHop := range rb.via {
+ via = append(via, viaHop)
+ }
+ hdrs = append(hdrs, via)
+ }
+
+ hdrs = append(hdrs, rb.cseq, rb.from, rb.to, rb.callID)
+
+ if rb.contact != nil {
+ hdrs = append(hdrs, rb.contact)
+ }
+ if rb.maxForwards != nil {
+ hdrs = append(hdrs, rb.maxForwards)
+ }
+ if rb.expires != nil {
+ hdrs = append(hdrs, rb.expires)
+ }
+ if rb.supported != nil {
+ hdrs = append(hdrs, rb.supported)
+ }
+ if rb.allow != nil {
+ hdrs = append(hdrs, rb.allow)
+ }
+ if rb.contentType != nil {
+ hdrs = append(hdrs, rb.contentType)
+ }
+ if rb.accept != nil {
+ hdrs = append(hdrs, rb.accept)
+ }
+ if rb.userAgent != nil {
+ hdrs = append(hdrs, rb.userAgent)
+ }
+
+ for _, header := range rb.generic {
+ hdrs = append(hdrs, header)
+ }
+
+ sipVersion := rb.protocol + "/" + rb.protocolVersion
+ // basic request
+ req := NewRequest("", rb.method, rb.recipient, sipVersion, hdrs, "", nil)
+ req.SetBody(rb.body, true)
+
+ return req, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/core.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/core.go
new file mode 100644
index 0000000000..582790f73b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/core.go
@@ -0,0 +1,412 @@
+package sip
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+
+ "github.com/ghettovoice/gosip/util"
+)
+
+const (
+ MTU uint = 1500
+
+ DefaultHost = "127.0.0.1"
+ DefaultProtocol = "UDP"
+
+ DefaultUdpPort Port = 5060
+ DefaultTcpPort Port = 5060
+ DefaultTlsPort Port = 5061
+ DefaultWsPort Port = 80
+ DefaultWssPort Port = 443
+)
+
+// TODO should be refactored, currently here the pit
+
+type Address struct {
+ DisplayName MaybeString
+ Uri Uri
+ Params Params
+}
+
+func NewAddressFromFromHeader(from *FromHeader) *Address {
+ addr := &Address{
+ DisplayName: from.DisplayName,
+ }
+ if from.Address != nil {
+ addr.Uri = from.Address.Clone()
+ }
+ if from.Params != nil {
+ addr.Params = from.Params.Clone()
+ }
+
+ return addr
+}
+
+func NewAddressFromToHeader(to *ToHeader) *Address {
+ addr := &Address{
+ DisplayName: to.DisplayName,
+ }
+ if to.Address != nil {
+ addr.Uri = to.Address.Clone()
+ }
+ if to.Params != nil {
+ addr.Params = to.Params.Clone()
+ }
+
+ return addr
+}
+
+func NewAddressFromContactHeader(cnt *ContactHeader) *Address {
+ addr := &Address{
+ DisplayName: cnt.DisplayName,
+ }
+ if cnt.Address != nil {
+ addr.Uri = cnt.Address.Clone()
+ }
+ if cnt.Params != nil {
+ addr.Params = cnt.Params.Clone()
+ }
+
+ return addr
+}
+
+func (addr *Address) String() string {
+ var buffer bytes.Buffer
+
+ if addr == nil {
+ return ""
+ }
+
+ if addr.DisplayName != nil {
+ if displayName, ok := addr.DisplayName.(String); ok && displayName.String() != "" {
+ buffer.WriteString(fmt.Sprintf("\"%s\" ", displayName))
+ }
+ }
+
+ buffer.WriteString(fmt.Sprintf("<%s>", addr.Uri))
+
+ if addr.Params != nil && addr.Params.Length() > 0 {
+ buffer.WriteString(";")
+ buffer.WriteString(addr.Params.ToString(';'))
+ }
+
+ return buffer.String()
+}
+
+func (addr *Address) Clone() *Address {
+ var name MaybeString
+ var uri Uri
+ var params Params
+
+ if addr.DisplayName != nil {
+ name = String{Str: addr.DisplayName.String()}
+ }
+ if addr.Uri != nil {
+ uri = addr.Uri.Clone()
+ }
+ if addr.Params != nil {
+ params = addr.Params.Clone()
+ }
+
+ return &Address{
+ DisplayName: name,
+ Uri: uri,
+ Params: params,
+ }
+}
+
+func (addr *Address) Equals(other interface{}) bool {
+ otherPtr, ok := other.(*Address)
+ if !ok {
+ return false
+ }
+
+ if addr == otherPtr {
+ return true
+ }
+ if addr == nil && otherPtr != nil || addr != nil && otherPtr == nil {
+ return false
+ }
+
+ res := true
+
+ if addr.DisplayName != otherPtr.DisplayName {
+ if addr.DisplayName == nil {
+ res = res && otherPtr.DisplayName == nil
+ } else {
+ res = res && addr.DisplayName.Equals(otherPtr.DisplayName)
+ }
+ }
+
+ if addr.Uri != otherPtr.Uri {
+ if addr.Uri == nil {
+ res = res && otherPtr.Uri == nil
+ } else {
+ res = res && addr.Uri.Equals(otherPtr.Uri)
+ }
+ }
+
+ if addr.Params != otherPtr.Params {
+ if addr.Params == nil {
+ res = res && otherPtr.Params == nil
+ } else {
+ res = res && addr.Params.Equals(otherPtr.Params)
+ }
+ }
+
+ return res
+}
+
+func (addr *Address) AsToHeader() *ToHeader {
+ to := &ToHeader{
+ DisplayName: addr.DisplayName,
+ }
+ if addr.Uri != nil {
+ to.Address = addr.Uri.Clone()
+ }
+ if addr.Params != nil {
+ to.Params = addr.Params.Clone()
+ }
+
+ return to
+}
+
+func (addr *Address) AsFromHeader() *FromHeader {
+ from := &FromHeader{
+ DisplayName: addr.DisplayName,
+ }
+ if addr.Uri != nil {
+ from.Address = addr.Uri.Clone()
+ }
+ if addr.Params != nil {
+ from.Params = addr.Params.Clone()
+ }
+
+ return from
+}
+
+func (addr *Address) AsContactHeader() *ContactHeader {
+ cnt := &ContactHeader{
+ DisplayName: addr.DisplayName,
+ }
+ if addr.Uri != nil {
+ cnt.Address = addr.Uri.Clone()
+ }
+ if addr.Params != nil {
+ cnt.Params = addr.Params.Clone()
+ }
+
+ return cnt
+}
+
+// Port number
+type Port uint16
+
+func (port *Port) Clone() *Port {
+ if port == nil {
+ return nil
+ }
+ newPort := *port
+ return &newPort
+}
+
+func (port *Port) String() string {
+ if port == nil {
+ return ""
+ }
+ return fmt.Sprintf("%d", *port)
+}
+
+func (port *Port) Equals(other interface{}) bool {
+ if p, ok := other.(*Port); ok {
+ return util.Uint16PtrEq((*uint16)(port), (*uint16)(p))
+ }
+
+ return false
+}
+
+// String wrapper
+type MaybeString interface {
+ String() string
+ Equals(other interface{}) bool
+}
+
+type String struct {
+ Str string
+}
+
+func (str String) String() string {
+ return str.Str
+}
+
+func (str String) Equals(other interface{}) bool {
+ if v, ok := other.(string); ok {
+ return str.Str == v
+ }
+ if v, ok := other.(String); ok {
+ return str.Str == v.Str
+ }
+
+ return false
+}
+
+type CancelError interface {
+ Canceled() bool
+}
+
+type ExpireError interface {
+ Expired() bool
+}
+
+type MessageError interface {
+ error
+ // Malformed indicates that message is syntactically valid but has invalid headers, or
+ // without required headers.
+ Malformed() bool
+ // Broken or incomplete message, or not a SIP message
+ Broken() bool
+}
+
+// Broken or incomplete messages, or not a SIP message.
+type BrokenMessageError struct {
+ Err error
+ Msg string
+}
+
+func (err *BrokenMessageError) Malformed() bool { return false }
+func (err *BrokenMessageError) Broken() bool { return true }
+func (err *BrokenMessageError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ s := "BrokenMessageError: " + err.Err.Error()
+ if err.Msg != "" {
+ s += fmt.Sprintf("\nMessage dump:\n%s", err.Msg)
+ }
+
+ return s
+}
+
+// syntactically valid but logically invalid message
+type MalformedMessageError struct {
+ Err error
+ Msg string
+}
+
+func (err *MalformedMessageError) Malformed() bool { return true }
+func (err *MalformedMessageError) Broken() bool { return false }
+func (err *MalformedMessageError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ s := "MalformedMessageError: " + err.Err.Error()
+ if err.Msg != "" {
+ s += fmt.Sprintf("\nMessage dump:\n%s", err.Msg)
+ }
+
+ return s
+}
+
+type UnsupportedMessageError struct {
+ Err error
+ Msg string
+}
+
+func (err *UnsupportedMessageError) Malformed() bool { return true }
+func (err *UnsupportedMessageError) Broken() bool { return false }
+func (err *UnsupportedMessageError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ s := "UnsupportedMessageError: " + err.Err.Error()
+ if err.Msg != "" {
+ s += fmt.Sprintf("\nMessage dump:\n%s", err.Msg)
+ }
+
+ return s
+}
+
+type UnexpectedMessageError struct {
+ Err error
+ Msg string
+}
+
+func (err *UnexpectedMessageError) Broken() bool { return false }
+func (err *UnexpectedMessageError) Malformed() bool { return false }
+func (err *UnexpectedMessageError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ s := "UnexpectedMessageError: " + err.Err.Error()
+ if err.Msg != "" {
+ s += fmt.Sprintf("\nMessage dump:\n%s", err.Msg)
+ }
+
+ return s
+}
+
+const RFC3261BranchMagicCookie = "z9hG4bK"
+
+// GenerateBranch returns random unique branch ID.
+func GenerateBranch() string {
+ return strings.Join([]string{
+ RFC3261BranchMagicCookie,
+ util.RandString(32),
+ }, ".")
+}
+
+// DefaultPort returns protocol default port by network.
+func DefaultPort(protocol string) Port {
+ switch strings.ToLower(protocol) {
+ case "tls":
+ return DefaultTlsPort
+ case "tcp":
+ return DefaultTcpPort
+ case "udp":
+ return DefaultUdpPort
+ case "ws":
+ return DefaultWsPort
+ case "wss":
+ return DefaultWssPort
+ default:
+ return DefaultTcpPort
+ }
+}
+
+func MakeDialogIDFromMessage(msg Message) (string, error) {
+ callID, ok := msg.CallID()
+ if !ok {
+ return "", fmt.Errorf("missing Call-ID header")
+ }
+
+ to, ok := msg.To()
+ if !ok {
+ return "", fmt.Errorf("missing To header")
+ }
+
+ toTag, ok := to.Params.Get("tag")
+ if !ok {
+ return "", fmt.Errorf("missing tag param in To header")
+ }
+
+ from, ok := msg.From()
+ if !ok {
+ return "", fmt.Errorf("missing To header")
+ }
+
+ fromTag, ok := from.Params.Get("tag")
+ if !ok {
+ return "", fmt.Errorf("missing tag param in From header")
+ }
+
+ return MakeDialogID(string(*callID), toTag.String(), fromTag.String()), nil
+}
+
+func MakeDialogID(callID, innerID, externalID string) string {
+ return strings.Join([]string{callID, innerID, externalID}, "__")
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/error.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/error.go
new file mode 100644
index 0000000000..ec50ffe2ec
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/error.go
@@ -0,0 +1,37 @@
+package sip
+
+import "fmt"
+
+type RequestError struct {
+ Request Request
+ Response Response
+ Code uint
+ Reason string
+}
+
+func NewRequestError(code uint, reason string, request Request, response Response) *RequestError {
+ err := &RequestError{
+ Code: code,
+ Reason: reason,
+ }
+ if request != nil {
+ err.Request = CopyRequest(request)
+ }
+ if response != nil {
+ err.Response = CopyResponse(response)
+ }
+ return err
+}
+
+func (err *RequestError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ reason := err.Reason
+ if err.Code != 0 {
+ reason += fmt.Sprintf(" (Code %d)", err.Code)
+ }
+
+ return fmt.Sprintf("sip.RequestError: request failed with reason '%s'", reason)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/escape.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/escape.go
new file mode 100644
index 0000000000..f80fdb3a77
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/escape.go
@@ -0,0 +1,230 @@
+package sip
+
+import (
+ "strconv"
+ "strings"
+)
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// This is actually shorten copy of escape/unescape helpers of the net/url package.
+
+type encoding int
+
+const (
+ EncodeUserPassword encoding = 1 + iota
+ EncodeHost
+ EncodeZone
+ EncodeQueryComponent
+)
+
+type EscapeError string
+
+func (e EscapeError) Error() string {
+ return "invalid URL escape " + strconv.Quote(string(e))
+}
+
+type InvalidHostError string
+
+func (e InvalidHostError) Error() string {
+ return "invalid character " + strconv.Quote(string(e)) + " in host name"
+}
+
+// unescape unescapes a string; the mode specifies
+// which section of the URL string is being unescaped.
+func Unescape(s string, mode encoding) (string, error) {
+ // Count %, check that they're well-formed.
+ n := 0
+ hasPlus := false
+ for i := 0; i < len(s); {
+ switch s[i] {
+ case '%':
+ n++
+ if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
+ s = s[i:]
+ if len(s) > 3 {
+ s = s[:3]
+ }
+ return "", EscapeError(s)
+ }
+ // Per https://tools.ietf.org/html/rfc3986#page-21
+ // in the host component %-encoding can only be used
+ // for non-ASCII bytes.
+ // But https://tools.ietf.org/html/rfc6874#section-2
+ // introduces %25 being allowed to escape a percent sign
+ // in IPv6 scoped-address literals. Yay.
+ if mode == EncodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" {
+ return "", EscapeError(s[i : i+3])
+ }
+ if mode == EncodeZone {
+ // RFC 6874 says basically "anything goes" for zone identifiers
+ // and that even non-ASCII can be redundantly escaped,
+ // but it seems prudent to restrict %-escaped bytes here to those
+ // that are valid host name bytes in their unescaped form.
+ // That is, you can use escaping in the zone identifier but not
+ // to introduce bytes you couldn't just write directly.
+ // But Windows puts spaces here! Yay.
+ v := unhex(s[i+1])<<4 | unhex(s[i+2])
+ if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, EncodeHost) {
+ return "", EscapeError(s[i : i+3])
+ }
+ }
+ i += 3
+ case '+':
+ hasPlus = mode == EncodeQueryComponent
+ i++
+ default:
+ if (mode == EncodeHost || mode == EncodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) {
+ return "", InvalidHostError(s[i : i+1])
+ }
+ i++
+ }
+ }
+
+ if n == 0 && !hasPlus {
+ return s, nil
+ }
+
+ var t strings.Builder
+ t.Grow(len(s) - 2*n)
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '%':
+ t.WriteByte(unhex(s[i+1])<<4 | unhex(s[i+2]))
+ i += 2
+ case '+':
+ t.WriteByte('+')
+ default:
+ t.WriteByte(s[i])
+ }
+ }
+ return t.String(), nil
+}
+
+func ishex(c byte) bool {
+ switch {
+ case '0' <= c && c <= '9':
+ return true
+ case 'a' <= c && c <= 'f':
+ return true
+ case 'A' <= c && c <= 'F':
+ return true
+ }
+ return false
+}
+
+func unhex(c byte) byte {
+ switch {
+ case '0' <= c && c <= '9':
+ return c - '0'
+ case 'a' <= c && c <= 'f':
+ return c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ return c - 'A' + 10
+ }
+ return 0
+}
+
+// Return true if the specified character should be escaped when
+// appearing in a URL string, according to RFC 3986.
+//
+// Please be informed that for now shouldEscape does not check all
+// reserved characters correctly. See golang.org/issue/5684.
+func shouldEscape(c byte, mode encoding) bool {
+ // §2.3 Unreserved characters (alphanum)
+ if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
+ return false
+ }
+
+ if mode == EncodeHost || mode == EncodeZone {
+ // §3.2.2 Host allows
+ // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
+ // as part of reg-name.
+ // We add : because we include :port as part of host.
+ // We add [ ] because we include [ipv6]:port as part of host.
+ // We add < > because they're the only characters left that
+ // we could possibly allow, and Parse will reject them if we
+ // escape them (because hosts can't use %-encoding for
+ // ASCII bytes).
+ switch c {
+ case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
+ return false
+ }
+ }
+
+ switch c {
+ case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
+ return false
+
+ case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
+ // Different sections of the URL allow a few of
+ // the reserved characters to appear unescaped.
+ switch mode {
+ case EncodeUserPassword: // §3.2.1
+ // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
+ // userinfo, so we must escape only '@', '/', and '?'.
+ // The parsing of userinfo treats ':' as special so we must escape
+ // that too.
+ return c == '@' || c == '/' || c == '?' || c == ':'
+
+ case EncodeQueryComponent: // §3.4
+ // The RFC reserves (so we must escape) everything.
+ return true
+ }
+ }
+
+ // Everything else must be escaped.
+ return true
+}
+
+const upperhex = "0123456789ABCDEF"
+
+func Escape(s string, mode encoding) string {
+ spaceCount, hexCount := 0, 0
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ if shouldEscape(c, mode) {
+ if c == ' ' && mode == EncodeQueryComponent {
+ spaceCount++
+ } else {
+ hexCount++
+ }
+ }
+ }
+
+ if spaceCount == 0 && hexCount == 0 {
+ return s
+ }
+
+ var buf [64]byte
+ var t []byte
+
+ required := len(s) + 2*hexCount
+ if required <= len(buf) {
+ t = buf[:required]
+ } else {
+ t = make([]byte, required)
+ }
+
+ if hexCount == 0 {
+ copy(t, s)
+ return string(t)
+ }
+
+ j := 0
+ for i := 0; i < len(s); i++ {
+ switch c := s[i]; {
+ case c == ' ' && mode == EncodeQueryComponent:
+ t[j] = c
+ j++
+ case shouldEscape(c, mode):
+ t[j] = '%'
+ t[j+1] = upperhex[c>>4]
+ t[j+2] = upperhex[c&15]
+ j += 3
+ default:
+ t[j] = s[i]
+ j++
+ }
+ }
+ return string(t)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/headers.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/headers.go
new file mode 100644
index 0000000000..83018d6c2f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/headers.go
@@ -0,0 +1,1655 @@
+package sip
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/ghettovoice/gosip/util"
+)
+
+// SIP Headers structs
+// Originally forked from github.com/stefankopieczek/gossip by @StefanKopieczek
+// with a tiny changes
+
+// Whitespace recognised by SIP protocol.
+const abnfWs = " \t"
+
+// Header is a single SIP header.
+type Header interface {
+ // Name returns header name.
+ Name() string
+ Value() string
+ // Clone returns copy of header struct.
+ Clone() Header
+ String() string
+ Equals(other interface{}) bool
+}
+
+// A URI from any schema (e.g. sip:, tel:, callto:)
+type Uri interface {
+ // Determine if the two URIs are equal according to the rules in RFC 3261 s. 19.1.4.
+ Equals(other interface{}) bool
+ String() string
+ Clone() Uri
+
+ IsEncrypted() bool
+ SetEncrypted(flag bool)
+ User() MaybeString
+ SetUser(user MaybeString)
+ Password() MaybeString
+ SetPassword(pass MaybeString)
+ Host() string
+ SetHost(host string)
+ Port() *Port
+ SetPort(port *Port)
+ UriParams() Params
+ SetUriParams(params Params)
+ Headers() Params
+ SetHeaders(params Params)
+ // Return true if and only if the URI is the special wildcard URI '*'; that is, if it is
+ // a WildcardUri struct.
+ IsWildcard() bool
+}
+
+// A URI from a schema suitable for inclusion in a Contact: header.
+// The only such URIs are sip/sips URIs and the special wildcard URI '*'.
+// hold this interface to not break other code
+type ContactUri interface {
+ Uri
+}
+
+// Generic list of parameters on a header.
+type Params interface {
+ Get(key string) (MaybeString, bool)
+ Add(key string, val MaybeString) Params
+ Remove(key string) Params
+ Clone() Params
+ Equals(params interface{}) bool
+ ToString(sep uint8) string
+ String() string
+ Length() int
+ Items() map[string]MaybeString
+ Keys() []string
+ Has(key string) bool
+}
+
+// IMPLEMENTATION
+
+// Params implementation.
+type headerParams struct {
+ mu sync.RWMutex
+ params map[string]MaybeString
+ paramOrder []string
+}
+
+// Create an empty set of parameters.
+func NewParams() Params {
+ return &headerParams{
+ params: make(map[string]MaybeString),
+ paramOrder: []string{},
+ }
+}
+
+// Returns the entire parameter map.
+func (params *headerParams) Items() map[string]MaybeString {
+ params.mu.RLock()
+ defer params.mu.RUnlock()
+ return params.params
+}
+
+// Returns a slice of keys, in order.
+func (params *headerParams) Keys() []string {
+ params.mu.RLock()
+ defer params.mu.RUnlock()
+ return params.paramOrder
+}
+
+// Returns the requested parameter value.
+func (params *headerParams) Get(key string) (MaybeString, bool) {
+ params.mu.RLock()
+ v, ok := params.params[key]
+ params.mu.RUnlock()
+ return v, ok
+}
+
+// Put a new parameter.
+func (params *headerParams) Add(key string, val MaybeString) Params {
+ params.mu.Lock()
+ // Add param to order list if new.
+ if _, ok := params.params[key]; !ok {
+ params.paramOrder = append(params.paramOrder, key)
+ }
+
+ // Set param value.
+ params.params[key] = val
+ params.mu.Unlock()
+ // Return the params so calls can be chained.
+ return params
+}
+
+func (params *headerParams) Remove(key string) Params {
+ params.mu.Lock()
+ if _, ok := params.params[key]; ok {
+ for k, v := range params.paramOrder {
+ if v == key {
+ params.paramOrder = append(params.paramOrder[:k], params.paramOrder[k+1:]...)
+ break
+ }
+ }
+ delete(params.params, key)
+ }
+ params.mu.Unlock()
+ // Return the params so calls can be chained.
+ return params
+}
+
+func (params *headerParams) Has(key string) bool {
+ params.mu.RLock()
+ _, ok := params.params[key]
+ params.mu.RUnlock()
+ return ok
+}
+
+// Copy a list of params.
+func (params *headerParams) Clone() Params {
+ if params == nil {
+ var dup *headerParams
+ return dup
+ }
+
+ dup := NewParams()
+ for _, key := range params.Keys() {
+ if val, ok := params.Get(key); ok {
+ dup.Add(key, val)
+ }
+ }
+
+ return dup
+}
+
+// Render params to a string.
+// Note that this does not escape special characters, this should already have been done before calling this method.
+func (params *headerParams) ToString(sep uint8) string {
+ if params == nil {
+ return ""
+ }
+
+ var buffer bytes.Buffer
+ first := true
+
+ for _, key := range params.Keys() {
+ val, ok := params.Get(key)
+ if !ok {
+ continue
+ }
+
+ if !first {
+ buffer.WriteString(fmt.Sprintf("%c", sep))
+ }
+ first = false
+
+ buffer.WriteString(fmt.Sprintf("%s", Escape(key, EncodeQueryComponent)))
+
+ if val, ok := val.(String); ok {
+ valStr := val.String()
+ if valStr[0] == '"' && valStr[len(valStr)-1] == '"' { // already escaped header param value
+ buffer.WriteString(fmt.Sprintf("=%s", valStr))
+ } else if strings.ContainsAny(valStr, abnfWs) {
+ buffer.WriteString(fmt.Sprintf("=\"%s\"", Escape(valStr, EncodeQueryComponent)))
+ } else {
+ buffer.WriteString(fmt.Sprintf("=%s", Escape(valStr, EncodeQueryComponent)))
+ }
+ }
+ }
+
+ return buffer.String()
+}
+
+// String returns params joined with '&' char.
+func (params *headerParams) String() string {
+ if params == nil {
+ return ""
+ }
+
+ return params.ToString('&')
+}
+
+// Returns number of params.
+func (params *headerParams) Length() int {
+ params.mu.RLock()
+ defer params.mu.RUnlock()
+ return len(params.params)
+}
+
+// Check if two maps of parameters are equal in the sense of having the same keys with the same values.
+// This does not rely on any ordering of the keys of the map in memory.
+func (params *headerParams) Equals(other interface{}) bool {
+ q, ok := other.(*headerParams)
+ if !ok {
+ return false
+ }
+
+ if params == q {
+ return true
+ }
+ if params == nil && q != nil || params != nil && q == nil {
+ return false
+ }
+
+ if params.Length() == 0 && q.Length() == 0 {
+ return true
+ }
+
+ if params.Length() != q.Length() {
+ return false
+ }
+
+ for key, pVal := range params.Items() {
+ qVal, ok := q.Get(key)
+ if !ok {
+ return false
+ }
+ if pVal != qVal {
+ return false
+ }
+ }
+
+ return true
+}
+
+func cloneWithNil(params Params) Params {
+ if params == nil {
+ return NewParams()
+ }
+ return params.Clone()
+}
+
+// SipUri
+// A SIP or SIPS URI, including all params and URI header params.
+// noinspection GoNameStartsWithPackageName
+type SipUri struct {
+ // True if and only if the URI is a SIPS URI.
+ FIsEncrypted bool
+
+ // The user part of the URI: the 'joe' in sip:joe@bloggs.com
+ // This is a pointer, so that URIs without a user part can have 'nil'.
+ FUser MaybeString
+
+ // The password field of the URI. This is represented in the URI as joe:hunter2@bloggs.com.
+ // Note that if a URI has a password field, it *must* have a user field as well.
+ // This is a pointer, so that URIs without a password field can have 'nil'.
+ // Note that RFC 3261 strongly recommends against the use of password fields in SIP URIs,
+ // as they are fundamentally insecure.
+ FPassword MaybeString
+
+ // The host part of the URI. This can be a domain, or a string representation of an IP address.
+ FHost string
+
+ // The port part of the URI. This is optional, and so is represented here as a pointer type.
+ FPort *Port
+
+ // Any parameters associated with the URI.
+ // These are used to provide information about requests that may be constructed from the URI.
+ // (For more details, see RFC 3261 section 19.1.1).
+ // These appear as a semicolon-separated list of key=value pairs following the host[:port] part.
+ FUriParams Params
+
+ // Any headers to be included on requests constructed from this URI.
+ // These appear as a '&'-separated list at the end of the URI, introduced by '?'.
+ // Although the values of the map are MaybeStrings, they will never be NoString in practice as the parser
+ // guarantees to not return blank values for header elements in SIP URIs.
+ // You should not set the values of headers to NoString.
+ FHeaders Params
+}
+
+func (uri *SipUri) IsEncrypted() bool {
+ return uri.FIsEncrypted
+}
+
+func (uri *SipUri) SetEncrypted(flag bool) {
+ uri.FIsEncrypted = flag
+}
+
+func (uri *SipUri) User() MaybeString {
+ return uri.FUser
+}
+
+func (uri *SipUri) SetUser(user MaybeString) {
+ uri.FUser = user
+}
+
+func (uri *SipUri) Password() MaybeString {
+ return uri.FPassword
+}
+
+func (uri *SipUri) SetPassword(pass MaybeString) {
+ uri.FPassword = pass
+}
+
+func (uri *SipUri) Host() string {
+ return uri.FHost
+}
+
+func (uri *SipUri) SetHost(host string) {
+ uri.FHost = host
+}
+
+func (uri *SipUri) Port() *Port {
+ return uri.FPort
+}
+
+func (uri *SipUri) SetPort(port *Port) {
+ uri.FPort = port
+}
+
+func (uri *SipUri) UriParams() Params {
+ return uri.FUriParams
+}
+
+func (uri *SipUri) SetUriParams(params Params) {
+ uri.FUriParams = params
+}
+
+func (uri *SipUri) Headers() Params {
+ return uri.FHeaders
+}
+
+func (uri *SipUri) SetHeaders(params Params) {
+ uri.FHeaders = params
+}
+
+func (uri *SipUri) IsWildcard() bool {
+ return false
+}
+
+// Determine if the SIP URI is equal to the specified URI according to the rules laid down in RFC 3261 s. 19.1.4.
+// TODO: The Equals method is not currently RFC-compliant; fix this!
+func (uri *SipUri) Equals(val interface{}) bool {
+ otherPtr, ok := val.(*SipUri)
+ if !ok {
+ return false
+ }
+
+ if uri == otherPtr {
+ return true
+ }
+ if uri == nil && otherPtr != nil || uri != nil && otherPtr == nil {
+ return false
+ }
+
+ other := *otherPtr
+ result := uri.FIsEncrypted == other.FIsEncrypted &&
+ uri.FUser == other.FUser &&
+ uri.FPassword == other.FPassword &&
+ uri.FHost == other.FHost &&
+ util.Uint16PtrEq((*uint16)(uri.FPort), (*uint16)(other.FPort))
+
+ if !result {
+ return false
+ }
+
+ if uri.FUriParams != otherPtr.FUriParams {
+ if uri.FUriParams == nil {
+ result = result && otherPtr.FUriParams != nil
+ } else {
+ result = result && uri.FUriParams.Equals(otherPtr.FUriParams)
+ }
+ }
+
+ if uri.FHeaders != otherPtr.FHeaders {
+ if uri.FHeaders == nil {
+ result = result && otherPtr.FHeaders != nil
+ } else {
+ result = result && uri.FHeaders.Equals(otherPtr.FHeaders)
+ }
+ }
+
+ return result
+}
+
+// Generates the string representation of a SipUri struct.
+func (uri *SipUri) String() string {
+ var buffer bytes.Buffer
+
+ // Compulsory protocol identifier.
+ if uri.FIsEncrypted {
+ buffer.WriteString("sips")
+ buffer.WriteString(":")
+ } else {
+ buffer.WriteString("sip")
+ buffer.WriteString(":")
+ }
+
+ // Optional userinfo part.
+ if user, ok := uri.FUser.(String); ok && user.String() != "" {
+ buffer.WriteString(Escape(uri.FUser.String(), EncodeUserPassword))
+ if pass, ok := uri.FPassword.(String); ok && pass.String() != "" {
+ buffer.WriteString(":")
+ buffer.WriteString(Escape(pass.String(), EncodeUserPassword))
+ }
+ buffer.WriteString("@")
+ }
+
+ // Compulsory hostname.
+ buffer.WriteString(Escape(uri.FHost, EncodeHost))
+
+ // Optional port number.
+ if uri.FPort != nil {
+ buffer.WriteString(fmt.Sprintf(":%d", *uri.FPort))
+ }
+
+ if (uri.FUriParams != nil) && uri.FUriParams.Length() > 0 {
+ buffer.WriteString(";")
+ buffer.WriteString(uri.FUriParams.ToString(';'))
+ }
+
+ if (uri.FHeaders != nil) && uri.FHeaders.Length() > 0 {
+ buffer.WriteString("?")
+ buffer.WriteString(uri.FHeaders.ToString('&'))
+ }
+
+ return buffer.String()
+}
+
+// Clone the Sip URI.
+func (uri *SipUri) Clone() Uri {
+ var newUri *SipUri
+ if uri == nil {
+ return newUri
+ }
+
+ newUri = &SipUri{
+ FIsEncrypted: uri.FIsEncrypted,
+ FUser: uri.FUser,
+ FPassword: uri.FPassword,
+ FHost: uri.FHost,
+ FUriParams: cloneWithNil(uri.FUriParams),
+ FHeaders: cloneWithNil(uri.FHeaders),
+ }
+ if uri.FPort != nil {
+ newUri.FPort = uri.FPort.Clone()
+ }
+ return newUri
+}
+
+// The special wildcard URI used in Contact: headers in REGISTER requests when expiring all registrations.
+type WildcardUri struct{}
+
+func (uri WildcardUri) IsEncrypted() bool { return false }
+
+func (uri WildcardUri) SetEncrypted(flag bool) {}
+
+func (uri WildcardUri) User() MaybeString { return nil }
+
+func (uri WildcardUri) SetUser(user MaybeString) {}
+
+func (uri WildcardUri) Password() MaybeString { return nil }
+
+func (uri WildcardUri) SetPassword(pass MaybeString) {}
+
+func (uri WildcardUri) Host() string { return "" }
+
+func (uri WildcardUri) SetHost(host string) {}
+
+func (uri WildcardUri) Port() *Port { return nil }
+
+func (uri WildcardUri) SetPort(port *Port) {}
+
+func (uri WildcardUri) UriParams() Params { return nil }
+
+func (uri WildcardUri) SetUriParams(params Params) {}
+
+func (uri WildcardUri) Headers() Params { return nil }
+
+func (uri WildcardUri) SetHeaders(params Params) {}
+
+// Copy the wildcard URI. Not hard!
+func (uri WildcardUri) Clone() Uri { return &WildcardUri{} }
+
+// Always returns 'true'.
+func (uri WildcardUri) IsWildcard() bool {
+ return true
+}
+
+// Always returns '*' - the representation of a wildcard URI in a SIP message.
+func (uri WildcardUri) String() string {
+ return "*"
+}
+
+// Determines if this wildcard URI equals the specified other URI.
+// This is true if and only if the other URI is also a wildcard URI.
+func (uri WildcardUri) Equals(other interface{}) bool {
+ switch other.(type) {
+ case WildcardUri:
+ return true
+ default:
+ return false
+ }
+}
+
+// Encapsulates a header that gossip does not natively support.
+// This allows header data that is not understood to be parsed by gossip and relayed to the parent application.
+type GenericHeader struct {
+ // The name of the header.
+ HeaderName string
+ // The contents of the header, including any parameters.
+ // This is transparent data that is not natively understood by gossip.
+ Contents string
+}
+
+// Convert the header to a flat string representation.
+func (header *GenericHeader) String() string {
+ return header.HeaderName + ": " + header.Contents
+}
+
+// Pull out the header name.
+func (header *GenericHeader) Name() string {
+ return header.HeaderName
+}
+
+func (header *GenericHeader) Value() string {
+ return header.Contents
+}
+
+// Copy the header.
+func (header *GenericHeader) Clone() Header {
+ if header == nil {
+ var newHeader *GenericHeader
+ return newHeader
+ }
+
+ return &GenericHeader{
+ HeaderName: header.HeaderName,
+ Contents: header.Contents,
+ }
+}
+
+func (header *GenericHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*GenericHeader); ok {
+ if header == h {
+ return true
+ }
+ if header == nil && h != nil || header != nil && h == nil {
+ return false
+ }
+
+ return header.HeaderName == h.HeaderName &&
+ header.Contents == h.Contents
+ }
+
+ return false
+}
+
+// ToHeader introduces SIP 'To' header
+type ToHeader struct {
+ // The display name from the header, may be omitted.
+ DisplayName MaybeString
+ Address Uri
+ // Any parameters present in the header.
+ Params Params
+}
+
+func (to *ToHeader) String() string {
+ return fmt.Sprintf("%s: %s", to.Name(), to.Value())
+}
+
+func (to *ToHeader) Name() string { return "To" }
+
+func (to *ToHeader) Value() string {
+ var buffer bytes.Buffer
+ if displayName, ok := to.DisplayName.(String); ok && displayName.String() != "" {
+ buffer.WriteString(fmt.Sprintf("\"%s\" ", displayName))
+ }
+
+ buffer.WriteString(fmt.Sprintf("<%s>", to.Address))
+
+ if to.Params != nil && to.Params.Length() > 0 {
+ buffer.WriteString(";")
+ buffer.WriteString(to.Params.ToString(';'))
+ }
+
+ return buffer.String()
+}
+
+// Copy the header.
+func (to *ToHeader) Clone() Header {
+ var newTo *ToHeader
+ if to == nil {
+ return newTo
+ }
+
+ newTo = &ToHeader{
+ DisplayName: to.DisplayName,
+ }
+ if to.Address != nil {
+ newTo.Address = to.Address.Clone()
+ }
+ if to.Params != nil {
+ newTo.Params = to.Params.Clone()
+ }
+ return newTo
+}
+
+func (to *ToHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*ToHeader); ok {
+ if to == h {
+ return true
+ }
+ if to == nil && h != nil || to != nil && h == nil {
+ return false
+ }
+
+ res := true
+
+ if to.DisplayName != h.DisplayName {
+ if to.DisplayName == nil {
+ res = res && h.DisplayName == nil
+ } else {
+ res = res && to.DisplayName.Equals(h.DisplayName)
+ }
+ }
+
+ if to.Address != h.Address {
+ if to.Address == nil {
+ res = res && h.Address == nil
+ } else {
+ res = res && to.Address.Equals(h.Address)
+ }
+ }
+
+ if to.Params != h.Params {
+ if to.Params == nil {
+ res = res && h.Params == nil
+ } else {
+ res = res && to.Params.Equals(h.Params)
+ }
+ }
+
+ return res
+ }
+
+ return false
+}
+
+type FromHeader struct {
+ // The display name from the header, may be omitted.
+ DisplayName MaybeString
+
+ Address Uri
+
+ // Any parameters present in the header.
+ Params Params
+}
+
+func (from *FromHeader) String() string {
+ return fmt.Sprintf("%s: %s", from.Name(), from.Value())
+}
+
+func (from *FromHeader) Name() string { return "From" }
+
+func (from *FromHeader) Value() string {
+ var buffer bytes.Buffer
+ if displayName, ok := from.DisplayName.(String); ok && displayName.String() != "" {
+ buffer.WriteString(fmt.Sprintf("\"%s\" ", displayName))
+ }
+
+ buffer.WriteString(fmt.Sprintf("<%s>", from.Address))
+
+ if from.Params != nil && from.Params.Length() > 0 {
+ buffer.WriteString(";")
+ buffer.WriteString(from.Params.ToString(';'))
+ }
+
+ return buffer.String()
+}
+
+// Copy the header.
+func (from *FromHeader) Clone() Header {
+ var newFrom *FromHeader
+ if from == nil {
+ return newFrom
+ }
+
+ newFrom = &FromHeader{
+ DisplayName: from.DisplayName,
+ }
+ if from.Address != nil {
+ newFrom.Address = from.Address.Clone()
+ }
+ if from.Params != nil {
+ newFrom.Params = from.Params.Clone()
+ }
+
+ return newFrom
+}
+
+func (from *FromHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*FromHeader); ok {
+ if from == h {
+ return true
+ }
+ if from == nil && h != nil || from != nil && h == nil {
+ return false
+ }
+
+ res := true
+
+ if from.DisplayName != h.DisplayName {
+ if from.DisplayName == nil {
+ res = res && h.DisplayName == nil
+ } else {
+ res = res && from.DisplayName.Equals(h.DisplayName)
+ }
+ }
+
+ if from.Address != h.Address {
+ if from.Address == nil {
+ res = res && h.Address == nil
+ } else {
+ res = res && from.Address.Equals(h.Address)
+ }
+ }
+
+ if from.Params != h.Params {
+ if from.Params == nil {
+ res = res && h.Params == nil
+ } else {
+ res = res && from.Params.Equals(h.Params)
+ }
+ }
+
+ return res
+ }
+
+ return false
+}
+
+type ContactHeader struct {
+ // The display name from the header, may be omitted.
+ DisplayName MaybeString
+ Address ContactUri
+ // Any parameters present in the header.
+ Params Params
+}
+
+func (contact *ContactHeader) String() string {
+ return fmt.Sprintf("%s: %s", contact.Name(), contact.Value())
+}
+
+func (contact *ContactHeader) Name() string { return "Contact" }
+
+func (contact *ContactHeader) Value() string {
+ var buffer bytes.Buffer
+
+ if displayName, ok := contact.DisplayName.(String); ok && displayName.String() != "" {
+ buffer.WriteString(fmt.Sprintf("\"%s\" ", displayName))
+ }
+
+ switch contact.Address.(type) {
+ case *WildcardUri:
+ // Treat the Wildcard URI separately as it must not be contained in < > angle brackets.
+ buffer.WriteString("*")
+ default:
+ buffer.WriteString(fmt.Sprintf("<%s>", contact.Address.String()))
+ }
+
+ if (contact.Params != nil) && (contact.Params.Length() > 0) {
+ buffer.WriteString(";")
+ buffer.WriteString(contact.Params.ToString(';'))
+ }
+
+ return buffer.String()
+}
+
+// Copy the header.
+func (contact *ContactHeader) Clone() Header {
+ var newCnt *ContactHeader
+ if contact == nil {
+ return newCnt
+ }
+
+ newCnt = &ContactHeader{
+ DisplayName: contact.DisplayName,
+ }
+ if contact.Address != nil {
+ newCnt.Address = contact.Address.Clone()
+ }
+ if contact.Params != nil {
+ newCnt.Params = contact.Params.Clone()
+ }
+
+ return newCnt
+}
+
+func (contact *ContactHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*ContactHeader); ok {
+ if contact == h {
+ return true
+ }
+ if contact == nil && h != nil || contact != nil && h == nil {
+ return false
+ }
+
+ res := true
+
+ if contact.DisplayName != h.DisplayName {
+ if contact.DisplayName == nil {
+ res = res && h.DisplayName == nil
+ } else {
+ res = res && contact.DisplayName.Equals(h.DisplayName)
+ }
+ }
+
+ if contact.Address != h.Address {
+ if contact.Address == nil {
+ res = res && h.Address == nil
+ } else {
+ res = res && contact.Address.Equals(h.Address)
+ }
+ }
+
+ if contact.Params != h.Params {
+ if contact.Params == nil {
+ res = res && h.Params == nil
+ } else {
+ res = res && contact.Params.Equals(h.Params)
+ }
+ }
+
+ return res
+ }
+
+ return false
+}
+
+// CallID - 'Call-ID' header.
+type CallID string
+
+func (callId CallID) String() string {
+ return fmt.Sprintf("%s: %s", callId.Name(), callId.Value())
+}
+
+func (callId *CallID) Name() string { return "Call-ID" }
+
+func (callId CallID) Value() string { return string(callId) }
+
+func (callId *CallID) Clone() Header {
+ return callId
+}
+
+func (callId *CallID) Equals(other interface{}) bool {
+ if h, ok := other.(CallID); ok {
+ if callId == nil {
+ return false
+ }
+
+ return *callId == h
+ }
+ if h, ok := other.(*CallID); ok {
+ if callId == h {
+ return true
+ }
+ if callId == nil && h != nil || callId != nil && h == nil {
+ return false
+ }
+
+ return *callId == *h
+ }
+
+ return false
+}
+
+type CSeq struct {
+ SeqNo uint32
+ MethodName RequestMethod
+}
+
+func (cseq *CSeq) String() string {
+ return fmt.Sprintf("%s: %s", cseq.Name(), cseq.Value())
+}
+
+func (cseq *CSeq) Name() string { return "CSeq" }
+
+func (cseq *CSeq) Value() string {
+ return fmt.Sprintf("%d %s", cseq.SeqNo, cseq.MethodName)
+}
+
+func (cseq *CSeq) Clone() Header {
+ if cseq == nil {
+ var newCSeq *CSeq
+ return newCSeq
+ }
+
+ return &CSeq{
+ SeqNo: cseq.SeqNo,
+ MethodName: cseq.MethodName,
+ }
+}
+
+func (cseq *CSeq) Equals(other interface{}) bool {
+ if h, ok := other.(*CSeq); ok {
+ if cseq == h {
+ return true
+ }
+ if cseq == nil && h != nil || cseq != nil && h == nil {
+ return false
+ }
+
+ return cseq.SeqNo == h.SeqNo &&
+ cseq.MethodName == h.MethodName
+ }
+
+ return false
+}
+
+type MaxForwards uint32
+
+func (maxForwards MaxForwards) String() string {
+ return fmt.Sprintf("%s: %s", maxForwards.Name(), maxForwards.Value())
+}
+
+func (maxForwards *MaxForwards) Name() string { return "Max-Forwards" }
+
+func (maxForwards MaxForwards) Value() string { return fmt.Sprintf("%d", maxForwards) }
+
+func (maxForwards *MaxForwards) Clone() Header { return maxForwards }
+
+func (maxForwards *MaxForwards) Equals(other interface{}) bool {
+ if h, ok := other.(MaxForwards); ok {
+ if maxForwards == nil {
+ return false
+ }
+
+ return *maxForwards == h
+ }
+ if h, ok := other.(*MaxForwards); ok {
+ if maxForwards == h {
+ return true
+ }
+ if maxForwards == nil && h != nil || maxForwards != nil && h == nil {
+ return false
+ }
+
+ return *maxForwards == *h
+ }
+
+ return false
+}
+
+type Expires uint32
+
+func (expires *Expires) String() string {
+ return fmt.Sprintf("%s: %s", expires.Name(), expires.Value())
+}
+
+func (expires *Expires) Name() string { return "Expires" }
+
+func (expires Expires) Value() string { return fmt.Sprintf("%d", expires) }
+
+func (expires *Expires) Clone() Header { return expires }
+
+func (expires *Expires) Equals(other interface{}) bool {
+ if h, ok := other.(Expires); ok {
+ if expires == nil {
+ return false
+ }
+
+ return *expires == h
+ }
+ if h, ok := other.(*Expires); ok {
+ if expires == h {
+ return true
+ }
+ if expires == nil && h != nil || expires != nil && h == nil {
+ return false
+ }
+
+ return *expires == *h
+ }
+
+ return false
+}
+
+type ContentLength uint32
+
+func (contentLength ContentLength) String() string {
+ return fmt.Sprintf("%s: %s", contentLength.Name(), contentLength.Value())
+}
+
+func (contentLength *ContentLength) Name() string { return "Content-Length" }
+
+func (contentLength ContentLength) Value() string { return fmt.Sprintf("%d", contentLength) }
+
+func (contentLength *ContentLength) Clone() Header { return contentLength }
+
+func (contentLength *ContentLength) Equals(other interface{}) bool {
+ if h, ok := other.(ContentLength); ok {
+ if contentLength == nil {
+ return false
+ }
+
+ return *contentLength == h
+ }
+ if h, ok := other.(*ContentLength); ok {
+ if contentLength == h {
+ return true
+ }
+ if contentLength == nil && h != nil || contentLength != nil && h == nil {
+ return false
+ }
+
+ return *contentLength == *h
+ }
+
+ return false
+}
+
+type ViaHeader []*ViaHop
+
+func (via ViaHeader) String() string {
+ return fmt.Sprintf("%s: %s", via.Name(), via.Value())
+}
+
+func (via ViaHeader) Name() string { return "Via" }
+
+func (via ViaHeader) Value() string {
+ var buffer bytes.Buffer
+ for idx, hop := range via {
+ buffer.WriteString(hop.String())
+ if idx != len(via)-1 {
+ buffer.WriteString(", ")
+ }
+ }
+
+ return buffer.String()
+}
+
+func (via ViaHeader) Clone() Header {
+ if via == nil {
+ var newVie ViaHeader
+ return newVie
+ }
+
+ dup := make([]*ViaHop, 0, len(via))
+ for _, hop := range via {
+ dup = append(dup, hop.Clone())
+ }
+ return ViaHeader(dup)
+}
+
+func (via ViaHeader) Equals(other interface{}) bool {
+ if h, ok := other.(ViaHeader); ok {
+ if len(via) != len(h) {
+ return false
+ }
+
+ for i, hop := range via {
+ if !hop.Equals(h[i]) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+// A single component in a Via header.
+// Via headers are composed of several segments of the same structure, added by successive nodes in a routing chain.
+type ViaHop struct {
+ // E.g. 'SIP'.
+ ProtocolName string
+ // E.g. '2.0'.
+ ProtocolVersion string
+ Transport string
+ Host string
+ // The port for this via hop. This is stored as a pointer type, since it is an optional field.
+ Port *Port
+ Params Params
+}
+
+func (hop *ViaHop) SentBy() string {
+ var buf bytes.Buffer
+ buf.WriteString(hop.Host)
+ if hop.Port != nil {
+ buf.WriteString(fmt.Sprintf(":%d", *hop.Port))
+ }
+
+ return buf.String()
+}
+
+func (hop *ViaHop) String() string {
+ var buffer bytes.Buffer
+ buffer.WriteString(
+ fmt.Sprintf(
+ "%s/%s/%s %s",
+ hop.ProtocolName,
+ hop.ProtocolVersion,
+ hop.Transport,
+ hop.Host,
+ ),
+ )
+ if hop.Port != nil {
+ buffer.WriteString(fmt.Sprintf(":%d", *hop.Port))
+ }
+
+ if hop.Params != nil && hop.Params.Length() > 0 {
+ buffer.WriteString(";")
+ buffer.WriteString(hop.Params.ToString(';'))
+ }
+
+ return buffer.String()
+}
+
+// Return an exact copy of this ViaHop.
+func (hop *ViaHop) Clone() *ViaHop {
+ var newHop *ViaHop
+ if hop == nil {
+ return newHop
+ }
+
+ newHop = &ViaHop{
+ ProtocolName: hop.ProtocolName,
+ ProtocolVersion: hop.ProtocolVersion,
+ Transport: hop.Transport,
+ Host: hop.Host,
+ }
+ if hop.Port != nil {
+ newHop.Port = hop.Port.Clone()
+ }
+ if hop.Params != nil {
+ newHop.Params = hop.Params.Clone()
+ }
+
+ return newHop
+}
+
+func (hop *ViaHop) Equals(other interface{}) bool {
+ if h, ok := other.(*ViaHop); ok {
+ if hop == h {
+ return true
+ }
+ if hop == nil && h != nil || hop != nil && h == nil {
+ return false
+ }
+
+ res := hop.ProtocolName == h.ProtocolName &&
+ hop.ProtocolVersion == h.ProtocolVersion &&
+ hop.Transport == h.Transport &&
+ hop.Host == h.Host &&
+ util.Uint16PtrEq((*uint16)(hop.Port), (*uint16)(h.Port))
+
+ if hop.Params != h.Params {
+ if hop.Params == nil {
+ res = res && h.Params == nil
+ } else {
+ res = res && hop.Params.Equals(h.Params)
+ }
+ }
+
+ return res
+ }
+
+ return false
+}
+
+type RequireHeader struct {
+ Options []string
+}
+
+func (require *RequireHeader) String() string {
+ return fmt.Sprintf("%s: %s", require.Name(), require.Value())
+}
+
+func (require *RequireHeader) Name() string { return "Require" }
+
+func (require *RequireHeader) Value() string {
+ return strings.Join(require.Options, ", ")
+}
+
+func (require *RequireHeader) Clone() Header {
+ if require == nil {
+ var newRequire *RequireHeader
+ return newRequire
+ }
+
+ dup := make([]string, len(require.Options))
+ copy(dup, require.Options)
+ return &RequireHeader{dup}
+}
+
+func (require *RequireHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*RequireHeader); ok {
+ if require == h {
+ return true
+ }
+ if require == nil && h != nil || require != nil && h == nil {
+ return false
+ }
+
+ if len(require.Options) != len(h.Options) {
+ return false
+ }
+
+ for i, opt := range require.Options {
+ if opt != h.Options[i] {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+type SupportedHeader struct {
+ Options []string
+}
+
+func (support *SupportedHeader) String() string {
+ return fmt.Sprintf("%s: %s", support.Name(), support.Value())
+}
+
+func (support *SupportedHeader) Name() string { return "Supported" }
+
+func (support *SupportedHeader) Value() string {
+ return strings.Join(support.Options, ", ")
+}
+
+func (support *SupportedHeader) Clone() Header {
+ if support == nil {
+ var newSupport *SupportedHeader
+ return newSupport
+ }
+
+ dup := make([]string, len(support.Options))
+ copy(dup, support.Options)
+ return &SupportedHeader{dup}
+}
+
+func (support *SupportedHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*SupportedHeader); ok {
+ if support == h {
+ return true
+ }
+ if support == nil && h != nil || support != nil && h == nil {
+ return false
+ }
+
+ if len(support.Options) != len(h.Options) {
+ return false
+ }
+
+ for i, opt := range support.Options {
+ if opt != h.Options[i] {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+type ProxyRequireHeader struct {
+ Options []string
+}
+
+func (proxyRequire *ProxyRequireHeader) String() string {
+ return fmt.Sprintf("%s: %s", proxyRequire.Name(), proxyRequire.Value())
+}
+
+func (proxyRequire *ProxyRequireHeader) Name() string { return "Proxy-Require" }
+
+func (proxyRequire *ProxyRequireHeader) Value() string {
+ return strings.Join(proxyRequire.Options, ", ")
+}
+
+func (proxyRequire *ProxyRequireHeader) Clone() Header {
+ if proxyRequire == nil {
+ var newProxy *ProxyRequireHeader
+ return newProxy
+ }
+
+ dup := make([]string, len(proxyRequire.Options))
+ copy(dup, proxyRequire.Options)
+ return &ProxyRequireHeader{dup}
+}
+
+func (proxyRequire *ProxyRequireHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*ProxyRequireHeader); ok {
+ if proxyRequire == h {
+ return true
+ }
+ if proxyRequire == nil && h != nil || proxyRequire != nil && h == nil {
+ return false
+ }
+
+ if len(proxyRequire.Options) != len(h.Options) {
+ return false
+ }
+
+ for i, opt := range proxyRequire.Options {
+ if opt != h.Options[i] {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+// 'Unsupported:' is a SIP header type - this doesn't indicate that the
+// header itself is not supported by gossip!
+type UnsupportedHeader struct {
+ Options []string
+}
+
+func (unsupported *UnsupportedHeader) String() string {
+ return fmt.Sprintf("%s: %s", unsupported.Name(), unsupported.Value())
+}
+
+func (unsupported *UnsupportedHeader) Name() string { return "Unsupported" }
+
+func (unsupported *UnsupportedHeader) Value() string {
+ return strings.Join(unsupported.Options, ", ")
+}
+
+func (unsupported *UnsupportedHeader) Clone() Header {
+ if unsupported == nil {
+ var newUnsup *UnsupportedHeader
+ return newUnsup
+ }
+
+ dup := make([]string, len(unsupported.Options))
+ copy(dup, unsupported.Options)
+ return &UnsupportedHeader{dup}
+}
+
+func (unsupported *UnsupportedHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*UnsupportedHeader); ok {
+ if unsupported == h {
+ return true
+ }
+ if unsupported == nil && h != nil || unsupported != nil && h == nil {
+ return false
+ }
+
+ if len(unsupported.Options) != len(h.Options) {
+ return false
+ }
+
+ for i, opt := range unsupported.Options {
+ if opt != h.Options[i] {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+type UserAgentHeader string
+
+func (ua *UserAgentHeader) String() string {
+ return fmt.Sprintf("%s: %s", ua.Name(), ua.Value())
+}
+
+func (ua *UserAgentHeader) Name() string { return "User-Agent" }
+
+func (ua UserAgentHeader) Value() string { return string(ua) }
+
+func (ua *UserAgentHeader) Clone() Header { return ua }
+
+func (ua *UserAgentHeader) Equals(other interface{}) bool {
+ if h, ok := other.(UserAgentHeader); ok {
+ if ua == nil {
+ return false
+ }
+
+ return *ua == h
+ }
+ if h, ok := other.(*UserAgentHeader); ok {
+ if ua == h {
+ return true
+ }
+ if ua == nil && h != nil || ua != nil && h == nil {
+ return false
+ }
+
+ return *ua == *h
+ }
+
+ return false
+}
+
+type AllowHeader []RequestMethod
+
+func (allow AllowHeader) String() string {
+ return fmt.Sprintf("%s: %s", allow.Name(), allow.Value())
+}
+
+func (allow AllowHeader) Name() string { return "Allow" }
+
+func (allow AllowHeader) Value() string {
+ parts := make([]string, 0)
+ for _, method := range allow {
+ parts = append(parts, string(method))
+ }
+ return strings.Join(parts, ", ")
+}
+
+func (allow AllowHeader) Clone() Header {
+ if allow == nil {
+ var newAllow AllowHeader
+ return newAllow
+ }
+
+ newAllow := make(AllowHeader, len(allow))
+ copy(newAllow, allow)
+
+ return newAllow
+}
+
+func (allow AllowHeader) Equals(other interface{}) bool {
+ if h, ok := other.(AllowHeader); ok {
+ if len(allow) != len(h) {
+ return false
+ }
+
+ for i, v := range allow {
+ if v != h[i] {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+type ContentType string
+
+func (ct *ContentType) String() string { return fmt.Sprintf("%s: %s", ct.Name(), ct.Value()) }
+
+func (ct *ContentType) Name() string { return "Content-Type" }
+
+func (ct ContentType) Value() string { return string(ct) }
+
+func (ct *ContentType) Clone() Header { return ct }
+
+func (ct *ContentType) Equals(other interface{}) bool {
+ if h, ok := other.(ContentType); ok {
+ if ct == nil {
+ return false
+ }
+
+ return *ct == h
+ }
+ if h, ok := other.(*ContentType); ok {
+ if ct == h {
+ return true
+ }
+ if ct == nil && h != nil || ct != nil && h == nil {
+ return false
+ }
+
+ return *ct == *h
+ }
+
+ return false
+}
+
+type Accept string
+
+func (ct *Accept) String() string { return fmt.Sprintf("%s: %s", ct.Name(), ct.Value()) }
+
+func (ct *Accept) Name() string { return "Accept" }
+
+func (ct Accept) Value() string { return string(ct) }
+
+func (ct *Accept) Clone() Header { return ct }
+
+func (ct *Accept) Equals(other interface{}) bool {
+ if h, ok := other.(Accept); ok {
+ if ct == nil {
+ return false
+ }
+
+ return *ct == h
+ }
+ if h, ok := other.(*Accept); ok {
+ if ct == h {
+ return true
+ }
+ if ct == nil && h != nil || ct != nil && h == nil {
+ return false
+ }
+
+ return *ct == *h
+ }
+
+ return false
+}
+
+type RouteHeader struct {
+ Addresses []Uri
+}
+
+func (route *RouteHeader) Name() string { return "Route" }
+
+func (route *RouteHeader) Value() string {
+ var addrs []string
+ for _, uri := range route.Addresses {
+ addrs = append(addrs, "<"+uri.String()+">")
+ }
+ return strings.Join(addrs, ", ")
+}
+
+func (route *RouteHeader) String() string {
+ return fmt.Sprintf("%s: %s", route.Name(), route.Value())
+}
+
+func (route *RouteHeader) Clone() Header {
+ var newRoute *RouteHeader
+ if route == nil {
+ return newRoute
+ }
+
+ newRoute = &RouteHeader{
+ Addresses: make([]Uri, len(route.Addresses)),
+ }
+
+ for i, uri := range route.Addresses {
+ newRoute.Addresses[i] = uri.Clone()
+ }
+
+ return newRoute
+}
+
+func (route *RouteHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*RouteHeader); ok {
+ if route == h {
+ return true
+ }
+ if route == nil && h != nil || route != nil && h == nil {
+ return false
+ }
+
+ for i, uri := range route.Addresses {
+ if !uri.Equals(h.Addresses[i]) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
+
+type RecordRouteHeader struct {
+ Addresses []Uri
+}
+
+func (route *RecordRouteHeader) Name() string { return "Record-Route" }
+
+func (route *RecordRouteHeader) Value() string {
+ var addrs []string
+ for _, uri := range route.Addresses {
+ addrs = append(addrs, "<"+uri.String()+">")
+ }
+ return strings.Join(addrs, ", ")
+}
+
+func (route *RecordRouteHeader) String() string {
+ return fmt.Sprintf("%s: %s", route.Name(), route.Value())
+}
+
+func (route *RecordRouteHeader) Clone() Header {
+ var newRoute *RecordRouteHeader
+ if route == nil {
+ return newRoute
+ }
+
+ newRoute = &RecordRouteHeader{
+ Addresses: make([]Uri, len(route.Addresses)),
+ }
+
+ for i, uri := range route.Addresses {
+ newRoute.Addresses[i] = uri.Clone()
+ }
+
+ return newRoute
+}
+
+func (route *RecordRouteHeader) Equals(other interface{}) bool {
+ if h, ok := other.(*RecordRouteHeader); ok {
+ if route == h {
+ return true
+ }
+ if route == nil && h != nil || route != nil && h == nil {
+ return false
+ }
+
+ for i, uri := range route.Addresses {
+ if !uri.Equals(h.Addresses[i]) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/message.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/message.go
new file mode 100644
index 0000000000..78e96e12e0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/message.go
@@ -0,0 +1,542 @@
+package sip
+
+import (
+ "bytes"
+ "strings"
+ "sync"
+
+ uuid "github.com/satori/go.uuid"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+// A representation of a SIP method.
+// This is syntactic sugar around the string type, so make sure to use
+// the Equals method rather than built-in equality, or you'll fall foul of case differences.
+// If you're defining your own Method, uppercase is preferred but not compulsory.
+type RequestMethod string
+
+// StatusCode - response status code: 1xx - 6xx
+type StatusCode uint16
+
+// Determine if the given method equals some other given method.
+// This is syntactic sugar for case insensitive equality checking.
+func (method *RequestMethod) Equals(other *RequestMethod) bool {
+ if method != nil && other != nil {
+ return strings.EqualFold(string(*method), string(*other))
+ } else {
+ return method == other
+ }
+}
+
+// It's nicer to avoid using raw strings to represent methods, so the following standard
+// method names are defined here as constants for convenience.
+const (
+ INVITE RequestMethod = "INVITE"
+ ACK RequestMethod = "ACK"
+ CANCEL RequestMethod = "CANCEL"
+ BYE RequestMethod = "BYE"
+ REGISTER RequestMethod = "REGISTER"
+ OPTIONS RequestMethod = "OPTIONS"
+ SUBSCRIBE RequestMethod = "SUBSCRIBE"
+ NOTIFY RequestMethod = "NOTIFY"
+ REFER RequestMethod = "REFER"
+ INFO RequestMethod = "INFO"
+ MESSAGE RequestMethod = "MESSAGE"
+ PRACK RequestMethod = "PRACK"
+ UPDATE RequestMethod = "UPDATE"
+ PUBLISH RequestMethod = "PUBLISH"
+)
+
+type MessageID string
+
+func NextMessageID() MessageID {
+ return MessageID(uuid.Must(uuid.NewV4()).String())
+}
+
+// Message introduces common SIP message RFC 3261 - 7.
+type Message interface {
+ MessageID() MessageID
+
+ Clone() Message
+ // Start line returns message start line.
+ StartLine() string
+ // String returns string representation of SIP message in RFC 3261 form.
+ String() string
+ // Short returns short string info about message.
+ Short() string
+ // SipVersion returns SIP protocol version.
+ SipVersion() string
+ // SetSipVersion sets SIP protocol version.
+ SetSipVersion(version string)
+
+ // Headers returns all message headers.
+ Headers() []Header
+ // GetHeaders returns slice of headers of the given type.
+ GetHeaders(name string) []Header
+ // AppendHeader appends header to message.
+ AppendHeader(header Header)
+ // PrependHeader prepends header to message.
+ PrependHeader(header Header)
+ PrependHeaderAfter(header Header, afterName string)
+ // RemoveHeader removes header from message.
+ RemoveHeader(name string)
+ ReplaceHeaders(name string, headers []Header)
+
+ // Body returns message body.
+ Body() string
+ // SetBody sets message body.
+ SetBody(body string, setContentLength bool)
+
+ /* Helper getters for common headers */
+ // CallID returns 'Call-ID' header.
+ CallID() (*CallID, bool)
+ // Via returns the top 'Via' header field.
+ Via() (ViaHeader, bool)
+ // ViaHop returns the first segment of the top 'Via' header.
+ ViaHop() (*ViaHop, bool)
+ // From returns 'From' header field.
+ From() (*FromHeader, bool)
+ // To returns 'To' header field.
+ To() (*ToHeader, bool)
+ // CSeq returns 'CSeq' header field.
+ CSeq() (*CSeq, bool)
+ ContentLength() (*ContentLength, bool)
+ ContentType() (*ContentType, bool)
+ Contact() (*ContactHeader, bool)
+
+ Transport() string
+ SetTransport(tp string)
+ Source() string
+ SetSource(src string)
+ Destination() string
+ SetDestination(dest string)
+
+ IsCancel() bool
+ IsAck() bool
+
+ Fields() log.Fields
+ WithFields(fields log.Fields) Message
+}
+
+// headers is a struct with methods to work with SIP headers.
+type headers struct {
+ mu sync.RWMutex
+ // The logical SIP headers attached to this message.
+ headers map[string][]Header
+ // The order the headers should be displayed in.
+ headerOrder []string
+}
+
+func newHeaders(hdrs []Header) *headers {
+ hs := new(headers)
+ hs.headers = make(map[string][]Header)
+ hs.headerOrder = make([]string, 0)
+ for _, header := range hdrs {
+ hs.AppendHeader(header)
+ }
+ return hs
+}
+
+func (hs *headers) String() string {
+ buffer := bytes.Buffer{}
+ hs.mu.RLock()
+ // Construct each header in turn and add it to the message.
+ for typeIdx, name := range hs.headerOrder {
+ headers := hs.headers[name]
+ for idx, header := range headers {
+ buffer.WriteString(header.String())
+ if typeIdx < len(hs.headerOrder) || idx < len(headers) {
+ buffer.WriteString("\r\n")
+ }
+ }
+ }
+ hs.mu.RUnlock()
+ return buffer.String()
+}
+
+// Add the given header.
+func (hs *headers) AppendHeader(header Header) {
+ name := strings.ToLower(header.Name())
+ hs.mu.Lock()
+ if _, ok := hs.headers[name]; ok {
+ hs.headers[name] = append(hs.headers[name], header)
+ } else {
+ hs.headers[name] = []Header{header}
+ hs.headerOrder = append(hs.headerOrder, name)
+ }
+ hs.mu.Unlock()
+}
+
+// AddFrontHeader adds header to the front of header list
+// if there is no header has h's name, add h to the font of all headers
+// if there are some headers have h's name, add h to front of the sublist
+func (hs *headers) PrependHeader(header Header) {
+ name := strings.ToLower(header.Name())
+ hs.mu.Lock()
+ if hdrs, ok := hs.headers[name]; ok {
+ hs.headers[name] = append([]Header{header}, hdrs...)
+ } else {
+ hs.headers[name] = []Header{header}
+ newOrder := make([]string, 1, len(hs.headerOrder)+1)
+ newOrder[0] = name
+ hs.headerOrder = append(newOrder, hs.headerOrder...)
+ }
+ hs.mu.Unlock()
+}
+
+func (hs *headers) PrependHeaderAfter(header Header, afterName string) {
+ headerName := strings.ToLower(header.Name())
+ afterName = strings.ToLower(afterName)
+ hs.mu.Lock()
+ if _, ok := hs.headers[afterName]; ok {
+ afterIdx := -1
+ headerIdx := -1
+ for i, name := range hs.headerOrder {
+ if name == afterName {
+ afterIdx = i
+ }
+ if name == headerName {
+ headerIdx = i
+ }
+ }
+
+ if headerIdx == -1 {
+ hs.headers[headerName] = []Header{header}
+ newOrder := make([]string, 0)
+ newOrder = append(newOrder, hs.headerOrder[:afterIdx+1]...)
+ newOrder = append(newOrder, headerName)
+ newOrder = append(newOrder, hs.headerOrder[afterIdx+1:]...)
+ hs.headerOrder = newOrder
+ } else {
+ hs.headers[headerName] = append([]Header{header}, hs.headers[headerName]...)
+ newOrder := make([]string, 0)
+ if afterIdx < headerIdx {
+ newOrder = append(newOrder, hs.headerOrder[:afterIdx+1]...)
+ newOrder = append(newOrder, headerName)
+ newOrder = append(newOrder, hs.headerOrder[afterIdx+1:headerIdx]...)
+ newOrder = append(newOrder, hs.headerOrder[headerIdx+1:]...)
+ } else {
+ newOrder = append(newOrder, hs.headerOrder[:headerIdx]...)
+ newOrder = append(newOrder, hs.headerOrder[headerIdx+1:afterIdx+1]...)
+ newOrder = append(newOrder, headerName)
+ newOrder = append(newOrder, hs.headerOrder[afterIdx+1:]...)
+ }
+ hs.headerOrder = newOrder
+ }
+ hs.mu.Unlock()
+ } else {
+ hs.mu.Unlock()
+ hs.PrependHeader(header)
+ }
+}
+
+func (hs *headers) ReplaceHeaders(name string, headers []Header) {
+ name = strings.ToLower(name)
+ hs.mu.Lock()
+ if _, ok := hs.headers[name]; ok {
+ hs.headers[name] = headers
+ }
+ hs.mu.Unlock()
+}
+
+// Gets some headers.
+func (hs *headers) Headers() []Header {
+ hdrs := make([]Header, 0)
+ hs.mu.RLock()
+ for _, key := range hs.headerOrder {
+ hdrs = append(hdrs, hs.headers[key]...)
+ }
+ hs.mu.RUnlock()
+
+ return hdrs
+}
+
+func (hs *headers) GetHeaders(name string) []Header {
+ name = strings.ToLower(name)
+ hs.mu.RLock()
+ defer hs.mu.RUnlock()
+ if hs.headers == nil {
+ hs.headers = map[string][]Header{}
+ hs.headerOrder = []string{}
+ }
+ if headers, ok := hs.headers[name]; ok {
+ return headers
+ }
+
+ return []Header{}
+}
+
+func (hs *headers) RemoveHeader(name string) {
+ name = strings.ToLower(name)
+ hs.mu.Lock()
+ delete(hs.headers, name)
+ // update order slice
+ for idx, entry := range hs.headerOrder {
+ if entry == name {
+ hs.headerOrder = append(hs.headerOrder[:idx], hs.headerOrder[idx+1:]...)
+ break
+ }
+ }
+ hs.mu.Unlock()
+}
+
+// CloneHeaders returns all cloned headers in slice.
+func (hs *headers) CloneHeaders() []Header {
+ return cloneHeaders(hs)
+}
+
+func cloneHeaders(msg interface{ Headers() []Header }) []Header {
+ hdrs := make([]Header, 0)
+ for _, header := range msg.Headers() {
+ hdrs = append(hdrs, header.Clone())
+ }
+ return hdrs
+}
+
+func (hs *headers) CallID() (*CallID, bool) {
+ hdrs := hs.GetHeaders("Call-ID")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ callId, ok := hdrs[0].(*CallID)
+ if !ok {
+ return nil, false
+ }
+ return callId, true
+}
+
+func (hs *headers) Via() (ViaHeader, bool) {
+ hdrs := hs.GetHeaders("Via")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ via, ok := (hdrs[0]).(ViaHeader)
+ if !ok {
+ return nil, false
+ }
+
+ return via, true
+}
+
+func (hs *headers) ViaHop() (*ViaHop, bool) {
+ via, ok := hs.Via()
+ if !ok {
+ return nil, false
+ }
+ hops := []*ViaHop(via)
+ if len(hops) == 0 {
+ return nil, false
+ }
+
+ return hops[0], true
+}
+
+func (hs *headers) From() (*FromHeader, bool) {
+ hdrs := hs.GetHeaders("From")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ from, ok := hdrs[0].(*FromHeader)
+ if !ok {
+ return nil, false
+ }
+ return from, true
+}
+
+func (hs *headers) To() (*ToHeader, bool) {
+ hdrs := hs.GetHeaders("To")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ to, ok := hdrs[0].(*ToHeader)
+ if !ok {
+ return nil, false
+ }
+ return to, true
+}
+
+func (hs *headers) CSeq() (*CSeq, bool) {
+ hdrs := hs.GetHeaders("CSeq")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ cseq, ok := hdrs[0].(*CSeq)
+ if !ok {
+ return nil, false
+ }
+ return cseq, true
+}
+
+func (hs *headers) ContentLength() (*ContentLength, bool) {
+ hdrs := hs.GetHeaders("Content-Length")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ contentLength, ok := hdrs[0].(*ContentLength)
+ if !ok {
+ return nil, false
+ }
+ return contentLength, true
+}
+
+func (hs *headers) ContentType() (*ContentType, bool) {
+ hdrs := hs.GetHeaders("Content-Type")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ contentType, ok := hdrs[0].(*ContentType)
+ if !ok {
+ return nil, false
+ }
+ return contentType, true
+}
+
+func (hs *headers) Contact() (*ContactHeader, bool) {
+ hdrs := hs.GetHeaders("Contact")
+ if len(hdrs) == 0 {
+ return nil, false
+ }
+ contactHeader, ok := hdrs[0].(*ContactHeader)
+ if !ok {
+ return nil, false
+ }
+ return contactHeader, true
+}
+
+// basic message implementation
+type message struct {
+ // message headers
+ *headers
+ mu sync.RWMutex
+ messID MessageID
+ sipVersion string
+ body string
+ startLine func() string
+ tp string
+ src string
+ dest string
+ fields log.Fields
+}
+
+func (msg *message) MessageID() MessageID {
+ return msg.messID
+}
+
+func (msg *message) StartLine() string {
+ return msg.startLine()
+}
+
+func (msg *message) Fields() log.Fields {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.fields.WithFields(log.Fields{
+ "transport": msg.tp,
+ "source": msg.src,
+ "destination": msg.dest,
+ })
+}
+
+func (msg *message) String() string {
+ var buffer bytes.Buffer
+
+ // write message start line
+ buffer.WriteString(msg.StartLine() + "\r\n")
+ // Write the headers.
+ msg.mu.RLock()
+ buffer.WriteString(msg.headers.String())
+ msg.mu.RUnlock()
+ // message body
+ buffer.WriteString("\r\n" + msg.Body())
+
+ return buffer.String()
+}
+
+func (msg *message) SipVersion() string {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.sipVersion
+}
+
+func (msg *message) SetSipVersion(version string) {
+ msg.mu.Lock()
+ msg.sipVersion = version
+ msg.mu.Unlock()
+}
+
+func (msg *message) Body() string {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.body
+}
+
+// SetBody sets message body, calculates it length and add 'Content-Length' header.
+func (msg *message) SetBody(body string, setContentLength bool) {
+ msg.mu.Lock()
+ msg.body = body
+ msg.mu.Unlock()
+ if setContentLength {
+ hdrs := msg.GetHeaders("Content-Length")
+ if len(hdrs) == 0 {
+ length := ContentLength(len(body))
+ msg.AppendHeader(&length)
+ } else {
+ length := ContentLength(len(body))
+ msg.ReplaceHeaders("Content-Length", []Header{&length})
+ }
+ }
+}
+
+func (msg *message) Transport() string {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.tp
+}
+
+func (msg *message) SetTransport(tp string) {
+ msg.mu.Lock()
+ msg.tp = strings.ToUpper(tp)
+ msg.mu.Unlock()
+}
+
+func (msg *message) Source() string {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.src
+}
+
+func (msg *message) SetSource(src string) {
+ msg.mu.Lock()
+ msg.src = src
+ msg.mu.Unlock()
+}
+
+func (msg *message) Destination() string {
+ msg.mu.RLock()
+ defer msg.mu.RUnlock()
+ return msg.dest
+}
+
+func (msg *message) SetDestination(dest string) {
+ msg.mu.Lock()
+ msg.dest = dest
+ msg.mu.Unlock()
+}
+
+// Copy all headers of one type from one message to another.
+// Appending to any headers that were already there.
+func CopyHeaders(name string, from, to Message) {
+ name = strings.ToLower(name)
+ for _, h := range from.GetHeaders(name) {
+ to.AppendHeader(h.Clone())
+ }
+}
+
+func PrependCopyHeaders(name string, from, to Message) {
+ name = strings.ToLower(name)
+ for _, h := range from.GetHeaders(name) {
+ to.PrependHeader(h.Clone())
+ }
+}
+
+type MessageMapper func(msg Message) Message
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/README.md
new file mode 100644
index 0000000000..656e264fe7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/README.md
@@ -0,0 +1,5 @@
+# SIP parser
+
+> Package implements SIP protocol parser compatible with [RFC 3261](https://tools.ietf.org/html/rfc3261)
+
+Originally forked from [gossip](https://github.com/StefanKopieczek/gossip) library by @StefanKopieczek.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/error.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/error.go
new file mode 100644
index 0000000000..caea6ef3c7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/error.go
@@ -0,0 +1,26 @@
+package parser
+
+type Error interface {
+ error
+ // Syntax indicates that this is syntax error
+ Syntax() bool
+}
+
+type InvalidStartLineError string
+
+func (err InvalidStartLineError) Syntax() bool { return true }
+func (err InvalidStartLineError) Malformed() bool { return false }
+func (err InvalidStartLineError) Broken() bool { return true }
+func (err InvalidStartLineError) Error() string { return "parser.InvalidStartLineError: " + string(err) }
+
+type InvalidMessageFormat string
+
+func (err InvalidMessageFormat) Syntax() bool { return true }
+func (err InvalidMessageFormat) Malformed() bool { return true }
+func (err InvalidMessageFormat) Broken() bool { return true }
+func (err InvalidMessageFormat) Error() string { return "parser.InvalidMessageFormat: " + string(err) }
+
+type WriteError string
+
+func (err WriteError) Syntax() bool { return false }
+func (err WriteError) Error() string { return "parser.WriteError: " + string(err) }
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parser.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parser.go
new file mode 100644
index 0000000000..d9b797b684
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parser.go
@@ -0,0 +1,1685 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package parser
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/tevino/abool"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+// The whitespace characters recognised by the Augmented Backus-Naur Form syntax
+// that SIP uses (RFC 3261 S.25).
+const abnfWs = " \t"
+
+// The maximum permissible CSeq number in a SIP message (2**31 - 1).
+// C.f. RFC 3261 S. 8.1.1.5.
+const maxCseq = 2147483647
+
+// The buffer size of the parser input channel.
+
+// A Parser converts the raw bytes of SIP messages into core.Message objects.
+// It allows
+type Parser interface {
+ // Implements io.Writer. Queues the given bytes to be parsed.
+ // If the parser has terminated due to a previous fatal error, it will return n=0 and an appropriate error.
+ // Otherwise, it will return n=len(p) and err=nil.
+ // Note that err=nil does not indicate that the data provided is valid - simply that the data was successfully queued for parsing.
+ Write(p []byte) (n int, err error)
+ // Register a custom header parser for a particular header type.
+ // This will overwrite any existing registered parser for that header type.
+ // If a parser is not available for a header type in a message, the parser will produce a core.GenericHeader struct.
+ SetHeaderParser(headerName string, headerParser HeaderParser)
+
+ Stop()
+
+ String() string
+ // Reset resets parser state
+ Reset()
+
+ ParseHeader(headerText string) (headers []sip.Header, err error)
+}
+
+// A HeaderParser is any function that turns raw header data into one or more Header objects.
+// The HeaderParser will receive arguments of the form ("max-forwards", "70").
+// It should return a slice of headers, which should have length > 1 unless it also returns an error.
+type HeaderParser func(headerName string, headerData string) ([]sip.Header, error)
+
+func defaultHeaderParsers() map[string]HeaderParser {
+ return map[string]HeaderParser{
+ "to": parseAddressHeader,
+ "t": parseAddressHeader,
+ "from": parseAddressHeader,
+ "f": parseAddressHeader,
+ "contact": parseAddressHeader,
+ "m": parseAddressHeader,
+ "call-id": parseCallId,
+ "i": parseCallId,
+ "cseq": parseCSeq,
+ "via": parseViaHeader,
+ "v": parseViaHeader,
+ "max-forwards": parseMaxForwards,
+ "content-length": parseContentLength,
+ "l": parseContentLength,
+ "expires": parseExpires,
+ "user-agent": parseUserAgent,
+ "allow": parseAllow,
+ "content-type": parseContentType,
+ "c": parseContentType,
+ "accept": parseAccept,
+ "require": parseRequire,
+ "supported": parseSupported,
+ "k": parseSupported,
+ "route": parseRouteHeader,
+ "record-route": parseRecordRouteHeader,
+ //"content-encoding","e"
+ //"subject": "s",
+ }
+}
+
+// Parse a SIP message by creating a parser on the fly.
+// This is more costly than reusing a parser, but is necessary when we do not
+// have a guarantee that all messages coming over a connection are from the
+// same endpoint (e.g. UDP).
+func ParseMessage(msgData []byte, logger log.Logger) (sip.Message, error) {
+ output := make(chan sip.Message, 0)
+ errs := make(chan error, 0)
+
+ parser := NewParser(output, errs, false, logger)
+ defer parser.Stop()
+
+ if _, err := parser.Write(msgData); err != nil {
+ return nil, err
+ }
+
+ select {
+ case msg := <-output:
+ return msg, nil
+ case err := <-errs:
+ return nil, err
+ }
+}
+
+type PacketParser struct {
+ p Parser
+ out chan sip.Message
+ errs chan error
+}
+
+func NewPacketParser(logger log.Logger) *PacketParser {
+ output := make(chan sip.Message, 0)
+ errs := make(chan error, 0)
+
+ return &PacketParser{
+ p: NewParser(output, errs, false, logger),
+ out: output,
+ errs: errs,
+ }
+}
+
+func (pp *PacketParser) ParseMessage(msgData []byte) (sip.Message, error) {
+ //defer pp.p.Reset()
+
+ if _, err := pp.p.Write(msgData); err != nil {
+ return nil, err
+ }
+
+ select {
+ case msg := <-pp.out:
+ return msg, nil
+ case err := <-pp.errs:
+ return nil, err
+ }
+}
+
+func (pp *PacketParser) Stop() {
+ if pp == nil {
+ return
+ }
+ if pp.p != nil {
+ pp.p.Stop()
+ }
+ close(pp.out)
+ close(pp.errs)
+}
+
+// Create a new Parser.
+//
+// Parsed SIP messages will be sent down the 'output' chan provided.
+// Any errors which force the parser to terminate will be sent down the 'errs' chan provided.
+//
+// If streamed=false, each Write call to the parser should contain data for one complete SIP message.
+
+// If streamed=true, Write calls can contain a portion of a full SIP message.
+// The end of one message and the start of the next may be provided in a single call to Write.
+// When streamed=true, all SIP messages provided must have a Content-Length header.
+// SIP messages without a Content-Length will cause the parser to permanently stop, and will result in an error on the errs chan.
+
+// 'streamed' should be set to true whenever the caller cannot reliably identify the starts and ends of messages from the transport frames,
+// e.g. when using streamed protocols such as TCP.
+func NewParser(
+ output chan<- sip.Message,
+ errs chan<- error,
+ streamed bool,
+ logger log.Logger,
+) Parser {
+ p := &parser{
+ streamed: streamed,
+ done: make(chan struct{}),
+ }
+ p.log = logger.
+ WithPrefix("parser.Parser").
+ WithFields(log.Fields{
+ "parser_ptr": fmt.Sprintf("%p", p),
+ })
+ // Configure the parser with the standard set of header parsers.
+ p.headerParsers = make(map[string]HeaderParser)
+ for headerName, headerParser := range defaultHeaderParsers() {
+ p.SetHeaderParser(headerName, headerParser)
+ }
+
+ p.output = output
+ p.errs = errs
+
+ // Create a managed buffer to allow message data to be asynchronously provided to the parser, and
+ // to allow the parser to block until enough data is available to parse.
+ p.input = newParserBuffer(p.Log())
+ // Done for input a line at a time, and produce SipMessages to send down p.output.
+ go p.parse(streamed, p.done)
+
+ return p
+}
+
+type parser struct {
+ headerParsers map[string]HeaderParser
+ streamed bool
+ input *parserBuffer
+
+ output chan<- sip.Message
+ errs chan<- error
+
+ stopped abool.AtomicBool
+
+ mu sync.Mutex
+ done chan struct{}
+
+ log log.Logger
+}
+
+func (p *parser) String() string {
+ if p == nil {
+ return "Parser "
+ }
+ return fmt.Sprintf("Parser %p", p)
+}
+
+func (p *parser) Log() log.Logger {
+ return p.log
+}
+
+func (p *parser) Write(data []byte) (int, error) {
+ if p.stopped.IsSet() {
+ return 0, WriteError(fmt.Sprintf("cannot write data to stopped %s", p))
+ }
+
+ var (
+ num int
+ err error
+ )
+ if !p.streamed {
+ bl := getBodyLength(data)
+
+ if bl == -1 {
+ err = InvalidMessageFormat(fmt.Sprintf("%s cannot write data: double CRLF sequence not found in the input data", p))
+ return num, err
+ }
+
+ data = append([]byte(fmt.Sprintf("%d|%d\r\n", bl, len(data))), data...)
+ }
+
+ num, err = p.input.Write(data)
+ if err != nil {
+ err = WriteError(fmt.Sprintf("%s write data failed: %s", p, err))
+ return num, err
+ }
+
+ p.Log().Tracef("write %d bytes to input buffer", num)
+
+ return num, nil
+}
+
+// Stop parser processing, and allow all resources to be garbage collected.
+// The parser will not release its resources until Stop() is called,
+// even if the parser object itself is garbage collected.
+func (p *parser) Stop() {
+ if p.stopped.IsSet() {
+ return
+ }
+
+ p.Log().Debug("stopping parser...")
+
+ p.stopped.Set()
+
+ p.input.Stop()
+ p.mu.Lock()
+ done := p.done
+ p.mu.Unlock()
+ <-done
+
+ p.Log().Debug("parser stopped")
+}
+
+func (p *parser) Reset() {
+ p.Stop()
+ // reset state
+ p.mu.Lock()
+ done := make(chan struct{})
+ p.done = done
+ p.mu.Unlock()
+ p.input.Reset()
+ // and re-run
+ go p.parse(p.streamed, done)
+
+ p.stopped.UnSet()
+}
+
+// Consume input lines one at a time, producing core.Message objects and sending them down p.output.
+func (p *parser) parse(requireContentLength bool, done chan<- struct{}) {
+ defer close(done)
+
+ var msg sip.Message
+
+ p.Log().Debug("start parsing")
+ defer p.Log().Debug("stop parsing")
+
+ var skipStreamedErr bool
+
+ for {
+ var bodyLen, msgLen int
+ if !p.streamed {
+ // extract body/msg len
+ line, err := p.input.NextLine()
+ if err != nil {
+ break
+ }
+ strs := strings.Split(line, "|")
+ if len(strs) != 2 {
+ continue
+ }
+ bodyLen, err = strconv.Atoi(strs[0])
+ if err != nil {
+ continue
+ }
+ msgLen, err = strconv.Atoi(strs[1])
+ if err != nil {
+ continue
+ }
+ }
+ // Parse the StartLine.
+ startLine, err := p.input.NextLine()
+ if err != nil {
+ break
+ }
+
+ p.Log().Tracef("start reading start line: %s", startLine)
+
+ var termErr error
+ if isRequest(startLine) {
+ method, recipient, sipVersion, err := ParseRequestLine(startLine)
+ if err == nil {
+ msg = sip.NewRequest("", method, recipient, sipVersion, []sip.Header{}, "", nil)
+ } else {
+ termErr = err
+ }
+ } else if isResponse(startLine) {
+ sipVersion, statusCode, reason, err := ParseStatusLine(startLine)
+ if err == nil {
+ msg = sip.NewResponse("", sipVersion, statusCode, reason, []sip.Header{}, "", nil)
+ } else {
+ termErr = err
+ }
+ } else {
+ termErr = fmt.Errorf("transmission beginning '%s' is not a SIP message", startLine)
+ }
+
+ if termErr != nil {
+ p.Log().Tracef("%s failed to read start line '%s'", p, startLine)
+
+ termErr = InvalidStartLineError(fmt.Sprintf("%s failed to parse first line of message: %s", p, termErr))
+
+ if p.streamed {
+ if !skipStreamedErr {
+ skipStreamedErr = true
+ p.errs <- termErr
+ }
+ } else {
+ skip := msgLen - len(startLine) - 2
+
+ p.Log().Tracef("skip %d - %d - 2 = %d bytes", p, msgLen, len(startLine), skip)
+
+ if _, err := p.input.NextChunk(skip); err != nil {
+ p.Log().Errorf("skip failed: %s", err)
+ }
+
+ p.errs <- termErr
+ }
+
+ continue
+ } else {
+ skipStreamedErr = false
+ }
+
+ p.Log().Tracef("%s starts reading headers", p)
+ // Parse the header section.
+ // Headers can be split across lines (marked by whitespace at the start of subsequent lines),
+ // so store lines into a buffer, and then flush and parse it when we hit the end of the header.
+ var buffer bytes.Buffer
+ headers := make([]sip.Header, 0)
+
+ flushBuffer := func() {
+ if buffer.Len() > 0 {
+ newHeaders, err := p.ParseHeader(buffer.String())
+ if err == nil {
+ headers = append(headers, newHeaders...)
+ } else {
+ p.Log().Warnf("skip header '%s' due to error: %s", buffer, err)
+ }
+ buffer.Reset()
+ }
+ }
+
+ for {
+ line, err := p.input.NextLine()
+
+ if err != nil {
+ break
+ }
+
+ if len(line) == 0 {
+ // We've hit the end of the header section.
+ // Parse anything remaining in the buffer, then break out.
+ flushBuffer()
+
+ break
+ }
+
+ if !strings.Contains(abnfWs, string(line[0])) {
+ // This line starts a new header.
+ // Parse anything currently in the buffer, then store the new header line in the buffer.
+ flushBuffer()
+ buffer.WriteString(line)
+ } else if buffer.Len() > 0 {
+ // This is a continuation line, so just add it to the buffer.
+ buffer.WriteString(" ")
+ buffer.WriteString(line)
+ } else {
+ // This is a continuation line, but also the first line of the whole header section.
+ // Discard it and log.
+ p.Log().Tracef(
+ "discard unexpected continuation line '%s' at start of header block in message '%s'",
+ line,
+ msg.Short(),
+ )
+ }
+ }
+
+ // Store the headers in the message object.
+ for _, header := range headers {
+ msg.AppendHeader(header)
+ }
+
+ var contentLength int
+ // Determine the length of the body, so we know when to stop parsing this message.
+ if p.streamed {
+ // Use the content-length header to identify the end of the message.
+ contentLengthHeaders := msg.GetHeaders("Content-Length")
+ if len(contentLengthHeaders) == 0 {
+ skipStreamedErr = true
+
+ termErr := &sip.MalformedMessageError{
+ Err: fmt.Errorf("missing required 'Content-Length' header"),
+ Msg: msg.String(),
+ }
+ p.errs <- termErr
+
+ continue
+ } else if len(contentLengthHeaders) > 1 {
+ skipStreamedErr = true
+
+ var errbuf bytes.Buffer
+ errbuf.WriteString("multiple 'Content-Length' headers on message '")
+ errbuf.WriteString(msg.Short())
+ errbuf.WriteString(fmt.Sprintf("'; parser: %s:\n", p))
+ for _, header := range contentLengthHeaders {
+ errbuf.WriteString("\t")
+ errbuf.WriteString(header.String())
+ }
+ termErr := &sip.MalformedMessageError{
+ Err: errors.New(errbuf.String()),
+ Msg: msg.String(),
+ }
+ p.errs <- termErr
+
+ continue
+ }
+
+ contentLength = int(*(contentLengthHeaders[0].(*sip.ContentLength)))
+ } else {
+ contentLength = bodyLen
+ }
+
+ // Extract the message body.
+ p.Log().Tracef("%s reads body with length = %d bytes", p, contentLength)
+ body, err := p.input.NextChunk(contentLength)
+ if err != nil {
+ termErr := &sip.BrokenMessageError{
+ Err: fmt.Errorf("read message body failed: %w", err),
+ Msg: msg.String(),
+ }
+ p.errs <- termErr
+
+ continue
+ }
+ // RFC 3261 - 18.3.
+ if len(body) != contentLength {
+ termErr := &sip.BrokenMessageError{
+ Err: fmt.Errorf("incomplete message body: read %d bytes, expected %d bytes", len(body), contentLength),
+ Msg: msg.String(),
+ }
+ p.errs <- termErr
+
+ continue
+ }
+
+ if strings.TrimSpace(body) != "" {
+ msg.SetBody(body, false)
+ }
+
+ p.output <- msg
+ }
+ return
+}
+
+// Implements ParserFactory.SetHeaderParser.
+func (p *parser) SetHeaderParser(headerName string, headerParser HeaderParser) {
+ headerName = strings.ToLower(headerName)
+ p.headerParsers[headerName] = headerParser
+}
+
+// Calculate the size of a SIP message's body, given the entire contents of the message as a byte array.
+func getBodyLength(data []byte) int {
+ s := string(data)
+
+ // Body starts with first character following a double-CRLF.
+ idx := strings.Index(s, "\r\n\r\n")
+ if idx == -1 {
+ return -1
+ }
+
+ bodyStart := idx + 4
+
+ return len(s) - bodyStart
+}
+
+// Heuristic to determine if the given transmission looks like a SIP request.
+// It is guaranteed that any RFC3261-compliant request will pass this test,
+// but invalid messages may not necessarily be rejected.
+func isRequest(startLine string) bool {
+ // SIP request lines contain precisely two spaces.
+ if strings.Count(startLine, " ") != 2 {
+ return false
+ }
+
+ // Check that the version string starts with SIP.
+ parts := strings.Split(startLine, " ")
+ if len(parts) < 3 {
+ return false
+ } else if len(parts[2]) < 3 {
+ return false
+ } else {
+ return strings.ToUpper(parts[2][:3]) == "SIP"
+ }
+}
+
+// Heuristic to determine if the given transmission looks like a SIP response.
+// It is guaranteed that any RFC3261-compliant response will pass this test,
+// but invalid messages may not necessarily be rejected.
+func isResponse(startLine string) bool {
+ // SIP status lines contain at least two spaces.
+ if strings.Count(startLine, " ") < 2 {
+ return false
+ }
+
+ // Check that the version string starts with SIP.
+ parts := strings.Split(startLine, " ")
+ if len(parts) < 3 {
+ return false
+ } else if len(parts[0]) < 3 {
+ return false
+ } else {
+ return strings.ToUpper(parts[0][:3]) == "SIP"
+ }
+}
+
+// Parse the first line of a SIP request, e.g:
+//
+// INVITE bob@example.com SIP/2.0
+// REGISTER jane@telco.com SIP/1.0
+func ParseRequestLine(requestLine string) (
+ method sip.RequestMethod, recipient sip.Uri, sipVersion string, err error) {
+ parts := strings.Split(requestLine, " ")
+ if len(parts) != 3 {
+ err = fmt.Errorf("request line should have 2 spaces: '%s'", requestLine)
+ return
+ }
+
+ method = sip.RequestMethod(strings.ToUpper(parts[0]))
+ recipient, err = ParseUri(parts[1])
+ sipVersion = parts[2]
+
+ switch recipient.(type) {
+ case *sip.WildcardUri:
+ err = fmt.Errorf("wildcard URI '*' not permitted in request line: '%s'", requestLine)
+ }
+
+ return
+}
+
+// Parse the first line of a SIP response, e.g:
+//
+// SIP/2.0 200 OK
+// SIP/1.0 403 Forbidden
+func ParseStatusLine(statusLine string) (
+ sipVersion string, statusCode sip.StatusCode, reasonPhrase string, err error) {
+ parts := strings.Split(statusLine, " ")
+ if len(parts) < 3 {
+ err = fmt.Errorf("status line has too few spaces: '%s'", statusLine)
+ return
+ }
+
+ sipVersion = parts[0]
+ statusCodeRaw, err := strconv.ParseUint(parts[1], 10, 16)
+ statusCode = sip.StatusCode(statusCodeRaw)
+ reasonPhrase = strings.Join(parts[2:], " ")
+
+ return
+}
+
+// parseUri converts a string representation of a URI into a Uri object.
+// If the URI is malformed, or the URI schema is not recognised, an error is returned.
+// URIs have the general form of schema:address.
+func ParseUri(uriStr string) (uri sip.Uri, err error) {
+ if strings.TrimSpace(uriStr) == "*" {
+ // Wildcard '*' URI used in the Contact headers of REGISTERs when unregistering.
+ return sip.WildcardUri{}, nil
+ }
+
+ colonIdx := strings.Index(uriStr, ":")
+ if colonIdx == -1 {
+ err = fmt.Errorf("no ':' in URI %s", uriStr)
+ return
+ }
+
+ switch strings.ToLower(uriStr[:colonIdx]) {
+ case "sip":
+ var sipUri sip.SipUri
+ sipUri, err = ParseSipUri(uriStr)
+ uri = &sipUri
+ case "sips":
+ // SIPS URIs have the same form as SIP uris, so we use the same parser.
+ var sipUri sip.SipUri
+ sipUri, err = ParseSipUri(uriStr)
+ uri = &sipUri
+ default:
+ err = fmt.Errorf("unsupported URI schema %s", uriStr[:colonIdx])
+ }
+
+ return
+}
+
+// ParseSipUri converts a string representation of a SIP or SIPS URI into a SipUri object.
+func ParseSipUri(uriStr string) (uri sip.SipUri, err error) {
+ // Store off the original URI in case we need to print it in an error.
+ uriStrCopy := uriStr
+
+ // URI should start 'sip' or 'sips'. Check the first 3 chars.
+ if strings.ToLower(uriStr[:3]) != "sip" {
+ err = fmt.Errorf("invalid SIP uri protocol name in '%s'", uriStrCopy)
+ return
+ }
+ uriStr = uriStr[3:]
+
+ if strings.ToLower(uriStr[0:1]) == "s" {
+ // URI started 'sips', so it's encrypted.
+ uri.FIsEncrypted = true
+ uriStr = uriStr[1:]
+ }
+
+ // The 'sip' or 'sips' protocol name should be followed by a ':' character.
+ if uriStr[0] != ':' {
+ err = fmt.Errorf("no ':' after protocol name in SIP uri '%s'", uriStrCopy)
+ return
+ }
+ uriStr = uriStr[1:]
+
+ // SIP URIs may contain a user-info part, ending in a '@'.
+ // This is the only place '@' may occur, so we can use it to check for the
+ // existence of a user-info part.
+ endOfUserInfoPart := strings.Index(uriStr, "@")
+ if endOfUserInfoPart != -1 {
+ // A user-info part is present. These take the form:
+ // user [ ":" password ] "@"
+ endOfUsernamePart := strings.Index(uriStr, ":")
+ if endOfUsernamePart > endOfUserInfoPart {
+ endOfUsernamePart = -1
+ }
+
+ if endOfUsernamePart == -1 {
+ // No password component; the whole of the user-info part before
+ // the '@' is a username.
+ if username, er := sip.Unescape(uriStr[:endOfUserInfoPart], sip.EncodeUserPassword); er == nil {
+ uri.FUser = sip.String{Str: username}
+ } else {
+ err = fmt.Errorf("unescape username: %w", er)
+ return
+ }
+ } else {
+ if username, er := sip.Unescape(uriStr[:endOfUsernamePart], sip.EncodeUserPassword); er == nil {
+ uri.FUser = sip.String{Str: username}
+ } else {
+ err = fmt.Errorf("unescape username: %w", er)
+ return
+ }
+ if password, er := sip.Unescape(uriStr[endOfUsernamePart+1:endOfUserInfoPart], sip.EncodeUserPassword); er == nil {
+ uri.FPassword = sip.String{Str: password}
+ } else {
+ err = fmt.Errorf("unescape password: %w", er)
+ return
+ }
+ }
+ uriStr = uriStr[endOfUserInfoPart+1:]
+ }
+
+ // A ';' indicates the beginning of a URI params section, and the end of the URI itself.
+ endOfUriPart := strings.Index(uriStr, ";")
+ if endOfUriPart == -1 {
+ // There are no URI parameters, but there might be header parameters (introduced by '?').
+ endOfUriPart = strings.Index(uriStr, "?")
+ }
+ if endOfUriPart == -1 {
+ // There are no parameters at all. The URI ends after the host[:port] part.
+ endOfUriPart = len(uriStr)
+ }
+
+ uri.FHost, uri.FPort, err = ParseHostPort(uriStr[:endOfUriPart])
+ uriStr = uriStr[endOfUriPart:]
+ if err != nil {
+ return
+ } else if len(uriStr) == 0 {
+ uri.FUriParams = sip.NewParams()
+ uri.FHeaders = sip.NewParams()
+ return
+ }
+
+ // Now parse any URI parameters.
+ // These are key-value pairs separated by ';'.
+ // They end at the end of the URI, or at the start of any URI headers
+ // which may be present (denoted by an initial '?').
+ var uriParams sip.Params
+ var n int
+ if uriStr[0] == ';' {
+ uriParams, n, err = ParseParams(uriStr, ';', ';', '?', true, true)
+ if err != nil {
+ return
+ }
+ } else {
+ uriParams, n = sip.NewParams(), 0
+ }
+ uri.FUriParams = uriParams
+ uriStr = uriStr[n:]
+
+ // Finally parse any URI headers.
+ // These are key-value pairs, starting with a '?' and separated by '&'.
+ var headers sip.Params
+ headers, n, err = ParseParams(uriStr, '?', '&', 0, true, false)
+ if err != nil {
+ return
+ }
+ uri.FHeaders = headers
+ uriStr = uriStr[n:]
+ if len(uriStr) > 0 {
+ err = fmt.Errorf("internal error: parse of SIP uri ended early! '%s'",
+ uriStrCopy)
+ return // Defensive return
+ }
+
+ return
+}
+
+// Parse a text representation of a host[:port] pair.
+// The port may or may not be present, so we represent it with a *uint16,
+// and return 'nil' if no port was present.
+func ParseHostPort(rawText string) (host string, port *sip.Port, err error) {
+ var rawHost, rawPort string
+ if i := strings.LastIndex(rawText, ":"); i == -1 {
+ rawHost = rawText
+ } else {
+ rawHost = rawText[:i]
+ rawPort = rawText[i+1:]
+ }
+
+ if strings.HasPrefix(rawHost, "[") {
+ // IPv6 with zone
+ if zone := strings.Index(rawHost, "%25"); zone >= 0 {
+ host1, er := sip.Unescape(rawHost[:zone], sip.EncodeHost)
+ if er != nil {
+ err = fmt.Errorf("unescape host: %w", er)
+ return
+ }
+ host2, er := sip.Unescape(rawHost[zone:len(rawHost)-1], sip.EncodeZone)
+ if er != nil {
+ err = fmt.Errorf("unescape zone: %w", er)
+ return
+ }
+ host3, er := sip.Unescape(rawHost[len(rawHost)-1:], sip.EncodeHost)
+ if er != nil {
+ err = fmt.Errorf("unescape host: %w", er)
+ return
+ }
+ host = host1 + host2 + host3
+ }
+ }
+ if host == "" {
+ // IPv4 or IPv6 without zone
+ if h, er := sip.Unescape(rawHost, sip.EncodeHost); er == nil {
+ host = h
+ } else {
+ err = fmt.Errorf("unescape host: %w", er)
+ return
+ }
+ }
+
+ if rawPort != "" {
+ // Surely there must be a better way..!
+ var portRaw64 uint64
+ var portRaw16 uint16
+ portRaw64, err = strconv.ParseUint(rawPort, 10, 16)
+ if err != nil {
+ err = fmt.Errorf("parse port: %w", err)
+ return
+ }
+ portRaw16 = uint16(portRaw64)
+ port = (*sip.Port)(&portRaw16)
+ }
+
+ return
+}
+
+// General utility method for parsing 'key=value' parameters.
+// Takes a string (source), ensures that it begins with the 'start' character provided,
+// and then parses successive key/value pairs separated with 'sep',
+// until either 'end' is reached or there are no characters remaining.
+// A map of keys to values will be returned, along with the number of characters consumed.
+// Provide 0 for start or end to indicate that there is no starting/ending delimiter.
+// If quoteValues is true, values can be enclosed in double-quotes which will be validated by the
+// parser and omitted from the returned map.
+// If permitSingletons is true, keys with no values are permitted.
+// These will result in a nil value in the returned map.
+func ParseParams(
+ source string,
+ start uint8,
+ sep uint8,
+ end uint8,
+ quoteValues bool,
+ permitSingletons bool,
+) (params sip.Params, consumed int, err error) {
+ params = sip.NewParams()
+
+ if len(source) == 0 {
+ // Key-value section is completely empty; return defaults.
+ return
+ }
+
+ // Ensure the starting character is correct.
+ if start != 0 {
+ if source[0] != start {
+ err = fmt.Errorf(
+ "expected %c at start of key-value section; got %c. section was %s",
+ start,
+ source[0],
+ source,
+ )
+ return
+ }
+ consumed++
+ }
+
+ // Statefully parse the given string one character at a time.
+ var buffer bytes.Buffer
+ var key string
+ parsingKey := true // false implies we are parsing a value
+ inQuotes := false
+parseLoop:
+ for ; consumed < len(source); consumed++ {
+ switch source[consumed] {
+ case end:
+ if inQuotes {
+ // We read an end character, but since we're inside quotations we should
+ // treat it as a literal part of the value.
+ buffer.WriteString(string(end))
+ continue
+ }
+
+ break parseLoop
+
+ case sep:
+ if inQuotes {
+ // We read a separator character, but since we're inside quotations
+ // we should treat it as a literal part of the value.
+ buffer.WriteString(string(sep))
+ continue
+ }
+ if parsingKey && permitSingletons {
+ if k, er := sip.Unescape(buffer.String(), sip.EncodeQueryComponent); er == nil {
+ params.Add(k, nil)
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ } else if parsingKey {
+ err = fmt.Errorf(
+ "singleton param '%s' when parsing params which disallow singletons: \"%s\"",
+ buffer.String(),
+ source,
+ )
+ return
+ } else {
+ if k, er := sip.Unescape(key, sip.EncodeQueryComponent); er == nil {
+ if v, er := sip.Unescape(buffer.String(), sip.EncodeQueryComponent); er == nil {
+ params.Add(k, sip.String{Str: v})
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ }
+ buffer.Reset()
+ parsingKey = true
+
+ case '"':
+ if !quoteValues {
+ // We hit a quote character, but since quoting is turned off we treat it as a literal.
+ buffer.WriteString("\"")
+ continue
+ }
+
+ if parsingKey {
+ // Quotes are never allowed in keys.
+ err = fmt.Errorf("unexpected '\"' in parameter key in params \"%s\"", source)
+ return
+ }
+
+ if !inQuotes && buffer.Len() != 0 {
+ // We hit an initial quote midway through a value; that's not allowed.
+ err = fmt.Errorf("unexpected '\"' in params \"%s\"", source)
+ return
+ }
+
+ if inQuotes &&
+ consumed != len(source)-1 &&
+ source[consumed+1] != sep {
+ // We hit an end-quote midway through a value; that's not allowed.
+ err = fmt.Errorf("unexpected character %c after quoted param in \"%s\"",
+ source[consumed+1], source)
+
+ return
+ }
+
+ inQuotes = !inQuotes
+
+ case '=':
+ if buffer.Len() == 0 {
+ err = fmt.Errorf("key of length 0 in params \"%s\"", source)
+ return
+ }
+ if !parsingKey {
+ err = fmt.Errorf("unexpected '=' char in value token: \"%s\"", source)
+ return
+ }
+ key = buffer.String()
+ buffer.Reset()
+ parsingKey = false
+
+ default:
+ if !inQuotes && strings.Contains(abnfWs, string(source[consumed])) {
+ // Skip unquoted whitespace.
+ continue
+ }
+
+ buffer.WriteString(string(source[consumed]))
+ }
+ }
+
+ // The param string has ended. Check that it ended in a valid place, and then store off the
+ // contents of the buffer.
+ if inQuotes {
+ err = fmt.Errorf("unclosed quotes in parameter string: %s", source)
+ } else if parsingKey && permitSingletons {
+ if k, er := sip.Unescape(buffer.String(), sip.EncodeQueryComponent); er == nil {
+ params.Add(k, nil)
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ } else if parsingKey {
+ err = fmt.Errorf("singleton param '%s' when parsing params which disallow singletons: \"%s\"",
+ buffer.String(), source)
+ } else {
+ if k, er := sip.Unescape(key, sip.EncodeQueryComponent); er == nil {
+ if v, er := sip.Unescape(buffer.String(), sip.EncodeQueryComponent); er == nil {
+ params.Add(k, sip.String{Str: v})
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ } else {
+ err = fmt.Errorf("unescape params: %w", er)
+ return
+ }
+ }
+ return
+}
+
+// Parse a header string, producing one or more Header objects.
+// (SIP messages containing multiple headers of the same type can express them as a
+// single header containing a comma-separated argument list).
+func (p *parser) ParseHeader(headerText string) (headers []sip.Header, err error) {
+ p.Log().Tracef("parsing header \"%s\"", headerText)
+
+ headers = make([]sip.Header, 0)
+
+ colonIdx := strings.Index(headerText, ":")
+ if colonIdx == -1 {
+ err = fmt.Errorf("field name with no value in header: %s", headerText)
+ return
+ }
+
+ fieldName := strings.TrimSpace(headerText[:colonIdx])
+ lowerFieldName := strings.ToLower(fieldName)
+ fieldText := strings.TrimSpace(headerText[colonIdx+1:])
+ if headerParser, ok := p.headerParsers[lowerFieldName]; ok {
+ // We have a registered parser for this header type - use it.
+ headers, err = headerParser(lowerFieldName, fieldText)
+ } else {
+ // We have no registered parser for this header type,
+ // so we encapsulate the header data in a GenericHeader struct.
+ p.Log().Tracef("no parser for header type %s", fieldName)
+
+ header := sip.GenericHeader{
+ HeaderName: fieldName,
+ Contents: fieldText,
+ }
+ headers = []sip.Header{&header}
+ }
+
+ return
+}
+
+// Parse a To, From or Contact header line, producing one or more logical SipHeaders.
+func parseAddressHeader(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ switch headerName {
+ case "to", "from", "contact", "t", "f", "m":
+ var displayNames []sip.MaybeString
+ var uris []sip.Uri
+ var paramSets []sip.Params
+
+ // Perform the actual parsing. The rest of this method is just typeclass bookkeeping.
+ displayNames, uris, paramSets, err = ParseAddressValues(headerText)
+
+ if err != nil {
+ return
+ }
+ if len(displayNames) != len(uris) || len(uris) != len(paramSets) {
+ // This shouldn't happen unless ParseAddressValues is bugged.
+ err = fmt.Errorf("internal parser error: parsed param mismatch. "+
+ "%d display names, %d uris and %d param sets "+
+ "in %s",
+ len(displayNames), len(uris), len(paramSets),
+ headerText)
+ return
+ }
+
+ // Build a slice of headers of the appropriate kind, populating them with the values parsed above.
+ // It is assumed that all headers returned by ParseAddressValues are of the same kind,
+ // although we do not check for this below.
+ for idx := 0; idx < len(displayNames); idx++ {
+ var header sip.Header
+ if headerName == "to" || headerName == "t" {
+ if idx > 0 {
+ // Only a single To header is permitted in a SIP message.
+ return nil,
+ fmt.Errorf("multiple to: headers in message:\n%s: %s",
+ headerName, headerText)
+ }
+ switch uris[idx].(type) {
+ case sip.WildcardUri:
+ // The Wildcard '*' URI is only permitted in Contact headers.
+ err = fmt.Errorf(
+ "wildcard uri not permitted in to: header: %s",
+ headerText,
+ )
+ return
+ default:
+ toHeader := sip.ToHeader{
+ DisplayName: displayNames[idx],
+ Address: uris[idx],
+ Params: paramSets[idx],
+ }
+ header = &toHeader
+ }
+ } else if headerName == "from" || headerName == "f" {
+ if idx > 0 {
+ // Only a single From header is permitted in a SIP message.
+ return nil,
+ fmt.Errorf(
+ "multiple from: headers in message:\n%s: %s",
+ headerName,
+ headerText,
+ )
+ }
+ switch uris[idx].(type) {
+ case sip.WildcardUri:
+ // The Wildcard '*' URI is only permitted in Contact headers.
+ err = fmt.Errorf(
+ "wildcard uri not permitted in from: header: %s",
+ headerText,
+ )
+ return
+ default:
+ fromHeader := sip.FromHeader{
+ DisplayName: displayNames[idx],
+ Address: uris[idx],
+ Params: paramSets[idx],
+ }
+ header = &fromHeader
+ }
+ } else if headerName == "contact" || headerName == "m" {
+ switch uris[idx].(type) {
+ case sip.ContactUri:
+ if uris[idx].(sip.ContactUri).IsWildcard() {
+ if paramSets[idx].Length() > 0 {
+ // Wildcard headers do not contain parameters.
+ err = fmt.Errorf(
+ "wildcard contact header should contain no parameters: '%s",
+ headerText,
+ )
+ return
+ }
+ if displayNames[idx] != nil {
+ // Wildcard headers do not contain display names.
+ err = fmt.Errorf(
+ "wildcard contact header should contain no display name %s",
+ headerText,
+ )
+ return
+ }
+ }
+ contactHeader := sip.ContactHeader{
+ DisplayName: displayNames[idx],
+ Address: uris[idx].(sip.ContactUri),
+ Params: paramSets[idx],
+ }
+ header = &contactHeader
+ default:
+ // URIs in contact headers are restricted to being either SIP URIs or 'Contact: *'.
+ return nil,
+ fmt.Errorf(
+ "uri %s not valid in Contact header. Must be SIP uri or '*'",
+ uris[idx].String(),
+ )
+ }
+ }
+
+ headers = append(headers, header)
+ }
+ }
+
+ return
+}
+
+// Parse a string representation of a CSeq header, returning a slice of at most one CSeq.
+func parseCSeq(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ var cseq sip.CSeq
+
+ parts := SplitByWhitespace(headerText)
+ if len(parts) != 2 {
+ err = fmt.Errorf(
+ "CSeq field should have precisely one whitespace section: '%s'",
+ headerText,
+ )
+ return
+ }
+
+ var seqno uint64
+ seqno, err = strconv.ParseUint(parts[0], 10, 32)
+ if err != nil {
+ return
+ }
+
+ if seqno > maxCseq {
+ err = fmt.Errorf("invalid CSeq %d: exceeds maximum permitted value "+
+ "2**31 - 1", seqno)
+ return
+ }
+
+ cseq.SeqNo = uint32(seqno)
+ cseq.MethodName = sip.RequestMethod(strings.TrimSpace(parts[1]))
+
+ if strings.Contains(string(cseq.MethodName), ";") {
+ err = fmt.Errorf("unexpected ';' in CSeq body: %s", headerText)
+ return
+ }
+
+ headers = []sip.Header{&cseq}
+
+ return
+}
+
+// Parse a string representation of a Call-ID header, returning a slice of at most one CallID.
+func parseCallId(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ headerText = strings.TrimSpace(headerText)
+ var callId = sip.CallID(headerText)
+
+ if strings.ContainsAny(string(callId), abnfWs) {
+ err = fmt.Errorf("unexpected whitespace in CallID header body '%s'", headerText)
+ return
+ }
+ if strings.Contains(string(callId), ";") {
+ err = fmt.Errorf("unexpected semicolon in CallID header body '%s'", headerText)
+ return
+ }
+ if len(string(callId)) == 0 {
+ err = fmt.Errorf("empty Call-ID body")
+ return
+ }
+
+ headers = []sip.Header{&callId}
+
+ return
+}
+
+// Parse a string representation of a Via header, returning a slice of at most one ViaHeader.
+// Note that although Via headers may contain a comma-separated list, RFC 3261 makes it clear that
+// these should not be treated as separate logical Via headers, but as multiple values on a single
+// Via header.
+func parseViaHeader(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ sections := strings.Split(headerText, ",")
+ var via = sip.ViaHeader{}
+ for _, section := range sections {
+ var hop sip.ViaHop
+ parts := strings.Split(section, "/")
+
+ if len(parts) < 3 {
+ err = fmt.Errorf("not enough protocol parts in via header: '%s'", parts)
+ return
+ }
+
+ parts[2] = strings.Join(parts[2:], "/")
+
+ // The transport part ends when whitespace is reached, but may also start with
+ // whitespace.
+ // So the end of the transport part is the first whitespace char following the
+ // first non-whitespace char.
+ initialSpaces := len(parts[2]) - len(strings.TrimLeft(parts[2], abnfWs))
+ sentByIdx := strings.IndexAny(parts[2][initialSpaces:], abnfWs) + initialSpaces + 1
+ if sentByIdx == 0 {
+ err = fmt.Errorf("expected whitespace after sent-protocol part "+
+ "in via header '%s'", section)
+ return
+ } else if sentByIdx == 1 {
+ err = fmt.Errorf("empty transport field in via header '%s'", section)
+ return
+ }
+
+ hop.ProtocolName = strings.TrimSpace(parts[0])
+ hop.ProtocolVersion = strings.TrimSpace(parts[1])
+ hop.Transport = strings.TrimSpace(parts[2][:sentByIdx-1])
+
+ if len(hop.ProtocolName) == 0 {
+ err = fmt.Errorf("no protocol name provided in via header '%s'", section)
+ } else if len(hop.ProtocolVersion) == 0 {
+ err = fmt.Errorf("no version provided in via header '%s'", section)
+ } else if len(hop.Transport) == 0 {
+ err = fmt.Errorf("no transport provided in via header '%s'", section)
+ }
+ if err != nil {
+ return
+ }
+
+ viaBody := parts[2][sentByIdx:]
+
+ paramsIdx := strings.Index(viaBody, ";")
+ var host string
+ var port *sip.Port
+ if paramsIdx == -1 {
+ // There are no header parameters, so the rest of the Via body is part of the host[:post].
+ host, port, err = ParseHostPort(viaBody)
+ hop.Host = host
+ hop.Port = port
+ if err != nil {
+ return
+ }
+ hop.Params = sip.NewParams()
+ } else {
+ host, port, err = ParseHostPort(viaBody[:paramsIdx])
+ if err != nil {
+ return
+ }
+ hop.Host = host
+ hop.Port = port
+
+ hop.Params, _, err = ParseParams(viaBody[paramsIdx:],
+ ';', ';', 0, true, true)
+ }
+ via = append(via, &hop)
+ }
+
+ headers = []sip.Header{via}
+ return
+}
+
+// Parse a string representation of a Max-Forwards header into a slice of at most one MaxForwards header object.
+func parseMaxForwards(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ var maxForwards sip.MaxForwards
+ var value uint64
+ value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32)
+ maxForwards = sip.MaxForwards(value)
+
+ headers = []sip.Header{&maxForwards}
+ return
+}
+
+func parseExpires(headerName string, headerText string) (headers []sip.Header, err error) {
+ var expires sip.Expires
+ var value uint64
+ value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32)
+ expires = sip.Expires(value)
+ headers = []sip.Header{&expires}
+
+ return
+}
+
+func parseUserAgent(headerName string, headerText string) (headers []sip.Header, err error) {
+ var userAgent sip.UserAgentHeader
+ headerText = strings.TrimSpace(headerText)
+ userAgent = sip.UserAgentHeader(headerText)
+ headers = []sip.Header{&userAgent}
+
+ return
+}
+
+func parseContentType(headerName string, headerText string) (headers []sip.Header, err error) {
+ var contentType sip.ContentType
+ headerText = strings.TrimSpace(headerText)
+ contentType = sip.ContentType(headerText)
+ headers = []sip.Header{&contentType}
+
+ return
+}
+
+func parseAccept(headerName string, headerText string) (headers []sip.Header, err error) {
+ var accept sip.Accept
+ headerText = strings.TrimSpace(headerText)
+ accept = sip.Accept(headerText)
+ headers = []sip.Header{&accept}
+
+ return
+}
+
+func parseAllow(headerName string, headerText string) (headers []sip.Header, err error) {
+ allow := make(sip.AllowHeader, 0)
+ methods := strings.Split(headerText, ",")
+ for _, method := range methods {
+ allow = append(allow, sip.RequestMethod(strings.TrimSpace(method)))
+ }
+ headers = []sip.Header{allow}
+
+ return
+}
+
+func parseRequire(headerName string, headerText string) (headers []sip.Header, err error) {
+ var require sip.RequireHeader
+ require.Options = make([]string, 0)
+ extensions := strings.Split(headerText, ",")
+ for _, ext := range extensions {
+ require.Options = append(require.Options, strings.TrimSpace(ext))
+ }
+ headers = []sip.Header{&require}
+
+ return
+}
+
+func parseSupported(headerName string, headerText string) (headers []sip.Header, err error) {
+ var supported sip.SupportedHeader
+ supported.Options = make([]string, 0)
+ extensions := strings.Split(headerText, ",")
+ for _, ext := range extensions {
+ supported.Options = append(supported.Options, strings.TrimSpace(ext))
+ }
+ headers = []sip.Header{&supported}
+
+ return
+}
+
+// Parse a string representation of a Content-Length header into a slice of at most one ContentLength header object.
+func parseContentLength(headerName string, headerText string) (
+ headers []sip.Header, err error) {
+ var contentLength sip.ContentLength
+ var value uint64
+ value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32)
+ contentLength = sip.ContentLength(value)
+
+ headers = []sip.Header{&contentLength}
+ return
+}
+
+// ParseAddressValues parses a comma-separated list of addresses, returning
+// any display names and header params, as well as the SIP URIs themselves.
+// ParseAddressValues is aware of < > bracketing and quoting, and will not
+// break on commas within these structures.
+func ParseAddressValues(addresses string) (
+ displayNames []sip.MaybeString,
+ uris []sip.Uri,
+ headerParams []sip.Params,
+ err error,
+) {
+
+ prevIdx := 0
+ inBrackets := false
+ inQuotes := false
+
+ // Append a comma to simplify the parsing code; we split address sections
+ // on commas, so use a comma to signify the end of the final address section.
+ addresses = addresses + ","
+
+ for idx, char := range addresses {
+ if char == '<' && !inQuotes {
+ inBrackets = true
+ } else if char == '>' && !inQuotes {
+ inBrackets = false
+ } else if char == '"' {
+ inQuotes = !inQuotes
+ } else if !inQuotes && !inBrackets && char == ',' {
+ var displayName sip.MaybeString
+ var uri sip.Uri
+ var params sip.Params
+ displayName, uri, params, err = ParseAddressValue(addresses[prevIdx:idx])
+ if err != nil {
+ return
+ }
+ prevIdx = idx + 1
+
+ displayNames = append(displayNames, displayName)
+ uris = append(uris, uri)
+ headerParams = append(headerParams, params)
+ }
+ }
+
+ return
+}
+
+// ParseAddressValue parses an address - such as from a From, To, or
+// Contact header. It returns:
+// - a MaybeString containing the display name (or not)
+// - a parsed SipUri object
+// - a map containing any header parameters present
+// - the error object
+//
+// See RFC 3261 section 20.10 for details on parsing an address.
+// Note that this method will not accept a comma-separated list of addresses;
+// addresses in that form should be handled by ParseAddressValues.
+func ParseAddressValue(addressText string) (
+ displayName sip.MaybeString,
+ uri sip.Uri,
+ headerParams sip.Params,
+ err error,
+) {
+
+ headerParams = sip.NewParams()
+
+ if len(addressText) == 0 {
+ err = fmt.Errorf("address-type header has empty body")
+ return
+ }
+
+ addressTextCopy := addressText
+ addressText = strings.TrimSpace(addressText)
+
+ firstAngleBracket := findUnescaped(addressText, '<', quotesDelim)
+ displayName = nil
+ if firstAngleBracket > 0 {
+ // We have an angle bracket, and it's not the first character.
+ // Since we have just trimmed whitespace, this means there must
+ // be a display name.
+ if addressText[0] == '"' {
+ // The display name is within quotations.
+ // So it is comprised of all text until the closing quote.
+ addressText = addressText[1:]
+ nextQuote := strings.Index(addressText, "\"")
+
+ if nextQuote == -1 {
+ // Unclosed quotes - parse error.
+ err = fmt.Errorf("unclosed quotes in header text: %s",
+ addressTextCopy)
+ return
+ }
+
+ nameField := addressText[:nextQuote]
+ displayName = sip.String{Str: nameField}
+ addressText = addressText[nextQuote+1:]
+ } else {
+ // The display name is unquoted, so it is comprised of
+ // all text until the opening angle bracket, except surrounding whitespace.
+ // According to the ABNF grammar: display-name = *(token LWS)/ quoted-string
+ // there are certain characters the display name cannot contain unless it's quoted,
+ // however we don't check for them here since it doesn't impact parsing.
+ // May as well be lenient.
+ nameField := addressText[:firstAngleBracket]
+ displayName = sip.String{Str: strings.TrimSpace(nameField)}
+ addressText = addressText[firstAngleBracket:]
+ }
+ }
+
+ // Work out where the SIP URI starts and ends.
+ addressText = strings.TrimSpace(addressText)
+ var endOfUri int
+ var startOfParams int
+ if addressText[0] != '<' {
+ if displayName != nil {
+ // The address must be in if a display name is
+ // present, so this is an invalid address line.
+ err = fmt.Errorf(
+ "invalid character '%c' following display "+
+ "name in address line; expected '<': %s",
+ addressText[0],
+ addressTextCopy,
+ )
+ return
+ }
+
+ endOfUri = strings.Index(addressText, ";")
+ if endOfUri == -1 {
+ endOfUri = len(addressText)
+ }
+ startOfParams = endOfUri
+
+ } else {
+ addressText = addressText[1:]
+ endOfUri = strings.Index(addressText, ">")
+ if endOfUri == 0 {
+ err = fmt.Errorf("'<' without closing '>' in address %s",
+ addressTextCopy)
+ return
+ }
+ startOfParams = endOfUri + 1
+
+ }
+
+ // Now parse the SIP URI.
+ uri, err = ParseUri(addressText[:endOfUri])
+ if err != nil {
+ return
+ }
+
+ if startOfParams >= len(addressText) {
+ return
+ }
+
+ // Finally, parse any header parameters and then return.
+ addressText = addressText[startOfParams:]
+ headerParams, _, err = ParseParams(addressText, ';', ';', ',', true, true)
+ return
+}
+
+func parseRouteHeader(headerName string, headerText string) (headers []sip.Header, err error) {
+ var routeHeader sip.RouteHeader
+ routeHeader.Addresses = make([]sip.Uri, 0)
+ if _, uris, _, err := ParseAddressValues(headerText); err == nil {
+ routeHeader.Addresses = uris
+ } else {
+ return nil, err
+ }
+ return []sip.Header{&routeHeader}, nil
+}
+
+func parseRecordRouteHeader(headerName string, headerText string) (headers []sip.Header, err error) {
+ var routeHeader sip.RecordRouteHeader
+ routeHeader.Addresses = make([]sip.Uri, 0)
+ if _, uris, _, err := ParseAddressValues(headerText); err == nil {
+ routeHeader.Addresses = uris
+ } else {
+ return nil, err
+ }
+ return []sip.Header{&routeHeader}, nil
+}
+
+// Extract the next logical header line from the message.
+// This may run over several actual lines; lines that start with whitespace are
+// a continuation of the previous line.
+// Therefore also return how many lines we consumed so the parent parser can
+// keep track of progress through the message.
+func GetNextHeaderLine(contents []string) (headerText string, consumed int) {
+ if len(contents) == 0 {
+ return
+ }
+ if len(contents[0]) == 0 {
+ return
+ }
+
+ var buffer bytes.Buffer
+ buffer.WriteString(contents[0])
+
+ for consumed = 1; consumed < len(contents); consumed++ {
+ firstChar, _ := utf8.DecodeRuneInString(contents[consumed])
+ if !unicode.IsSpace(firstChar) {
+ break
+ } else if len(contents[consumed]) == 0 {
+ break
+ }
+
+ buffer.WriteString(" " + strings.TrimSpace(contents[consumed]))
+ }
+
+ headerText = buffer.String()
+ return
+}
+
+// A delimiter is any pair of characters used for quoting text (i.e. bulk escaping literals).
+type delimiter struct {
+ start uint8
+ end uint8
+}
+
+// Define common quote characters needed in parsing.
+var quotesDelim = delimiter{'"', '"'}
+
+var anglesDelim = delimiter{'<', '>'}
+
+// Find the first instance of the target in the given text which is not enclosed in any delimiters
+// from the list provided.
+func findUnescaped(text string, target uint8, delims ...delimiter) int {
+ return findAnyUnescaped(text, string(target), delims...)
+}
+
+// Find the first instance of any of the targets in the given text that are not enclosed in any delimiters
+// from the list provided.
+func findAnyUnescaped(text string, targets string, delims ...delimiter) int {
+ escaped := false
+ var endEscape uint8 = 0
+
+ endChars := make(map[uint8]uint8)
+ for _, delim := range delims {
+ endChars[delim.start] = delim.end
+ }
+
+ for idx := 0; idx < len(text); idx++ {
+ if !escaped && strings.Contains(targets, string(text[idx])) {
+ return idx
+ }
+
+ if escaped {
+ escaped = text[idx] != endEscape
+ continue
+ } else {
+ endEscape, escaped = endChars[text[idx]]
+ }
+ }
+
+ return -1
+}
+
+// Splits the given string into sections, separated by one or more characters
+// from c_ABNF_WS.
+func SplitByWhitespace(text string) []string {
+ var buffer bytes.Buffer
+ var inString = true
+ result := make([]string, 0)
+
+ for _, char := range text {
+ s := string(char)
+ if strings.Contains(abnfWs, s) {
+ if inString {
+ // First whitespace char following text; flush buffer to the results array.
+ result = append(result, buffer.String())
+ buffer.Reset()
+ }
+ inString = false
+ } else {
+ buffer.WriteString(s)
+ inString = true
+ }
+ }
+
+ if buffer.Len() > 0 {
+ result = append(result, buffer.String())
+ }
+
+ return result
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parserbuffer.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parserbuffer.go
new file mode 100644
index 0000000000..cdd94d7cc8
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/parser/parserbuffer.go
@@ -0,0 +1,131 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package parser
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "sync"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+// parserBuffer is a specialized buffer for use in the parser.
+// It is written to via the non-blocking Write.
+// It exposes various blocking read methods, which wait until the requested
+// data is available, and then return it.
+type parserBuffer struct {
+ mu sync.RWMutex
+
+ writer io.Writer
+ buffer bytes.Buffer
+
+ // Wraps parserBuffer.pipeReader
+ reader *bufio.Reader
+
+ // Don't access this directly except when closing.
+ pipeReader *io.PipeReader
+
+ log log.Logger
+}
+
+// Create a new parserBuffer object (see struct comment for object details).
+// Note that resources owned by the parserBuffer may not be able to be GCed
+// until the Dispose() method is called.
+func newParserBuffer(logger log.Logger) *parserBuffer {
+ var pb parserBuffer
+ pb.pipeReader, pb.writer = io.Pipe()
+ pb.reader = bufio.NewReader(pb.pipeReader)
+ pb.log = logger.
+ WithPrefix("parser.parserBuffer").
+ WithFields(log.Fields{
+ "parser_buffer_ptr": fmt.Sprintf("%p", &pb),
+ })
+
+ return &pb
+}
+
+func (pb *parserBuffer) Log() log.Logger {
+ return pb.log
+}
+
+func (pb *parserBuffer) Write(p []byte) (n int, err error) {
+ pb.mu.RLock()
+ defer pb.mu.RUnlock()
+
+ return pb.writer.Write(p)
+}
+
+// Block until the buffer contains at least one CRLF-terminated line.
+// Return the line, excluding the terminal CRLF, and delete it from the buffer.
+// Returns an error if the parserbuffer has been stopped.
+func (pb *parserBuffer) NextLine() (response string, err error) {
+ var buffer bytes.Buffer
+ var data string
+ var b byte
+
+ // There has to be a better way!
+ for {
+ data, err = pb.reader.ReadString('\r')
+ if err != nil {
+ return
+ }
+
+ buffer.WriteString(data)
+
+ b, err = pb.reader.ReadByte()
+ if err != nil {
+ return
+ }
+
+ buffer.WriteByte(b)
+ if b == '\n' {
+ response = buffer.String()
+ response = response[:len(response)-2]
+
+ pb.Log().Tracef("return line '%s'", response)
+
+ return
+ }
+ }
+}
+
+// Block until the buffer contains at least n characters.
+// Return precisely those n characters, then delete them from the buffer.
+func (pb *parserBuffer) NextChunk(n int) (response string, err error) {
+ var data = make([]byte, n)
+
+ var read int
+ for total := 0; total < n; {
+ read, err = pb.reader.Read(data[total:])
+ total += read
+ if err != nil {
+ return
+ }
+ }
+
+ response = string(data)
+
+ pb.Log().Tracef("return chunk:\n%s", response)
+
+ return
+}
+
+// Stop the parser buffer.
+func (pb *parserBuffer) Stop() {
+ pb.mu.RLock()
+ if err := pb.pipeReader.Close(); err != nil {
+ pb.Log().Errorf("parser pipe reader close failed: %s", err)
+ }
+ pb.mu.RUnlock()
+
+ pb.Log().Trace("parser buffer stopped")
+}
+
+func (pb *parserBuffer) Reset() {
+ pb.mu.Lock()
+ pb.pipeReader, pb.writer = io.Pipe()
+ pb.reader.Reset(pb.pipeReader)
+ pb.mu.Unlock()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/request.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/request.go
new file mode 100644
index 0000000000..bbc65edd85
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/request.go
@@ -0,0 +1,382 @@
+package sip
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+// Request RFC 3261 - 7.1.
+type Request interface {
+ Message
+ Method() RequestMethod
+ SetMethod(method RequestMethod)
+ Recipient() Uri
+ SetRecipient(recipient Uri)
+ /* Common Helpers */
+ IsInvite() bool
+}
+
+type request struct {
+ message
+ method RequestMethod
+ recipient Uri
+}
+
+func NewRequest(
+ messID MessageID,
+ method RequestMethod,
+ recipient Uri,
+ sipVersion string,
+ hdrs []Header,
+ body string,
+ fields log.Fields,
+) Request {
+ req := new(request)
+ if messID == "" {
+ req.messID = NextMessageID()
+ } else {
+ req.messID = messID
+ }
+ req.startLine = req.StartLine
+ req.sipVersion = sipVersion
+ req.headers = newHeaders(hdrs)
+ req.method = method
+ req.recipient = recipient
+ req.body = body
+ req.fields = fields.WithFields(log.Fields{
+ "request_id": req.messID,
+ })
+
+ return req
+}
+
+func (req *request) Short() string {
+ if req == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "method": req.Method(),
+ "recipient": req.Recipient(),
+ "transport": req.Transport(),
+ "source": req.Source(),
+ "destination": req.Destination(),
+ }
+ if cseq, ok := req.CSeq(); ok {
+ fields["sequence"] = cseq.SeqNo
+ }
+ fields = req.Fields().WithFields(fields)
+
+ return fmt.Sprintf("sip.Request<%s>", fields)
+}
+
+func (req *request) Method() RequestMethod {
+ req.mu.RLock()
+ defer req.mu.RUnlock()
+ return req.method
+}
+func (req *request) SetMethod(method RequestMethod) {
+ req.mu.Lock()
+ req.method = method
+ req.mu.Unlock()
+}
+
+func (req *request) Recipient() Uri {
+ req.mu.RLock()
+ defer req.mu.RUnlock()
+ return req.recipient
+}
+func (req *request) SetRecipient(recipient Uri) {
+ req.mu.Lock()
+ req.recipient = recipient
+ req.mu.Unlock()
+}
+
+// StartLine returns Request Line - RFC 2361 7.1.
+func (req *request) StartLine() string {
+ var buffer bytes.Buffer
+
+ // Every SIP request starts with a Request Line - RFC 2361 7.1.
+ buffer.WriteString(
+ fmt.Sprintf(
+ "%s %s %s",
+ string(req.Method()),
+ req.Recipient(),
+ req.SipVersion(),
+ ),
+ )
+
+ return buffer.String()
+}
+
+func (req *request) Clone() Message {
+ return cloneRequest(req, "", nil)
+}
+
+func (req *request) Fields() log.Fields {
+ return req.fields.WithFields(log.Fields{
+ "transport": req.Transport(),
+ "source": req.Source(),
+ "destination": req.Destination(),
+ })
+}
+
+func (req *request) WithFields(fields log.Fields) Message {
+ req.mu.Lock()
+ req.fields = req.fields.WithFields(fields)
+ req.mu.Unlock()
+
+ return req
+}
+
+func (req *request) IsInvite() bool {
+ return req.Method() == INVITE
+}
+
+func (req *request) IsAck() bool {
+ return req.Method() == ACK
+}
+
+func (req *request) IsCancel() bool {
+ return req.Method() == CANCEL
+}
+
+func (req *request) Transport() string {
+ if tp := req.message.Transport(); tp != "" {
+ return strings.ToUpper(tp)
+ }
+
+ var tp string
+ if viaHop, ok := req.ViaHop(); ok && viaHop.Transport != "" {
+ tp = viaHop.Transport
+ } else {
+ tp = DefaultProtocol
+ }
+
+ uri := req.Recipient()
+ if hdrs := req.GetHeaders("Route"); len(hdrs) > 0 {
+ routeHeader, ok := hdrs[0].(*RouteHeader)
+ if ok && len(routeHeader.Addresses) > 0 {
+ uri = routeHeader.Addresses[0]
+ }
+ }
+
+ if uri != nil {
+ if uri.UriParams() != nil {
+ if val, ok := uri.UriParams().Get("transport"); ok && !val.Equals("") {
+ tp = strings.ToUpper(val.String())
+ }
+ }
+
+ if uri.IsEncrypted() {
+ if tp == "TCP" {
+ tp = "TLS"
+ } else if tp == "WS" {
+ tp = "WSS"
+ }
+ }
+ }
+
+ if tp == "UDP" && len(req.String()) > int(MTU)-200 {
+ tp = "TCP"
+ }
+
+ return tp
+}
+
+func (req *request) Source() string {
+ if src := req.message.Source(); src != "" {
+ return src
+ }
+
+ viaHop, ok := req.ViaHop()
+ if !ok {
+ return ""
+ }
+
+ var (
+ host string
+ port Port
+ )
+
+ host = viaHop.Host
+ if viaHop.Port != nil {
+ port = *viaHop.Port
+ } else {
+ port = DefaultPort(req.Transport())
+ }
+
+ if viaHop.Params != nil {
+ if received, ok := viaHop.Params.Get("received"); ok && received.String() != "" {
+ host = received.String()
+ }
+ if rport, ok := viaHop.Params.Get("rport"); ok && rport != nil && rport.String() != "" {
+ if p, err := strconv.Atoi(rport.String()); err == nil {
+ port = Port(uint16(p))
+ }
+ }
+ }
+
+ return fmt.Sprintf("%v:%v", host, port)
+}
+
+func (req *request) Destination() string {
+ if dest := req.message.Destination(); dest != "" {
+ return dest
+ }
+
+ var uri *SipUri
+ if hdrs := req.GetHeaders("Route"); len(hdrs) > 0 {
+ routeHeader, ok := hdrs[0].(*RouteHeader)
+ if ok && len(routeHeader.Addresses) > 0 {
+ uri = routeHeader.Addresses[0].(*SipUri)
+ }
+ }
+ if uri == nil {
+ if u, ok := req.Recipient().(*SipUri); ok {
+ uri = u
+ } else {
+ return ""
+ }
+ }
+
+ host := uri.FHost
+ var port Port
+ if uri.FPort != nil {
+ port = *uri.FPort
+ } else {
+ port = DefaultPort(req.Transport())
+ }
+
+ return fmt.Sprintf("%v:%v", host, port)
+}
+
+// NewAckRequest creates ACK request for 2xx INVITE
+// https://tools.ietf.org/html/rfc3261#section-13.2.2.4
+func NewAckRequest(ackID MessageID, inviteRequest Request, inviteResponse Response, body string, fields log.Fields) Request {
+ recipient := inviteRequest.Recipient()
+ if contact, ok := inviteResponse.Contact(); ok {
+ // For ws and wss (like clients in browser), don't use Contact
+ if strings.Index(strings.ToLower(recipient.String()), "transport=ws") == -1 {
+ recipient = contact.Address
+ }
+ }
+ ackRequest := NewRequest(
+ ackID,
+ ACK,
+ recipient,
+ inviteRequest.SipVersion(),
+ []Header{},
+ body,
+ inviteRequest.Fields().
+ WithFields(fields).
+ WithFields(log.Fields{
+ "invite_request_id": inviteRequest.MessageID(),
+ "invite_response_id": inviteResponse.MessageID(),
+ }),
+ )
+
+ CopyHeaders("Via", inviteRequest, ackRequest)
+ if inviteResponse.IsSuccess() {
+ // update branch, 2xx ACK is separate Tx
+ viaHop, _ := ackRequest.ViaHop()
+ viaHop.Params.Add("branch", String{Str: GenerateBranch()})
+ }
+
+ if len(inviteRequest.GetHeaders("Route")) > 0 {
+ CopyHeaders("Route", inviteRequest, ackRequest)
+ } else {
+ hdrs := inviteResponse.GetHeaders("Record-Route")
+ for i := len(hdrs) - 1; i >= 0; i-- {
+ h := hdrs[i]
+ uris := make([]Uri, 0)
+ for j := len(h.(*RecordRouteHeader).Addresses) - 1; j >= 0; j-- {
+ uris = append(uris, h.(*RecordRouteHeader).Addresses[j].Clone())
+ }
+ ackRequest.AppendHeader(&RouteHeader{
+ Addresses: uris,
+ })
+ }
+ }
+
+ maxForwardsHeader := MaxForwards(70)
+ ackRequest.AppendHeader(&maxForwardsHeader)
+ CopyHeaders("From", inviteRequest, ackRequest)
+ CopyHeaders("To", inviteResponse, ackRequest)
+ CopyHeaders("Call-ID", inviteRequest, ackRequest)
+ CopyHeaders("CSeq", inviteRequest, ackRequest)
+ cseq, _ := ackRequest.CSeq()
+ cseq.MethodName = ACK
+
+ ackRequest.SetBody("", true)
+ ackRequest.SetTransport(inviteRequest.Transport())
+ ackRequest.SetSource(inviteRequest.Source())
+ ackRequest.SetDestination(inviteRequest.Destination())
+
+ return ackRequest
+}
+
+func NewCancelRequest(cancelID MessageID, requestForCancel Request, fields log.Fields) Request {
+ cancelReq := NewRequest(
+ cancelID,
+ CANCEL,
+ requestForCancel.Recipient(),
+ requestForCancel.SipVersion(),
+ []Header{},
+ "",
+ requestForCancel.Fields().
+ WithFields(fields).
+ WithFields(log.Fields{
+ "cancelling_request_id": requestForCancel.MessageID(),
+ }),
+ )
+
+ viaHop, _ := requestForCancel.ViaHop()
+ cancelReq.AppendHeader(ViaHeader{viaHop.Clone()})
+ CopyHeaders("Route", requestForCancel, cancelReq)
+ maxForwardsHeader := MaxForwards(70)
+ cancelReq.AppendHeader(&maxForwardsHeader)
+ CopyHeaders("From", requestForCancel, cancelReq)
+ CopyHeaders("To", requestForCancel, cancelReq)
+ CopyHeaders("Call-ID", requestForCancel, cancelReq)
+ CopyHeaders("CSeq", requestForCancel, cancelReq)
+ cseq, _ := cancelReq.CSeq()
+ cseq.MethodName = CANCEL
+
+ cancelReq.SetBody("", true)
+ cancelReq.SetTransport(requestForCancel.Transport())
+ cancelReq.SetSource(requestForCancel.Source())
+ cancelReq.SetDestination(requestForCancel.Destination())
+
+ return cancelReq
+}
+
+func cloneRequest(req Request, id MessageID, fields log.Fields) Request {
+ newFields := req.Fields()
+ if fields != nil {
+ newFields = newFields.WithFields(fields)
+ }
+
+ newReq := NewRequest(
+ id,
+ req.Method(),
+ req.Recipient().Clone(),
+ req.SipVersion(),
+ cloneHeaders(req),
+ req.Body(),
+ newFields,
+ )
+ newReq.SetTransport(req.Transport())
+ newReq.SetSource(req.Source())
+ newReq.SetDestination(req.Destination())
+
+ return newReq
+}
+
+func CopyRequest(req Request) Request {
+ return cloneRequest(req, req.MessageID(), nil)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/response.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/response.go
new file mode 100644
index 0000000000..420c9f26cd
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/response.go
@@ -0,0 +1,310 @@
+package sip
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+// Response RFC 3261 - 7.2.
+type Response interface {
+ Message
+ StatusCode() StatusCode
+ SetStatusCode(code StatusCode)
+ Reason() string
+ SetReason(reason string)
+ // Previous returns previous provisional responses
+ Previous() []Response
+ SetPrevious(responses []Response)
+ /* Common helpers */
+ IsProvisional() bool
+ IsSuccess() bool
+ IsRedirection() bool
+ IsClientError() bool
+ IsServerError() bool
+ IsGlobalError() bool
+}
+
+type response struct {
+ message
+ status StatusCode
+ reason string
+ previous []Response
+}
+
+func NewResponse(
+ messID MessageID,
+ sipVersion string,
+ statusCode StatusCode,
+ reason string,
+ hdrs []Header,
+ body string,
+ fields log.Fields,
+) Response {
+ res := new(response)
+ if messID == "" {
+ res.messID = NextMessageID()
+ } else {
+ res.messID = messID
+ }
+ res.startLine = res.StartLine
+ res.sipVersion = sipVersion
+ res.headers = newHeaders(hdrs)
+ res.status = statusCode
+ res.reason = reason
+ res.body = body
+ res.fields = fields.WithFields(log.Fields{
+ "response_id": res.messID,
+ })
+ res.previous = make([]Response, 0)
+
+ return res
+}
+
+func (res *response) Short() string {
+ if res == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "status": res.StatusCode(),
+ "reason": res.Reason(),
+ "transport": res.Transport(),
+ "source": res.Source(),
+ "destination": res.Destination(),
+ }
+ if cseq, ok := res.CSeq(); ok {
+ fields["method"] = cseq.MethodName
+ fields["sequence"] = cseq.SeqNo
+ }
+ fields = res.Fields().WithFields(fields)
+
+ return fmt.Sprintf("sip.Response<%s>", fields)
+}
+
+func (res *response) StatusCode() StatusCode {
+ res.mu.RLock()
+ defer res.mu.RUnlock()
+ return res.status
+}
+func (res *response) SetStatusCode(code StatusCode) {
+ res.mu.Lock()
+ res.status = code
+ res.mu.Unlock()
+}
+
+func (res *response) Reason() string {
+ res.mu.RLock()
+ defer res.mu.RUnlock()
+ return res.reason
+}
+func (res *response) SetReason(reason string) {
+ res.mu.Lock()
+ res.reason = reason
+ res.mu.Unlock()
+}
+
+func (res *response) Previous() []Response {
+ res.mu.RLock()
+ defer res.mu.RUnlock()
+ return res.previous
+}
+
+func (res *response) SetPrevious(responses []Response) {
+ res.mu.Lock()
+ res.previous = responses
+ res.mu.Unlock()
+}
+
+// StartLine returns Response Status Line - RFC 2361 7.2.
+func (res *response) StartLine() string {
+ var buffer bytes.Buffer
+
+ // Every SIP response starts with a Status Line - RFC 2361 7.2.
+ buffer.WriteString(
+ fmt.Sprintf(
+ "%s %d %s",
+ res.SipVersion(),
+ res.StatusCode(),
+ res.Reason(),
+ ),
+ )
+
+ return buffer.String()
+}
+
+func (res *response) Clone() Message {
+ return cloneResponse(res, "", nil)
+}
+
+func (res *response) Fields() log.Fields {
+ return res.fields.WithFields(log.Fields{
+ "transport": res.Transport(),
+ "source": res.Source(),
+ "destination": res.Destination(),
+ })
+}
+
+func (res *response) WithFields(fields log.Fields) Message {
+ res.mu.Lock()
+ res.fields = res.fields.WithFields(fields)
+ res.mu.Unlock()
+
+ return res
+}
+
+func (res *response) IsProvisional() bool {
+ return res.StatusCode() < 200
+}
+
+func (res *response) IsSuccess() bool {
+ return res.StatusCode() >= 200 && res.StatusCode() < 300
+}
+
+func (res *response) IsRedirection() bool {
+ return res.StatusCode() >= 300 && res.StatusCode() < 400
+}
+
+func (res *response) IsClientError() bool {
+ return res.StatusCode() >= 400 && res.StatusCode() < 500
+}
+
+func (res *response) IsServerError() bool {
+ return res.StatusCode() >= 500 && res.StatusCode() < 600
+}
+
+func (res *response) IsGlobalError() bool {
+ return res.StatusCode() >= 600
+}
+
+func (res *response) IsAck() bool {
+ if cseq, ok := res.CSeq(); ok {
+ return cseq.MethodName == ACK
+ }
+ return false
+}
+
+func (res *response) IsCancel() bool {
+ if cseq, ok := res.CSeq(); ok {
+ return cseq.MethodName == CANCEL
+ }
+ return false
+}
+
+func (res *response) Transport() string {
+ if tp := res.message.Transport(); tp != "" {
+ return strings.ToUpper(tp)
+ }
+
+ var tp string
+ if viaHop, ok := res.ViaHop(); ok && viaHop.Transport != "" {
+ tp = viaHop.Transport
+ } else {
+ tp = DefaultProtocol
+ }
+
+ return tp
+}
+
+func (res *response) Destination() string {
+ if dest := res.message.Destination(); dest != "" {
+ return dest
+ }
+
+ viaHop, ok := res.ViaHop()
+ if !ok {
+ return ""
+ }
+
+ var (
+ host string
+ port Port
+ )
+
+ host = viaHop.Host
+ if viaHop.Port != nil {
+ port = *viaHop.Port
+ } else {
+ port = DefaultPort(res.Transport())
+ }
+
+ if viaHop.Params != nil {
+ if received, ok := viaHop.Params.Get("received"); ok && received.String() != "" {
+ host = received.String()
+ }
+ if rport, ok := viaHop.Params.Get("rport"); ok && rport != nil && rport.String() != "" {
+ if p, err := strconv.Atoi(rport.String()); err == nil {
+ port = Port(uint16(p))
+ }
+ }
+ }
+
+ return fmt.Sprintf("%v:%v", host, port)
+}
+
+// RFC 3261 - 8.2.6
+func NewResponseFromRequest(
+ resID MessageID,
+ req Request,
+ statusCode StatusCode,
+ reason string,
+ body string,
+) Response {
+ res := NewResponse(
+ resID,
+ req.SipVersion(),
+ statusCode,
+ reason,
+ []Header{},
+ "",
+ req.Fields(),
+ )
+ CopyHeaders("Record-Route", req, res)
+ CopyHeaders("Via", req, res)
+ CopyHeaders("From", req, res)
+ CopyHeaders("To", req, res)
+ CopyHeaders("Call-ID", req, res)
+ CopyHeaders("CSeq", req, res)
+
+ if statusCode == 100 {
+ CopyHeaders("Timestamp", req, res)
+ }
+
+ res.SetBody(body, true)
+
+ res.SetTransport(req.Transport())
+ res.SetSource(req.Destination())
+ res.SetDestination(req.Source())
+
+ return res
+}
+
+func cloneResponse(res Response, id MessageID, fields log.Fields) Response {
+ newFields := res.Fields()
+ if fields != nil {
+ newFields = newFields.WithFields(fields)
+ }
+
+ newRes := NewResponse(
+ id,
+ res.SipVersion(),
+ res.StatusCode(),
+ res.Reason(),
+ cloneHeaders(res),
+ res.Body(),
+ newFields,
+ )
+ newRes.SetPrevious(res.Previous())
+ newRes.SetTransport(res.Transport())
+ newRes.SetSource(res.Source())
+ newRes.SetDestination(res.Destination())
+
+ return newRes
+}
+
+func CopyResponse(res Response) Response {
+ return cloneResponse(res, res.MessageID(), nil)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transaction.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transaction.go
new file mode 100644
index 0000000000..ff010d8ada
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transaction.go
@@ -0,0 +1,28 @@
+package sip
+
+type TransactionKey string
+
+func (key TransactionKey) String() string {
+ return string(key)
+}
+
+type Transaction interface {
+ Origin() Request
+ Key() TransactionKey
+ String() string
+ Errors() <-chan error
+ Done() <-chan bool
+}
+
+type ServerTransaction interface {
+ Transaction
+ Respond(res Response) error
+ Acks() <-chan Request
+ Cancels() <-chan Request
+}
+
+type ClientTransaction interface {
+ Transaction
+ Responses() <-chan Response
+ Cancel() error
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transport.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transport.go
new file mode 100644
index 0000000000..42787bac2e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/sip/transport.go
@@ -0,0 +1,8 @@
+package sip
+
+type Transport interface {
+ Messages() <-chan Message
+ Send(msg Message) error
+ IsReliable(network string) bool
+ IsStreamed(network string) bool
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/timing/timing.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/timing/timing.go
new file mode 100644
index 0000000000..06ad197b56
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/timing/timing.go
@@ -0,0 +1,234 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package timing
+
+import (
+ "sync"
+ "time"
+)
+
+// Controls whether library calls should be mocked, or whether we should use the standard Go time library.
+// If we're in Mock Mode, then time does not pass as normal, but only progresses when Elapse is called.
+// False by default, indicating that we just call through to standard Go functions.
+var MockMode = false
+var currentTimeMock = time.Unix(0, 0)
+var mockTimers = make([]*mockTimer, 0)
+var mockTimerMu = new(sync.Mutex)
+
+// Interface over Golang's built-in Timers, allowing them to be swapped out for mocked timers.
+type Timer interface {
+ // Returns a channel which sends the current time immediately when the timer expires.
+ // Equivalent to time.Timer.C; however, we have to use a method here instead of a member since this is an interface.
+ C() <-chan time.Time
+
+ // Resets the timer such that it will expire in duration 'd' after the current time.
+ // Returns true if the timer had been active, and false if it had expired or been stopped.
+ Reset(d time.Duration) bool
+
+ // Stops the timer, preventing it from firing.
+ // Returns true if the timer had been active, and false if it had expired or been stopped.
+ Stop() bool
+}
+
+// Implementation of Timer that just wraps time.Timer.
+type realTimer struct {
+ *time.Timer
+}
+
+func (t *realTimer) C() <-chan time.Time {
+ return t.Timer.C
+}
+
+func (t *realTimer) Reset(d time.Duration) bool {
+ t.Stop()
+ return t.Timer.Reset(d)
+}
+
+func (t *realTimer) Stop() bool {
+ // return t.Timer.Stop()
+ if !t.Timer.Stop() {
+ select {
+ case <-t.Timer.C:
+ return true
+ default:
+ return false
+ }
+ }
+ return true
+}
+
+// Implementation of Timer that mocks time.Timer, firing when the total elapsed time (as controlled by Elapse)
+// exceeds the duration specified when the timer was constructed.
+type mockTimer struct {
+ EndTime time.Time
+ Chan chan time.Time
+ fired bool
+ toRun func()
+}
+
+func (t *mockTimer) C() <-chan time.Time {
+ return t.Chan
+}
+
+func (t *mockTimer) Reset(d time.Duration) bool {
+ wasActive := removeMockTimer(t)
+
+ t.EndTime = currentTimeMock.Add(d)
+ if d > 0 {
+ mockTimerMu.Lock()
+ mockTimers = append(mockTimers, t)
+ mockTimerMu.Unlock()
+ } else {
+ // The new timer has an expiry time of 0.
+ // Fire it right away, and don't bother tracking it.
+ t.Chan <- currentTimeMock
+ }
+
+ return wasActive
+}
+
+func (t *mockTimer) Stop() bool {
+ if !removeMockTimer(t) {
+ select {
+ case <-t.Chan:
+ return true
+ default:
+ return false
+ }
+ }
+ return true
+}
+
+// Creates a new Timer; either a wrapper around a standard Go time.Timer, or a mocked-out Timer,
+// depending on whether MockMode is set.
+func NewTimer(d time.Duration) Timer {
+ if MockMode {
+ t := mockTimer{currentTimeMock.Add(d), make(chan time.Time, 1), false, nil}
+ if d == 0 {
+ t.Chan <- currentTimeMock
+ } else {
+ mockTimerMu.Lock()
+ mockTimers = append(mockTimers, &t)
+ mockTimerMu.Unlock()
+ }
+ return &t
+ } else {
+ return &realTimer{time.NewTimer(d)}
+ }
+}
+
+// See built-in time.After() function.
+func After(d time.Duration) <-chan time.Time {
+ return NewTimer(d).C()
+}
+
+// See built-in time.AfterFunc() function.
+func AfterFunc(d time.Duration, f func()) Timer {
+ if MockMode {
+ mockTimerMu.Lock()
+ t := mockTimer{currentTimeMock.Add(d), make(chan time.Time, 1), false, f}
+ mockTimerMu.Unlock()
+ if d == 0 {
+ go f()
+ t.Chan <- currentTimeMock
+ } else {
+ mockTimerMu.Lock()
+ mockTimers = append(mockTimers, &t)
+ mockTimerMu.Unlock()
+ }
+ return &t
+ } else {
+ return &realTimer{time.AfterFunc(d, f)}
+ }
+}
+
+// See built-in time.Sleep() function.
+func Sleep(d time.Duration) {
+ <-After(d)
+}
+
+// Increment the current time by the given Duration.
+// This function can only be called in Mock Mode, otherwise we will panic.
+func Elapse(d time.Duration) {
+ requireMockMode()
+ mockTimerMu.Lock()
+ currentTimeMock = currentTimeMock.Add(d)
+ mockTimerMu.Unlock()
+
+ // Fire any timers whose time has come up.
+ mockTimerMu.Lock()
+ for _, t := range mockTimers {
+ t.fired = false
+ if !t.EndTime.After(currentTimeMock) {
+ if t.toRun != nil {
+ go t.toRun()
+ }
+
+ // Clear the channel if something is already in it.
+ select {
+ case <-t.Chan:
+ default:
+ }
+
+ t.Chan <- currentTimeMock
+ t.fired = true
+ }
+ }
+ mockTimerMu.Unlock()
+
+ // Stop tracking any fired timers.
+ remainingTimers := make([]*mockTimer, 0)
+ mockTimerMu.Lock()
+ for _, t := range mockTimers {
+ if !t.fired {
+ remainingTimers = append(remainingTimers, t)
+ }
+ }
+
+ mockTimers = remainingTimers
+ mockTimerMu.Unlock()
+}
+
+// Returns the current time.
+// If Mock Mode is set, this will be the sum of all Durations passed into Elapse calls;
+// otherwise it will be the true system time.
+func Now() time.Time {
+ if MockMode {
+ return currentTimeMock
+ } else {
+ return time.Now()
+ }
+}
+
+// Shortcut method to enforce that Mock Mode is enabled.
+func requireMockMode() {
+ if !MockMode {
+ panic("This method requires MockMode to be enabled")
+ }
+}
+
+// Utility method to remove a mockTimer from the list of outstanding timers.
+func removeMockTimer(t *mockTimer) bool {
+ // First, find the index of the timer in our list.
+ found := false
+ var idx int
+ var elt *mockTimer
+ mockTimerMu.Lock()
+ for idx, elt = range mockTimers {
+ if elt == t {
+ found = true
+ break
+ }
+ }
+ mockTimerMu.Unlock()
+
+ if found {
+ mockTimerMu.Lock()
+ // We found the given timer. Remove it.
+ mockTimers = append(mockTimers[:idx], mockTimers[idx+1:]...)
+ mockTimerMu.Unlock()
+ return true
+ } else {
+ // The timer was not present, indicating that it was already expired.
+ return false
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection.go
new file mode 100644
index 0000000000..10b8c64aba
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection.go
@@ -0,0 +1,219 @@
+package transport
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+var (
+ bufferSize uint16 = 65535 - 20 - 8 // IPv4 max size - IPv4 Header size - UDP Header size
+)
+
+// Wrapper around net.Conn.
+type Connection interface {
+ net.Conn
+
+ Key() ConnectionKey
+ Network() string
+ Streamed() bool
+ String() string
+ ReadFrom(buf []byte) (num int, raddr net.Addr, err error)
+ WriteTo(buf []byte, raddr net.Addr) (num int, err error)
+}
+
+// Connection implementation.
+type connection struct {
+ baseConn net.Conn
+ key ConnectionKey
+ network string
+ laddr net.Addr
+ raddr net.Addr
+ streamed bool
+ mu sync.RWMutex
+
+ log log.Logger
+}
+
+func NewConnection(baseConn net.Conn, key ConnectionKey, network string, logger log.Logger) Connection {
+ var stream bool
+ switch baseConn.(type) {
+ case net.PacketConn:
+ stream = false
+ default:
+ stream = true
+ }
+
+ conn := &connection{
+ baseConn: baseConn,
+ key: key,
+ network: network,
+ laddr: baseConn.LocalAddr(),
+ raddr: baseConn.RemoteAddr(),
+ streamed: stream,
+ }
+ conn.log = logger.
+ WithPrefix("transport.Connection").
+ WithFields(log.Fields{
+ "connection_ptr": fmt.Sprintf("%p", conn),
+ "connection_key": conn.Key(),
+ })
+
+ return conn
+}
+
+func (conn *connection) String() string {
+ if conn == nil {
+ return ""
+ }
+
+ fields := conn.Log().Fields().WithFields(log.Fields{
+ "key": conn.Key(),
+ "network": conn.Network(),
+ "local_addr": conn.LocalAddr(),
+ "remote_addr": conn.RemoteAddr(),
+ })
+
+ return fmt.Sprintf("transport.Connection<%s>", fields)
+}
+
+func (conn *connection) Log() log.Logger {
+ return conn.log
+}
+
+func (conn *connection) Key() ConnectionKey {
+ return conn.key
+}
+
+func (conn *connection) Streamed() bool {
+ return conn.streamed
+}
+
+func (conn *connection) Network() string {
+ return strings.ToUpper(conn.network)
+}
+
+func (conn *connection) Read(buf []byte) (int, error) {
+ var (
+ num int
+ err error
+ )
+
+ num, err = conn.baseConn.Read(buf)
+
+ if err != nil {
+ return num, &ConnectionError{
+ err,
+ "read",
+ conn.Network(),
+ fmt.Sprintf("%v", conn.RemoteAddr()),
+ fmt.Sprintf("%v", conn.LocalAddr()),
+ fmt.Sprintf("%p", conn),
+ }
+ }
+
+ conn.Log().Tracef("read %d bytes %s <- %s:\n%s", num, conn.LocalAddr(), conn.RemoteAddr(), buf[:num])
+
+ return num, err
+}
+
+func (conn *connection) ReadFrom(buf []byte) (num int, raddr net.Addr, err error) {
+ num, raddr, err = conn.baseConn.(net.PacketConn).ReadFrom(buf)
+ if err != nil {
+ return num, raddr, &ConnectionError{
+ err,
+ "read",
+ conn.Network(),
+ fmt.Sprintf("%v", raddr),
+ fmt.Sprintf("%v", conn.LocalAddr()),
+ fmt.Sprintf("%p", conn),
+ }
+ }
+
+ conn.Log().Tracef("read %d bytes %s <- %s:\n%s", num, conn.LocalAddr(), raddr, buf[:num])
+
+ return num, raddr, err
+}
+
+func (conn *connection) Write(buf []byte) (int, error) {
+ var (
+ num int
+ err error
+ )
+
+ num, err = conn.baseConn.Write(buf)
+ if err != nil {
+ return num, &ConnectionError{
+ err,
+ "write",
+ conn.Network(),
+ fmt.Sprintf("%v", conn.LocalAddr()),
+ fmt.Sprintf("%v", conn.RemoteAddr()),
+ fmt.Sprintf("%p", conn),
+ }
+ }
+
+ conn.Log().Tracef("write %d bytes %s -> %s:\n%s", num, conn.LocalAddr(), conn.RemoteAddr(), buf[:num])
+
+ return num, err
+}
+
+func (conn *connection) WriteTo(buf []byte, raddr net.Addr) (num int, err error) {
+ num, err = conn.baseConn.(net.PacketConn).WriteTo(buf, raddr)
+ if err != nil {
+ return num, &ConnectionError{
+ err,
+ "write",
+ conn.Network(),
+ fmt.Sprintf("%v", conn.LocalAddr()),
+ fmt.Sprintf("%v", raddr),
+ fmt.Sprintf("%p", conn),
+ }
+ }
+
+ conn.Log().Tracef("write %d bytes %s -> %s:\n%s", num, conn.LocalAddr(), raddr, buf[:num])
+
+ return num, err
+}
+
+func (conn *connection) LocalAddr() net.Addr {
+ return conn.baseConn.LocalAddr()
+}
+
+func (conn *connection) RemoteAddr() net.Addr {
+ return conn.baseConn.RemoteAddr()
+}
+
+func (conn *connection) Close() error {
+ err := conn.baseConn.Close()
+ if err != nil {
+ return &ConnectionError{
+ err,
+ "close",
+ conn.Network(),
+ "",
+ "",
+ fmt.Sprintf("%p", conn),
+ }
+ }
+
+ conn.Log().Trace("connection closed")
+
+ return nil
+}
+
+func (conn *connection) SetDeadline(t time.Time) error {
+ return conn.baseConn.SetDeadline(t)
+}
+
+func (conn *connection) SetReadDeadline(t time.Time) error {
+ return conn.baseConn.SetReadDeadline(t)
+}
+
+func (conn *connection) SetWriteDeadline(t time.Time) error {
+ return conn.baseConn.SetWriteDeadline(t)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection_pool.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection_pool.go
new file mode 100644
index 0000000000..c32e0f021d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/connection_pool.go
@@ -0,0 +1,802 @@
+package transport
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+ "github.com/ghettovoice/gosip/sip/parser"
+ "github.com/ghettovoice/gosip/timing"
+ "github.com/ghettovoice/gosip/util"
+)
+
+type ConnectionKey string
+
+func (key ConnectionKey) String() string {
+ return string(key)
+}
+
+// ConnectionPool used for active connection management.
+type ConnectionPool interface {
+ Done() <-chan struct{}
+ String() string
+ Put(connection Connection, ttl time.Duration) error
+ Get(key ConnectionKey) (Connection, error)
+ All() []Connection
+ Drop(key ConnectionKey) error
+ DropAll() error
+ Length() int
+}
+
+// ConnectionHandler serves associated connection, i.e. parses
+// incoming data, manages expiry time & etc.
+type ConnectionHandler interface {
+ Cancel()
+ Done() <-chan struct{}
+ String() string
+ Key() ConnectionKey
+ Connection() Connection
+ // Expiry returns connection expiry time.
+ Expiry() time.Time
+ Expired() bool
+ // Update updates connection expiry time.
+ // TODO put later to allow runtime update
+ // Update(conn Connection, ttl time.Duration)
+ // Manage runs connection serving.
+ Serve()
+}
+
+type connectionPool struct {
+ store map[ConnectionKey]ConnectionHandler
+ msgMapper sip.MessageMapper
+
+ output chan<- sip.Message
+ errs chan<- error
+ cancel <-chan struct{}
+
+ done chan struct{}
+ hmess chan sip.Message
+ herrs chan error
+
+ hwg sync.WaitGroup
+ mu sync.RWMutex
+
+ log log.Logger
+}
+
+func NewConnectionPool(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) ConnectionPool {
+ pool := &connectionPool{
+ store: make(map[ConnectionKey]ConnectionHandler),
+ msgMapper: msgMapper,
+
+ output: output,
+ errs: errs,
+ cancel: cancel,
+
+ done: make(chan struct{}),
+ hmess: make(chan sip.Message),
+ herrs: make(chan error),
+ }
+
+ pool.log = logger.
+ WithPrefix("transport.ConnectionPool").
+ WithFields(log.Fields{
+ "connection_pool_ptr": fmt.Sprintf("%p", pool),
+ })
+
+ go func() {
+ <-pool.cancel
+ pool.dispose()
+ }()
+ go pool.serveHandlers()
+
+ return pool
+}
+
+func (pool *connectionPool) String() string {
+ if pool == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("transport.ConnectionPool<%s>", pool.Log().Fields())
+}
+
+func (pool *connectionPool) Log() log.Logger {
+ return pool.log
+}
+
+func (pool *connectionPool) Done() <-chan struct{} {
+ return pool.done
+}
+
+// Put adds new connection to pool or updates TTL of existing connection
+// TTL - 0 - unlimited; 1 - ... - time to live in pool
+func (pool *connectionPool) Put(connection Connection, ttl time.Duration) error {
+ select {
+ case <-pool.cancel:
+ return &PoolError{
+ fmt.Errorf("connection pool closed"),
+ "get connection",
+ pool.String(),
+ }
+ default:
+ }
+
+ key := connection.Key()
+ if key == "" {
+ return &PoolError{
+ fmt.Errorf("empty connection key"),
+ "put connection",
+ pool.String(),
+ }
+ }
+
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.put(key, connection, ttl)
+}
+
+func (pool *connectionPool) Get(key ConnectionKey) (Connection, error) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return pool.getConnection(key)
+}
+
+func (pool *connectionPool) Drop(key ConnectionKey) error {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.drop(key)
+}
+
+func (pool *connectionPool) DropAll() error {
+ pool.mu.Lock()
+ for key := range pool.store {
+ if err := pool.drop(key); err != nil {
+ pool.Log().Errorf("drop connection %s failed: %s", key, err)
+ }
+ }
+ pool.mu.Unlock()
+
+ return nil
+}
+
+func (pool *connectionPool) All() []Connection {
+ pool.mu.RLock()
+ conns := make([]Connection, 0)
+ for _, handler := range pool.store {
+ conns = append(conns, handler.Connection())
+ }
+ pool.mu.RUnlock()
+
+ return conns
+}
+
+func (pool *connectionPool) Length() int {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return len(pool.store)
+}
+
+func (pool *connectionPool) dispose() {
+ // clean pool
+ pool.DropAll()
+ pool.hwg.Wait()
+
+ // stop serveHandlers goroutine
+ close(pool.hmess)
+ close(pool.herrs)
+
+ close(pool.done)
+}
+
+func (pool *connectionPool) serveHandlers() {
+ pool.Log().Debug("begin serve connection handlers")
+ defer pool.Log().Debug("stop serve connection handlers")
+
+ for {
+ logger := pool.Log()
+
+ select {
+ case msg, ok := <-pool.hmess:
+ // cancel signal, serveStore exists
+ if !ok {
+ return
+ }
+ if msg == nil {
+ continue
+ }
+
+ logger = logger.WithFields(msg.Fields())
+ logger.Trace("passing up SIP message")
+
+ select {
+ case <-pool.cancel:
+ return
+ case pool.output <- msg:
+ logger.Trace("SIP message passed up")
+
+ continue
+ }
+ case err, ok := <-pool.herrs:
+ // cancel signal, serveStore exists
+ if !ok {
+ return
+ }
+ if err == nil {
+ continue
+ }
+
+ // on ConnectionHandleError we should drop handler in some cases
+ // all other possible errors ignored because in pool.herrs should be only ConnectionHandlerErrors
+ // so ConnectionPool passes up only Network (when connection falls) and MalformedMessage errors
+ var herr *ConnectionHandlerError
+ if !errors.As(err, &herr) {
+ // all other possible errors
+ logger.Tracef("ignore non connection error: %s", err)
+
+ continue
+ }
+
+ pool.mu.RLock()
+ handler, gerr := pool.get(herr.Key)
+ pool.mu.RUnlock()
+ if gerr != nil {
+ // ignore, handler already dropped out
+ logger.Tracef("ignore error from already dropped out connection %s: %s", herr.Key, gerr)
+
+ continue
+ }
+
+ logger = logger.WithFields(log.Fields{
+ "connection_handler": handler.String(),
+ })
+
+ if herr.Expired() {
+ // handler expired, drop it from pool and continue without emitting error
+ if handler.Expired() {
+ // connection expired
+ logger.Debug("connection expired, drop it and go further")
+
+ if err := pool.Drop(handler.Key()); err != nil {
+ logger.Error(err)
+ }
+ } else {
+ // Due to a race condition, the socket has been updated since this expiry happened.
+ // Ignore the expiry since we already have a new socket for this address.
+ logger.Trace("ignore spurious connection expiry")
+ }
+
+ continue
+ } else if herr.EOF() {
+ select {
+ case <-pool.cancel:
+ return
+ default:
+ }
+
+ // remote endpoint closed
+ logger.Debugf("connection EOF: %s; drop it and go further", herr)
+
+ if err := pool.Drop(handler.Key()); err != nil {
+ logger.Error(err)
+ }
+
+ var connErr *ConnectionError
+ if errors.As(herr.Err, &connErr) {
+ pool.errs <- herr.Err
+ }
+
+ continue
+ } else if herr.Network() {
+ // connection broken or closed
+ logger.Debugf("connection network error: %s; drop it and pass the error up", herr)
+
+ if err := pool.Drop(handler.Key()); err != nil {
+ logger.Error(err)
+ }
+ } else {
+ // syntax errors, malformed message errors and other
+ logger.Tracef("connection error: %s; pass the error up", herr)
+ }
+ // send initial error
+ select {
+ case <-pool.cancel:
+ return
+ case pool.errs <- herr.Err:
+ logger.Trace("error passed up")
+
+ continue
+ }
+ }
+ }
+}
+
+func (pool *connectionPool) put(key ConnectionKey, conn Connection, ttl time.Duration) error {
+ if _, err := pool.get(key); err == nil {
+ return &PoolError{
+ fmt.Errorf("key %s already exists in the pool", key),
+ "put connection",
+ pool.String(),
+ }
+ }
+
+ // wrap to handler
+ handler := NewConnectionHandler(
+ conn,
+ ttl,
+ pool.hmess,
+ pool.herrs,
+ pool.msgMapper,
+ pool.Log(),
+ )
+
+ logger := log.AddFieldsFrom(pool.Log(), handler)
+ logger.Tracef("put connection to the pool with TTL = %s", ttl)
+
+ pool.store[handler.Key()] = handler
+
+ // start serving
+ pool.hwg.Add(1)
+ go handler.Serve()
+ go func() {
+ <-handler.Done()
+ pool.hwg.Done()
+ }()
+
+ return nil
+}
+
+func (pool *connectionPool) drop(key ConnectionKey) error {
+ // check existence in pool
+ handler, err := pool.get(key)
+ if err != nil {
+ return err
+ }
+
+ handler.Cancel()
+
+ logger := log.AddFieldsFrom(pool.Log(), handler)
+ logger.Trace("drop connection from the pool")
+
+ // modify store
+ delete(pool.store, key)
+
+ return nil
+}
+
+func (pool *connectionPool) get(key ConnectionKey) (ConnectionHandler, error) {
+ if handler, ok := pool.store[key]; ok {
+ return handler, nil
+ }
+
+ return nil, &PoolError{
+ fmt.Errorf("connection %s not found in the pool", key),
+ "get connection",
+ pool.String(),
+ }
+}
+
+func (pool *connectionPool) getConnection(key ConnectionKey) (Connection, error) {
+ var conn Connection
+ handler, err := pool.get(key)
+ if err == nil {
+ conn = handler.Connection()
+ }
+ return conn, err
+}
+
+// connectionHandler actually serves associated connection
+type connectionHandler struct {
+ connection Connection
+ msgMapper sip.MessageMapper
+
+ timer timing.Timer
+ ttl time.Duration
+ expiry time.Time
+
+ output chan<- sip.Message
+ errs chan<- error
+ cancelOnce sync.Once
+ canceled chan struct{}
+ done chan struct{}
+ addrs util.ElasticChan
+
+ log log.Logger
+}
+
+func NewConnectionHandler(
+ conn Connection,
+ ttl time.Duration,
+ output chan<- sip.Message,
+ errs chan<- error,
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) ConnectionHandler {
+ handler := &connectionHandler{
+ connection: conn,
+ msgMapper: msgMapper,
+
+ output: output,
+ errs: errs,
+ canceled: make(chan struct{}),
+ done: make(chan struct{}),
+
+ ttl: ttl,
+ }
+
+ handler.log = logger.
+ WithPrefix("transport.ConnectionHandler").
+ WithFields(log.Fields{
+ "connection_handler_ptr": fmt.Sprintf("%p", handler),
+ "connection_ptr": fmt.Sprintf("%p", conn),
+ "connection_key": conn.Key(),
+ "connection_network": conn.Network(),
+ })
+
+ // handler.Update(ttl)
+ if ttl > 0 {
+ handler.expiry = time.Now().Add(ttl)
+ handler.timer = timing.NewTimer(ttl)
+ } else {
+ handler.expiry = time.Time{}
+ handler.timer = timing.NewTimer(0)
+ if !handler.timer.Stop() {
+ <-handler.timer.C()
+ }
+ }
+
+ if handler.msgMapper == nil {
+ handler.msgMapper = func(msg sip.Message) sip.Message {
+ return msg
+ }
+ }
+
+ return handler
+}
+
+func (handler *connectionHandler) String() string {
+ if handler == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("transport.ConnectionHandler<%s>", handler.Log().Fields())
+}
+
+func (handler *connectionHandler) Log() log.Logger {
+ return handler.log
+}
+
+func (handler *connectionHandler) Key() ConnectionKey {
+ return handler.connection.Key()
+}
+
+func (handler *connectionHandler) Connection() Connection {
+ return handler.connection
+}
+
+func (handler *connectionHandler) Expiry() time.Time {
+ return handler.expiry
+}
+
+func (handler *connectionHandler) Expired() bool {
+ return !handler.Expiry().IsZero() && handler.Expiry().Before(time.Now())
+}
+
+// resets the timeout timer.
+// func (handler *connectionHandler) Update(ttl time.Duration) {
+// if ttl > 0 {
+// expiryTime := timing.Now().Put(ttl)
+// handler.Log().Debugf("set %s expiry time to %s", handler, expiryTime)
+// handler.expiry = expiryTime
+//
+// if handler.timer == nil {
+// handler.timer = timing.NewTimer(ttl)
+// } else {
+// handler.timer.Reset(ttl)
+// }
+// } else {
+// handler.Log().Debugf("set %s unlimited expiry time")
+// handler.expiry = time.Time{}
+//
+// if handler.timer == nil {
+// handler.timer = timing.NewTimer(0)
+// }
+// handler.timer.Stop()
+// }
+// }
+
+// Serve is connection serving loop.
+// Waits for the connection to expire, and notifies the pool when it does.
+func (handler *connectionHandler) Serve() {
+ defer close(handler.done)
+
+ handler.Log().Debug("begin serve connection")
+ defer handler.Log().Debug("stop serve connection")
+
+ // start connection serving goroutines
+ msgs, errs := handler.readConnection()
+ handler.pipeOutputs(msgs, errs)
+}
+
+func (handler *connectionHandler) readConnection() (<-chan sip.Message, <-chan error) {
+ msgs := make(chan sip.Message)
+ errs := make(chan error)
+ streamed := handler.Connection().Streamed()
+ var (
+ pktPrs *parser.PacketParser
+ strPrs parser.Parser
+ )
+ if streamed {
+ strPrs = parser.NewParser(msgs, errs, streamed, handler.Log())
+ } else {
+ pktPrs = parser.NewPacketParser(handler.Log())
+ }
+
+ var raddr net.Addr
+ if streamed {
+ raddr = handler.Connection().RemoteAddr()
+ } else {
+ handler.addrs.Init()
+ handler.addrs.SetLog(handler.Log())
+ handler.addrs.Run()
+ }
+
+ go func() {
+ defer func() {
+ handler.Connection().Close()
+ if streamed {
+ strPrs.Stop()
+ } else {
+ pktPrs.Stop()
+ }
+
+ if !streamed {
+ handler.addrs.Stop()
+ }
+
+ close(msgs)
+ close(errs)
+ }()
+
+ handler.Log().Debug("begin read connection")
+ defer handler.Log().Debug("stop read connection")
+
+ buf := make([]byte, bufferSize)
+
+ var (
+ num int
+ err error
+ )
+
+ for {
+ // wait for data
+ if streamed {
+ num, err = handler.Connection().Read(buf)
+ } else {
+ num, raddr, err = handler.Connection().ReadFrom(buf)
+ }
+
+ if err != nil {
+ //// if we get timeout error just go further and try read on the next iteration
+ //var netErr net.Error
+ //if errors.As(err, &netErr) {
+ // if netErr.Timeout() || netErr.Temporary() {
+ // handler.Log().Tracef(
+ // "connection read failed due to timeout or temporary unavailable reason: %s, sleep by %s",
+ // err,
+ // netErrRetryTime,
+ // )
+ //
+ // time.Sleep(netErrRetryTime)
+ //
+ // continue
+ // }
+ //}
+
+ // broken or closed connection
+ // so send error and exit
+ handler.handleError(err, fmt.Sprintf("%v", raddr))
+
+ return
+ }
+
+ data := buf[:num]
+
+ // skip empty udp packets
+ if len(bytes.Trim(data, "\x00")) == 0 {
+ handler.Log().Tracef("skip empty data: %#v", data)
+
+ continue
+ }
+
+ // parse received data
+ if streamed {
+ if _, err := strPrs.Write(data); err != nil {
+ handler.handleError(err, fmt.Sprintf("%v", raddr))
+ }
+ } else {
+ if msg, err := pktPrs.ParseMessage(data); err == nil {
+ handler.handleMessage(msg, fmt.Sprintf("%v", raddr))
+ } else {
+ handler.handleError(err, fmt.Sprintf("%v", raddr))
+ }
+ }
+ }
+ }()
+
+ return msgs, errs
+}
+
+func (handler *connectionHandler) pipeOutputs(msgs <-chan sip.Message, errs <-chan error) {
+ streamed := handler.Connection().Streamed()
+
+ handler.Log().Debug("begin pipe outputs")
+ defer handler.Log().Debug("stop pipe outputs")
+
+ for {
+ select {
+ case <-handler.timer.C():
+ var raddr string
+ if streamed {
+ raddr = fmt.Sprintf("%v", handler.Connection().RemoteAddr())
+ }
+
+ if handler.Expiry().IsZero() {
+ // handler expiryTime is zero only when TTL = 0 (unlimited handler)
+ // so we must not get here with zero expiryTime
+ handler.Log().Panic("fires expiry timer with ZERO expiryTime")
+ }
+
+ // pass up to the pool
+ handler.handleError(ExpireError("connection expired"), raddr)
+ case msg, ok := <-msgs:
+ if !ok {
+ return
+ }
+
+ handler.handleMessage(msg, handler.getRemoteAddr())
+ case err, ok := <-errs:
+ if !ok {
+ return
+ }
+
+ handler.handleError(err, handler.getRemoteAddr())
+ }
+ }
+}
+
+func (handler *connectionHandler) getRemoteAddr() string {
+ if handler.Connection().Streamed() {
+ return fmt.Sprintf("%v", handler.Connection().RemoteAddr())
+ } else {
+ // use non-blocking read because remote address already should be here
+ // or error occurred in read connection goroutine
+ select {
+ case v := <-handler.addrs.Out:
+ return v.(string)
+ default:
+ return ""
+ }
+ }
+}
+
+func (handler *connectionHandler) handleMessage(msg sip.Message, raddr string) {
+ msg.SetDestination(handler.Connection().LocalAddr().String())
+ rhost, rport, _ := net.SplitHostPort(raddr)
+
+ switch msg := msg.(type) {
+ case sip.Request:
+ // RFC 3261 - 18.2.1
+ viaHop, ok := msg.ViaHop()
+ if !ok {
+ handler.Log().Warn("ignore message without 'Via' header")
+
+ return
+ }
+
+ if rhost != "" && rhost != viaHop.Host {
+ viaHop.Params.Add("received", sip.String{Str: rhost})
+ }
+
+ // rfc3581
+ if viaHop.Params.Has("rport") {
+ viaHop.Params.Add("rport", sip.String{Str: rport})
+ }
+
+ if !handler.Connection().Streamed() {
+ if !viaHop.Params.Has("rport") {
+ var port sip.Port
+ if viaHop.Port != nil {
+ port = *viaHop.Port
+ } else {
+ port = sip.DefaultPort(handler.Connection().Network())
+ }
+ raddr = fmt.Sprintf("%s:%d", rhost, port)
+ }
+ }
+
+ msg.SetTransport(handler.connection.Network())
+ msg.SetSource(raddr)
+ case sip.Response:
+ // Set Remote Address as response source
+ msg.SetTransport(handler.connection.Network())
+ msg.SetSource(raddr)
+ }
+
+ msg = handler.msgMapper(msg.WithFields(log.Fields{
+ "connection_key": handler.Connection().Key(),
+ "received_at": time.Now(),
+ }))
+
+ // pass up
+ handler.output <- msg
+
+ if !handler.Expiry().IsZero() {
+ handler.expiry = time.Now().Add(handler.ttl)
+ handler.timer.Reset(handler.ttl)
+ }
+}
+
+func (handler *connectionHandler) handleError(err error, raddr string) {
+ if isSyntaxError(err) {
+ handler.Log().Tracef("ignore error: %s", err)
+ return
+ }
+
+ err = &ConnectionHandlerError{
+ err,
+ handler.Key(),
+ fmt.Sprintf("%p", handler),
+ handler.Connection().Network(),
+ fmt.Sprintf("%v", handler.Connection().LocalAddr()),
+ raddr,
+ }
+
+ select {
+ case <-handler.canceled:
+ case handler.errs <- err:
+ }
+}
+
+func isSyntaxError(err error) bool {
+ var perr parser.Error
+ if errors.As(err, &perr) && perr.Syntax() {
+ return true
+ }
+
+ var merr sip.MessageError
+ if errors.As(err, &merr) && merr.Broken() {
+ return true
+ }
+
+ return false
+}
+
+// Cancel simply calls runtime provided cancel function.
+func (handler *connectionHandler) Cancel() {
+ handler.cancelOnce.Do(func() {
+ close(handler.canceled)
+ handler.Connection().Close()
+
+ handler.Log().Debug("connection handler canceled")
+ })
+}
+
+func (handler *connectionHandler) Done() <-chan struct{} {
+ return handler.done
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/layer.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/layer.go
new file mode 100644
index 0000000000..ddb7b3e9e1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/layer.go
@@ -0,0 +1,477 @@
+package transport
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/rand"
+ "net"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
+// Layer is responsible for the actual transmission of messages - RFC 3261 - 18.
+type Layer interface {
+ Cancel()
+ Done() <-chan struct{}
+ Messages() <-chan sip.Message
+ Errors() <-chan error
+ // Listen starts listening on `addr` for each registered protocol.
+ Listen(network string, addr string, options ...ListenOption) error
+ // Send sends message on suitable protocol.
+ Send(msg sip.Message) error
+ String() string
+ IsReliable(network string) bool
+ IsStreamed(network string) bool
+}
+
+var protocolFactory ProtocolFactory = func(
+ network string,
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) (Protocol, error) {
+ switch strings.ToLower(network) {
+ case "udp":
+ return NewUdpProtocol(output, errs, cancel, msgMapper, logger), nil
+ case "tcp":
+ return NewTcpProtocol(output, errs, cancel, msgMapper, logger), nil
+ case "tls":
+ return NewTlsProtocol(output, errs, cancel, msgMapper, logger), nil
+ case "ws":
+ return NewWsProtocol(output, errs, cancel, msgMapper, logger), nil
+ case "wss":
+ return NewWssProtocol(output, errs, cancel, msgMapper, logger), nil
+ default:
+ return nil, UnsupportedProtocolError(fmt.Sprintf("protocol %s is not supported", network))
+ }
+}
+
+// SetProtocolFactory replaces default protocol factory
+func SetProtocolFactory(factory ProtocolFactory) {
+ protocolFactory = factory
+}
+
+// GetProtocolFactory returns default protocol factory
+func GetProtocolFactory() ProtocolFactory {
+ return protocolFactory
+}
+
+// TransportLayer implementation.
+type layer struct {
+ protocols *protocolStore
+ listenPorts map[string][]sip.Port
+ ip net.IP
+ dnsResolver *net.Resolver
+ msgMapper sip.MessageMapper
+
+ msgs chan sip.Message
+ errs chan error
+ pmsgs chan sip.Message
+ perrs chan error
+ canceled chan struct{}
+ done chan struct{}
+
+ wg sync.WaitGroup
+ cancelOnce sync.Once
+
+ log log.Logger
+}
+
+// NewLayer creates transport layer.
+// - ip - host IP
+// - dnsAddr - DNS server address, default is 127.0.0.1:53
+func NewLayer(
+ ip net.IP,
+ dnsResolver *net.Resolver,
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Layer {
+ tpl := &layer{
+ protocols: newProtocolStore(),
+ listenPorts: make(map[string][]sip.Port),
+ ip: ip,
+ dnsResolver: dnsResolver,
+ msgMapper: msgMapper,
+
+ msgs: make(chan sip.Message),
+ errs: make(chan error),
+ pmsgs: make(chan sip.Message),
+ perrs: make(chan error),
+ canceled: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+
+ tpl.log = logger.
+ WithPrefix("transport.Layer").
+ WithFields(map[string]interface{}{
+ "transport_layer_ptr": fmt.Sprintf("%p", tpl),
+ })
+
+ go tpl.serveProtocols()
+
+ return tpl
+}
+
+func (tpl *layer) String() string {
+ if tpl == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("transport.Layer<%s>", tpl.Log().Fields())
+}
+
+func (tpl *layer) Log() log.Logger {
+ return tpl.log
+}
+
+func (tpl *layer) Cancel() {
+ select {
+ case <-tpl.canceled:
+ return
+ default:
+ }
+
+ tpl.cancelOnce.Do(func() {
+ close(tpl.canceled)
+
+ tpl.Log().Debug("transport layer canceled")
+ })
+}
+
+func (tpl *layer) Done() <-chan struct{} {
+ return tpl.done
+}
+
+func (tpl *layer) Messages() <-chan sip.Message {
+ return tpl.msgs
+}
+
+func (tpl *layer) Errors() <-chan error {
+ return tpl.errs
+}
+
+func (tpl *layer) IsReliable(network string) bool {
+ if protocol, ok := tpl.protocols.get(protocolKey(network)); ok && protocol.Reliable() {
+ return true
+ }
+ return false
+}
+
+func (tpl *layer) IsStreamed(network string) bool {
+ if protocol, ok := tpl.protocols.get(protocolKey(network)); ok && protocol.Streamed() {
+ return true
+ }
+ return false
+}
+
+func (tpl *layer) Listen(network string, addr string, options ...ListenOption) error {
+ select {
+ case <-tpl.canceled:
+ return fmt.Errorf("transport layer is canceled")
+ default:
+ }
+
+ protocol, err := tpl.getProtocol(network)
+ if err != nil {
+ return err
+ }
+ target, err := NewTargetFromAddr(addr)
+ if err != nil {
+ return err
+ }
+ target = FillTargetHostAndPort(protocol.Network(), target)
+
+ err = protocol.Listen(target, options...)
+ if err == nil {
+ if _, ok := tpl.listenPorts[protocol.Network()]; !ok {
+ if tpl.listenPorts[protocol.Network()] == nil {
+ tpl.listenPorts[protocol.Network()] = make([]sip.Port, 0)
+ }
+ tpl.listenPorts[protocol.Network()] = append(tpl.listenPorts[protocol.Network()], *target.Port)
+ }
+ }
+
+ return err
+}
+
+func (tpl *layer) Send(msg sip.Message) error {
+ select {
+ case <-tpl.canceled:
+ return fmt.Errorf("transport layer is canceled")
+ default:
+ }
+
+ viaHop, ok := msg.ViaHop()
+ if !ok {
+ return &sip.MalformedMessageError{
+ Err: fmt.Errorf("missing required 'Via' header"),
+ Msg: msg.String(),
+ }
+ }
+
+ switch msg := msg.(type) {
+ // RFC 3261 - 18.1.1.
+ case sip.Request:
+ network := msg.Transport()
+ // rewrite sent-by transport
+ viaHop.Transport = strings.ToUpper(network)
+ viaHop.Host = tpl.ip.String()
+
+ protocol, err := tpl.getProtocol(network)
+ if err != nil {
+ return err
+ }
+
+ // rewrite sent-by port
+ if viaHop.Port == nil {
+ if ports, ok := tpl.listenPorts[network]; ok {
+ port := ports[rand.Intn(len(ports))]
+ viaHop.Port = &port
+ } else {
+ defPort := sip.DefaultPort(network)
+ viaHop.Port = &defPort
+ }
+ }
+
+ target, err := NewTargetFromAddr(msg.Destination())
+ if err != nil {
+ return fmt.Errorf("build address target for %s: %w", msg.Destination(), err)
+ }
+
+ // dns srv lookup
+ if net.ParseIP(target.Host) == nil {
+ ctx := context.Background()
+ proto := strings.ToLower(network)
+ if _, addrs, err := tpl.dnsResolver.LookupSRV(ctx, "sip", proto, target.Host); err == nil && len(addrs) > 0 {
+ addr := addrs[0]
+ addrStr := fmt.Sprintf("%s:%d", addr.Target[:len(addr.Target)-1], addr.Port)
+ switch network {
+ case "UDP":
+ if addr, err := net.ResolveUDPAddr("udp", addrStr); err == nil {
+ port := sip.Port(addr.Port)
+ if addr.IP.To4() == nil {
+ target.Host = fmt.Sprintf("[%v]", addr.IP.String())
+ } else {
+ target.Host = addr.IP.String()
+ }
+ target.Port = &port
+ }
+ case "TLS":
+ fallthrough
+ case "WS":
+ fallthrough
+ case "WSS":
+ fallthrough
+ case "TCP":
+ if addr, err := net.ResolveTCPAddr("tcp", addrStr); err == nil {
+ port := sip.Port(addr.Port)
+ if addr.IP.To4() == nil {
+ target.Host = fmt.Sprintf("[%v]", addr.IP.String())
+ } else {
+ target.Host = addr.IP.String()
+ }
+ target.Port = &port
+ }
+ }
+ }
+ }
+
+ logger := log.AddFieldsFrom(tpl.Log(), protocol, msg)
+ logger.Debugf("sending SIP request:\n%s", msg)
+
+ if err = protocol.Send(target, msg); err != nil {
+ return fmt.Errorf("send SIP message through %s protocol to %s: %w", protocol.Network(), target.Addr(), err)
+ }
+
+ return nil
+ // RFC 3261 - 18.2.2.
+ case sip.Response:
+ // resolve protocol from Via
+ protocol, err := tpl.getProtocol(msg.Transport())
+ if err != nil {
+ return err
+ }
+
+ target, err := NewTargetFromAddr(msg.Destination())
+ if err != nil {
+ return fmt.Errorf("build address target for %s: %w", msg.Destination(), err)
+ }
+
+ logger := log.AddFieldsFrom(tpl.Log(), protocol, msg)
+ logger.Debugf("sending SIP response:\n%s", msg)
+
+ if err = protocol.Send(target, msg); err != nil {
+ return fmt.Errorf("send SIP message through %s protocol to %s: %w", protocol.Network(), target.Addr(), err)
+ }
+
+ return nil
+ default:
+ return &sip.UnsupportedMessageError{
+ Err: fmt.Errorf("unsupported message %s", msg.Short()),
+ Msg: msg.String(),
+ }
+ }
+}
+
+func (tpl *layer) getProtocol(network string) (Protocol, error) {
+ network = strings.ToLower(network)
+ return tpl.protocols.getOrPutNew(protocolKey(network), func() (Protocol, error) {
+ return protocolFactory(
+ network,
+ tpl.pmsgs,
+ tpl.perrs,
+ tpl.canceled,
+ tpl.msgMapper,
+ tpl.Log(),
+ )
+ })
+}
+
+func (tpl *layer) serveProtocols() {
+ defer func() {
+ tpl.dispose()
+ close(tpl.done)
+ }()
+
+ tpl.Log().Debug("begin serve protocols")
+ defer tpl.Log().Debug("stop serve protocols")
+
+ for {
+ select {
+ case <-tpl.canceled:
+ return
+ case msg := <-tpl.pmsgs:
+ tpl.handleMessage(msg)
+ case err := <-tpl.perrs:
+ tpl.handlerError(err)
+ }
+ }
+}
+
+func (tpl *layer) dispose() {
+ tpl.Log().Debug("disposing...")
+ // wait for protocols
+ for _, protocol := range tpl.protocols.all() {
+ tpl.protocols.drop(protocolKey(protocol.Network()))
+ <-protocol.Done()
+ }
+
+ tpl.listenPorts = make(map[string][]sip.Port)
+
+ close(tpl.pmsgs)
+ close(tpl.perrs)
+ close(tpl.msgs)
+ close(tpl.errs)
+}
+
+// handles incoming message from protocol
+// should be called inside goroutine for non-blocking forwarding
+func (tpl *layer) handleMessage(msg sip.Message) {
+ logger := tpl.Log().WithFields(msg.Fields())
+
+ logger.Debugf("received SIP message:\n%s", msg)
+ logger.Trace("passing up SIP message...")
+
+ // pass up message
+ select {
+ case <-tpl.canceled:
+ case tpl.msgs <- msg:
+ logger.Trace("SIP message passed up")
+ }
+}
+
+func (tpl *layer) handlerError(err error) {
+ // TODO: implement re-connection strategy for listeners
+ var terr Error
+ if errors.As(err, &terr) {
+ // currently log
+ tpl.Log().Warnf("SIP transport error: %s", err)
+ }
+
+ logger := tpl.Log().WithFields(log.Fields{
+ "sip_error": err.Error(),
+ })
+
+ logger.Trace("passing up error...")
+
+ select {
+ case <-tpl.canceled:
+ case tpl.errs <- err:
+ logger.Trace("error passed up")
+ }
+}
+
+type protocolKey string
+
+// Thread-safe protocols pool.
+type protocolStore struct {
+ protocols map[protocolKey]Protocol
+ mu sync.RWMutex
+}
+
+func newProtocolStore() *protocolStore {
+ return &protocolStore{
+ protocols: make(map[protocolKey]Protocol),
+ }
+}
+
+func (store *protocolStore) put(key protocolKey, protocol Protocol) {
+ store.mu.Lock()
+ store.protocols[key] = protocol
+ store.mu.Unlock()
+}
+
+func (store *protocolStore) get(key protocolKey) (Protocol, bool) {
+ store.mu.RLock()
+ defer store.mu.RUnlock()
+ protocol, ok := store.protocols[key]
+ return protocol, ok
+}
+
+func (store *protocolStore) getOrPutNew(key protocolKey, factory func() (Protocol, error)) (Protocol, error) {
+ store.mu.Lock()
+ defer store.mu.Unlock()
+ protocol, ok := store.protocols[key]
+ if ok {
+ return protocol, nil
+ }
+ var err error
+ protocol, err = factory()
+ if err != nil {
+ return nil, err
+ }
+ store.protocols[key] = protocol
+ return protocol, nil
+}
+
+func (store *protocolStore) drop(key protocolKey) bool {
+ if _, ok := store.get(key); !ok {
+ return false
+ }
+ store.mu.Lock()
+ defer store.mu.Unlock()
+ delete(store.protocols, key)
+ return true
+}
+
+func (store *protocolStore) all() []Protocol {
+ all := make([]Protocol, 0)
+ store.mu.RLock()
+ defer store.mu.RUnlock()
+ for _, protocol := range store.protocols {
+ all = append(all, protocol)
+ }
+
+ return all
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/listener_pool.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/listener_pool.go
new file mode 100644
index 0000000000..1f166e8bc9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/listener_pool.go
@@ -0,0 +1,498 @@
+package transport
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "strings"
+ "sync"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+type ListenerKey string
+
+func (key ListenerKey) String() string {
+ return string(key)
+}
+
+type ListenerPool interface {
+ log.Loggable
+
+ Done() <-chan struct{}
+ String() string
+ Put(key ListenerKey, listener net.Listener) error
+ Get(key ListenerKey) (net.Listener, error)
+ All() []net.Listener
+ Drop(key ListenerKey) error
+ DropAll() error
+ Length() int
+}
+
+type ListenerHandler interface {
+ log.Loggable
+
+ Cancel()
+ Done() <-chan struct{}
+ String() string
+ Key() ListenerKey
+ Listener() net.Listener
+ Serve()
+ // TODO implement later, runtime replace of the net.Listener in handler
+ // Update(ls net.Listener)
+}
+
+type listenerPool struct {
+ hwg sync.WaitGroup
+ mu sync.RWMutex
+ store map[ListenerKey]ListenerHandler
+
+ output chan<- Connection
+ errs chan<- error
+ cancel <-chan struct{}
+
+ done chan struct{}
+ hconns chan Connection
+ herrs chan error
+
+ log log.Logger
+}
+
+func NewListenerPool(
+ output chan<- Connection,
+ errs chan<- error,
+ cancel <-chan struct{},
+ logger log.Logger,
+) ListenerPool {
+ pool := &listenerPool{
+ store: make(map[ListenerKey]ListenerHandler),
+
+ output: output,
+ errs: errs,
+ cancel: cancel,
+
+ done: make(chan struct{}),
+ hconns: make(chan Connection),
+ herrs: make(chan error),
+ }
+ pool.log = logger.
+ WithPrefix("transport.ListenerPool").
+ WithFields(log.Fields{
+ "listener_pool_ptr": fmt.Sprintf("%p", pool),
+ })
+
+ go func() {
+ <-pool.cancel
+ pool.dispose()
+ }()
+ go pool.serveHandlers()
+
+ return pool
+}
+
+func (pool *listenerPool) String() string {
+ if pool == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("transport.ListenerPool<%s>", pool.Log().Fields())
+}
+
+func (pool *listenerPool) Log() log.Logger {
+ return pool.log
+}
+
+// Done returns channel that resolves when pool gracefully completes it work.
+func (pool *listenerPool) Done() <-chan struct{} {
+ return pool.done
+}
+
+func (pool *listenerPool) Put(key ListenerKey, listener net.Listener) error {
+ select {
+ case <-pool.cancel:
+ return &PoolError{
+ fmt.Errorf("listener pool closed"),
+ "put listener",
+ pool.String(),
+ }
+ default:
+ }
+ if key == "" {
+ return &PoolError{
+ fmt.Errorf("empty listener key"),
+ "put listener",
+ pool.String(),
+ }
+ }
+
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.put(key, listener)
+}
+
+func (pool *listenerPool) Get(key ListenerKey) (net.Listener, error) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return pool.getListener(key)
+}
+
+func (pool *listenerPool) Drop(key ListenerKey) error {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ return pool.drop(key)
+}
+
+func (pool *listenerPool) DropAll() error {
+ pool.mu.Lock()
+ for key := range pool.store {
+ if err := pool.drop(key); err != nil {
+ pool.Log().Errorf("drop listener %s failed: %s", key, err)
+ }
+ }
+ pool.mu.Unlock()
+
+ return nil
+}
+
+func (pool *listenerPool) All() []net.Listener {
+ pool.mu.RLock()
+ listns := make([]net.Listener, 0)
+ for _, handler := range pool.store {
+ listns = append(listns, handler.Listener())
+ }
+ pool.mu.RUnlock()
+
+ return listns
+}
+
+func (pool *listenerPool) Length() int {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ return len(pool.store)
+}
+
+func (pool *listenerPool) dispose() {
+ // clean pool
+ pool.DropAll()
+ pool.hwg.Wait()
+
+ // stop serveHandlers goroutine
+ close(pool.hconns)
+ close(pool.herrs)
+
+ close(pool.done)
+}
+
+func (pool *listenerPool) serveHandlers() {
+ pool.Log().Debug("start serve listener handlers")
+ defer pool.Log().Debug("stop serve listener handlers")
+
+ for {
+ logger := pool.Log()
+
+ select {
+ case conn, ok := <-pool.hconns:
+ if !ok {
+ return
+ }
+ if conn == nil {
+ continue
+ }
+
+ logger = log.AddFieldsFrom(logger, conn)
+ logger.Trace("passing up connection")
+
+ select {
+ case <-pool.cancel:
+ return
+ case pool.output <- conn:
+ logger.Trace("connection passed up")
+ }
+ case err, ok := <-pool.herrs:
+ if !ok {
+ return
+ }
+ if err == nil {
+ continue
+ }
+
+ var lerr *ListenerHandlerError
+ if errors.As(err, &lerr) {
+ pool.mu.RLock()
+ handler, gerr := pool.get(lerr.Key)
+ pool.mu.RUnlock()
+ if gerr == nil {
+ logger = logger.WithFields(handler.Log().Fields())
+
+ if lerr.Network() {
+ // listener broken or closed, should be dropped
+ logger.Debugf("listener network error: %s; drop it and go further", lerr)
+
+ if err := pool.Drop(handler.Key()); err != nil {
+ logger.Error(err)
+ }
+ } else {
+ // other
+ logger.Tracef("listener error: %s; pass the error up", lerr)
+ }
+ } else {
+ // ignore, handler already dropped out
+ logger.Tracef("ignore error from already dropped out listener %s: %s", lerr.Key, lerr)
+
+ continue
+ }
+ } else {
+ // all other possible errors
+ logger.Tracef("ignore non listener error: %s", err)
+
+ continue
+ }
+
+ select {
+ case <-pool.cancel:
+ return
+ case pool.errs <- err:
+ logger.Trace("error passed up")
+ }
+ }
+ }
+}
+
+func (pool *listenerPool) put(key ListenerKey, listener net.Listener) error {
+ if _, err := pool.get(key); err == nil {
+ return &PoolError{
+ fmt.Errorf("key %s already exists in the pool", key),
+ "put listener",
+ pool.String(),
+ }
+ }
+
+ // wrap to handler
+ handler := NewListenerHandler(key, listener, pool.hconns, pool.herrs, pool.Log())
+
+ pool.Log().WithFields(handler.Log().Fields()).Trace("put listener to the pool")
+
+ // update store
+ pool.store[handler.Key()] = handler
+
+ // start serving
+ pool.hwg.Add(1)
+ go handler.Serve()
+ go func() {
+ <-handler.Done()
+ pool.hwg.Done()
+ }()
+
+ return nil
+}
+
+func (pool *listenerPool) drop(key ListenerKey) error {
+ // check existence in pool
+ handler, err := pool.get(key)
+ if err != nil {
+ return err
+ }
+
+ handler.Cancel()
+
+ pool.Log().WithFields(handler.Log().Fields()).Trace("drop listener from the pool")
+
+ // modify store
+ delete(pool.store, key)
+
+ return nil
+}
+
+func (pool *listenerPool) get(key ListenerKey) (ListenerHandler, error) {
+ if handler, ok := pool.store[key]; ok {
+ return handler, nil
+ }
+
+ return nil, &PoolError{
+ fmt.Errorf("listenr %s not found in the pool", key),
+ "get listener",
+ pool.String(),
+ }
+}
+
+func (pool *listenerPool) getListener(key ListenerKey) (net.Listener, error) {
+ if handler, err := pool.get(key); err == nil {
+ return handler.Listener(), nil
+ } else {
+ return nil, err
+ }
+}
+
+type listenerHandler struct {
+ key ListenerKey
+ listener net.Listener
+
+ output chan<- Connection
+ errs chan<- error
+
+ cancelOnce sync.Once
+ canceled chan struct{}
+ done chan struct{}
+
+ log log.Logger
+}
+
+func NewListenerHandler(
+ key ListenerKey,
+ listener net.Listener,
+ output chan<- Connection,
+ errs chan<- error,
+ logger log.Logger,
+) ListenerHandler {
+ handler := &listenerHandler{
+ key: key,
+ listener: listener,
+
+ output: output,
+ errs: errs,
+
+ canceled: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+
+ handler.log = logger.
+ WithPrefix("transport.ListenerHandler").
+ WithFields(log.Fields{
+ "listener_handler_ptr": fmt.Sprintf("%p", handler),
+ "listener_ptr": fmt.Sprintf("%p", listener),
+ "listener_key": key,
+ })
+
+ return handler
+}
+
+func (handler *listenerHandler) String() string {
+ if handler == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("transport.ListenerHandler<%s>", handler.Log().Fields())
+}
+
+func (handler *listenerHandler) Log() log.Logger {
+ return handler.log
+}
+
+func (handler *listenerHandler) Key() ListenerKey {
+ return handler.key
+}
+
+func (handler *listenerHandler) Listener() net.Listener {
+ return handler.listener
+}
+
+func (handler *listenerHandler) Serve() {
+ defer close(handler.done)
+
+ handler.Log().Debug("begin serve listener")
+ defer handler.Log().Debugf("stop serve listener")
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+ go handler.acceptConnections(wg)
+
+ wg.Wait()
+}
+
+func (handler *listenerHandler) acceptConnections(wg *sync.WaitGroup) {
+ defer func() {
+ handler.Listener().Close()
+ wg.Done()
+ }()
+
+ handler.Log().Debug("begin accept connections")
+ defer handler.Log().Debug("stop accept connections")
+
+ for {
+ // wait for the new connection
+ baseConn, err := handler.Listener().Accept()
+ if err != nil {
+ //// if we get timeout error just go further and try accept on the next iteration
+ //var netErr net.Error
+ //if errors.As(err, &netErr) {
+ // if netErr.Timeout() || netErr.Temporary() {
+ // handler.Log().Warnf("listener timeout or temporary unavailable, sleep by %s", netErrRetryTime)
+ //
+ // time.Sleep(netErrRetryTime)
+ //
+ // continue
+ // }
+ //}
+
+ // broken or closed listener
+ // pass up error and exit
+ err = &ListenerHandlerError{
+ err,
+ handler.Key(),
+ fmt.Sprintf("%p", handler),
+ listenerNetwork(handler.Listener()),
+ handler.Listener().Addr().String(),
+ }
+
+ select {
+ case <-handler.canceled:
+ case handler.errs <- err:
+ }
+
+ return
+ }
+
+ var network string
+ switch bc := baseConn.(type) {
+ case *tls.Conn:
+ network = "tls"
+ case *wsConn:
+ if _, ok := bc.Conn.(*tls.Conn); ok {
+ network = "wss"
+ } else {
+ network = "ws"
+ }
+ default:
+ network = strings.ToLower(baseConn.RemoteAddr().Network())
+ }
+
+ key := ConnectionKey(network + ":" + baseConn.RemoteAddr().String())
+ handler.output <- NewConnection(baseConn, key, network, handler.Log())
+ }
+}
+
+// Cancel stops serving.
+// blocked until Serve completes
+func (handler *listenerHandler) Cancel() {
+ handler.cancelOnce.Do(func() {
+ close(handler.canceled)
+ handler.Listener().Close()
+
+ handler.Log().Debug("listener handler canceled")
+ })
+}
+
+// Done returns channel that resolves when handler gracefully completes it work.
+func (handler *listenerHandler) Done() <-chan struct{} {
+ return handler.done
+}
+
+func listenerNetwork(ls net.Listener) string {
+ if val, ok := ls.(interface{ Network() string }); ok {
+ return val.Network()
+ }
+
+ switch ls.(type) {
+ case *net.TCPListener:
+ return "tcp"
+ case *net.UnixListener:
+ return "unix"
+ default:
+ return ""
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/option.go
new file mode 100644
index 0000000000..864ee43b82
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/option.go
@@ -0,0 +1,90 @@
+package transport
+
+import (
+ "net"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+// TODO migrate other factories to functional arguments
+type Options struct {
+ MessageMapper sip.MessageMapper
+ Logger log.Logger
+}
+
+type LayerOption interface {
+ ApplyLayer(opts *LayerOptions)
+}
+
+type LayerOptions struct {
+ Options
+ DNSResolver *net.Resolver
+}
+
+type ProtocolOption interface {
+ ApplyProtocol(opts *ProtocolOptions)
+}
+
+type ProtocolOptions struct {
+ Options
+}
+
+func WithMessageMapper(mapper sip.MessageMapper) interface {
+ LayerOption
+ ProtocolOption
+} {
+ return withMessageMapper{mapper}
+}
+
+type withMessageMapper struct {
+ mapper sip.MessageMapper
+}
+
+func (o withMessageMapper) ApplyLayer(opts *LayerOptions) {
+ opts.MessageMapper = o.mapper
+}
+
+func (o withMessageMapper) ApplyProtocol(opts *ProtocolOptions) {
+ opts.MessageMapper = o.mapper
+}
+
+func WithLogger(logger log.Logger) interface {
+ LayerOption
+ ProtocolOption
+} {
+ return withLogger{logger}
+}
+
+type withLogger struct {
+ logger log.Logger
+}
+
+func (o withLogger) ApplyLayer(opts *LayerOptions) {
+ opts.Logger = o.logger
+}
+
+func (o withLogger) ApplyProtocol(opts *ProtocolOptions) {
+ opts.Logger = o.logger
+}
+
+func WithDNSResolver(resolver *net.Resolver) LayerOption {
+ return withDnsResolver{resolver}
+}
+
+type withDnsResolver struct {
+ resolver *net.Resolver
+}
+
+func (o withDnsResolver) ApplyLayer(opts *LayerOptions) {
+ opts.DNSResolver = o.resolver
+}
+
+// Listen method options
+type ListenOption interface {
+ ApplyListen(opts *ListenOptions)
+}
+
+type ListenOptions struct {
+ TLSConfig TLSConfig
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/protocol.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/protocol.go
new file mode 100644
index 0000000000..ca453687cc
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/protocol.go
@@ -0,0 +1,71 @@
+package transport
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+const (
+ //netErrRetryTime = 5 * time.Second
+ sockTTL = time.Hour
+)
+
+// Protocol implements network specific features.
+type Protocol interface {
+ Done() <-chan struct{}
+ Network() string
+ Reliable() bool
+ Streamed() bool
+ Listen(target *Target, options ...ListenOption) error
+ Send(target *Target, msg sip.Message) error
+ String() string
+}
+
+type ProtocolFactory func(
+ network string,
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) (Protocol, error)
+
+type protocol struct {
+ network string
+ reliable bool
+ streamed bool
+
+ log log.Logger
+}
+
+func (pr *protocol) Log() log.Logger {
+ return pr.log
+}
+
+func (pr *protocol) String() string {
+ if pr == nil {
+ return ""
+ }
+
+ fields := pr.Log().Fields().WithFields(log.Fields{
+ "network": pr.network,
+ })
+
+ return fmt.Sprintf("transport.Protocol<%s>", fields)
+}
+
+func (pr *protocol) Network() string {
+ return strings.ToUpper(pr.network)
+}
+
+func (pr *protocol) Reliable() bool {
+ return pr.reliable
+}
+
+func (pr *protocol) Streamed() bool {
+ return pr.streamed
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tcp.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tcp.go
new file mode 100644
index 0000000000..c2eae57a3a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tcp.go
@@ -0,0 +1,209 @@
+package transport
+
+import (
+ "fmt"
+ "net"
+ "strings"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+type tcpListener struct {
+ net.Listener
+ network string
+}
+
+func (l *tcpListener) Network() string {
+ return strings.ToUpper(l.network)
+}
+
+// TCP protocol implementation
+type tcpProtocol struct {
+ protocol
+ listeners ListenerPool
+ connections ConnectionPool
+ conns chan Connection
+ listen func(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error)
+ dial func(addr *net.TCPAddr) (net.Conn, error)
+ resolveAddr func(addr string) (*net.TCPAddr, error)
+}
+
+func NewTcpProtocol(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Protocol {
+ p := new(tcpProtocol)
+ p.network = "tcp"
+ p.reliable = true
+ p.streamed = true
+ p.conns = make(chan Connection)
+ p.log = logger.
+ WithPrefix("transport.Protocol").
+ WithFields(log.Fields{
+ "protocol_ptr": fmt.Sprintf("%p", p),
+ })
+ // TODO: add separate errs chan to listen errors from pool for reconnection?
+ p.listeners = NewListenerPool(p.conns, errs, cancel, p.Log())
+ p.connections = NewConnectionPool(output, errs, cancel, msgMapper, p.Log())
+ p.listen = p.defaultListen
+ p.dial = p.defaultDial
+ p.resolveAddr = p.defaultResolveAddr
+ // pipe listener and connection pools
+ go p.pipePools()
+
+ return p
+}
+
+func (p *tcpProtocol) defaultListen(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error) {
+ return net.ListenTCP(p.network, addr)
+}
+
+func (p *tcpProtocol) defaultDial(addr *net.TCPAddr) (net.Conn, error) {
+ return net.DialTCP(p.network, nil, addr)
+}
+
+func (p *tcpProtocol) defaultResolveAddr(addr string) (*net.TCPAddr, error) {
+ return net.ResolveTCPAddr(p.network, addr)
+}
+
+func (p *tcpProtocol) Done() <-chan struct{} {
+ return p.connections.Done()
+}
+
+// piping new connections to connection pool for serving
+func (p *tcpProtocol) pipePools() {
+ defer close(p.conns)
+
+ p.Log().Debug("start pipe pools")
+ defer p.Log().Debug("stop pipe pools")
+
+ for {
+ select {
+ case <-p.listeners.Done():
+ return
+ case conn := <-p.conns:
+ logger := log.AddFieldsFrom(p.Log(), conn)
+
+ if err := p.connections.Put(conn, sockTTL); err != nil {
+ // TODO should it be passed up to UA?
+ logger.Errorf("put %s connection to the pool failed: %s", conn.Key(), err)
+
+ conn.Close()
+
+ continue
+ }
+ }
+ }
+}
+
+func (p *tcpProtocol) Listen(target *Target, options ...ListenOption) error {
+ target = FillTargetHostAndPort(p.Network(), target)
+ laddr, err := p.resolveAddr(target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ listener, err := p.listen(laddr, options...)
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("listen on %s %s address", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ p.Log().Debugf("begin listening on %s %s", p.Network(), target.Addr())
+
+ // index listeners by local address
+ // should live infinitely
+ key := ListenerKey(fmt.Sprintf("%s:0.0.0.0:%d", p.network, target.Port))
+ err = p.listeners.Put(key, &tcpListener{
+ Listener: listener,
+ network: p.network,
+ })
+ if err != nil {
+ err = &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("put %s listener to the pool", key),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+ return err // should be nil here
+}
+
+func (p *tcpProtocol) Send(target *Target, msg sip.Message) error {
+ target = FillTargetHostAndPort(p.Network(), target)
+
+ // validate remote address
+ if target.Host == "" {
+ return &ProtocolError{
+ fmt.Errorf("empty remote target host"),
+ fmt.Sprintf("send SIP message to %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ // resolve remote address
+ raddr, err := p.resolveAddr(target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ // find or create connection
+ conn, err := p.getOrCreateConnection(raddr)
+ if err != nil {
+ return &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("get or create %s connection", p.Network()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ logger := log.AddFieldsFrom(p.Log(), conn, msg)
+ logger.Tracef("writing SIP message to %s %s", p.Network(), raddr)
+
+ // send message
+ _, err = conn.Write([]byte(msg.String()))
+ if err != nil {
+ err = &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("write SIP message to the %s connection", conn.Key()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ return err
+}
+
+func (p *tcpProtocol) getOrCreateConnection(raddr *net.TCPAddr) (Connection, error) {
+ key := ConnectionKey(p.network + ":" + raddr.String())
+ conn, err := p.connections.Get(key)
+ if err != nil {
+ p.Log().Debugf("connection for remote address %s %s not found, create a new one", p.Network(), raddr)
+
+ tcpConn, err := p.dial(raddr)
+ if err != nil {
+ return nil, fmt.Errorf("dial to %s %s: %w", p.Network(), raddr, err)
+ }
+
+ conn = NewConnection(tcpConn, key, p.network, p.Log())
+
+ if err := p.connections.Put(conn, sockTTL); err != nil {
+ return conn, fmt.Errorf("put %s connection to the pool: %w", conn.Key(), err)
+ }
+ }
+
+ return conn, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tls.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tls.go
new file mode 100644
index 0000000000..401abe81b5
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/tls.go
@@ -0,0 +1,67 @@
+package transport
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+type tlsProtocol struct {
+ tcpProtocol
+}
+
+func NewTlsProtocol(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Protocol {
+ p := new(tlsProtocol)
+ p.network = "tls"
+ p.reliable = true
+ p.streamed = true
+ p.conns = make(chan Connection)
+ p.log = logger.
+ WithPrefix("transport.Protocol").
+ WithFields(log.Fields{
+ "protocol_ptr": fmt.Sprintf("%p", p),
+ })
+ //TODO: add separate errs chan to listen errors from pool for reconnection?
+ p.listeners = NewListenerPool(p.conns, errs, cancel, p.Log())
+ p.connections = NewConnectionPool(output, errs, cancel, msgMapper, p.Log())
+ p.listen = func(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error) {
+ if len(options) == 0 {
+ return net.ListenTCP("tcp", addr)
+ }
+ optsHash := ListenOptions{}
+ for _, opt := range options {
+ opt.ApplyListen(&optsHash)
+ }
+ cert, err := tls.LoadX509KeyPair(optsHash.TLSConfig.Cert, optsHash.TLSConfig.Key)
+ if err != nil {
+ return nil, fmt.Errorf("load TLS certficate %s: %w", optsHash.TLSConfig.Cert, err)
+ }
+ return tls.Listen("tcp", addr.String(), &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ })
+ }
+ p.dial = func(addr *net.TCPAddr) (net.Conn, error) {
+ return tls.Dial("tcp", addr.String(), &tls.Config{
+ VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
+ return nil
+ },
+ })
+ }
+ p.resolveAddr = func(addr string) (*net.TCPAddr, error) {
+ return net.ResolveTCPAddr("tcp", addr)
+ }
+ //pipe listener and connection pools
+ go p.pipePools()
+
+ return p
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/transport.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/transport.go
new file mode 100644
index 0000000000..a48731f116
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/transport.go
@@ -0,0 +1,357 @@
+// transport package implements SIP transport layer.
+package transport
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+const (
+ MTU = sip.MTU
+
+ DefaultHost = sip.DefaultHost
+ DefaultProtocol = sip.DefaultProtocol
+
+ DefaultUdpPort = sip.DefaultUdpPort
+ DefaultTcpPort = sip.DefaultTcpPort
+ DefaultTlsPort = sip.DefaultTlsPort
+ DefaultWsPort = sip.DefaultWsPort
+ DefaultWssPort = sip.DefaultWssPort
+)
+
+// Target endpoint
+type Target struct {
+ Host string
+ Port *sip.Port
+}
+
+func (trg *Target) Addr() string {
+ var (
+ host string
+ port sip.Port
+ )
+
+ if strings.TrimSpace(trg.Host) != "" {
+ host = trg.Host
+ } else {
+ host = DefaultHost
+ }
+
+ if trg.Port != nil {
+ port = *trg.Port
+ }
+
+ return fmt.Sprintf("%v:%v", host, port)
+}
+
+func (trg *Target) String() string {
+ if trg == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "target_addr": trg.Addr(),
+ }
+
+ return fmt.Sprintf("transport.Target<%s>", fields)
+}
+
+func NewTarget(host string, port int) *Target {
+ cport := sip.Port(port)
+
+ return &Target{Host: host, Port: &cport}
+}
+
+func NewTargetFromAddr(addr string) (*Target, error) {
+ host, port, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+ iport, err := strconv.Atoi(port)
+ if err != nil {
+ return nil, err
+ }
+ return NewTarget(host, iport), nil
+}
+
+// Fills endpoint target with default values.
+func FillTargetHostAndPort(network string, target *Target) *Target {
+ if strings.TrimSpace(target.Host) == "" {
+ target.Host = DefaultHost
+ }
+ if target.Port == nil {
+ p := sip.DefaultPort(network)
+ target.Port = &p
+ }
+
+ return target
+}
+
+// Transport error
+type Error interface {
+ net.Error
+ // Network indicates network level errors
+ Network() bool
+}
+
+func isNetwork(err error) bool {
+ var netErr net.Error
+ if errors.As(err, &netErr) {
+ return true
+ } else {
+ return errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe)
+ }
+}
+func isTimeout(err error) bool {
+ var netErr net.Error
+ if errors.As(err, &netErr) {
+ return netErr.Timeout()
+ }
+ return false
+}
+func isTemporary(err error) bool {
+ var netErr net.Error
+ if errors.As(err, &netErr) {
+ return netErr.Temporary()
+ }
+ return false
+}
+func isCanceled(err error) bool {
+ var cancelErr sip.CancelError
+ if errors.As(err, &cancelErr) {
+ return cancelErr.Canceled()
+ }
+ return false
+}
+func isExpired(err error) bool {
+ var expiryErr sip.ExpireError
+ if errors.As(err, &expiryErr) {
+ return expiryErr.Expired()
+ }
+ return false
+}
+
+// Connection level error.
+type ConnectionError struct {
+ Err error
+ Op string
+ Net string
+ Source string
+ Dest string
+ ConnPtr string
+}
+
+func (err *ConnectionError) Unwrap() error { return err.Err }
+func (err *ConnectionError) Network() bool { return isNetwork(err.Err) }
+func (err *ConnectionError) Timeout() bool { return isTimeout(err.Err) }
+func (err *ConnectionError) Temporary() bool { return isTemporary(err.Err) }
+func (err *ConnectionError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "network": "???",
+ "connection_ptr": "???",
+ "source": "???",
+ "destination": "???",
+ }
+
+ if err.Net != "" {
+ fields["network"] = err.Net
+ }
+ if err.ConnPtr != "" {
+ fields["connection_ptr"] = err.ConnPtr
+ }
+ if err.Source != "" {
+ fields["source"] = err.Source
+ }
+ if err.Dest != "" {
+ fields["destination"] = err.Dest
+ }
+
+ return fmt.Sprintf("transport.ConnectionError<%s> %s failed: %s", fields, err.Op, err.Err)
+}
+
+type ExpireError string
+
+func (err ExpireError) Network() bool { return false }
+func (err ExpireError) Timeout() bool { return true }
+func (err ExpireError) Temporary() bool { return false }
+func (err ExpireError) Canceled() bool { return false }
+func (err ExpireError) Expired() bool { return true }
+func (err ExpireError) Error() string { return "transport.ExpireError: " + string(err) }
+
+// Net Protocol level error
+type ProtocolError struct {
+ Err error
+ Op string
+ ProtoPtr string
+}
+
+func (err *ProtocolError) Unwrap() error { return err.Err }
+func (err *ProtocolError) Network() bool { return isNetwork(err.Err) }
+func (err *ProtocolError) Timeout() bool { return isTimeout(err.Err) }
+func (err *ProtocolError) Temporary() bool { return isTemporary(err.Err) }
+func (err *ProtocolError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "protocol_ptr": "???",
+ }
+
+ if err.ProtoPtr != "" {
+ fields["protocol_ptr"] = err.ProtoPtr
+ }
+
+ return fmt.Sprintf("transport.ProtocolError<%s> %s failed: %s", fields, err.Op, err.Err)
+}
+
+type ConnectionHandlerError struct {
+ Err error
+ Key ConnectionKey
+ HandlerPtr string
+ Net string
+ LAddr string
+ RAddr string
+}
+
+func (err *ConnectionHandlerError) Unwrap() error { return err.Err }
+func (err *ConnectionHandlerError) Network() bool { return isNetwork(err.Err) }
+func (err *ConnectionHandlerError) Timeout() bool { return isTimeout(err.Err) }
+func (err *ConnectionHandlerError) Temporary() bool { return isTemporary(err.Err) }
+func (err *ConnectionHandlerError) Canceled() bool { return isCanceled(err.Err) }
+func (err *ConnectionHandlerError) Expired() bool { return isExpired(err.Err) }
+func (err *ConnectionHandlerError) EOF() bool {
+ if err.Err == io.EOF {
+ return true
+ }
+ ok, _ := regexp.MatchString("(?i)eof", err.Err.Error())
+ return ok
+}
+func (err *ConnectionHandlerError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "handler_ptr": "???",
+ "network": "???",
+ "local_addr": "???",
+ "remote_addr": "???",
+ }
+
+ if err.HandlerPtr != "" {
+ fields["handler_ptr"] = err.HandlerPtr
+ }
+ if err.Net != "" {
+ fields["network"] = err.Net
+ }
+ if err.LAddr != "" {
+ fields["local_addr"] = err.LAddr
+ }
+ if err.RAddr != "" {
+ fields["remote_addr"] = err.RAddr
+ }
+
+ return fmt.Sprintf("transport.ConnectionHandlerError<%s>: %s", fields, err.Err)
+}
+
+type ListenerHandlerError struct {
+ Err error
+ Key ListenerKey
+ HandlerPtr string
+ Net string
+ Addr string
+}
+
+func (err *ListenerHandlerError) Unwrap() error { return err.Err }
+func (err *ListenerHandlerError) Network() bool { return isNetwork(err.Err) }
+func (err *ListenerHandlerError) Timeout() bool { return isTimeout(err.Err) }
+func (err *ListenerHandlerError) Temporary() bool { return isTemporary(err.Err) }
+func (err *ListenerHandlerError) Canceled() bool { return isCanceled(err.Err) }
+func (err *ListenerHandlerError) Expired() bool { return isExpired(err.Err) }
+func (err *ListenerHandlerError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "handler_ptr": "???",
+ "network": "???",
+ "local_addr": "???",
+ "remote_addr": "???",
+ }
+
+ if err.HandlerPtr != "" {
+ fields["handler_ptr"] = err.HandlerPtr
+ }
+ if err.Net != "" {
+ fields["network"] = err.Net
+ }
+ if err.Addr != "" {
+ fields["local_addr"] = err.Addr
+ }
+
+ return fmt.Sprintf("transport.ListenerHandlerError<%s>: %s", fields, err.Err)
+}
+
+type PoolError struct {
+ Err error
+ Op string
+ Pool string
+}
+
+func (err *PoolError) Unwrap() error { return err.Err }
+func (err *PoolError) Network() bool { return isNetwork(err.Err) }
+func (err *PoolError) Timeout() bool { return isTimeout(err.Err) }
+func (err *PoolError) Temporary() bool { return isTemporary(err.Err) }
+func (err *PoolError) Error() string {
+ if err == nil {
+ return ""
+ }
+
+ fields := log.Fields{
+ "pool": "???",
+ }
+
+ if err.Pool != "" {
+ fields["pool"] = err.Pool
+ }
+
+ return fmt.Sprintf("transport.PoolError<%s> %s failed: %s", fields, err.Op, err.Err)
+}
+
+type UnsupportedProtocolError string
+
+func (err UnsupportedProtocolError) Network() bool { return false }
+func (err UnsupportedProtocolError) Timeout() bool { return false }
+func (err UnsupportedProtocolError) Temporary() bool { return false }
+func (err UnsupportedProtocolError) Error() string {
+ return "transport.UnsupportedProtocolError: " + string(err)
+}
+
+//TLSConfig for TLS and WSS only
+type TLSConfig struct {
+ Domain string
+ Cert string
+ Key string
+ Pass string
+}
+
+func (c TLSConfig) ApplyListen(opts *ListenOptions) {
+ opts.TLSConfig.Domain = c.Domain
+ opts.TLSConfig.Cert = c.Cert
+ opts.TLSConfig.Key = c.Key
+ opts.TLSConfig.Pass = c.Pass
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/udp.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/udp.go
new file mode 100644
index 0000000000..ea0955e5a9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/udp.go
@@ -0,0 +1,138 @@
+package transport
+
+import (
+ "fmt"
+ "net"
+ "strings"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+// UDP protocol implementation
+type udpProtocol struct {
+ protocol
+ connections ConnectionPool
+}
+
+func NewUdpProtocol(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Protocol {
+ p := new(udpProtocol)
+ p.network = "udp"
+ p.reliable = false
+ p.streamed = false
+ p.log = logger.
+ WithPrefix("transport.Protocol").
+ WithFields(log.Fields{
+ "protocol_ptr": fmt.Sprintf("%p", p),
+ })
+ // TODO: add separate errs chan to listen errors from pool for reconnection?
+ p.connections = NewConnectionPool(output, errs, cancel, msgMapper, p.Log())
+
+ return p
+}
+
+func (p *udpProtocol) Done() <-chan struct{} {
+ return p.connections.Done()
+}
+
+func (p *udpProtocol) Listen(target *Target, options ...ListenOption) error {
+ // fill empty target props with default values
+ target = FillTargetHostAndPort(p.Network(), target)
+ // resolve local UDP endpoint
+ laddr, err := net.ResolveUDPAddr(p.network, target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+ // create UDP connection
+ udpConn, err := net.ListenUDP(p.network, laddr)
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("listen on %s %s address", p.Network(), laddr),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ p.Log().Debugf("begin listening on %s %s", p.Network(), laddr)
+
+ // register new connection
+ // index by local address, TTL=0 - unlimited expiry time
+ key := ConnectionKey(fmt.Sprintf("%s:0.0.0.0:%d", p.network, laddr.Port))
+ conn := NewConnection(udpConn, key, p.network, p.Log())
+ err = p.connections.Put(conn, 0)
+ if err != nil {
+ err = &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("put %s connection to the pool", conn.Key()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ return err // should be nil here
+}
+
+func (p *udpProtocol) Send(target *Target, msg sip.Message) error {
+ target = FillTargetHostAndPort(p.Network(), target)
+
+ // validate remote address
+ if target.Host == "" {
+ return &ProtocolError{
+ fmt.Errorf("empty remote target host"),
+ fmt.Sprintf("send SIP message to %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ // resolve remote address
+ raddr, err := net.ResolveUDPAddr(p.network, target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ _, port, err := net.SplitHostPort(msg.Source())
+ if err != nil {
+ return &ProtocolError{
+ Err: err,
+ Op: "resolve source port",
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ for _, conn := range p.connections.All() {
+ parts := strings.Split(string(conn.Key()), ":")
+ if parts[2] == port {
+ logger := log.AddFieldsFrom(p.Log(), conn, msg)
+ logger.Tracef("writing SIP message to %s %s", p.Network(), raddr)
+
+ if _, err = conn.WriteTo([]byte(msg.String()), raddr); err != nil {
+ return &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("write SIP message to the %s connection", conn.Key()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ return nil
+ }
+ }
+
+ return &ProtocolError{
+ fmt.Errorf("connection on port %s not found", port),
+ "search connection",
+ fmt.Sprintf("%p", p),
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/ws.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/ws.go
new file mode 100644
index 0000000000..b49f5c7b35
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/ws.go
@@ -0,0 +1,301 @@
+package transport
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "time"
+
+ "github.com/gobwas/ws"
+ "github.com/gobwas/ws/wsutil"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+var (
+ wsSubProtocol = "sip"
+)
+
+type wsConn struct {
+ net.Conn
+ client bool
+}
+
+func (wc *wsConn) Read(b []byte) (n int, err error) {
+ var msg []byte
+ var op ws.OpCode
+ if wc.client {
+ msg, op, err = wsutil.ReadServerData(wc.Conn)
+ } else {
+ msg, op, err = wsutil.ReadClientData(wc.Conn)
+ }
+ if err != nil {
+ // handle error
+ var wsErr wsutil.ClosedError
+ if errors.As(err, &wsErr) {
+ return n, io.EOF
+ }
+ return n, err
+ }
+ if op == ws.OpClose {
+ return n, io.EOF
+ }
+ copy(b, msg)
+ return len(msg), err
+}
+
+func (wc *wsConn) Write(b []byte) (n int, err error) {
+ if wc.client {
+ err = wsutil.WriteClientMessage(wc.Conn, ws.OpText, b)
+ } else {
+ err = wsutil.WriteServerMessage(wc.Conn, ws.OpText, b)
+ }
+ if err != nil {
+ // handle error
+ var wsErr wsutil.ClosedError
+ if errors.As(err, &wsErr) {
+ return n, io.EOF
+ }
+ return n, err
+ }
+ return len(b), nil
+}
+
+type wsListener struct {
+ net.Listener
+ network string
+ u ws.Upgrader
+ log log.Logger
+}
+
+func NewWsListener(listener net.Listener, network string, log log.Logger) *wsListener {
+ l := &wsListener{
+ Listener: listener,
+ network: network,
+ log: log,
+ }
+ l.u.Protocol = func(val []byte) bool {
+ return string(val) == wsSubProtocol
+ }
+ return l
+}
+
+func (l *wsListener) Accept() (net.Conn, error) {
+ conn, err := l.Listener.Accept()
+ if err != nil {
+ return nil, fmt.Errorf("accept new connection: %w", err)
+ }
+ if _, err = l.u.Upgrade(conn); err == nil {
+ conn = &wsConn{
+ Conn: conn,
+ client: false,
+ }
+ } else {
+ l.log.Warnf("fallback to simple TCP connection due to WS upgrade error: %s", err)
+ err = nil
+ }
+ return conn, err
+}
+
+func (l *wsListener) Network() string {
+ return strings.ToUpper(l.network)
+}
+
+type wsProtocol struct {
+ protocol
+ listeners ListenerPool
+ connections ConnectionPool
+ conns chan Connection
+ listen func(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error)
+ resolveAddr func(addr string) (*net.TCPAddr, error)
+ dialer ws.Dialer
+}
+
+func NewWsProtocol(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Protocol {
+ p := new(wsProtocol)
+ p.network = "ws"
+ p.reliable = true
+ p.streamed = true
+ p.conns = make(chan Connection)
+ p.log = logger.
+ WithPrefix("transport.Protocol").
+ WithFields(log.Fields{
+ "protocol_ptr": fmt.Sprintf("%p", p),
+ })
+ //TODO: add separate errs chan to listen errors from pool for reconnection?
+ p.listeners = NewListenerPool(p.conns, errs, cancel, p.Log())
+ p.connections = NewConnectionPool(output, errs, cancel, msgMapper, p.Log())
+ p.listen = p.defaultListen
+ p.resolveAddr = p.defaultResolveAddr
+ p.dialer.Protocols = []string{wsSubProtocol}
+ p.dialer.Timeout = time.Minute
+ //pipe listener and connection pools
+ go p.pipePools()
+
+ return p
+}
+
+func (p *wsProtocol) defaultListen(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error) {
+ return net.ListenTCP("tcp", addr)
+}
+
+func (p *wsProtocol) defaultResolveAddr(addr string) (*net.TCPAddr, error) {
+ return net.ResolveTCPAddr("tcp", addr)
+}
+
+func (p *wsProtocol) Done() <-chan struct{} {
+ return p.connections.Done()
+}
+
+//piping new connections to connection pool for serving
+func (p *wsProtocol) pipePools() {
+ defer close(p.conns)
+
+ p.Log().Debug("start pipe pools")
+ defer p.Log().Debug("stop pipe pools")
+
+ for {
+ select {
+ case <-p.listeners.Done():
+ return
+ case conn := <-p.conns:
+ logger := log.AddFieldsFrom(p.Log(), conn)
+
+ if err := p.connections.Put(conn, sockTTL); err != nil {
+ // TODO should it be passed up to UA?
+ logger.Errorf("put %s connection to the pool failed: %s", conn.Key(), err)
+
+ conn.Close()
+
+ continue
+ }
+ }
+ }
+}
+
+func (p *wsProtocol) Listen(target *Target, options ...ListenOption) error {
+ target = FillTargetHostAndPort(p.Network(), target)
+ laddr, err := p.resolveAddr(target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ listener, err := p.listen(laddr, options...)
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("listen on %s %s address", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ p.Log().Debugf("begin listening on %s %s", p.Network(), target.Addr())
+
+ //index listeners by local address
+ // should live infinitely
+ key := ListenerKey(fmt.Sprintf("%s:0.0.0.0:%d", p.network, target.Port))
+ err = p.listeners.Put(key, NewWsListener(listener, p.network, p.Log()))
+ if err != nil {
+ err = &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("put %s listener to the pool", key),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ return err //should be nil here
+}
+
+func (p *wsProtocol) Send(target *Target, msg sip.Message) error {
+ target = FillTargetHostAndPort(p.Network(), target)
+
+ //validate remote address
+ if target.Host == "" {
+ return &ProtocolError{
+ fmt.Errorf("empty remote target host"),
+ fmt.Sprintf("send SIP message to %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+ //resolve remote address
+ raddr, err := p.resolveAddr(target.Addr())
+ if err != nil {
+ return &ProtocolError{
+ err,
+ fmt.Sprintf("resolve target address %s %s", p.Network(), target.Addr()),
+ fmt.Sprintf("%p", p),
+ }
+ }
+
+ //find or create connection
+ conn, err := p.getOrCreateConnection(raddr)
+ if err != nil {
+ return &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("get or create %s connection", p.Network()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ logger := log.AddFieldsFrom(p.Log(), conn, msg)
+ logger.Tracef("writing SIP message to %s %s", p.Network(), raddr)
+
+ //send message
+ _, err = conn.Write([]byte(msg.String()))
+ if err != nil {
+ err = &ProtocolError{
+ Err: err,
+ Op: fmt.Sprintf("write SIP message to the %s connection", conn.Key()),
+ ProtoPtr: fmt.Sprintf("%p", p),
+ }
+ }
+
+ return err
+}
+
+func (p *wsProtocol) getOrCreateConnection(raddr *net.TCPAddr) (Connection, error) {
+ key := ConnectionKey(p.network + ":" + raddr.String())
+ conn, err := p.connections.Get(key)
+ if err != nil {
+ p.Log().Debugf("connection for address %s %s not found; create a new one", p.Network(), raddr)
+
+ ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
+ defer cancel()
+ url := fmt.Sprintf("%s://%s", p.network, raddr)
+ baseConn, _, _, err := p.dialer.Dial(ctx, url)
+ if err == nil {
+ baseConn = &wsConn{
+ Conn: baseConn,
+ client: true,
+ }
+ } else {
+ if baseConn == nil {
+ return nil, fmt.Errorf("dial to %s %s: %w", p.Network(), raddr, err)
+ }
+
+ p.Log().Warnf("fallback to TCP connection due to WS upgrade error: %s", err)
+ }
+
+ conn = NewConnection(baseConn, key, p.network, p.Log())
+
+ if err := p.connections.Put(conn, sockTTL); err != nil {
+ return conn, fmt.Errorf("put %s connection to the pool: %w", conn.Key(), err)
+ }
+ }
+
+ return conn, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/wss.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/wss.go
new file mode 100644
index 0000000000..696ca14944
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/transport/wss.go
@@ -0,0 +1,66 @@
+package transport
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/ghettovoice/gosip/log"
+ "github.com/ghettovoice/gosip/sip"
+)
+
+type wssProtocol struct {
+ wsProtocol
+}
+
+func NewWssProtocol(
+ output chan<- sip.Message,
+ errs chan<- error,
+ cancel <-chan struct{},
+ msgMapper sip.MessageMapper,
+ logger log.Logger,
+) Protocol {
+ p := new(wssProtocol)
+ p.network = "wss"
+ p.reliable = true
+ p.streamed = true
+ p.conns = make(chan Connection)
+ p.log = logger.
+ WithPrefix("transport.Protocol").
+ WithFields(log.Fields{
+ "protocol_ptr": fmt.Sprintf("%p", p),
+ })
+ //TODO: add separate errs chan to listen errors from pool for reconnection?
+ p.listeners = NewListenerPool(p.conns, errs, cancel, p.Log())
+ p.connections = NewConnectionPool(output, errs, cancel, msgMapper, p.Log())
+ p.listen = func(addr *net.TCPAddr, options ...ListenOption) (net.Listener, error) {
+ if len(options) == 0 {
+ return net.ListenTCP("tcp", addr)
+ }
+ optsHash := ListenOptions{}
+ for _, opt := range options {
+ opt.ApplyListen(&optsHash)
+ }
+ cert, err := tls.LoadX509KeyPair(optsHash.TLSConfig.Cert, optsHash.TLSConfig.Key)
+ if err != nil {
+ return nil, fmt.Errorf("load TLS certficate %s: %w", optsHash.TLSConfig.Cert, err)
+ }
+ return tls.Listen("tcp", addr.String(), &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ })
+ }
+ p.resolveAddr = p.defaultResolveAddr
+ p.dialer.Protocols = []string{wsSubProtocol}
+ p.dialer.Timeout = time.Minute
+ p.dialer.TLSConfig = &tls.Config{
+ VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
+ return nil
+ },
+ }
+ //pipe listener and connection pools
+ go p.pipePools()
+
+ return p
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/elasticchan.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/elasticchan.go
new file mode 100644
index 0000000000..d98ff3b636
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/elasticchan.go
@@ -0,0 +1,147 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package util
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/ghettovoice/gosip/log"
+)
+
+// The buffer size of the primitive input and output chans.
+const c_ELASTIC_CHANSIZE = 3
+
+// A dynamic channel that does not block on send, but has an unlimited buffer capacity.
+// ElasticChan uses a dynamic slice to buffer signals received on the input channel until
+// the output channel is ready to process them.
+type ElasticChan struct {
+ In chan interface{}
+ Out chan interface{}
+ buffer []interface{}
+ stopped bool
+ done chan struct{}
+
+ log log.Logger
+ logMu sync.RWMutex
+}
+
+// Initialise the Elastic channel, and start the management goroutine.
+func (c *ElasticChan) Init() {
+ c.In = make(chan interface{}, c_ELASTIC_CHANSIZE)
+ c.Out = make(chan interface{}, c_ELASTIC_CHANSIZE)
+ c.buffer = make([]interface{}, 0)
+ c.done = make(chan struct{})
+}
+
+func (c *ElasticChan) Run() {
+ go c.manage()
+}
+
+func (c *ElasticChan) Stop() {
+ select {
+ case <-c.done:
+ return
+ default:
+ }
+
+ logger := c.Log()
+
+ if logger != nil {
+ logger.Trace("stopping elastic chan...")
+ }
+
+ close(c.In)
+ <-c.done
+
+ if logger != nil {
+ logger.Trace("elastic chan stopped")
+ }
+}
+
+func (c *ElasticChan) Log() log.Logger {
+ c.logMu.RLock()
+ defer c.logMu.RUnlock()
+
+ return c.log
+
+}
+
+func (c *ElasticChan) SetLog(logger log.Logger) {
+ c.logMu.Lock()
+
+ c.log = logger.
+ WithPrefix("util.ElasticChan").
+ WithFields(log.Fields{
+ "elastic_chan_ptr": fmt.Sprintf("%p", c),
+ })
+
+ c.logMu.Unlock()
+}
+
+// Poll for input from one end of the channel and add it to the buffer.
+// Also poll sending buffered signals out over the output chan.
+// TODO: add cancel chan
+func (c *ElasticChan) manage() {
+ defer close(c.done)
+
+loop:
+ for {
+ logger := c.Log()
+
+ if len(c.buffer) > 0 {
+ // The buffer has something in it, so try to send as well as
+ // receive.
+ // (Receive first in order to minimize blocked Send() calls).
+ select {
+ case in, ok := <-c.In:
+ if !ok {
+ if logger != nil {
+ logger.Trace("elastic chan will dispose")
+ }
+
+ break loop
+ }
+ c.Log().Tracef("ElasticChan %p gets '%v'", c, in)
+ c.buffer = append(c.buffer, in)
+ case c.Out <- c.buffer[0]:
+ c.Log().Tracef("ElasticChan %p sends '%v'", c, c.buffer[0])
+ c.buffer = c.buffer[1:]
+ }
+ } else {
+ // The buffer is empty, so there's nothing to send.
+ // Just wait to receive.
+ in, ok := <-c.In
+ if !ok {
+ if logger != nil {
+ logger.Trace("elastic chan will dispose")
+ }
+
+ break loop
+ }
+ c.Log().Tracef("ElasticChan %p gets '%v'", c, in)
+ c.buffer = append(c.buffer, in)
+ }
+ }
+
+ c.dispose()
+}
+
+func (c *ElasticChan) dispose() {
+ logger := c.Log()
+
+ if logger != nil {
+ logger.Trace("elastic chan disposing...")
+ }
+
+ for len(c.buffer) > 0 {
+ select {
+ case c.Out <- c.buffer[0]:
+ c.buffer = c.buffer[1:]
+ default:
+ }
+ }
+
+ if logger != nil {
+ logger.Trace("elastic chan disposed")
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/helpers.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/helpers.go
new file mode 100644
index 0000000000..0e9499c2e7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/helpers.go
@@ -0,0 +1,104 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package util
+
+import (
+ "errors"
+ "net"
+ "sync"
+)
+
+// Check two string pointers for equality as follows:
+// - If neither pointer is nil, check equality of the underlying strings.
+// - If either pointer is nil, return true if and only if they both are.
+func StrPtrEq(a *string, b *string) bool {
+ if a == nil || b == nil {
+ return a == b
+ }
+
+ return *a == *b
+}
+
+// Check two uint16 pointers for equality as follows:
+// - If neither pointer is nil, check equality of the underlying uint16s.
+// - If either pointer is nil, return true if and only if they both are.
+func Uint16PtrEq(a *uint16, b *uint16) bool {
+ if a == nil || b == nil {
+ return a == b
+ }
+
+ return *a == *b
+}
+
+func Coalesce(arg1 interface{}, arg2 interface{}, args ...interface{}) interface{} {
+ all := append([]interface{}{arg1, arg2}, args...)
+ for _, arg := range all {
+ if arg != nil {
+ return arg
+ }
+ }
+
+ return nil
+}
+
+func Noop() {}
+
+func MergeErrs(chs ...<-chan error) <-chan error {
+ wg := new(sync.WaitGroup)
+ out := make(chan error)
+
+ pipe := func(ch <-chan error) {
+ defer wg.Done()
+ for err := range ch {
+ out <- err
+ }
+ }
+
+ wg.Add(len(chs))
+ for _, ch := range chs {
+ go pipe(ch)
+ }
+
+ go func() {
+ wg.Wait()
+ close(out)
+ }()
+
+ return out
+}
+
+func ResolveSelfIP() (net.IP, error) {
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ for _, iface := range ifaces {
+ if iface.Flags&net.FlagUp == 0 {
+ continue // interface down
+ }
+ if iface.Flags&net.FlagLoopback != 0 {
+ continue // loopback interface
+ }
+ addrs, err := iface.Addrs()
+ if err != nil {
+ return nil, err
+ }
+ for _, addr := range addrs {
+ var ip net.IP
+ switch v := addr.(type) {
+ case *net.IPNet:
+ ip = v.IP
+ case *net.IPAddr:
+ ip = v.IP
+ }
+ if ip == nil || ip.IsLoopback() {
+ continue
+ }
+ ip = ip.To4()
+ if ip == nil {
+ continue // not an ipv4 address
+ }
+ return ip, nil
+ }
+ }
+ return nil, errors.New("server not connected to any network")
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/rand.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/rand.go
new file mode 100644
index 0000000000..20382e69b6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/rand.go
@@ -0,0 +1,38 @@
+package util
+
+import (
+ "math/rand"
+ "time"
+)
+
+const (
+ letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
+// https://github.com/kpbird/golang_random_string
+func RandString(n int) string {
+ output := make([]byte, n)
+ // We will take n bytes, one byte for each character of output.
+ randomness := make([]byte, n)
+ // read all random
+ _, err := rand.Read(randomness)
+ if err != nil {
+ panic(err)
+ }
+ l := len(letterBytes)
+ // fill output
+ for pos := range output {
+ // get random item
+ random := randomness[pos]
+ // random % 64
+ randomPos := random % uint8(l)
+ // put into output
+ output[pos] = letterBytes[randomPos]
+ }
+
+ return string(output)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/semaphore.go b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/semaphore.go
new file mode 100644
index 0000000000..5594ecfdc1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/ghettovoice/gosip/util/semaphore.go
@@ -0,0 +1,75 @@
+// Forked from github.com/StefanKopieczek/gossip by @StefanKopieczek
+package util
+
+import "sync"
+
+// Simple semaphore implementation.
+// Any number of calls to Acquire() can be made; these will not block.
+// If the semaphore has been acquired more times than it has been released, it is called 'blocked'.
+// Otherwise, it is called 'free'.
+type Semaphore interface {
+ // Take a semaphore lock.
+ Acquire()
+
+ // Release an acquired semaphore lock.
+ // This should only be called when the semaphore is blocked, otherwise behaviour is undefined
+ Release()
+
+ // Block execution until the semaphore is free.
+ Wait()
+
+ // Clean up the semaphore object.
+ Dispose()
+}
+
+func NewSemaphore() Semaphore {
+ sem := new(semaphore)
+ sem.cond = sync.NewCond(&sync.Mutex{})
+ go func(s *semaphore) {
+ select {
+ case <-s.stop:
+ return
+ case <-s.acquired:
+ s.locks += 1
+ case <-s.released:
+ s.locks -= 1
+ if s.locks == 0 {
+ s.cond.Broadcast()
+ }
+ }
+ }(sem)
+ return sem
+}
+
+// Concrete implementation of Semaphore.
+type semaphore struct {
+ held bool
+ locks int
+ acquired chan bool
+ released chan bool
+ stop chan bool
+ cond *sync.Cond
+}
+
+// Implements Semaphore.Acquire()
+func (sem *semaphore) Acquire() {
+ sem.acquired <- true
+}
+
+// Implements Semaphore.Release()
+func (sem *semaphore) Release() {
+ sem.released <- true
+}
+
+// Implements Semaphore.Wait()
+func (sem *semaphore) Wait() {
+ sem.cond.L.Lock()
+ for sem.locks != 0 {
+ sem.cond.Wait()
+ }
+}
+
+// Implements Semaphore.Dispose()
+func (sem *semaphore) Dispose() {
+ sem.stop <- true
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/LICENSE
new file mode 100644
index 0000000000..274431766f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Sergey Kamardin
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/README.md
new file mode 100644
index 0000000000..67a97fdbe9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/README.md
@@ -0,0 +1,63 @@
+# httphead.[go](https://golang.org)
+
+[![GoDoc][godoc-image]][godoc-url]
+
+> Tiny HTTP header value parsing library in go.
+
+## Overview
+
+This library contains low-level functions for scanning HTTP RFC2616 compatible header value grammars.
+
+## Install
+
+```shell
+ go get github.com/gobwas/httphead
+```
+
+## Example
+
+The example below shows how multiple-choise HTTP header value could be parsed with this library:
+
+```go
+ options, ok := httphead.ParseOptions([]byte(`foo;bar=1,baz`), nil)
+ fmt.Println(options, ok)
+ // Output: [{foo map[bar:1]} {baz map[]}] true
+```
+
+The low-level example below shows how to optimize keys skipping and selection
+of some key:
+
+```go
+ // The right part of full header line like:
+ // X-My-Header: key;foo=bar;baz,key;baz
+ header := []byte(`foo;a=0,foo;a=1,foo;a=2,foo;a=3`)
+
+ // We want to search key "foo" with an "a" parameter that equal to "2".
+ var (
+ foo = []byte(`foo`)
+ a = []byte(`a`)
+ v = []byte(`2`)
+ )
+ var found bool
+ httphead.ScanOptions(header, func(i int, key, param, value []byte) Control {
+ if !bytes.Equal(key, foo) {
+ return ControlSkip
+ }
+ if !bytes.Equal(param, a) {
+ if bytes.Equal(value, v) {
+ // Found it!
+ found = true
+ return ControlBreak
+ }
+ return ControlSkip
+ }
+ return ControlContinue
+ })
+```
+
+For more usage examples please see [docs][godoc-url] or package tests.
+
+[godoc-image]: https://godoc.org/github.com/gobwas/httphead?status.svg
+[godoc-url]: https://godoc.org/github.com/gobwas/httphead
+[travis-image]: https://travis-ci.org/gobwas/httphead.svg?branch=master
+[travis-url]: https://travis-ci.org/gobwas/httphead
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/cookie.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/cookie.go
new file mode 100644
index 0000000000..05c9a1fb6a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/cookie.go
@@ -0,0 +1,200 @@
+package httphead
+
+import (
+ "bytes"
+)
+
+// ScanCookie scans cookie pairs from data using DefaultCookieScanner.Scan()
+// method.
+func ScanCookie(data []byte, it func(key, value []byte) bool) bool {
+ return DefaultCookieScanner.Scan(data, it)
+}
+
+// DefaultCookieScanner is a CookieScanner which is used by ScanCookie().
+// Note that it is intended to have the same behavior as http.Request.Cookies()
+// has.
+var DefaultCookieScanner = CookieScanner{}
+
+// CookieScanner contains options for scanning cookie pairs.
+// See https://tools.ietf.org/html/rfc6265#section-4.1.1
+type CookieScanner struct {
+ // DisableNameValidation disables name validation of a cookie. If false,
+ // only RFC2616 "tokens" are accepted.
+ DisableNameValidation bool
+
+ // DisableValueValidation disables value validation of a cookie. If false,
+ // only RFC6265 "cookie-octet" characters are accepted.
+ //
+ // Note that Strict option also affects validation of a value.
+ //
+ // If Strict is false, then scanner begins to allow space and comma
+ // characters inside the value for better compatibility with non standard
+ // cookies implementations.
+ DisableValueValidation bool
+
+ // BreakOnPairError sets scanner to immediately return after first pair syntax
+ // validation error.
+ // If false, scanner will try to skip invalid pair bytes and go ahead.
+ BreakOnPairError bool
+
+ // Strict enables strict RFC6265 mode scanning. It affects name and value
+ // validation, as also some other rules.
+ // If false, it is intended to bring the same behavior as
+ // http.Request.Cookies().
+ Strict bool
+}
+
+// Scan maps data to name and value pairs. Usually data represents value of the
+// Cookie header.
+func (c CookieScanner) Scan(data []byte, it func(name, value []byte) bool) bool {
+ lexer := &Scanner{data: data}
+
+ const (
+ statePair = iota
+ stateBefore
+ )
+
+ state := statePair
+
+ for lexer.Buffered() > 0 {
+ switch state {
+ case stateBefore:
+ // Pairs separated by ";" and space, according to the RFC6265:
+ // cookie-pair *( ";" SP cookie-pair )
+ //
+ // Cookie pairs MUST be separated by (";" SP). So our only option
+ // here is to fail as syntax error.
+ a, b := lexer.Peek2()
+ if a != ';' {
+ return false
+ }
+
+ state = statePair
+
+ advance := 1
+ if b == ' ' {
+ advance++
+ } else if c.Strict {
+ return false
+ }
+
+ lexer.Advance(advance)
+
+ case statePair:
+ if !lexer.FetchUntil(';') {
+ return false
+ }
+
+ var value []byte
+ name := lexer.Bytes()
+ if i := bytes.IndexByte(name, '='); i != -1 {
+ value = name[i+1:]
+ name = name[:i]
+ } else if c.Strict {
+ if !c.BreakOnPairError {
+ goto nextPair
+ }
+ return false
+ }
+
+ if !c.Strict {
+ trimLeft(name)
+ }
+ if !c.DisableNameValidation && !ValidCookieName(name) {
+ if !c.BreakOnPairError {
+ goto nextPair
+ }
+ return false
+ }
+
+ if !c.Strict {
+ value = trimRight(value)
+ }
+ value = stripQuotes(value)
+ if !c.DisableValueValidation && !ValidCookieValue(value, c.Strict) {
+ if !c.BreakOnPairError {
+ goto nextPair
+ }
+ return false
+ }
+
+ if !it(name, value) {
+ return true
+ }
+
+ nextPair:
+ state = stateBefore
+ }
+ }
+
+ return true
+}
+
+// ValidCookieValue reports whether given value is a valid RFC6265
+// "cookie-octet" bytes.
+//
+// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+// ; US-ASCII characters excluding CTLs,
+// ; whitespace DQUOTE, comma, semicolon,
+// ; and backslash
+//
+// Note that the false strict parameter disables errors on space 0x20 and comma
+// 0x2c. This could be useful to bring some compatibility with non-compliant
+// clients/servers in the real world.
+// It acts the same as standard library cookie parser if strict is false.
+func ValidCookieValue(value []byte, strict bool) bool {
+ if len(value) == 0 {
+ return true
+ }
+ for _, c := range value {
+ switch c {
+ case '"', ';', '\\':
+ return false
+ case ',', ' ':
+ if strict {
+ return false
+ }
+ default:
+ if c <= 0x20 {
+ return false
+ }
+ if c >= 0x7f {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// ValidCookieName reports wheter given bytes is a valid RFC2616 "token" bytes.
+func ValidCookieName(name []byte) bool {
+ for _, c := range name {
+ if !OctetTypes[c].IsToken() {
+ return false
+ }
+ }
+ return true
+}
+
+func stripQuotes(bts []byte) []byte {
+ if last := len(bts) - 1; last > 0 && bts[0] == '"' && bts[last] == '"' {
+ return bts[1:last]
+ }
+ return bts
+}
+
+func trimLeft(p []byte) []byte {
+ var i int
+ for i < len(p) && OctetTypes[p[i]].IsSpace() {
+ i++
+ }
+ return p[i:]
+}
+
+func trimRight(p []byte) []byte {
+ j := len(p)
+ for j > 0 && OctetTypes[p[j-1]].IsSpace() {
+ j--
+ }
+ return p[:j]
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/go.mod
new file mode 100644
index 0000000000..2393cedc2c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/go.mod
@@ -0,0 +1,3 @@
+module github.com/gobwas/httphead
+
+go 1.15
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/head.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/head.go
new file mode 100644
index 0000000000..a50e907dd1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/head.go
@@ -0,0 +1,275 @@
+package httphead
+
+import (
+ "bufio"
+ "bytes"
+)
+
+// Version contains protocol major and minor version.
+type Version struct {
+ Major int
+ Minor int
+}
+
+// RequestLine contains parameters parsed from the first request line.
+type RequestLine struct {
+ Method []byte
+ URI []byte
+ Version Version
+}
+
+// ResponseLine contains parameters parsed from the first response line.
+type ResponseLine struct {
+ Version Version
+ Status int
+ Reason []byte
+}
+
+// SplitRequestLine splits given slice of bytes into three chunks without
+// parsing.
+func SplitRequestLine(line []byte) (method, uri, version []byte) {
+ return split3(line, ' ')
+}
+
+// ParseRequestLine parses http request line like "GET / HTTP/1.0".
+func ParseRequestLine(line []byte) (r RequestLine, ok bool) {
+ var i int
+ for i = 0; i < len(line); i++ {
+ c := line[i]
+ if !OctetTypes[c].IsToken() {
+ if i > 0 && c == ' ' {
+ break
+ }
+ return
+ }
+ }
+ if i == len(line) {
+ return
+ }
+
+ var proto []byte
+ r.Method = line[:i]
+ r.URI, proto = split2(line[i+1:], ' ')
+ if len(r.URI) == 0 {
+ return
+ }
+ if major, minor, ok := ParseVersion(proto); ok {
+ r.Version.Major = major
+ r.Version.Minor = minor
+ return r, true
+ }
+
+ return r, false
+}
+
+// SplitResponseLine splits given slice of bytes into three chunks without
+// parsing.
+func SplitResponseLine(line []byte) (version, status, reason []byte) {
+ return split3(line, ' ')
+}
+
+// ParseResponseLine parses first response line into ResponseLine struct.
+func ParseResponseLine(line []byte) (r ResponseLine, ok bool) {
+ var (
+ proto []byte
+ status []byte
+ )
+ proto, status, r.Reason = split3(line, ' ')
+ if major, minor, ok := ParseVersion(proto); ok {
+ r.Version.Major = major
+ r.Version.Minor = minor
+ } else {
+ return r, false
+ }
+ if n, ok := IntFromASCII(status); ok {
+ r.Status = n
+ } else {
+ return r, false
+ }
+ // TODO(gobwas): parse here r.Reason fot TEXT rule:
+ // TEXT =
+ return r, true
+}
+
+var (
+ httpVersion10 = []byte("HTTP/1.0")
+ httpVersion11 = []byte("HTTP/1.1")
+ httpVersionPrefix = []byte("HTTP/")
+)
+
+// ParseVersion parses major and minor version of HTTP protocol.
+// It returns parsed values and true if parse is ok.
+func ParseVersion(bts []byte) (major, minor int, ok bool) {
+ switch {
+ case bytes.Equal(bts, httpVersion11):
+ return 1, 1, true
+ case bytes.Equal(bts, httpVersion10):
+ return 1, 0, true
+ case len(bts) < 8:
+ return
+ case !bytes.Equal(bts[:5], httpVersionPrefix):
+ return
+ }
+
+ bts = bts[5:]
+
+ dot := bytes.IndexByte(bts, '.')
+ if dot == -1 {
+ return
+ }
+ major, ok = IntFromASCII(bts[:dot])
+ if !ok {
+ return
+ }
+ minor, ok = IntFromASCII(bts[dot+1:])
+ if !ok {
+ return
+ }
+
+ return major, minor, true
+}
+
+// ReadLine reads line from br. It reads until '\n' and returns bytes without
+// '\n' or '\r\n' at the end.
+// It returns err if and only if line does not end in '\n'. Note that read
+// bytes returned in any case of error.
+//
+// It is much like the textproto/Reader.ReadLine() except the thing that it
+// returns raw bytes, instead of string. That is, it avoids copying bytes read
+// from br.
+//
+// textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be
+// safe with future I/O operations on br.
+//
+// We could control I/O operations on br and do not need to make additional
+// copy for safety.
+func ReadLine(br *bufio.Reader) ([]byte, error) {
+ var line []byte
+ for {
+ bts, err := br.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ // Copy bytes because next read will discard them.
+ line = append(line, bts...)
+ continue
+ }
+ // Avoid copy of single read.
+ if line == nil {
+ line = bts
+ } else {
+ line = append(line, bts...)
+ }
+ if err != nil {
+ return line, err
+ }
+ // Size of line is at least 1.
+ // In other case bufio.ReadSlice() returns error.
+ n := len(line)
+ // Cut '\n' or '\r\n'.
+ if n > 1 && line[n-2] == '\r' {
+ line = line[:n-2]
+ } else {
+ line = line[:n-1]
+ }
+ return line, nil
+ }
+}
+
+// ParseHeaderLine parses HTTP header as key-value pair. It returns parsed
+// values and true if parse is ok.
+func ParseHeaderLine(line []byte) (k, v []byte, ok bool) {
+ colon := bytes.IndexByte(line, ':')
+ if colon == -1 {
+ return
+ }
+ k = trim(line[:colon])
+ for _, c := range k {
+ if !OctetTypes[c].IsToken() {
+ return nil, nil, false
+ }
+ }
+ v = trim(line[colon+1:])
+ return k, v, true
+}
+
+// IntFromASCII converts ascii encoded decimal numeric value from HTTP entities
+// to an integer.
+func IntFromASCII(bts []byte) (ret int, ok bool) {
+ // ASCII numbers all start with the high-order bits 0011.
+ // If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those
+ // bits and interpret them directly as an integer.
+ var n int
+ if n = len(bts); n < 1 {
+ return 0, false
+ }
+ for i := 0; i < n; i++ {
+ if bts[i]&0xf0 != 0x30 {
+ return 0, false
+ }
+ ret += int(bts[i]&0xf) * pow(10, n-i-1)
+ }
+ return ret, true
+}
+
+const (
+ toLower = 'a' - 'A' // for use with OR.
+ toUpper = ^byte(toLower) // for use with AND.
+)
+
+// CanonicalizeHeaderKey is like standard textproto/CanonicalMIMEHeaderKey,
+// except that it operates with slice of bytes and modifies it inplace without
+// copying.
+func CanonicalizeHeaderKey(k []byte) {
+ upper := true
+ for i, c := range k {
+ if upper && 'a' <= c && c <= 'z' {
+ k[i] &= toUpper
+ } else if !upper && 'A' <= c && c <= 'Z' {
+ k[i] |= toLower
+ }
+ upper = c == '-'
+ }
+}
+
+// pow for integers implementation.
+// See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3
+func pow(a, b int) int {
+ p := 1
+ for b > 0 {
+ if b&1 != 0 {
+ p *= a
+ }
+ b >>= 1
+ a *= a
+ }
+ return p
+}
+
+func split3(p []byte, sep byte) (p1, p2, p3 []byte) {
+ a := bytes.IndexByte(p, sep)
+ b := bytes.IndexByte(p[a+1:], sep)
+ if a == -1 || b == -1 {
+ return p, nil, nil
+ }
+ b += a + 1
+ return p[:a], p[a+1 : b], p[b+1:]
+}
+
+func split2(p []byte, sep byte) (p1, p2 []byte) {
+ i := bytes.IndexByte(p, sep)
+ if i == -1 {
+ return p, nil
+ }
+ return p[:i], p[i+1:]
+}
+
+func trim(p []byte) []byte {
+ var i, j int
+ for i = 0; i < len(p) && (p[i] == ' ' || p[i] == '\t'); {
+ i++
+ }
+ for j = len(p); j > i && (p[j-1] == ' ' || p[j-1] == '\t'); {
+ j--
+ }
+ return p[i:j]
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/httphead.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/httphead.go
new file mode 100644
index 0000000000..2387e8033c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/httphead.go
@@ -0,0 +1,331 @@
+// Package httphead contains utils for parsing HTTP and HTTP-grammar compatible
+// text protocols headers.
+//
+// That is, this package first aim is to bring ability to easily parse
+// constructions, described here https://tools.ietf.org/html/rfc2616#section-2
+package httphead
+
+import (
+ "bytes"
+ "strings"
+)
+
+// ScanTokens parses data in this form:
+//
+// list = 1#token
+//
+// It returns false if data is malformed.
+func ScanTokens(data []byte, it func([]byte) bool) bool {
+ lexer := &Scanner{data: data}
+
+ var ok bool
+ for lexer.Next() {
+ switch lexer.Type() {
+ case ItemToken:
+ ok = true
+ if !it(lexer.Bytes()) {
+ return true
+ }
+ case ItemSeparator:
+ if !isComma(lexer.Bytes()) {
+ return false
+ }
+ default:
+ return false
+ }
+ }
+
+ return ok && !lexer.err
+}
+
+// ParseOptions parses all header options and appends it to given slice of
+// Option. It returns flag of successful (wellformed input) parsing.
+//
+// Note that appended options are all consist of subslices of data. That is,
+// mutation of data will mutate appended options.
+func ParseOptions(data []byte, options []Option) ([]Option, bool) {
+ var i int
+ index := -1
+ return options, ScanOptions(data, func(idx int, name, attr, val []byte) Control {
+ if idx != index {
+ index = idx
+ i = len(options)
+ options = append(options, Option{Name: name})
+ }
+ if attr != nil {
+ options[i].Parameters.Set(attr, val)
+ }
+ return ControlContinue
+ })
+}
+
+// SelectFlag encodes way of options selection.
+type SelectFlag byte
+
+// String represetns flag as string.
+func (f SelectFlag) String() string {
+ var flags [2]string
+ var n int
+ if f&SelectCopy != 0 {
+ flags[n] = "copy"
+ n++
+ }
+ if f&SelectUnique != 0 {
+ flags[n] = "unique"
+ n++
+ }
+ return "[" + strings.Join(flags[:n], "|") + "]"
+}
+
+const (
+ // SelectCopy causes selector to copy selected option before appending it
+ // to resulting slice.
+ // If SelectCopy flag is not passed to selector, then appended options will
+ // contain sub-slices of the initial data.
+ SelectCopy SelectFlag = 1 << iota
+
+ // SelectUnique causes selector to append only not yet existing option to
+ // resulting slice. Unique is checked by comparing option names.
+ SelectUnique
+)
+
+// OptionSelector contains configuration for selecting Options from header value.
+type OptionSelector struct {
+ // Check is a filter function that applied to every Option that possibly
+ // could be selected.
+ // If Check is nil all options will be selected.
+ Check func(Option) bool
+
+ // Flags contains flags for options selection.
+ Flags SelectFlag
+
+ // Alloc used to allocate slice of bytes when selector is configured with
+ // SelectCopy flag. It will be called with number of bytes needed for copy
+ // of single Option.
+ // If Alloc is nil make is used.
+ Alloc func(n int) []byte
+}
+
+// Select parses header data and appends it to given slice of Option.
+// It also returns flag of successful (wellformed input) parsing.
+func (s OptionSelector) Select(data []byte, options []Option) ([]Option, bool) {
+ var current Option
+ var has bool
+ index := -1
+
+ alloc := s.Alloc
+ if alloc == nil {
+ alloc = defaultAlloc
+ }
+ check := s.Check
+ if check == nil {
+ check = defaultCheck
+ }
+
+ ok := ScanOptions(data, func(idx int, name, attr, val []byte) Control {
+ if idx != index {
+ if has && check(current) {
+ if s.Flags&SelectCopy != 0 {
+ current = current.Copy(alloc(current.Size()))
+ }
+ options = append(options, current)
+ has = false
+ }
+ if s.Flags&SelectUnique != 0 {
+ for i := len(options) - 1; i >= 0; i-- {
+ if bytes.Equal(options[i].Name, name) {
+ return ControlSkip
+ }
+ }
+ }
+ index = idx
+ current = Option{Name: name}
+ has = true
+ }
+ if attr != nil {
+ current.Parameters.Set(attr, val)
+ }
+
+ return ControlContinue
+ })
+ if has && check(current) {
+ if s.Flags&SelectCopy != 0 {
+ current = current.Copy(alloc(current.Size()))
+ }
+ options = append(options, current)
+ }
+
+ return options, ok
+}
+
+func defaultAlloc(n int) []byte { return make([]byte, n) }
+func defaultCheck(Option) bool { return true }
+
+// Control represents operation that scanner should perform.
+type Control byte
+
+const (
+ // ControlContinue causes scanner to continue scan tokens.
+ ControlContinue Control = iota
+ // ControlBreak causes scanner to stop scan tokens.
+ ControlBreak
+ // ControlSkip causes scanner to skip current entity.
+ ControlSkip
+)
+
+// ScanOptions parses data in this form:
+//
+// values = 1#value
+// value = token *( ";" param )
+// param = token [ "=" (token | quoted-string) ]
+//
+// It calls given callback with the index of the option, option itself and its
+// parameter (attribute and its value, both could be nil). Index is useful when
+// header contains multiple choises for the same named option.
+//
+// Given callback should return one of the defined Control* values.
+// ControlSkip means that passed key is not in caller's interest. That is, all
+// parameters of that key will be skipped.
+// ControlBreak means that no more keys and parameters should be parsed. That
+// is, it must break parsing immediately.
+// ControlContinue means that caller want to receive next parameter and its
+// value or the next key.
+//
+// It returns false if data is malformed.
+func ScanOptions(data []byte, it func(index int, option, attribute, value []byte) Control) bool {
+ lexer := &Scanner{data: data}
+
+ var ok bool
+ var state int
+ const (
+ stateKey = iota
+ stateParamBeforeName
+ stateParamName
+ stateParamBeforeValue
+ stateParamValue
+ )
+
+ var (
+ index int
+ key, param, value []byte
+ mustCall bool
+ )
+ for lexer.Next() {
+ var (
+ call bool
+ growIndex int
+ )
+
+ t := lexer.Type()
+ v := lexer.Bytes()
+
+ switch t {
+ case ItemToken:
+ switch state {
+ case stateKey, stateParamBeforeName:
+ key = v
+ state = stateParamBeforeName
+ mustCall = true
+ case stateParamName:
+ param = v
+ state = stateParamBeforeValue
+ mustCall = true
+ case stateParamValue:
+ value = v
+ state = stateParamBeforeName
+ call = true
+ default:
+ return false
+ }
+
+ case ItemString:
+ if state != stateParamValue {
+ return false
+ }
+ value = v
+ state = stateParamBeforeName
+ call = true
+
+ case ItemSeparator:
+ switch {
+ case isComma(v) && state == stateKey:
+ // Nothing to do.
+
+ case isComma(v) && state == stateParamBeforeName:
+ state = stateKey
+ // Make call only if we have not called this key yet.
+ call = mustCall
+ if !call {
+ // If we have already called callback with the key
+ // that just ended.
+ index++
+ } else {
+ // Else grow the index after calling callback.
+ growIndex = 1
+ }
+
+ case isComma(v) && state == stateParamBeforeValue:
+ state = stateKey
+ growIndex = 1
+ call = true
+
+ case isSemicolon(v) && state == stateParamBeforeName:
+ state = stateParamName
+
+ case isSemicolon(v) && state == stateParamBeforeValue:
+ state = stateParamName
+ call = true
+
+ case isEquality(v) && state == stateParamBeforeValue:
+ state = stateParamValue
+
+ default:
+ return false
+ }
+
+ default:
+ return false
+ }
+
+ if call {
+ switch it(index, key, param, value) {
+ case ControlBreak:
+ // User want to stop to parsing parameters.
+ return true
+
+ case ControlSkip:
+ // User want to skip current param.
+ state = stateKey
+ lexer.SkipEscaped(',')
+
+ case ControlContinue:
+ // User is interested in rest of parameters.
+ // Nothing to do.
+
+ default:
+ panic("unexpected control value")
+ }
+ ok = true
+ param = nil
+ value = nil
+ mustCall = false
+ index += growIndex
+ }
+ }
+ if mustCall {
+ ok = true
+ it(index, key, param, value)
+ }
+
+ return ok && !lexer.err
+}
+
+func isComma(b []byte) bool {
+ return len(b) == 1 && b[0] == ','
+}
+func isSemicolon(b []byte) bool {
+ return len(b) == 1 && b[0] == ';'
+}
+func isEquality(b []byte) bool {
+ return len(b) == 1 && b[0] == '='
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/lexer.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/lexer.go
new file mode 100644
index 0000000000..729855ed0d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/lexer.go
@@ -0,0 +1,360 @@
+package httphead
+
+import (
+ "bytes"
+)
+
+// ItemType encodes type of the lexing token.
+type ItemType int
+
+const (
+ // ItemUndef reports that token is undefined.
+ ItemUndef ItemType = iota
+ // ItemToken reports that token is RFC2616 token.
+ ItemToken
+ // ItemSeparator reports that token is RFC2616 separator.
+ ItemSeparator
+ // ItemString reports that token is RFC2616 quouted string.
+ ItemString
+ // ItemComment reports that token is RFC2616 comment.
+ ItemComment
+ // ItemOctet reports that token is octet slice.
+ ItemOctet
+)
+
+// Scanner represents header tokens scanner.
+// See https://tools.ietf.org/html/rfc2616#section-2
+type Scanner struct {
+ data []byte
+ pos int
+
+ itemType ItemType
+ itemBytes []byte
+
+ err bool
+}
+
+// NewScanner creates new RFC2616 data scanner.
+func NewScanner(data []byte) *Scanner {
+ return &Scanner{data: data}
+}
+
+// Next scans for next token. It returns true on successful scanning, and false
+// on error or EOF.
+func (l *Scanner) Next() bool {
+ c, ok := l.nextChar()
+ if !ok {
+ return false
+ }
+ switch c {
+ case '"': // quoted-string;
+ return l.fetchQuotedString()
+
+ case '(': // comment;
+ return l.fetchComment()
+
+ case '\\', ')': // unexpected chars;
+ l.err = true
+ return false
+
+ default:
+ return l.fetchToken()
+ }
+}
+
+// FetchUntil fetches ItemOctet from current scanner position to first
+// occurence of the c or to the end of the underlying data.
+func (l *Scanner) FetchUntil(c byte) bool {
+ l.resetItem()
+ if l.pos == len(l.data) {
+ return false
+ }
+ return l.fetchOctet(c)
+}
+
+// Peek reads byte at current position without advancing it. On end of data it
+// returns 0.
+func (l *Scanner) Peek() byte {
+ if l.pos == len(l.data) {
+ return 0
+ }
+ return l.data[l.pos]
+}
+
+// Peek2 reads two first bytes at current position without advancing it.
+// If there not enough data it returs 0.
+func (l *Scanner) Peek2() (a, b byte) {
+ if l.pos == len(l.data) {
+ return 0, 0
+ }
+ if l.pos+1 == len(l.data) {
+ return l.data[l.pos], 0
+ }
+ return l.data[l.pos], l.data[l.pos+1]
+}
+
+// Buffered reporst how many bytes there are left to scan.
+func (l *Scanner) Buffered() int {
+ return len(l.data) - l.pos
+}
+
+// Advance moves current position index at n bytes. It returns true on
+// successful move.
+func (l *Scanner) Advance(n int) bool {
+ l.pos += n
+ if l.pos > len(l.data) {
+ l.pos = len(l.data)
+ return false
+ }
+ return true
+}
+
+// Skip skips all bytes until first occurence of c.
+func (l *Scanner) Skip(c byte) {
+ if l.err {
+ return
+ }
+ // Reset scanner state.
+ l.resetItem()
+
+ if i := bytes.IndexByte(l.data[l.pos:], c); i == -1 {
+ // Reached the end of data.
+ l.pos = len(l.data)
+ } else {
+ l.pos += i + 1
+ }
+}
+
+// SkipEscaped skips all bytes until first occurence of non-escaped c.
+func (l *Scanner) SkipEscaped(c byte) {
+ if l.err {
+ return
+ }
+ // Reset scanner state.
+ l.resetItem()
+
+ if i := ScanUntil(l.data[l.pos:], c); i == -1 {
+ // Reached the end of data.
+ l.pos = len(l.data)
+ } else {
+ l.pos += i + 1
+ }
+}
+
+// Type reports current token type.
+func (l *Scanner) Type() ItemType {
+ return l.itemType
+}
+
+// Bytes returns current token bytes.
+func (l *Scanner) Bytes() []byte {
+ return l.itemBytes
+}
+
+func (l *Scanner) nextChar() (byte, bool) {
+ // Reset scanner state.
+ l.resetItem()
+
+ if l.err {
+ return 0, false
+ }
+ l.pos += SkipSpace(l.data[l.pos:])
+ if l.pos == len(l.data) {
+ return 0, false
+ }
+ return l.data[l.pos], true
+}
+
+func (l *Scanner) resetItem() {
+ l.itemType = ItemUndef
+ l.itemBytes = nil
+}
+
+func (l *Scanner) fetchOctet(c byte) bool {
+ i := l.pos
+ if j := bytes.IndexByte(l.data[l.pos:], c); j == -1 {
+ // Reached the end of data.
+ l.pos = len(l.data)
+ } else {
+ l.pos += j
+ }
+
+ l.itemType = ItemOctet
+ l.itemBytes = l.data[i:l.pos]
+
+ return true
+}
+
+func (l *Scanner) fetchToken() bool {
+ n, t := ScanToken(l.data[l.pos:])
+ if n == -1 {
+ l.err = true
+ return false
+ }
+
+ l.itemType = t
+ l.itemBytes = l.data[l.pos : l.pos+n]
+ l.pos += n
+
+ return true
+}
+
+func (l *Scanner) fetchQuotedString() (ok bool) {
+ l.pos++
+
+ n := ScanUntil(l.data[l.pos:], '"')
+ if n == -1 {
+ l.err = true
+ return false
+ }
+
+ l.itemType = ItemString
+ l.itemBytes = RemoveByte(l.data[l.pos:l.pos+n], '\\')
+ l.pos += n + 1
+
+ return true
+}
+
+func (l *Scanner) fetchComment() (ok bool) {
+ l.pos++
+
+ n := ScanPairGreedy(l.data[l.pos:], '(', ')')
+ if n == -1 {
+ l.err = true
+ return false
+ }
+
+ l.itemType = ItemComment
+ l.itemBytes = RemoveByte(l.data[l.pos:l.pos+n], '\\')
+ l.pos += n + 1
+
+ return true
+}
+
+// ScanUntil scans for first non-escaped character c in given data.
+// It returns index of matched c and -1 if c is not found.
+func ScanUntil(data []byte, c byte) (n int) {
+ for {
+ i := bytes.IndexByte(data[n:], c)
+ if i == -1 {
+ return -1
+ }
+ n += i
+ if n == 0 || data[n-1] != '\\' {
+ break
+ }
+ n++
+ }
+ return
+}
+
+// ScanPairGreedy scans for complete pair of opening and closing chars in greedy manner.
+// Note that first opening byte must not be present in data.
+func ScanPairGreedy(data []byte, open, close byte) (n int) {
+ var m int
+ opened := 1
+ for {
+ i := bytes.IndexByte(data[n:], close)
+ if i == -1 {
+ return -1
+ }
+ n += i
+ // If found index is not escaped then it is the end.
+ if n == 0 || data[n-1] != '\\' {
+ opened--
+ }
+
+ for m < i {
+ j := bytes.IndexByte(data[m:i], open)
+ if j == -1 {
+ break
+ }
+ m += j + 1
+ opened++
+ }
+
+ if opened == 0 {
+ break
+ }
+
+ n++
+ m = n
+ }
+ return
+}
+
+// RemoveByte returns data without c. If c is not present in data it returns
+// the same slice. If not, it copies data without c.
+func RemoveByte(data []byte, c byte) []byte {
+ j := bytes.IndexByte(data, c)
+ if j == -1 {
+ return data
+ }
+
+ n := len(data) - 1
+
+ // If character is present, than allocate slice with n-1 capacity. That is,
+ // resulting bytes could be at most n-1 length.
+ result := make([]byte, n)
+ k := copy(result, data[:j])
+
+ for i := j + 1; i < n; {
+ j = bytes.IndexByte(data[i:], c)
+ if j != -1 {
+ k += copy(result[k:], data[i:i+j])
+ i = i + j + 1
+ } else {
+ k += copy(result[k:], data[i:])
+ break
+ }
+ }
+
+ return result[:k]
+}
+
+// SkipSpace skips spaces and lws-sequences from p.
+// It returns number ob bytes skipped.
+func SkipSpace(p []byte) (n int) {
+ for len(p) > 0 {
+ switch {
+ case len(p) >= 3 &&
+ p[0] == '\r' &&
+ p[1] == '\n' &&
+ OctetTypes[p[2]].IsSpace():
+ p = p[3:]
+ n += 3
+ case OctetTypes[p[0]].IsSpace():
+ p = p[1:]
+ n++
+ default:
+ return
+ }
+ }
+ return
+}
+
+// ScanToken scan for next token in p. It returns length of the token and its
+// type. It do not trim p.
+func ScanToken(p []byte) (n int, t ItemType) {
+ if len(p) == 0 {
+ return 0, ItemUndef
+ }
+
+ c := p[0]
+ switch {
+ case OctetTypes[c].IsSeparator():
+ return 1, ItemSeparator
+
+ case OctetTypes[c].IsToken():
+ for n = 1; n < len(p); n++ {
+ c := p[n]
+ if !OctetTypes[c].IsToken() {
+ break
+ }
+ }
+ return n, ItemToken
+
+ default:
+ return -1, ItemUndef
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/octet.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/octet.go
new file mode 100644
index 0000000000..2a04cdd090
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/octet.go
@@ -0,0 +1,83 @@
+package httphead
+
+// OctetType desribes character type.
+//
+// From the "Basic Rules" chapter of RFC2616
+// See https://tools.ietf.org/html/rfc2616#section-2.2
+//
+// OCTET =
+// CHAR =
+// UPALPHA =
+// LOALPHA =
+// ALPHA = UPALPHA | LOALPHA
+// DIGIT =
+// CTL =
+// CR =
+// LF =
+// SP =
+// HT =
+// <"> =
+// CRLF = CR LF
+// LWS = [CRLF] 1*( SP | HT )
+//
+// Many HTTP/1.1 header field values consist of words separated by LWS
+// or special characters. These special characters MUST be in a quoted
+// string to be used within a parameter value (as defined in section
+// 3.6).
+//
+// token = 1*
+// separators = "(" | ")" | "<" | ">" | "@"
+// | "," | ";" | ":" | "\" | <">
+// | "/" | "[" | "]" | "?" | "="
+// | "{" | "}" | SP | HT
+type OctetType byte
+
+// IsChar reports whether octet is CHAR.
+func (t OctetType) IsChar() bool { return t&octetChar != 0 }
+
+// IsControl reports whether octet is CTL.
+func (t OctetType) IsControl() bool { return t&octetControl != 0 }
+
+// IsSeparator reports whether octet is separator.
+func (t OctetType) IsSeparator() bool { return t&octetSeparator != 0 }
+
+// IsSpace reports whether octet is space (SP or HT).
+func (t OctetType) IsSpace() bool { return t&octetSpace != 0 }
+
+// IsToken reports whether octet is token.
+func (t OctetType) IsToken() bool { return t&octetToken != 0 }
+
+const (
+ octetChar OctetType = 1 << iota
+ octetControl
+ octetSpace
+ octetSeparator
+ octetToken
+)
+
+// OctetTypes is a table of octets.
+var OctetTypes [256]OctetType
+
+func init() {
+ for c := 32; c < 256; c++ {
+ var t OctetType
+ if c <= 127 {
+ t |= octetChar
+ }
+ if 0 <= c && c <= 31 || c == 127 {
+ t |= octetControl
+ }
+ switch c {
+ case '(', ')', '<', '>', '@', ',', ';', ':', '"', '/', '[', ']', '?', '=', '{', '}', '\\':
+ t |= octetSeparator
+ case ' ', '\t':
+ t |= octetSpace | octetSeparator
+ }
+
+ if t.IsChar() && !t.IsControl() && !t.IsSeparator() && !t.IsSpace() {
+ t |= octetToken
+ }
+
+ OctetTypes[c] = t
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/option.go
new file mode 100644
index 0000000000..0a18c7c7c6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/option.go
@@ -0,0 +1,193 @@
+package httphead
+
+import (
+ "bytes"
+ "sort"
+)
+
+// Option represents a header option.
+type Option struct {
+ Name []byte
+ Parameters Parameters
+}
+
+// Size returns number of bytes need to be allocated for use in opt.Copy.
+func (opt Option) Size() int {
+ return len(opt.Name) + opt.Parameters.bytes
+}
+
+// Copy copies all underlying []byte slices into p and returns new Option.
+// Note that p must be at least of opt.Size() length.
+func (opt Option) Copy(p []byte) Option {
+ n := copy(p, opt.Name)
+ opt.Name = p[:n]
+ opt.Parameters, p = opt.Parameters.Copy(p[n:])
+ return opt
+}
+
+// Clone is a shorthand for making slice of opt.Size() sequenced with Copy()
+// call.
+func (opt Option) Clone() Option {
+ return opt.Copy(make([]byte, opt.Size()))
+}
+
+// String represents option as a string.
+func (opt Option) String() string {
+ return "{" + string(opt.Name) + " " + opt.Parameters.String() + "}"
+}
+
+// NewOption creates named option with given parameters.
+func NewOption(name string, params map[string]string) Option {
+ p := Parameters{}
+ for k, v := range params {
+ p.Set([]byte(k), []byte(v))
+ }
+ return Option{
+ Name: []byte(name),
+ Parameters: p,
+ }
+}
+
+// Equal reports whether option is equal to b.
+func (opt Option) Equal(b Option) bool {
+ if bytes.Equal(opt.Name, b.Name) {
+ return opt.Parameters.Equal(b.Parameters)
+ }
+ return false
+}
+
+// Parameters represents option's parameters.
+type Parameters struct {
+ pos int
+ bytes int
+ arr [8]pair
+ dyn []pair
+}
+
+// Equal reports whether a equal to b.
+func (p Parameters) Equal(b Parameters) bool {
+ switch {
+ case p.dyn == nil && b.dyn == nil:
+ case p.dyn != nil && b.dyn != nil:
+ default:
+ return false
+ }
+
+ ad, bd := p.data(), b.data()
+ if len(ad) != len(bd) {
+ return false
+ }
+
+ sort.Sort(pairs(ad))
+ sort.Sort(pairs(bd))
+
+ for i := 0; i < len(ad); i++ {
+ av, bv := ad[i], bd[i]
+ if !bytes.Equal(av.key, bv.key) || !bytes.Equal(av.value, bv.value) {
+ return false
+ }
+ }
+ return true
+}
+
+// Size returns number of bytes that needed to copy p.
+func (p *Parameters) Size() int {
+ return p.bytes
+}
+
+// Copy copies all underlying []byte slices into dst and returns new
+// Parameters.
+// Note that dst must be at least of p.Size() length.
+func (p *Parameters) Copy(dst []byte) (Parameters, []byte) {
+ ret := Parameters{
+ pos: p.pos,
+ bytes: p.bytes,
+ }
+ if p.dyn != nil {
+ ret.dyn = make([]pair, len(p.dyn))
+ for i, v := range p.dyn {
+ ret.dyn[i], dst = v.copy(dst)
+ }
+ } else {
+ for i, p := range p.arr {
+ ret.arr[i], dst = p.copy(dst)
+ }
+ }
+ return ret, dst
+}
+
+// Get returns value by key and flag about existence such value.
+func (p *Parameters) Get(key string) (value []byte, ok bool) {
+ for _, v := range p.data() {
+ if string(v.key) == key {
+ return v.value, true
+ }
+ }
+ return nil, false
+}
+
+// Set sets value by key.
+func (p *Parameters) Set(key, value []byte) {
+ p.bytes += len(key) + len(value)
+
+ if p.pos < len(p.arr) {
+ p.arr[p.pos] = pair{key, value}
+ p.pos++
+ return
+ }
+
+ if p.dyn == nil {
+ p.dyn = make([]pair, len(p.arr), len(p.arr)+1)
+ copy(p.dyn, p.arr[:])
+ }
+ p.dyn = append(p.dyn, pair{key, value})
+}
+
+// ForEach iterates over parameters key-value pairs and calls cb for each one.
+func (p *Parameters) ForEach(cb func(k, v []byte) bool) {
+ for _, v := range p.data() {
+ if !cb(v.key, v.value) {
+ break
+ }
+ }
+}
+
+// String represents parameters as a string.
+func (p *Parameters) String() (ret string) {
+ ret = "["
+ for i, v := range p.data() {
+ if i > 0 {
+ ret += " "
+ }
+ ret += string(v.key) + ":" + string(v.value)
+ }
+ return ret + "]"
+}
+
+func (p *Parameters) data() []pair {
+ if p.dyn != nil {
+ return p.dyn
+ }
+ return p.arr[:p.pos]
+}
+
+type pair struct {
+ key, value []byte
+}
+
+func (p pair) copy(dst []byte) (pair, []byte) {
+ n := copy(dst, p.key)
+ p.key = dst[:n]
+ m := n + copy(dst[n:], p.value)
+ p.value = dst[n:m]
+
+ dst = dst[m:]
+
+ return p, dst
+}
+
+type pairs []pair
+
+func (p pairs) Len() int { return len(p) }
+func (p pairs) Less(a, b int) bool { return bytes.Compare(p[a].key, p[b].key) == -1 }
+func (p pairs) Swap(a, b int) { p[a], p[b] = p[b], p[a] }
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/writer.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/writer.go
new file mode 100644
index 0000000000..e5df3ddf40
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/httphead/writer.go
@@ -0,0 +1,101 @@
+package httphead
+
+import "io"
+
+var (
+ comma = []byte{','}
+ equality = []byte{'='}
+ semicolon = []byte{';'}
+ quote = []byte{'"'}
+ escape = []byte{'\\'}
+)
+
+// WriteOptions write options list to the dest.
+// It uses the same form as {Scan,Parse}Options functions:
+// values = 1#value
+// value = token *( ";" param )
+// param = token [ "=" (token | quoted-string) ]
+//
+// It wraps valuse into the quoted-string sequence if it contains any
+// non-token characters.
+func WriteOptions(dest io.Writer, options []Option) (n int, err error) {
+ w := writer{w: dest}
+ for i, opt := range options {
+ if i > 0 {
+ w.write(comma)
+ }
+
+ writeTokenSanitized(&w, opt.Name)
+
+ for _, p := range opt.Parameters.data() {
+ w.write(semicolon)
+ writeTokenSanitized(&w, p.key)
+ if len(p.value) != 0 {
+ w.write(equality)
+ writeTokenSanitized(&w, p.value)
+ }
+ }
+ }
+ return w.result()
+}
+
+// writeTokenSanitized writes token as is or as quouted string if it contains
+// non-token characters.
+//
+// Note that is is not expects LWS sequnces be in s, cause LWS is used only as
+// header field continuation:
+// "A CRLF is allowed in the definition of TEXT only as part of a header field
+// continuation. It is expected that the folding LWS will be replaced with a
+// single SP before interpretation of the TEXT value."
+// See https://tools.ietf.org/html/rfc2616#section-2
+//
+// That is we sanitizing s for writing, so there could not be any header field
+// continuation.
+// That is any CRLF will be escaped as any other control characters not allowd in TEXT.
+func writeTokenSanitized(bw *writer, bts []byte) {
+ var qt bool
+ var pos int
+ for i := 0; i < len(bts); i++ {
+ c := bts[i]
+ if !OctetTypes[c].IsToken() && !qt {
+ qt = true
+ bw.write(quote)
+ }
+ if OctetTypes[c].IsControl() || c == '"' {
+ if !qt {
+ qt = true
+ bw.write(quote)
+ }
+ bw.write(bts[pos:i])
+ bw.write(escape)
+ bw.write(bts[i : i+1])
+ pos = i + 1
+ }
+ }
+ if !qt {
+ bw.write(bts)
+ } else {
+ bw.write(bts[pos:])
+ bw.write(quote)
+ }
+}
+
+type writer struct {
+ w io.Writer
+ n int
+ err error
+}
+
+func (w *writer) write(p []byte) {
+ if w.err != nil {
+ return
+ }
+ var n int
+ n, w.err = w.w.Write(p)
+ w.n += n
+ return
+}
+
+func (w *writer) result() (int, error) {
+ return w.n, w.err
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/LICENSE
new file mode 100644
index 0000000000..c41ffde6f7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-2019 Sergey Kamardin
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/README.md
new file mode 100644
index 0000000000..45685581da
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/README.md
@@ -0,0 +1,107 @@
+# pool
+
+[![GoDoc][godoc-image]][godoc-url]
+
+> Tiny memory reuse helpers for Go.
+
+## generic
+
+Without use of subpackages, `pool` allows to reuse any struct distinguishable
+by size in generic way:
+
+```go
+package main
+
+import "github.com/gobwas/pool"
+
+func main() {
+ x, n := pool.Get(100) // Returns object with size 128 or nil.
+ if x == nil {
+ // Create x somehow with knowledge that n is 128.
+ }
+ defer pool.Put(x, n)
+
+ // Work with x.
+}
+```
+
+Pool allows you to pass specific options for constructing custom pool:
+
+```go
+package main
+
+import "github.com/gobwas/pool"
+
+func main() {
+ p := pool.Custom(
+ pool.WithLogSizeMapping(), // Will ceil size n passed to Get(n) to nearest power of two.
+ pool.WithLogSizeRange(64, 512), // Will reuse objects in logarithmic range [64, 512].
+ pool.WithSize(65536), // Will reuse object with size 65536.
+ )
+ x, n := p.Get(1000) // Returns nil and 1000 because mapped size 1000 => 1024 is not reusing by the pool.
+ defer pool.Put(x, n) // Will not reuse x.
+
+ // Work with x.
+}
+```
+
+Note that there are few non-generic pooling implementations inside subpackages.
+
+## pbytes
+
+Subpackage `pbytes` is intended for `[]byte` reuse.
+
+```go
+package main
+
+import "github.com/gobwas/pool/pbytes"
+
+func main() {
+ bts := pbytes.GetCap(100) // Returns make([]byte, 0, 128).
+ defer pbytes.Put(bts)
+
+ // Work with bts.
+}
+```
+
+You can also create your own range for pooling:
+
+```go
+package main
+
+import "github.com/gobwas/pool/pbytes"
+
+func main() {
+ // Reuse only slices whose capacity is 128, 256, 512 or 1024.
+ pool := pbytes.New(128, 1024)
+
+ bts := pool.GetCap(100) // Returns make([]byte, 0, 128).
+ defer pool.Put(bts)
+
+ // Work with bts.
+}
+```
+
+## pbufio
+
+Subpackage `pbufio` is intended for `*bufio.{Reader, Writer}` reuse.
+
+```go
+package main
+
+import "github.com/gobwas/pool/pbufio"
+
+func main() {
+ bw := pbufio.GetWriter(os.Stdout, 100) // Returns bufio.NewWriterSize(128).
+ defer pbufio.PutWriter(bw)
+
+ // Work with bw.
+}
+```
+
+Like with `pbytes`, you can also create pool with custom reuse bounds.
+
+
+
+[godoc-image]: https://godoc.org/github.com/gobwas/pool?status.svg
+[godoc-url]: https://godoc.org/github.com/gobwas/pool
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/generic.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/generic.go
new file mode 100644
index 0000000000..d40b362458
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/generic.go
@@ -0,0 +1,87 @@
+package pool
+
+import (
+ "sync"
+
+ "github.com/gobwas/pool/internal/pmath"
+)
+
+var DefaultPool = New(128, 65536)
+
+// Get pulls object whose generic size is at least of given size. It also
+// returns a real size of x for further pass to Put(). It returns -1 as real
+// size for nil x. Size >-1 does not mean that x is non-nil, so checks must be
+// done.
+//
+// Note that size could be ceiled to the next power of two.
+//
+// Get is a wrapper around DefaultPool.Get().
+func Get(size int) (interface{}, int) { return DefaultPool.Get(size) }
+
+// Put takes x and its size for future reuse.
+// Put is a wrapper around DefaultPool.Put().
+func Put(x interface{}, size int) { DefaultPool.Put(x, size) }
+
+// Pool contains logic of reusing objects distinguishable by size in generic
+// way.
+type Pool struct {
+ pool map[int]*sync.Pool
+ size func(int) int
+}
+
+// New creates new Pool that reuses objects which size is in logarithmic range
+// [min, max].
+//
+// Note that it is a shortcut for Custom() constructor with Options provided by
+// WithLogSizeMapping() and WithLogSizeRange(min, max) calls.
+func New(min, max int) *Pool {
+ return Custom(
+ WithLogSizeMapping(),
+ WithLogSizeRange(min, max),
+ )
+}
+
+// Custom creates new Pool with given options.
+func Custom(opts ...Option) *Pool {
+ p := &Pool{
+ pool: make(map[int]*sync.Pool),
+ size: pmath.Identity,
+ }
+
+ c := (*poolConfig)(p)
+ for _, opt := range opts {
+ opt(c)
+ }
+
+ return p
+}
+
+// Get pulls object whose generic size is at least of given size.
+// It also returns a real size of x for further pass to Put() even if x is nil.
+// Note that size could be ceiled to the next power of two.
+func (p *Pool) Get(size int) (interface{}, int) {
+ n := p.size(size)
+ if pool := p.pool[n]; pool != nil {
+ return pool.Get(), n
+ }
+ return nil, size
+}
+
+// Put takes x and its size for future reuse.
+func (p *Pool) Put(x interface{}, size int) {
+ if pool := p.pool[size]; pool != nil {
+ pool.Put(x)
+ }
+}
+
+type poolConfig Pool
+
+// AddSize adds size n to the map.
+func (p *poolConfig) AddSize(n int) {
+ p.pool[n] = new(sync.Pool)
+}
+
+// SetSizeMapping sets up incoming size mapping function.
+func (p *poolConfig) SetSizeMapping(size func(int) int) {
+ p.size = size
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/internal/pmath/pmath.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/internal/pmath/pmath.go
new file mode 100644
index 0000000000..df152ed12a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/internal/pmath/pmath.go
@@ -0,0 +1,65 @@
+package pmath
+
+const (
+ bitsize = 32 << (^uint(0) >> 63)
+ maxint = int(1<<(bitsize-1) - 1)
+ maxintHeadBit = 1 << (bitsize - 2)
+)
+
+// LogarithmicRange iterates from ceiled to power of two min to max,
+// calling cb on each iteration.
+func LogarithmicRange(min, max int, cb func(int)) {
+ if min == 0 {
+ min = 1
+ }
+ for n := CeilToPowerOfTwo(min); n <= max; n <<= 1 {
+ cb(n)
+ }
+}
+
+// IsPowerOfTwo reports whether given integer is a power of two.
+func IsPowerOfTwo(n int) bool {
+ return n&(n-1) == 0
+}
+
+// Identity is identity.
+func Identity(n int) int {
+ return n
+}
+
+// CeilToPowerOfTwo returns the least power of two integer value greater than
+// or equal to n.
+func CeilToPowerOfTwo(n int) int {
+ if n&maxintHeadBit != 0 && n > maxintHeadBit {
+ panic("argument is too large")
+ }
+ if n <= 2 {
+ return n
+ }
+ n--
+ n = fillBits(n)
+ n++
+ return n
+}
+
+// FloorToPowerOfTwo returns the greatest power of two integer value less than
+// or equal to n.
+func FloorToPowerOfTwo(n int) int {
+ if n <= 2 {
+ return n
+ }
+ n = fillBits(n)
+ n >>= 1
+ n++
+ return n
+}
+
+func fillBits(n int) int {
+ n |= n >> 1
+ n |= n >> 2
+ n |= n >> 4
+ n |= n >> 8
+ n |= n >> 16
+ n |= n >> 32
+ return n
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/option.go
new file mode 100644
index 0000000000..d6e42b7005
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/option.go
@@ -0,0 +1,43 @@
+package pool
+
+import "github.com/gobwas/pool/internal/pmath"
+
+// Option configures pool.
+type Option func(Config)
+
+// Config describes generic pool configuration.
+type Config interface {
+ AddSize(n int)
+ SetSizeMapping(func(int) int)
+}
+
+// WithSizeLogRange returns an Option that will add logarithmic range of
+// pooling sizes containing [min, max] values.
+func WithLogSizeRange(min, max int) Option {
+ return func(c Config) {
+ pmath.LogarithmicRange(min, max, func(n int) {
+ c.AddSize(n)
+ })
+ }
+}
+
+// WithSize returns an Option that will add given pooling size to the pool.
+func WithSize(n int) Option {
+ return func(c Config) {
+ c.AddSize(n)
+ }
+}
+
+func WithSizeMapping(sz func(int) int) Option {
+ return func(c Config) {
+ c.SetSizeMapping(sz)
+ }
+}
+
+func WithLogSizeMapping() Option {
+ return WithSizeMapping(pmath.CeilToPowerOfTwo)
+}
+
+func WithIdentitySizeMapping() Option {
+ return WithSizeMapping(pmath.Identity)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio.go
new file mode 100644
index 0000000000..d526bd80da
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio.go
@@ -0,0 +1,106 @@
+// Package pbufio contains tools for pooling bufio.Reader and bufio.Writers.
+package pbufio
+
+import (
+ "bufio"
+ "io"
+
+ "github.com/gobwas/pool"
+)
+
+var (
+ DefaultWriterPool = NewWriterPool(256, 65536)
+ DefaultReaderPool = NewReaderPool(256, 65536)
+)
+
+// GetWriter returns bufio.Writer whose buffer has at least size bytes.
+// Note that size could be ceiled to the next power of two.
+// GetWriter is a wrapper around DefaultWriterPool.Get().
+func GetWriter(w io.Writer, size int) *bufio.Writer { return DefaultWriterPool.Get(w, size) }
+
+// PutWriter takes bufio.Writer for future reuse.
+// It does not reuse bufio.Writer which underlying buffer size is not power of
+// PutWriter is a wrapper around DefaultWriterPool.Put().
+func PutWriter(bw *bufio.Writer) { DefaultWriterPool.Put(bw) }
+
+// GetReader returns bufio.Reader whose buffer has at least size bytes. It returns
+// its capacity for further pass to Put().
+// Note that size could be ceiled to the next power of two.
+// GetReader is a wrapper around DefaultReaderPool.Get().
+func GetReader(w io.Reader, size int) *bufio.Reader { return DefaultReaderPool.Get(w, size) }
+
+// PutReader takes bufio.Reader and its size for future reuse.
+// It does not reuse bufio.Reader if size is not power of two or is out of pool
+// min/max range.
+// PutReader is a wrapper around DefaultReaderPool.Put().
+func PutReader(bw *bufio.Reader) { DefaultReaderPool.Put(bw) }
+
+// WriterPool contains logic of *bufio.Writer reuse with various size.
+type WriterPool struct {
+ pool *pool.Pool
+}
+
+// NewWriterPool creates new WriterPool that reuses writers which size is in
+// logarithmic range [min, max].
+func NewWriterPool(min, max int) *WriterPool {
+ return &WriterPool{pool.New(min, max)}
+}
+
+// CustomWriterPool creates new WriterPool with given options.
+func CustomWriterPool(opts ...pool.Option) *WriterPool {
+ return &WriterPool{pool.Custom(opts...)}
+}
+
+// Get returns bufio.Writer whose buffer has at least size bytes.
+func (wp *WriterPool) Get(w io.Writer, size int) *bufio.Writer {
+ v, n := wp.pool.Get(size)
+ if v != nil {
+ bw := v.(*bufio.Writer)
+ bw.Reset(w)
+ return bw
+ }
+ return bufio.NewWriterSize(w, n)
+}
+
+// Put takes ownership of bufio.Writer for further reuse.
+func (wp *WriterPool) Put(bw *bufio.Writer) {
+ // Should reset even if we do Reset() inside Get().
+ // This is done to prevent locking underlying io.Writer from GC.
+ bw.Reset(nil)
+ wp.pool.Put(bw, writerSize(bw))
+}
+
+// ReaderPool contains logic of *bufio.Reader reuse with various size.
+type ReaderPool struct {
+ pool *pool.Pool
+}
+
+// NewReaderPool creates new ReaderPool that reuses writers which size is in
+// logarithmic range [min, max].
+func NewReaderPool(min, max int) *ReaderPool {
+ return &ReaderPool{pool.New(min, max)}
+}
+
+// CustomReaderPool creates new ReaderPool with given options.
+func CustomReaderPool(opts ...pool.Option) *ReaderPool {
+ return &ReaderPool{pool.Custom(opts...)}
+}
+
+// Get returns bufio.Reader whose buffer has at least size bytes.
+func (rp *ReaderPool) Get(r io.Reader, size int) *bufio.Reader {
+ v, n := rp.pool.Get(size)
+ if v != nil {
+ br := v.(*bufio.Reader)
+ br.Reset(r)
+ return br
+ }
+ return bufio.NewReaderSize(r, n)
+}
+
+// Put takes ownership of bufio.Reader for further reuse.
+func (rp *ReaderPool) Put(br *bufio.Reader) {
+ // Should reset even if we do Reset() inside Get().
+ // This is done to prevent locking underlying io.Reader from GC.
+ br.Reset(nil)
+ rp.pool.Put(br, readerSize(br))
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go
new file mode 100644
index 0000000000..c736ae56e1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go110.go
@@ -0,0 +1,13 @@
+// +build go1.10
+
+package pbufio
+
+import "bufio"
+
+func writerSize(bw *bufio.Writer) int {
+ return bw.Size()
+}
+
+func readerSize(br *bufio.Reader) int {
+ return br.Size()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go
new file mode 100644
index 0000000000..e71dd447d2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbufio/pbufio_go19.go
@@ -0,0 +1,27 @@
+// +build !go1.10
+
+package pbufio
+
+import "bufio"
+
+func writerSize(bw *bufio.Writer) int {
+ return bw.Available() + bw.Buffered()
+}
+
+// readerSize returns buffer size of the given buffered reader.
+// NOTE: current workaround implementation resets underlying io.Reader.
+func readerSize(br *bufio.Reader) int {
+ br.Reset(sizeReader)
+ br.ReadByte()
+ n := br.Buffered() + 1
+ br.Reset(nil)
+ return n
+}
+
+var sizeReader optimisticReader
+
+type optimisticReader struct{}
+
+func (optimisticReader) Read(p []byte) (int, error) {
+ return len(p), nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pbytes.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pbytes.go
new file mode 100644
index 0000000000..919705b109
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pbytes.go
@@ -0,0 +1,24 @@
+// Package pbytes contains tools for pooling byte pool.
+// Note that by default it reuse slices with capacity from 128 to 65536 bytes.
+package pbytes
+
+// DefaultPool is used by pacakge level functions.
+var DefaultPool = New(128, 65536)
+
+// Get returns probably reused slice of bytes with at least capacity of c and
+// exactly len of n.
+// Get is a wrapper around DefaultPool.Get().
+func Get(n, c int) []byte { return DefaultPool.Get(n, c) }
+
+// GetCap returns probably reused slice of bytes with at least capacity of n.
+// GetCap is a wrapper around DefaultPool.GetCap().
+func GetCap(c int) []byte { return DefaultPool.GetCap(c) }
+
+// GetLen returns probably reused slice of bytes with at least capacity of n
+// and exactly len of n.
+// GetLen is a wrapper around DefaultPool.GetLen().
+func GetLen(n int) []byte { return DefaultPool.GetLen(n) }
+
+// Put returns given slice to reuse pool.
+// Put is a wrapper around DefaultPool.Put().
+func Put(p []byte) { DefaultPool.Put(p) }
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool.go
new file mode 100644
index 0000000000..1dde225f3e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool.go
@@ -0,0 +1,59 @@
+// +build !pool_sanitize
+
+package pbytes
+
+import "github.com/gobwas/pool"
+
+// Pool contains logic of reusing byte slices of various size.
+type Pool struct {
+ pool *pool.Pool
+}
+
+// New creates new Pool that reuses slices which size is in logarithmic range
+// [min, max].
+//
+// Note that it is a shortcut for Custom() constructor with Options provided by
+// pool.WithLogSizeMapping() and pool.WithLogSizeRange(min, max) calls.
+func New(min, max int) *Pool {
+ return &Pool{pool.New(min, max)}
+}
+
+// New creates new Pool with given options.
+func Custom(opts ...pool.Option) *Pool {
+ return &Pool{pool.Custom(opts...)}
+}
+
+// Get returns probably reused slice of bytes with at least capacity of c and
+// exactly len of n.
+func (p *Pool) Get(n, c int) []byte {
+ if n > c {
+ panic("requested length is greater than capacity")
+ }
+
+ v, x := p.pool.Get(c)
+ if v != nil {
+ bts := v.([]byte)
+ bts = bts[:n]
+ return bts
+ }
+
+ return make([]byte, n, x)
+}
+
+// Put returns given slice to reuse pool.
+// It does not reuse bytes whose size is not power of two or is out of pool
+// min/max range.
+func (p *Pool) Put(bts []byte) {
+ p.pool.Put(bts, cap(bts))
+}
+
+// GetCap returns probably reused slice of bytes with at least capacity of n.
+func (p *Pool) GetCap(c int) []byte {
+ return p.Get(0, c)
+}
+
+// GetLen returns probably reused slice of bytes with at least capacity of n
+// and exactly len of n.
+func (p *Pool) GetLen(n int) []byte {
+ return p.Get(n, n)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go
new file mode 100644
index 0000000000..fae9af49b1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pbytes/pool_sanitize.go
@@ -0,0 +1,121 @@
+// +build pool_sanitize
+
+package pbytes
+
+import (
+ "reflect"
+ "runtime"
+ "sync/atomic"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+const magic = uint64(0x777742)
+
+type guard struct {
+ magic uint64
+ size int
+ owners int32
+}
+
+const guardSize = int(unsafe.Sizeof(guard{}))
+
+type Pool struct {
+ min, max int
+}
+
+func New(min, max int) *Pool {
+ return &Pool{min, max}
+}
+
+// Get returns probably reused slice of bytes with at least capacity of c and
+// exactly len of n.
+func (p *Pool) Get(n, c int) []byte {
+ if n > c {
+ panic("requested length is greater than capacity")
+ }
+
+ pageSize := syscall.Getpagesize()
+ pages := (c+guardSize)/pageSize + 1
+ size := pages * pageSize
+
+ bts := alloc(size)
+
+ g := (*guard)(unsafe.Pointer(&bts[0]))
+ *g = guard{
+ magic: magic,
+ size: size,
+ owners: 1,
+ }
+
+ return bts[guardSize : guardSize+n]
+}
+
+func (p *Pool) GetCap(c int) []byte { return p.Get(0, c) }
+func (p *Pool) GetLen(n int) []byte { return Get(n, n) }
+
+// Put returns given slice to reuse pool.
+func (p *Pool) Put(bts []byte) {
+ hdr := *(*reflect.SliceHeader)(unsafe.Pointer(&bts))
+ ptr := hdr.Data - uintptr(guardSize)
+
+ g := (*guard)(unsafe.Pointer(ptr))
+ if g.magic != magic {
+ panic("unknown slice returned to the pool")
+ }
+ if n := atomic.AddInt32(&g.owners, -1); n < 0 {
+ panic("multiple Put() detected")
+ }
+
+ // Disable read and write on bytes memory pages. This will cause panic on
+ // incorrect access to returned slice.
+ mprotect(ptr, false, false, g.size)
+
+ runtime.SetFinalizer(&bts, func(b *[]byte) {
+ mprotect(ptr, true, true, g.size)
+ free(*(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
+ Data: ptr,
+ Len: g.size,
+ Cap: g.size,
+ })))
+ })
+}
+
+func alloc(n int) []byte {
+ b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE|unix.PROT_EXEC, unix.MAP_SHARED|unix.MAP_ANONYMOUS)
+ if err != nil {
+ panic(err.Error())
+ }
+ return b
+}
+
+func free(b []byte) {
+ if err := unix.Munmap(b); err != nil {
+ panic(err.Error())
+ }
+}
+
+func mprotect(ptr uintptr, r, w bool, size int) {
+ // Need to avoid "EINVAL addr is not a valid pointer,
+ // or not a multiple of PAGESIZE."
+ start := ptr & ^(uintptr(syscall.Getpagesize() - 1))
+
+ prot := uintptr(syscall.PROT_EXEC)
+ switch {
+ case r && w:
+ prot |= syscall.PROT_READ | syscall.PROT_WRITE
+ case r:
+ prot |= syscall.PROT_READ
+ case w:
+ prot |= syscall.PROT_WRITE
+ }
+
+ _, _, err := syscall.Syscall(syscall.SYS_MPROTECT,
+ start, uintptr(size), prot,
+ )
+ if err != 0 {
+ panic(err.Error())
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pool.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pool.go
new file mode 100644
index 0000000000..1fe9e602fc
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/pool/pool.go
@@ -0,0 +1,25 @@
+// Package pool contains helpers for pooling structures distinguishable by
+// size.
+//
+// Quick example:
+//
+// import "github.com/gobwas/pool"
+//
+// func main() {
+// // Reuse objects in logarithmic range from 0 to 64 (0,1,2,4,6,8,16,32,64).
+// p := pool.New(0, 64)
+//
+// buf, n := p.Get(10) // Returns buffer with 16 capacity.
+// if buf == nil {
+// buf = bytes.NewBuffer(make([]byte, n))
+// }
+// defer p.Put(buf, n)
+//
+// // Work with buf.
+// }
+//
+// There are non-generic implementations for pooling:
+// - pool/pbytes for []byte reuse;
+// - pool/pbufio for *bufio.Reader and *bufio.Writer reuse;
+//
+package pool
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/.gitignore
new file mode 100644
index 0000000000..e3e2b1080d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/.gitignore
@@ -0,0 +1,5 @@
+bin/
+reports/
+cpu.out
+mem.out
+ws.test
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/LICENSE
new file mode 100644
index 0000000000..d2611fddf5
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-2018 Sergey Kamardin
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/Makefile b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/Makefile
new file mode 100644
index 0000000000..8f727393ac
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/Makefile
@@ -0,0 +1,54 @@
+BENCH ?=.
+BENCH_BASE?=master
+
+clean:
+ rm -f bin/reporter
+ rm -fr autobahn/report/*
+
+bin/reporter:
+ go build -o bin/reporter ./autobahn
+
+bin/gocovmerge:
+ go build -o bin/gocovmerge github.com/wadey/gocovmerge
+
+.PHONY: autobahn
+autobahn: clean bin/reporter
+ ./autobahn/script/test.sh --build --follow-logs
+ bin/reporter $(PWD)/autobahn/report/index.json
+
+.PHONY: autobahn/report
+autobahn/report: bin/reporter
+ ./bin/reporter -http localhost:5555 ./autobahn/report/index.json
+
+test:
+ go test -coverprofile=ws.coverage .
+ go test -coverprofile=wsutil.coverage ./wsutil
+ go test -coverprofile=wsfalte.coverage ./wsflate
+ # No statemenets to cover in ./tests (there are only tests).
+ go test ./tests
+
+cover: bin/gocovmerge test autobahn
+ bin/gocovmerge ws.coverage wsutil.coverage wsflate.coverage autobahn/report/server.coverage > total.coverage
+
+benchcmp: BENCH_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
+benchcmp: BENCH_OLD:=$(shell mktemp -t old.XXXX)
+benchcmp: BENCH_NEW:=$(shell mktemp -t new.XXXX)
+benchcmp:
+ if [ ! -z "$(shell git status -s)" ]; then\
+ echo "could not compare with $(BENCH_BASE) – found unstaged changes";\
+ exit 1;\
+ fi;\
+ if [ "$(BENCH_BRANCH)" == "$(BENCH_BASE)" ]; then\
+ echo "comparing the same branches";\
+ exit 1;\
+ fi;\
+ echo "benchmarking $(BENCH_BRANCH)...";\
+ go test -run=none -bench=$(BENCH) -benchmem > $(BENCH_NEW);\
+ echo "benchmarking $(BENCH_BASE)...";\
+ git checkout -q $(BENCH_BASE);\
+ go test -run=none -bench=$(BENCH) -benchmem > $(BENCH_OLD);\
+ git checkout -q $(BENCH_BRANCH);\
+ echo "\nresults:";\
+ echo "========\n";\
+ benchcmp $(BENCH_OLD) $(BENCH_NEW);\
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/README.md
new file mode 100644
index 0000000000..ee54c58553
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/README.md
@@ -0,0 +1,450 @@
+# ws
+
+[![GoDoc][godoc-image]][godoc-url]
+[![CI][ci-badge]][ci-url]
+
+> [RFC6455][rfc-url] WebSocket implementation in Go.
+
+# Features
+
+- Zero-copy upgrade
+- No intermediate allocations during I/O
+- Low-level API which allows to build your own logic of packet handling and
+ buffers reuse
+- High-level wrappers and helpers around API in `wsutil` package, which allow
+ to start fast without digging the protocol internals
+
+# Documentation
+
+[GoDoc][godoc-url].
+
+# Why
+
+Existing WebSocket implementations do not allow users to reuse I/O buffers
+between connections in clear way. This library aims to export efficient
+low-level interface for working with the protocol without forcing only one way
+it could be used.
+
+By the way, if you want get the higher-level tools, you can use `wsutil`
+package.
+
+# Status
+
+Library is tagged as `v1*` so its API must not be broken during some
+improvements or refactoring.
+
+This implementation of RFC6455 passes [Autobahn Test
+Suite](https://github.com/crossbario/autobahn-testsuite) and currently has
+about 78% coverage.
+
+# Examples
+
+Example applications using `ws` are developed in separate repository
+[ws-examples](https://github.com/gobwas/ws-examples).
+
+# Usage
+
+The higher-level example of WebSocket echo server:
+
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/gobwas/ws"
+ "github.com/gobwas/ws/wsutil"
+)
+
+func main() {
+ http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ conn, _, _, err := ws.UpgradeHTTP(r, w)
+ if err != nil {
+ // handle error
+ }
+ go func() {
+ defer conn.Close()
+
+ for {
+ msg, op, err := wsutil.ReadClientData(conn)
+ if err != nil {
+ // handle error
+ }
+ err = wsutil.WriteServerMessage(conn, op, msg)
+ if err != nil {
+ // handle error
+ }
+ }
+ }()
+ }))
+}
+```
+
+Lower-level, but still high-level example:
+
+
+```go
+import (
+ "net/http"
+ "io"
+
+ "github.com/gobwas/ws"
+ "github.com/gobwas/ws/wsutil"
+)
+
+func main() {
+ http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ conn, _, _, err := ws.UpgradeHTTP(r, w)
+ if err != nil {
+ // handle error
+ }
+ go func() {
+ defer conn.Close()
+
+ var (
+ state = ws.StateServerSide
+ reader = wsutil.NewReader(conn, state)
+ writer = wsutil.NewWriter(conn, state, ws.OpText)
+ )
+ for {
+ header, err := reader.NextFrame()
+ if err != nil {
+ // handle error
+ }
+
+ // Reset writer to write frame with right operation code.
+ writer.Reset(conn, state, header.OpCode)
+
+ if _, err = io.Copy(writer, reader); err != nil {
+ // handle error
+ }
+ if err = writer.Flush(); err != nil {
+ // handle error
+ }
+ }
+ }()
+ }))
+}
+```
+
+We can apply the same pattern to read and write structured responses through a JSON encoder and decoder.:
+
+```go
+ ...
+ var (
+ r = wsutil.NewReader(conn, ws.StateServerSide)
+ w = wsutil.NewWriter(conn, ws.StateServerSide, ws.OpText)
+ decoder = json.NewDecoder(r)
+ encoder = json.NewEncoder(w)
+ )
+ for {
+ hdr, err = r.NextFrame()
+ if err != nil {
+ return err
+ }
+ if hdr.OpCode == ws.OpClose {
+ return io.EOF
+ }
+ var req Request
+ if err := decoder.Decode(&req); err != nil {
+ return err
+ }
+ var resp Response
+ if err := encoder.Encode(&resp); err != nil {
+ return err
+ }
+ if err = w.Flush(); err != nil {
+ return err
+ }
+ }
+ ...
+```
+
+The lower-level example without `wsutil`:
+
+```go
+package main
+
+import (
+ "net"
+ "io"
+
+ "github.com/gobwas/ws"
+)
+
+func main() {
+ ln, err := net.Listen("tcp", "localhost:8080")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ // handle error
+ }
+ _, err = ws.Upgrade(conn)
+ if err != nil {
+ // handle error
+ }
+
+ go func() {
+ defer conn.Close()
+
+ for {
+ header, err := ws.ReadHeader(conn)
+ if err != nil {
+ // handle error
+ }
+
+ payload := make([]byte, header.Length)
+ _, err = io.ReadFull(conn, payload)
+ if err != nil {
+ // handle error
+ }
+ if header.Masked {
+ ws.Cipher(payload, header.Mask, 0)
+ }
+
+ // Reset the Masked flag, server frames must not be masked as
+ // RFC6455 says.
+ header.Masked = false
+
+ if err := ws.WriteHeader(conn, header); err != nil {
+ // handle error
+ }
+ if _, err := conn.Write(payload); err != nil {
+ // handle error
+ }
+
+ if header.OpCode == ws.OpClose {
+ return
+ }
+ }
+ }()
+ }
+}
+```
+
+# Zero-copy upgrade
+
+Zero-copy upgrade helps to avoid unnecessary allocations and copying while
+handling HTTP Upgrade request.
+
+Processing of all non-websocket headers is made in place with use of registered
+user callbacks whose arguments are only valid until callback returns.
+
+The simple example looks like this:
+
+```go
+package main
+
+import (
+ "net"
+ "log"
+
+ "github.com/gobwas/ws"
+)
+
+func main() {
+ ln, err := net.Listen("tcp", "localhost:8080")
+ if err != nil {
+ log.Fatal(err)
+ }
+ u := ws.Upgrader{
+ OnHeader: func(key, value []byte) (err error) {
+ log.Printf("non-websocket header: %q=%q", key, value)
+ return
+ },
+ }
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ // handle error
+ }
+
+ _, err = u.Upgrade(conn)
+ if err != nil {
+ // handle error
+ }
+ }
+}
+```
+
+Usage of `ws.Upgrader` here brings ability to control incoming connections on
+tcp level and simply not to accept them by some logic.
+
+Zero-copy upgrade is for high-load services which have to control many
+resources such as connections buffers.
+
+The real life example could be like this:
+
+```go
+package main
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "runtime"
+
+ "github.com/gobwas/httphead"
+ "github.com/gobwas/ws"
+)
+
+func main() {
+ ln, err := net.Listen("tcp", "localhost:8080")
+ if err != nil {
+ // handle error
+ }
+
+ // Prepare handshake header writer from http.Header mapping.
+ header := ws.HandshakeHeaderHTTP(http.Header{
+ "X-Go-Version": []string{runtime.Version()},
+ })
+
+ u := ws.Upgrader{
+ OnHost: func(host []byte) error {
+ if string(host) == "github.com" {
+ return nil
+ }
+ return ws.RejectConnectionError(
+ ws.RejectionStatus(403),
+ ws.RejectionHeader(ws.HandshakeHeaderString(
+ "X-Want-Host: github.com\r\n",
+ )),
+ )
+ },
+ OnHeader: func(key, value []byte) error {
+ if string(key) != "Cookie" {
+ return nil
+ }
+ ok := httphead.ScanCookie(value, func(key, value []byte) bool {
+ // Check session here or do some other stuff with cookies.
+ // Maybe copy some values for future use.
+ return true
+ })
+ if ok {
+ return nil
+ }
+ return ws.RejectConnectionError(
+ ws.RejectionReason("bad cookie"),
+ ws.RejectionStatus(400),
+ )
+ },
+ OnBeforeUpgrade: func() (ws.HandshakeHeader, error) {
+ return header, nil
+ },
+ }
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ log.Fatal(err)
+ }
+ _, err = u.Upgrade(conn)
+ if err != nil {
+ log.Printf("upgrade error: %s", err)
+ }
+ }
+}
+```
+
+# Compression
+
+There is a `ws/wsflate` package to support [Permessage-Deflate Compression
+Extension][rfc-pmce].
+
+It provides minimalistic I/O wrappers to be used in conjunction with any
+deflate implementation (for example, the standard library's
+[compress/flate][compress/flate].
+
+```go
+package main
+
+import (
+ "bytes"
+ "log"
+ "net"
+
+ "github.com/gobwas/ws"
+ "github.com/gobwas/ws/wsflate"
+)
+
+func main() {
+ ln, err := net.Listen("tcp", "localhost:8080")
+ if err != nil {
+ // handle error
+ }
+ e := wsflate.Extension{
+ // We are using default parameters here since we use
+ // wsflate.{Compress,Decompress}Frame helpers below in the code.
+ // This assumes that we use standard compress/flate package as flate
+ // implementation.
+ Parameters: wsflate.DefaultParameters,
+ }
+ u := ws.Upgrader{
+ Negotiate: e.Negotiate,
+ }
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Reset extension after previous upgrades.
+ e.Reset()
+
+ _, err = u.Upgrade(conn)
+ if err != nil {
+ log.Printf("upgrade error: %s", err)
+ continue
+ }
+ if _, ok := e.Accepted(); !ok {
+ log.Printf("didn't negotiate compression for %s", conn.RemoteAddr())
+ conn.Close()
+ continue
+ }
+
+ go func() {
+ defer conn.Close()
+ for {
+ frame, err := ws.ReadFrame(conn)
+ if err != nil {
+ // Handle error.
+ return
+ }
+ frame = ws.UnmaskFrameInPlace(frame)
+ frame, err = wsflate.DecompressFrame(frame)
+ if err != nil {
+ // Handle error.
+ return
+ }
+
+ // Do something with frame...
+
+ ack := ws.NewTextFrame([]byte("this is an acknowledgement"))
+ ack, err = wsflate.CompressFrame(ack)
+ if err != nil {
+ // Handle error.
+ return
+ }
+ if err = ws.WriteFrame(conn, ack); err != nil {
+ // Handle error.
+ return
+ }
+ }
+ }()
+ }
+}
+```
+
+
+[rfc-url]: https://tools.ietf.org/html/rfc6455
+[rfc-pmce]: https://tools.ietf.org/html/rfc7692#section-7
+[godoc-image]: https://godoc.org/github.com/gobwas/ws?status.svg
+[godoc-url]: https://godoc.org/github.com/gobwas/ws
+[compress/flate]: https://golang.org/pkg/compress/flate/
+[ci-badge]: https://github.com/gobwas/ws/workflows/CI/badge.svg
+[ci-url]: https://github.com/gobwas/ws/actions?query=workflow%3ACI
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/check.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/check.go
new file mode 100644
index 0000000000..8aa0df8cc2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/check.go
@@ -0,0 +1,145 @@
+package ws
+
+import "unicode/utf8"
+
+// State represents state of websocket endpoint.
+// It used by some functions to be more strict when checking compatibility with RFC6455.
+type State uint8
+
+const (
+ // StateServerSide means that endpoint (caller) is a server.
+ StateServerSide State = 0x1 << iota
+ // StateClientSide means that endpoint (caller) is a client.
+ StateClientSide
+ // StateExtended means that extension was negotiated during handshake.
+ StateExtended
+ // StateFragmented means that endpoint (caller) has received fragmented
+ // frame and waits for continuation parts.
+ StateFragmented
+)
+
+// Is checks whether the s has v enabled.
+func (s State) Is(v State) bool {
+ return uint8(s)&uint8(v) != 0
+}
+
+// Set enables v state on s.
+func (s State) Set(v State) State {
+ return s | v
+}
+
+// Clear disables v state on s.
+func (s State) Clear(v State) State {
+ return s & (^v)
+}
+
+// ServerSide reports whether states represents server side.
+func (s State) ServerSide() bool { return s.Is(StateServerSide) }
+
+// ClientSide reports whether state represents client side.
+func (s State) ClientSide() bool { return s.Is(StateClientSide) }
+
+// Extended reports whether state is extended.
+func (s State) Extended() bool { return s.Is(StateExtended) }
+
+// Fragmented reports whether state is fragmented.
+func (s State) Fragmented() bool { return s.Is(StateFragmented) }
+
+// ProtocolError describes error during checking/parsing websocket frames or
+// headers.
+type ProtocolError string
+
+// Error implements error interface.
+func (p ProtocolError) Error() string { return string(p) }
+
+// Errors used by the protocol checkers.
+var (
+ ErrProtocolOpCodeReserved = ProtocolError("use of reserved op code")
+ ErrProtocolControlPayloadOverflow = ProtocolError("control frame payload limit exceeded")
+ ErrProtocolControlNotFinal = ProtocolError("control frame is not final")
+ ErrProtocolNonZeroRsv = ProtocolError("non-zero rsv bits with no extension negotiated")
+ ErrProtocolMaskRequired = ProtocolError("frames from client to server must be masked")
+ ErrProtocolMaskUnexpected = ProtocolError("frames from server to client must be not masked")
+ ErrProtocolContinuationExpected = ProtocolError("unexpected non-continuation data frame")
+ ErrProtocolContinuationUnexpected = ProtocolError("unexpected continuation data frame")
+ ErrProtocolStatusCodeNotInUse = ProtocolError("status code is not in use")
+ ErrProtocolStatusCodeApplicationLevel = ProtocolError("status code is only application level")
+ ErrProtocolStatusCodeNoMeaning = ProtocolError("status code has no meaning yet")
+ ErrProtocolStatusCodeUnknown = ProtocolError("status code is not defined in spec")
+ ErrProtocolInvalidUTF8 = ProtocolError("invalid utf8 sequence in close reason")
+)
+
+// CheckHeader checks h to contain valid header data for given state s.
+//
+// Note that zero state (0) means that state is clean,
+// neither server or client side, nor fragmented, nor extended.
+func CheckHeader(h Header, s State) error {
+ if h.OpCode.IsReserved() {
+ return ErrProtocolOpCodeReserved
+ }
+ if h.OpCode.IsControl() {
+ if h.Length > MaxControlFramePayloadSize {
+ return ErrProtocolControlPayloadOverflow
+ }
+ if !h.Fin {
+ return ErrProtocolControlNotFinal
+ }
+ }
+
+ switch {
+ // [RFC6455]: MUST be 0 unless an extension is negotiated that defines meanings for
+ // non-zero values. If a nonzero value is received and none of the
+ // negotiated extensions defines the meaning of such a nonzero value, the
+ // receiving endpoint MUST _Fail the WebSocket Connection_.
+ case h.Rsv != 0 && !s.Extended():
+ return ErrProtocolNonZeroRsv
+
+ // [RFC6455]: The server MUST close the connection upon receiving a frame that is not masked.
+ // In this case, a server MAY send a Close frame with a status code of 1002 (protocol error)
+ // as defined in Section 7.4.1. A server MUST NOT mask any frames that it sends to the client.
+ // A client MUST close a connection if it detects a masked frame. In this case, it MAY use the
+ // status code 1002 (protocol error) as defined in Section 7.4.1.
+ case s.ServerSide() && !h.Masked:
+ return ErrProtocolMaskRequired
+ case s.ClientSide() && h.Masked:
+ return ErrProtocolMaskUnexpected
+
+ // [RFC6455]: See detailed explanation in 5.4 section.
+ case s.Fragmented() && !h.OpCode.IsControl() && h.OpCode != OpContinuation:
+ return ErrProtocolContinuationExpected
+ case !s.Fragmented() && h.OpCode == OpContinuation:
+ return ErrProtocolContinuationUnexpected
+
+ default:
+ return nil
+ }
+}
+
+// CheckCloseFrameData checks received close information
+// to be valid RFC6455 compatible close info.
+//
+// Note that code.Empty() or code.IsAppLevel() will raise error.
+//
+// If endpoint sends close frame without status code (with frame.Length = 0),
+// application should not check its payload.
+func CheckCloseFrameData(code StatusCode, reason string) error {
+ switch {
+ case code.IsNotUsed():
+ return ErrProtocolStatusCodeNotInUse
+
+ case code.IsProtocolReserved():
+ return ErrProtocolStatusCodeApplicationLevel
+
+ case code == StatusNoMeaningYet:
+ return ErrProtocolStatusCodeNoMeaning
+
+ case code.IsProtocolSpec() && !code.IsProtocolDefined():
+ return ErrProtocolStatusCodeUnknown
+
+ case !utf8.ValidString(reason):
+ return ErrProtocolInvalidUTF8
+
+ default:
+ return nil
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/cipher.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/cipher.go
new file mode 100644
index 0000000000..026f4fd065
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/cipher.go
@@ -0,0 +1,61 @@
+package ws
+
+import (
+ "encoding/binary"
+)
+
+// Cipher applies XOR cipher to the payload using mask.
+// Offset is used to cipher chunked data (e.g. in io.Reader implementations).
+//
+// To convert masked data into unmasked data, or vice versa, the following
+// algorithm is applied. The same algorithm applies regardless of the
+// direction of the translation, e.g., the same steps are applied to
+// mask the data as to unmask the data.
+func Cipher(payload []byte, mask [4]byte, offset int) {
+ n := len(payload)
+ if n < 8 {
+ for i := 0; i < n; i++ {
+ payload[i] ^= mask[(offset+i)%4]
+ }
+ return
+ }
+
+ // Calculate position in mask due to previously processed bytes number.
+ mpos := offset % 4
+ // Count number of bytes will processed one by one from the beginning of payload.
+ ln := remain[mpos]
+ // Count number of bytes will processed one by one from the end of payload.
+ // This is done to process payload by 8 bytes in each iteration of main loop.
+ rn := (n - ln) % 8
+
+ for i := 0; i < ln; i++ {
+ payload[i] ^= mask[(mpos+i)%4]
+ }
+ for i := n - rn; i < n; i++ {
+ payload[i] ^= mask[(mpos+i)%4]
+ }
+
+ // NOTE: we use here binary.LittleEndian regardless of what is real
+ // endianess on machine is. To do so, we have to use binary.LittleEndian in
+ // the masking loop below as well.
+ var (
+ m = binary.LittleEndian.Uint32(mask[:])
+ m2 = uint64(m)<<32 | uint64(m)
+ )
+ // Skip already processed right part.
+ // Get number of uint64 parts remaining to process.
+ n = (n - ln - rn) >> 3
+ for i := 0; i < n; i++ {
+ var (
+ j = ln + (i << 3)
+ chunk = payload[j : j+8]
+ )
+ p := binary.LittleEndian.Uint64(chunk)
+ p = p ^ m2
+ binary.LittleEndian.PutUint64(chunk, p)
+ }
+}
+
+// remain maps position in masking key [0,4) to number
+// of bytes that need to be processed manually inside Cipher().
+var remain = [4]int{0, 3, 2, 1}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer.go
new file mode 100644
index 0000000000..d35dc14b8b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer.go
@@ -0,0 +1,563 @@
+package ws
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gobwas/httphead"
+ "github.com/gobwas/pool/pbufio"
+)
+
+// Constants used by Dialer.
+const (
+ DefaultClientReadBufferSize = 4096
+ DefaultClientWriteBufferSize = 4096
+)
+
+// Handshake represents handshake result.
+type Handshake struct {
+ // Protocol is the subprotocol selected during handshake.
+ Protocol string
+
+ // Extensions is the list of negotiated extensions.
+ Extensions []httphead.Option
+}
+
+// Errors used by the websocket client.
+var (
+ ErrHandshakeBadStatus = fmt.Errorf("unexpected http status")
+ ErrHandshakeBadSubProtocol = fmt.Errorf("unexpected protocol in %q header", headerSecProtocol)
+ ErrHandshakeBadExtensions = fmt.Errorf("unexpected extensions in %q header", headerSecProtocol)
+)
+
+// DefaultDialer is dialer that holds no options and is used by Dial function.
+var DefaultDialer Dialer
+
+// Dial is like Dialer{}.Dial().
+func Dial(ctx context.Context, urlstr string) (net.Conn, *bufio.Reader, Handshake, error) {
+ return DefaultDialer.Dial(ctx, urlstr)
+}
+
+// Dialer contains options for establishing websocket connection to an url.
+type Dialer struct {
+ // ReadBufferSize and WriteBufferSize is an I/O buffer sizes.
+ // They used to read and write http data while upgrading to WebSocket.
+ // Allocated buffers are pooled with sync.Pool to avoid extra allocations.
+ //
+ // If a size is zero then default value is used.
+ ReadBufferSize, WriteBufferSize int
+
+ // Timeout is the maximum amount of time a Dial() will wait for a connect
+ // and an handshake to complete.
+ //
+ // The default is no timeout.
+ Timeout time.Duration
+
+ // Protocols is the list of subprotocols that the client wants to speak,
+ // ordered by preference.
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-4.1
+ Protocols []string
+
+ // Extensions is the list of extensions that client wants to speak.
+ //
+ // Note that if server decides to use some of this extensions, Dial() will
+ // return Handshake struct containing a slice of items, which are the
+ // shallow copies of the items from this list. That is, internals of
+ // Extensions items are shared during Dial().
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-4.1
+ // See https://tools.ietf.org/html/rfc6455#section-9.1
+ Extensions []httphead.Option
+
+ // Header is an optional HandshakeHeader instance that could be used to
+ // write additional headers to the handshake request.
+ //
+ // It used instead of any key-value mappings to avoid allocations in user
+ // land.
+ Header HandshakeHeader
+
+ // OnStatusError is the callback that will be called after receiving non
+ // "101 Continue" HTTP response status. It receives an io.Reader object
+ // representing server response bytes. That is, it gives ability to parse
+ // HTTP response somehow (probably with http.ReadResponse call) and make a
+ // decision of further logic.
+ //
+ // The arguments are only valid until the callback returns.
+ OnStatusError func(status int, reason []byte, resp io.Reader)
+
+ // OnHeader is the callback that will be called after successful parsing of
+ // header, that is not used during WebSocket handshake procedure. That is,
+ // it will be called with non-websocket headers, which could be relevant
+ // for application-level logic.
+ //
+ // The arguments are only valid until the callback returns.
+ //
+ // Returned value could be used to prevent processing response.
+ OnHeader func(key, value []byte) (err error)
+
+ // NetDial is the function that is used to get plain tcp connection.
+ // If it is not nil, then it is used instead of net.Dialer.
+ NetDial func(ctx context.Context, network, addr string) (net.Conn, error)
+
+ // TLSClient is the callback that will be called after successful dial with
+ // received connection and its remote host name. If it is nil, then the
+ // default tls.Client() will be used.
+ // If it is not nil, then TLSConfig field is ignored.
+ TLSClient func(conn net.Conn, hostname string) net.Conn
+
+ // TLSConfig is passed to tls.Client() to start TLS over established
+ // connection. If TLSClient is not nil, then it is ignored. If TLSConfig is
+ // non-nil and its ServerName is empty, then for every Dial() it will be
+ // cloned and appropriate ServerName will be set.
+ TLSConfig *tls.Config
+
+ // WrapConn is the optional callback that will be called when connection is
+ // ready for an i/o. That is, it will be called after successful dial and
+ // TLS initialization (for "wss" schemes). It may be helpful for different
+ // user land purposes such as end to end encryption.
+ //
+ // Note that for debugging purposes of an http handshake (e.g. sent request
+ // and received response), there is an wsutil.DebugDialer struct.
+ WrapConn func(conn net.Conn) net.Conn
+}
+
+// Dial connects to the url host and upgrades connection to WebSocket.
+//
+// If server has sent frames right after successful handshake then returned
+// buffer will be non-nil. In other cases buffer is always nil. For better
+// memory efficiency received non-nil bufio.Reader should be returned to the
+// inner pool with PutReader() function after use.
+//
+// Note that Dialer does not implement IDNA (RFC5895) logic as net/http does.
+// If you want to dial non-ascii host name, take care of its name serialization
+// avoiding bad request issues. For more info see net/http Request.Write()
+// implementation, especially cleanHost() function.
+func (d Dialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *bufio.Reader, hs Handshake, err error) {
+ u, err := url.ParseRequestURI(urlstr)
+ if err != nil {
+ return
+ }
+
+ // Prepare context to dial with. Initially it is the same as original, but
+ // if d.Timeout is non-zero and points to time that is before ctx.Deadline,
+ // we use more shorter context for dial.
+ dialctx := ctx
+
+ var deadline time.Time
+ if t := d.Timeout; t != 0 {
+ deadline = time.Now().Add(t)
+ if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
+ var cancel context.CancelFunc
+ dialctx, cancel = context.WithDeadline(ctx, deadline)
+ defer cancel()
+ }
+ }
+ if conn, err = d.dial(dialctx, u); err != nil {
+ return
+ }
+ defer func() {
+ if err != nil {
+ conn.Close()
+ }
+ }()
+ if ctx == context.Background() {
+ // No need to start I/O interrupter goroutine which is not zero-cost.
+ conn.SetDeadline(deadline)
+ defer conn.SetDeadline(noDeadline)
+ } else {
+ // Context could be canceled or its deadline could be exceeded.
+ // Start the interrupter goroutine to handle context cancelation.
+ done := setupContextDeadliner(ctx, conn)
+ defer func() {
+ // Map Upgrade() error to a possible context expiration error. That
+ // is, even if Upgrade() err is nil, context could be already
+ // expired and connection be "poisoned" by SetDeadline() call.
+ // In that case we must not return ctx.Err() error.
+ done(&err)
+ }()
+ }
+
+ br, hs, err = d.Upgrade(conn, u)
+
+ return
+}
+
+var (
+ // netEmptyDialer is a net.Dialer without options, used in Dialer.dial() if
+ // Dialer.NetDial is not provided.
+ netEmptyDialer net.Dialer
+ // tlsEmptyConfig is an empty tls.Config used as default one.
+ tlsEmptyConfig tls.Config
+)
+
+func tlsDefaultConfig() *tls.Config {
+ return &tlsEmptyConfig
+}
+
+func hostport(host string, defaultPort string) (hostname, addr string) {
+ var (
+ colon = strings.LastIndexByte(host, ':')
+ bracket = strings.IndexByte(host, ']')
+ )
+ if colon > bracket {
+ return host[:colon], host
+ }
+ return host, host + defaultPort
+}
+
+func (d Dialer) dial(ctx context.Context, u *url.URL) (conn net.Conn, err error) {
+ dial := d.NetDial
+ if dial == nil {
+ dial = netEmptyDialer.DialContext
+ }
+ switch u.Scheme {
+ case "ws":
+ _, addr := hostport(u.Host, ":80")
+ conn, err = dial(ctx, "tcp", addr)
+ case "wss":
+ hostname, addr := hostport(u.Host, ":443")
+ conn, err = dial(ctx, "tcp", addr)
+ if err != nil {
+ return
+ }
+ tlsClient := d.TLSClient
+ if tlsClient == nil {
+ tlsClient = d.tlsClient
+ }
+ conn = tlsClient(conn, hostname)
+ default:
+ return nil, fmt.Errorf("unexpected websocket scheme: %q", u.Scheme)
+ }
+ if wrap := d.WrapConn; wrap != nil {
+ conn = wrap(conn)
+ }
+ return
+}
+
+func (d Dialer) tlsClient(conn net.Conn, hostname string) net.Conn {
+ config := d.TLSConfig
+ if config == nil {
+ config = tlsDefaultConfig()
+ }
+ if config.ServerName == "" {
+ config = tlsCloneConfig(config)
+ config.ServerName = hostname
+ }
+ // Do not make conn.Handshake() here because downstairs we will prepare
+ // i/o on this conn with proper context's timeout handling.
+ return tls.Client(conn, config)
+}
+
+var (
+ // This variables are set like in net/net.go.
+ // noDeadline is just zero value for readability.
+ noDeadline = time.Time{}
+ // aLongTimeAgo is a non-zero time, far in the past, used for immediate
+ // cancelation of dials.
+ aLongTimeAgo = time.Unix(42, 0)
+)
+
+// Upgrade writes an upgrade request to the given io.ReadWriter conn at given
+// url u and reads a response from it.
+//
+// It is a caller responsibility to manage I/O deadlines on conn.
+//
+// It returns handshake info and some bytes which could be written by the peer
+// right after response and be caught by us during buffered read.
+func (d Dialer) Upgrade(conn io.ReadWriter, u *url.URL) (br *bufio.Reader, hs Handshake, err error) {
+ // headerSeen constants helps to report whether or not some header was seen
+ // during reading request bytes.
+ const (
+ headerSeenUpgrade = 1 << iota
+ headerSeenConnection
+ headerSeenSecAccept
+
+ // headerSeenAll is the value that we expect to receive at the end of
+ // headers read/parse loop.
+ headerSeenAll = 0 |
+ headerSeenUpgrade |
+ headerSeenConnection |
+ headerSeenSecAccept
+ )
+
+ br = pbufio.GetReader(conn,
+ nonZero(d.ReadBufferSize, DefaultClientReadBufferSize),
+ )
+ bw := pbufio.GetWriter(conn,
+ nonZero(d.WriteBufferSize, DefaultClientWriteBufferSize),
+ )
+ defer func() {
+ pbufio.PutWriter(bw)
+ if br.Buffered() == 0 || err != nil {
+ // Server does not wrote additional bytes to the connection or
+ // error occurred. That is, no reason to return buffer.
+ pbufio.PutReader(br)
+ br = nil
+ }
+ }()
+
+ nonce := make([]byte, nonceSize)
+ initNonce(nonce)
+
+ httpWriteUpgradeRequest(bw, u, nonce, d.Protocols, d.Extensions, d.Header)
+ if err = bw.Flush(); err != nil {
+ return
+ }
+
+ // Read HTTP status line like "HTTP/1.1 101 Switching Protocols".
+ sl, err := readLine(br)
+ if err != nil {
+ return
+ }
+ // Begin validation of the response.
+ // See https://tools.ietf.org/html/rfc6455#section-4.2.2
+ // Parse request line data like HTTP version, uri and method.
+ resp, err := httpParseResponseLine(sl)
+ if err != nil {
+ return
+ }
+ // Even if RFC says "1.1 or higher" without mentioning the part of the
+ // version, we apply it only to minor part.
+ if resp.major != 1 || resp.minor < 1 {
+ err = ErrHandshakeBadProtocol
+ return
+ }
+ if resp.status != 101 {
+ err = StatusError(resp.status)
+ if onStatusError := d.OnStatusError; onStatusError != nil {
+ // Invoke callback with multireader of status-line bytes br.
+ onStatusError(resp.status, resp.reason,
+ io.MultiReader(
+ bytes.NewReader(sl),
+ strings.NewReader(crlf),
+ br,
+ ),
+ )
+ }
+ return
+ }
+ // If response status is 101 then we expect all technical headers to be
+ // valid. If not, then we stop processing response without giving user
+ // ability to read non-technical headers. That is, we do not distinguish
+ // technical errors (such as parsing error) and protocol errors.
+ var headerSeen byte
+ for {
+ line, e := readLine(br)
+ if e != nil {
+ err = e
+ return
+ }
+ if len(line) == 0 {
+ // Blank line, no more lines to read.
+ break
+ }
+
+ k, v, ok := httpParseHeaderLine(line)
+ if !ok {
+ err = ErrMalformedResponse
+ return
+ }
+
+ switch btsToString(k) {
+ case headerUpgradeCanonical:
+ headerSeen |= headerSeenUpgrade
+ if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) {
+ err = ErrHandshakeBadUpgrade
+ return
+ }
+
+ case headerConnectionCanonical:
+ headerSeen |= headerSeenConnection
+ // Note that as RFC6455 says:
+ // > A |Connection| header field with value "Upgrade".
+ // That is, in server side, "Connection" header could contain
+ // multiple token. But in response it must contains exactly one.
+ if !bytes.Equal(v, specHeaderValueConnection) && !bytes.EqualFold(v, specHeaderValueConnection) {
+ err = ErrHandshakeBadConnection
+ return
+ }
+
+ case headerSecAcceptCanonical:
+ headerSeen |= headerSeenSecAccept
+ if !checkAcceptFromNonce(v, nonce) {
+ err = ErrHandshakeBadSecAccept
+ return
+ }
+
+ case headerSecProtocolCanonical:
+ // RFC6455 1.3:
+ // "The server selects one or none of the acceptable protocols
+ // and echoes that value in its handshake to indicate that it has
+ // selected that protocol."
+ for _, want := range d.Protocols {
+ if string(v) == want {
+ hs.Protocol = want
+ break
+ }
+ }
+ if hs.Protocol == "" {
+ // Server echoed subprotocol that is not present in client
+ // requested protocols.
+ err = ErrHandshakeBadSubProtocol
+ return
+ }
+
+ case headerSecExtensionsCanonical:
+ hs.Extensions, err = matchSelectedExtensions(v, d.Extensions, hs.Extensions)
+ if err != nil {
+ return
+ }
+
+ default:
+ if onHeader := d.OnHeader; onHeader != nil {
+ if e := onHeader(k, v); e != nil {
+ err = e
+ return
+ }
+ }
+ }
+ }
+ if err == nil && headerSeen != headerSeenAll {
+ switch {
+ case headerSeen&headerSeenUpgrade == 0:
+ err = ErrHandshakeBadUpgrade
+ case headerSeen&headerSeenConnection == 0:
+ err = ErrHandshakeBadConnection
+ case headerSeen&headerSeenSecAccept == 0:
+ err = ErrHandshakeBadSecAccept
+ default:
+ panic("unknown headers state")
+ }
+ }
+ return
+}
+
+// PutReader returns bufio.Reader instance to the inner reuse pool.
+// It is useful in rare cases, when Dialer.Dial() returns non-nil buffer which
+// contains unprocessed buffered data, that was sent by the server quickly
+// right after handshake.
+func PutReader(br *bufio.Reader) {
+ pbufio.PutReader(br)
+}
+
+// StatusError contains an unexpected status-line code from the server.
+type StatusError int
+
+func (s StatusError) Error() string {
+ return "unexpected HTTP response status: " + strconv.Itoa(int(s))
+}
+
+func isTimeoutError(err error) bool {
+ t, ok := err.(net.Error)
+ return ok && t.Timeout()
+}
+
+func matchSelectedExtensions(selected []byte, wanted, received []httphead.Option) ([]httphead.Option, error) {
+ if len(selected) == 0 {
+ return received, nil
+ }
+ var (
+ index int
+ option httphead.Option
+ err error
+ )
+ index = -1
+ match := func() (ok bool) {
+ for _, want := range wanted {
+ // A server accepts one or more extensions by including a
+ // |Sec-WebSocket-Extensions| header field containing one or more
+ // extensions that were requested by the client.
+ //
+ // The interpretation of any extension parameters, and what
+ // constitutes a valid response by a server to a requested set of
+ // parameters by a client, will be defined by each such extension.
+ if bytes.Equal(option.Name, want.Name) {
+ // Check parsed extension to be present in client
+ // requested extensions. We move matched extension
+ // from client list to avoid allocation.
+ received = append(received, option)
+ return true
+ }
+ }
+ return false
+ }
+ ok := httphead.ScanOptions(selected, func(i int, name, attr, val []byte) httphead.Control {
+ if i != index {
+ // Met next option.
+ index = i
+ if i != 0 && !match() {
+ // Server returned non-requested extension.
+ err = ErrHandshakeBadExtensions
+ return httphead.ControlBreak
+ }
+ option = httphead.Option{Name: name}
+ }
+ if attr != nil {
+ option.Parameters.Set(attr, val)
+ }
+ return httphead.ControlContinue
+ })
+ if !ok {
+ err = ErrMalformedResponse
+ return received, err
+ }
+ if !match() {
+ return received, ErrHandshakeBadExtensions
+ }
+ return received, err
+}
+
+// setupContextDeadliner is a helper function that starts connection I/O
+// interrupter goroutine.
+//
+// Started goroutine calls SetDeadline() with long time ago value when context
+// become expired to make any I/O operations failed. It returns done function
+// that stops started goroutine and maps error received from conn I/O methods
+// to possible context expiration error.
+//
+// In concern with possible SetDeadline() call inside interrupter goroutine,
+// caller passes pointer to its I/O error (even if it is nil) to done(&err).
+// That is, even if I/O error is nil, context could be already expired and
+// connection "poisoned" by SetDeadline() call. In that case done(&err) will
+// store at *err ctx.Err() result. If err is caused not by timeout, it will
+// leaved untouched.
+func setupContextDeadliner(ctx context.Context, conn net.Conn) (done func(*error)) {
+ var (
+ quit = make(chan struct{})
+ interrupt = make(chan error, 1)
+ )
+ go func() {
+ select {
+ case <-quit:
+ interrupt <- nil
+ case <-ctx.Done():
+ // Cancel i/o immediately.
+ conn.SetDeadline(aLongTimeAgo)
+ interrupt <- ctx.Err()
+ }
+ }()
+ return func(err *error) {
+ close(quit)
+ // If ctx.Err() is non-nil and the original err is net.Error with
+ // Timeout() == true, then it means that I/O was canceled by us by
+ // SetDeadline(aLongTimeAgo) call, or by somebody else previously
+ // by conn.SetDeadline(x).
+ //
+ // Even on race condition when both deadlines are expired
+ // (SetDeadline() made not by us and context's), we prefer ctx.Err() to
+ // be returned.
+ if ctxErr := <-interrupt; ctxErr != nil && (*err == nil || isTimeoutError(*err)) {
+ *err = ctxErr
+ }
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go17.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go17.go
new file mode 100644
index 0000000000..b606e0ad90
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go17.go
@@ -0,0 +1,35 @@
+// +build !go1.8
+
+package ws
+
+import "crypto/tls"
+
+func tlsCloneConfig(c *tls.Config) *tls.Config {
+ // NOTE: we copying SessionTicketsDisabled and SessionTicketKey here
+ // without calling inner c.initOnceServer somehow because we only could get
+ // here from the ws.Dialer code, which is obviously a client and makes
+ // tls.Client() when it gets new net.Conn.
+ return &tls.Config{
+ Rand: c.Rand,
+ Time: c.Time,
+ Certificates: c.Certificates,
+ NameToCertificate: c.NameToCertificate,
+ GetCertificate: c.GetCertificate,
+ RootCAs: c.RootCAs,
+ NextProtos: c.NextProtos,
+ ServerName: c.ServerName,
+ ClientAuth: c.ClientAuth,
+ ClientCAs: c.ClientCAs,
+ InsecureSkipVerify: c.InsecureSkipVerify,
+ CipherSuites: c.CipherSuites,
+ PreferServerCipherSuites: c.PreferServerCipherSuites,
+ SessionTicketsDisabled: c.SessionTicketsDisabled,
+ SessionTicketKey: c.SessionTicketKey,
+ ClientSessionCache: c.ClientSessionCache,
+ MinVersion: c.MinVersion,
+ MaxVersion: c.MaxVersion,
+ CurvePreferences: c.CurvePreferences,
+ DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
+ Renegotiation: c.Renegotiation,
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go18.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go18.go
new file mode 100644
index 0000000000..a6704d5173
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/dialer_tls_go18.go
@@ -0,0 +1,9 @@
+// +build go1.8
+
+package ws
+
+import "crypto/tls"
+
+func tlsCloneConfig(c *tls.Config) *tls.Config {
+ return c.Clone()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/doc.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/doc.go
new file mode 100644
index 0000000000..c9d5791570
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/doc.go
@@ -0,0 +1,81 @@
+/*
+Package ws implements a client and server for the WebSocket protocol as
+specified in RFC 6455.
+
+The main purpose of this package is to provide simple low-level API for
+efficient work with protocol.
+
+Overview.
+
+Upgrade to WebSocket (or WebSocket handshake) can be done in two ways.
+
+The first way is to use `net/http` server:
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ conn, _, _, err := ws.UpgradeHTTP(r, w)
+ })
+
+The second and much more efficient way is so-called "zero-copy upgrade". It
+avoids redundant allocations and copying of not used headers or other request
+data. User decides by himself which data should be copied.
+
+ ln, err := net.Listen("tcp", ":8080")
+ if err != nil {
+ // handle error
+ }
+
+ conn, err := ln.Accept()
+ if err != nil {
+ // handle error
+ }
+
+ handshake, err := ws.Upgrade(conn)
+ if err != nil {
+ // handle error
+ }
+
+For customization details see `ws.Upgrader` documentation.
+
+After WebSocket handshake you can work with connection in multiple ways.
+That is, `ws` does not force the only one way of how to work with WebSocket:
+
+ header, err := ws.ReadHeader(conn)
+ if err != nil {
+ // handle err
+ }
+
+ buf := make([]byte, header.Length)
+ _, err := io.ReadFull(conn, buf)
+ if err != nil {
+ // handle err
+ }
+
+ resp := ws.NewBinaryFrame([]byte("hello, world!"))
+ if err := ws.WriteFrame(conn, frame); err != nil {
+ // handle err
+ }
+
+As you can see, it stream friendly:
+
+ const N = 42
+
+ ws.WriteHeader(ws.Header{
+ Fin: true,
+ Length: N,
+ OpCode: ws.OpBinary,
+ })
+
+ io.CopyN(conn, rand.Reader, N)
+
+Or:
+
+ header, err := ws.ReadHeader(conn)
+ if err != nil {
+ // handle err
+ }
+
+ io.CopyN(ioutil.Discard, conn, header.Length)
+
+For more info see the documentation.
+*/
+package ws
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/errors.go
new file mode 100644
index 0000000000..48fce3b72c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/errors.go
@@ -0,0 +1,54 @@
+package ws
+
+// RejectOption represents an option used to control the way connection is
+// rejected.
+type RejectOption func(*rejectConnectionError)
+
+// RejectionReason returns an option that makes connection to be rejected with
+// given reason.
+func RejectionReason(reason string) RejectOption {
+ return func(err *rejectConnectionError) {
+ err.reason = reason
+ }
+}
+
+// RejectionStatus returns an option that makes connection to be rejected with
+// given HTTP status code.
+func RejectionStatus(code int) RejectOption {
+ return func(err *rejectConnectionError) {
+ err.code = code
+ }
+}
+
+// RejectionHeader returns an option that makes connection to be rejected with
+// given HTTP headers.
+func RejectionHeader(h HandshakeHeader) RejectOption {
+ return func(err *rejectConnectionError) {
+ err.header = h
+ }
+}
+
+// RejectConnectionError constructs an error that could be used to control the way
+// handshake is rejected by Upgrader.
+func RejectConnectionError(options ...RejectOption) error {
+ err := new(rejectConnectionError)
+ for _, opt := range options {
+ opt(err)
+ }
+ return err
+}
+
+// rejectConnectionError represents a rejection of upgrade error.
+//
+// It can be returned by Upgrader's On* hooks to control the way WebSocket
+// handshake is rejected.
+type rejectConnectionError struct {
+ reason string
+ code int
+ header HandshakeHeader
+}
+
+// Error implements error interface.
+func (r *rejectConnectionError) Error() string {
+ return r.reason
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/frame.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/frame.go
new file mode 100644
index 0000000000..a4b9ddb3c2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/frame.go
@@ -0,0 +1,420 @@
+package ws
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math/rand"
+)
+
+// Constants defined by specification.
+const (
+ // All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
+ MaxControlFramePayloadSize = 125
+)
+
+// OpCode represents operation code.
+type OpCode byte
+
+// Operation codes defined by specification.
+// See https://tools.ietf.org/html/rfc6455#section-5.2
+const (
+ OpContinuation OpCode = 0x0
+ OpText OpCode = 0x1
+ OpBinary OpCode = 0x2
+ OpClose OpCode = 0x8
+ OpPing OpCode = 0x9
+ OpPong OpCode = 0xa
+)
+
+// IsControl checks whether the c is control operation code.
+// See https://tools.ietf.org/html/rfc6455#section-5.5
+func (c OpCode) IsControl() bool {
+ // RFC6455: Control frames are identified by opcodes where
+ // the most significant bit of the opcode is 1.
+ //
+ // Note that OpCode is only 4 bit length.
+ return c&0x8 != 0
+}
+
+// IsData checks whether the c is data operation code.
+// See https://tools.ietf.org/html/rfc6455#section-5.6
+func (c OpCode) IsData() bool {
+ // RFC6455: Data frames (e.g., non-control frames) are identified by opcodes
+ // where the most significant bit of the opcode is 0.
+ //
+ // Note that OpCode is only 4 bit length.
+ return c&0x8 == 0
+}
+
+// IsReserved checks whether the c is reserved operation code.
+// See https://tools.ietf.org/html/rfc6455#section-5.2
+func (c OpCode) IsReserved() bool {
+ // RFC6455:
+ // %x3-7 are reserved for further non-control frames
+ // %xB-F are reserved for further control frames
+ return (0x3 <= c && c <= 0x7) || (0xb <= c && c <= 0xf)
+}
+
+// StatusCode represents the encoded reason for closure of websocket connection.
+//
+// There are few helper methods on StatusCode that helps to define a range in
+// which given code is lay in. accordingly to ranges defined in specification.
+//
+// See https://tools.ietf.org/html/rfc6455#section-7.4
+type StatusCode uint16
+
+// StatusCodeRange describes range of StatusCode values.
+type StatusCodeRange struct {
+ Min, Max StatusCode
+}
+
+// Status code ranges defined by specification.
+// See https://tools.ietf.org/html/rfc6455#section-7.4.2
+var (
+ StatusRangeNotInUse = StatusCodeRange{0, 999}
+ StatusRangeProtocol = StatusCodeRange{1000, 2999}
+ StatusRangeApplication = StatusCodeRange{3000, 3999}
+ StatusRangePrivate = StatusCodeRange{4000, 4999}
+)
+
+// Status codes defined by specification.
+// See https://tools.ietf.org/html/rfc6455#section-7.4.1
+const (
+ StatusNormalClosure StatusCode = 1000
+ StatusGoingAway StatusCode = 1001
+ StatusProtocolError StatusCode = 1002
+ StatusUnsupportedData StatusCode = 1003
+ StatusNoMeaningYet StatusCode = 1004
+ StatusInvalidFramePayloadData StatusCode = 1007
+ StatusPolicyViolation StatusCode = 1008
+ StatusMessageTooBig StatusCode = 1009
+ StatusMandatoryExt StatusCode = 1010
+ StatusInternalServerError StatusCode = 1011
+ StatusTLSHandshake StatusCode = 1015
+
+ // StatusAbnormalClosure is a special code designated for use in
+ // applications.
+ StatusAbnormalClosure StatusCode = 1006
+
+ // StatusNoStatusRcvd is a special code designated for use in applications.
+ StatusNoStatusRcvd StatusCode = 1005
+)
+
+// In reports whether the code is defined in given range.
+func (s StatusCode) In(r StatusCodeRange) bool {
+ return r.Min <= s && s <= r.Max
+}
+
+// Empty reports whether the code is empty.
+// Empty code has no any meaning neither app level codes nor other.
+// This method is useful just to check that code is golang default value 0.
+func (s StatusCode) Empty() bool {
+ return s == 0
+}
+
+// IsNotUsed reports whether the code is predefined in not used range.
+func (s StatusCode) IsNotUsed() bool {
+ return s.In(StatusRangeNotInUse)
+}
+
+// IsApplicationSpec reports whether the code should be defined by
+// application, framework or libraries specification.
+func (s StatusCode) IsApplicationSpec() bool {
+ return s.In(StatusRangeApplication)
+}
+
+// IsPrivateSpec reports whether the code should be defined privately.
+func (s StatusCode) IsPrivateSpec() bool {
+ return s.In(StatusRangePrivate)
+}
+
+// IsProtocolSpec reports whether the code should be defined by protocol specification.
+func (s StatusCode) IsProtocolSpec() bool {
+ return s.In(StatusRangeProtocol)
+}
+
+// IsProtocolDefined reports whether the code is already defined by protocol specification.
+func (s StatusCode) IsProtocolDefined() bool {
+ switch s {
+ case StatusNormalClosure,
+ StatusGoingAway,
+ StatusProtocolError,
+ StatusUnsupportedData,
+ StatusInvalidFramePayloadData,
+ StatusPolicyViolation,
+ StatusMessageTooBig,
+ StatusMandatoryExt,
+ StatusInternalServerError,
+ StatusNoStatusRcvd,
+ StatusAbnormalClosure,
+ StatusTLSHandshake:
+ return true
+ }
+ return false
+}
+
+// IsProtocolReserved reports whether the code is defined by protocol specification
+// to be reserved only for application usage purpose.
+func (s StatusCode) IsProtocolReserved() bool {
+ switch s {
+ // [RFC6455]: {1005,1006,1015} is a reserved value and MUST NOT be set as a status code in a
+ // Close control frame by an endpoint.
+ case StatusNoStatusRcvd, StatusAbnormalClosure, StatusTLSHandshake:
+ return true
+ default:
+ return false
+ }
+}
+
+// Compiled control frames for common use cases.
+// For construct-serialize optimizations.
+var (
+ CompiledPing = MustCompileFrame(NewPingFrame(nil))
+ CompiledPong = MustCompileFrame(NewPongFrame(nil))
+ CompiledClose = MustCompileFrame(NewCloseFrame(nil))
+
+ CompiledCloseNormalClosure = MustCompileFrame(closeFrameNormalClosure)
+ CompiledCloseGoingAway = MustCompileFrame(closeFrameGoingAway)
+ CompiledCloseProtocolError = MustCompileFrame(closeFrameProtocolError)
+ CompiledCloseUnsupportedData = MustCompileFrame(closeFrameUnsupportedData)
+ CompiledCloseNoMeaningYet = MustCompileFrame(closeFrameNoMeaningYet)
+ CompiledCloseInvalidFramePayloadData = MustCompileFrame(closeFrameInvalidFramePayloadData)
+ CompiledClosePolicyViolation = MustCompileFrame(closeFramePolicyViolation)
+ CompiledCloseMessageTooBig = MustCompileFrame(closeFrameMessageTooBig)
+ CompiledCloseMandatoryExt = MustCompileFrame(closeFrameMandatoryExt)
+ CompiledCloseInternalServerError = MustCompileFrame(closeFrameInternalServerError)
+ CompiledCloseTLSHandshake = MustCompileFrame(closeFrameTLSHandshake)
+)
+
+// Header represents websocket frame header.
+// See https://tools.ietf.org/html/rfc6455#section-5.2
+type Header struct {
+ Fin bool
+ Rsv byte
+ OpCode OpCode
+ Masked bool
+ Mask [4]byte
+ Length int64
+}
+
+// Rsv1 reports whether the header has first rsv bit set.
+func (h Header) Rsv1() bool { return h.Rsv&bit5 != 0 }
+
+// Rsv2 reports whether the header has second rsv bit set.
+func (h Header) Rsv2() bool { return h.Rsv&bit6 != 0 }
+
+// Rsv3 reports whether the header has third rsv bit set.
+func (h Header) Rsv3() bool { return h.Rsv&bit7 != 0 }
+
+// Rsv creates rsv byte representation from bits.
+func Rsv(r1, r2, r3 bool) (rsv byte) {
+ if r1 {
+ rsv |= bit5
+ }
+ if r2 {
+ rsv |= bit6
+ }
+ if r3 {
+ rsv |= bit7
+ }
+ return rsv
+}
+
+// RsvBits returns rsv bits from bytes representation.
+func RsvBits(rsv byte) (r1, r2, r3 bool) {
+ r1 = rsv&bit5 != 0
+ r2 = rsv&bit6 != 0
+ r3 = rsv&bit7 != 0
+ return
+}
+
+// Frame represents websocket frame.
+// See https://tools.ietf.org/html/rfc6455#section-5.2
+type Frame struct {
+ Header Header
+ Payload []byte
+}
+
+// NewFrame creates frame with given operation code,
+// flag of completeness and payload bytes.
+func NewFrame(op OpCode, fin bool, p []byte) Frame {
+ return Frame{
+ Header: Header{
+ Fin: fin,
+ OpCode: op,
+ Length: int64(len(p)),
+ },
+ Payload: p,
+ }
+}
+
+// NewTextFrame creates text frame with p as payload.
+// Note that p is not copied.
+func NewTextFrame(p []byte) Frame {
+ return NewFrame(OpText, true, p)
+}
+
+// NewBinaryFrame creates binary frame with p as payload.
+// Note that p is not copied.
+func NewBinaryFrame(p []byte) Frame {
+ return NewFrame(OpBinary, true, p)
+}
+
+// NewPingFrame creates ping frame with p as payload.
+// Note that p is not copied.
+// Note that p must have length of MaxControlFramePayloadSize bytes or less due
+// to RFC.
+func NewPingFrame(p []byte) Frame {
+ return NewFrame(OpPing, true, p)
+}
+
+// NewPongFrame creates pong frame with p as payload.
+// Note that p is not copied.
+// Note that p must have length of MaxControlFramePayloadSize bytes or less due
+// to RFC.
+func NewPongFrame(p []byte) Frame {
+ return NewFrame(OpPong, true, p)
+}
+
+// NewCloseFrame creates close frame with given close body.
+// Note that p is not copied.
+// Note that p must have length of MaxControlFramePayloadSize bytes or less due
+// to RFC.
+func NewCloseFrame(p []byte) Frame {
+ return NewFrame(OpClose, true, p)
+}
+
+// NewCloseFrameBody encodes a closure code and a reason into a binary
+// representation.
+//
+// It returns slice which is at most MaxControlFramePayloadSize bytes length.
+// If the reason is too big it will be cropped to fit the limit defined by the
+// spec.
+//
+// See https://tools.ietf.org/html/rfc6455#section-5.5
+func NewCloseFrameBody(code StatusCode, reason string) []byte {
+ n := min(2+len(reason), MaxControlFramePayloadSize)
+ p := make([]byte, n)
+
+ crop := min(MaxControlFramePayloadSize-2, len(reason))
+ PutCloseFrameBody(p, code, reason[:crop])
+
+ return p
+}
+
+// PutCloseFrameBody encodes code and reason into buf.
+//
+// It will panic if the buffer is too small to accommodate a code or a reason.
+//
+// PutCloseFrameBody does not check buffer to be RFC compliant, but note that
+// by RFC it must be at most MaxControlFramePayloadSize.
+func PutCloseFrameBody(p []byte, code StatusCode, reason string) {
+ _ = p[1+len(reason)]
+ binary.BigEndian.PutUint16(p, uint16(code))
+ copy(p[2:], reason)
+}
+
+// MaskFrame masks frame and returns frame with masked payload and Mask header's field set.
+// Note that it copies f payload to prevent collisions.
+// For less allocations you could use MaskFrameInPlace or construct frame manually.
+func MaskFrame(f Frame) Frame {
+ return MaskFrameWith(f, NewMask())
+}
+
+// MaskFrameWith masks frame with given mask and returns frame
+// with masked payload and Mask header's field set.
+// Note that it copies f payload to prevent collisions.
+// For less allocations you could use MaskFrameInPlaceWith or construct frame manually.
+func MaskFrameWith(f Frame, mask [4]byte) Frame {
+ // TODO(gobwas): check CopyCipher ws copy() Cipher().
+ p := make([]byte, len(f.Payload))
+ copy(p, f.Payload)
+ f.Payload = p
+ return MaskFrameInPlaceWith(f, mask)
+}
+
+// MaskFrameInPlace masks frame and returns frame with masked payload and Mask
+// header's field set.
+// Note that it applies xor cipher to f.Payload without copying, that is, it
+// modifies f.Payload inplace.
+func MaskFrameInPlace(f Frame) Frame {
+ return MaskFrameInPlaceWith(f, NewMask())
+}
+
+var zeroMask [4]byte
+
+// UnmaskFrame unmasks frame and returns frame with unmasked payload and Mask
+// header's field cleared.
+// Note that it copies f payload.
+func UnmaskFrame(f Frame) Frame {
+ p := make([]byte, len(f.Payload))
+ copy(p, f.Payload)
+ f.Payload = p
+ return UnmaskFrameInPlace(f)
+}
+
+// UnmaskFrameInPlace unmasks frame and returns frame with unmasked payload and
+// Mask header's field cleared.
+// Note that it applies xor cipher to f.Payload without copying, that is, it
+// modifies f.Payload inplace.
+func UnmaskFrameInPlace(f Frame) Frame {
+ Cipher(f.Payload, f.Header.Mask, 0)
+ f.Header.Masked = false
+ f.Header.Mask = zeroMask
+ return f
+}
+
+// MaskFrameInPlaceWith masks frame with given mask and returns frame
+// with masked payload and Mask header's field set.
+// Note that it applies xor cipher to f.Payload without copying, that is, it
+// modifies f.Payload inplace.
+func MaskFrameInPlaceWith(f Frame, m [4]byte) Frame {
+ f.Header.Masked = true
+ f.Header.Mask = m
+ Cipher(f.Payload, m, 0)
+ return f
+}
+
+// NewMask creates new random mask.
+func NewMask() (ret [4]byte) {
+ binary.BigEndian.PutUint32(ret[:], rand.Uint32())
+ return
+}
+
+// CompileFrame returns byte representation of given frame.
+// In terms of memory consumption it is useful to precompile static frames
+// which are often used.
+func CompileFrame(f Frame) (bts []byte, err error) {
+ buf := bytes.NewBuffer(make([]byte, 0, 16))
+ err = WriteFrame(buf, f)
+ bts = buf.Bytes()
+ return
+}
+
+// MustCompileFrame is like CompileFrame but panics if frame can not be
+// encoded.
+func MustCompileFrame(f Frame) []byte {
+ bts, err := CompileFrame(f)
+ if err != nil {
+ panic(err)
+ }
+ return bts
+}
+
+func makeCloseFrame(code StatusCode) Frame {
+ return NewCloseFrame(NewCloseFrameBody(code, ""))
+}
+
+var (
+ closeFrameNormalClosure = makeCloseFrame(StatusNormalClosure)
+ closeFrameGoingAway = makeCloseFrame(StatusGoingAway)
+ closeFrameProtocolError = makeCloseFrame(StatusProtocolError)
+ closeFrameUnsupportedData = makeCloseFrame(StatusUnsupportedData)
+ closeFrameNoMeaningYet = makeCloseFrame(StatusNoMeaningYet)
+ closeFrameInvalidFramePayloadData = makeCloseFrame(StatusInvalidFramePayloadData)
+ closeFramePolicyViolation = makeCloseFrame(StatusPolicyViolation)
+ closeFrameMessageTooBig = makeCloseFrame(StatusMessageTooBig)
+ closeFrameMandatoryExt = makeCloseFrame(StatusMandatoryExt)
+ closeFrameInternalServerError = makeCloseFrame(StatusInternalServerError)
+ closeFrameTLSHandshake = makeCloseFrame(StatusTLSHandshake)
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.mod
new file mode 100644
index 0000000000..b0bc12a61b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.mod
@@ -0,0 +1,9 @@
+module github.com/gobwas/ws
+
+go 1.15
+
+require (
+ github.com/gobwas/httphead v0.1.0
+ github.com/gobwas/pool v0.2.1
+ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.sum b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.sum
new file mode 100644
index 0000000000..40b998a0b8
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/go.sum
@@ -0,0 +1,6 @@
+github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
+github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
+github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
+github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE=
+golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/http.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/http.go
new file mode 100644
index 0000000000..7d7175a2b9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/http.go
@@ -0,0 +1,504 @@
+package ws
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "net/http"
+ "net/textproto"
+ "net/url"
+ "strconv"
+
+ "github.com/gobwas/httphead"
+)
+
+const (
+ crlf = "\r\n"
+ colonAndSpace = ": "
+ commaAndSpace = ", "
+)
+
+const (
+ textHeadUpgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
+)
+
+var (
+ textHeadBadRequest = statusText(http.StatusBadRequest)
+ textHeadInternalServerError = statusText(http.StatusInternalServerError)
+ textHeadUpgradeRequired = statusText(http.StatusUpgradeRequired)
+
+ textTailErrHandshakeBadProtocol = errorText(ErrHandshakeBadProtocol)
+ textTailErrHandshakeBadMethod = errorText(ErrHandshakeBadMethod)
+ textTailErrHandshakeBadHost = errorText(ErrHandshakeBadHost)
+ textTailErrHandshakeBadUpgrade = errorText(ErrHandshakeBadUpgrade)
+ textTailErrHandshakeBadConnection = errorText(ErrHandshakeBadConnection)
+ textTailErrHandshakeBadSecAccept = errorText(ErrHandshakeBadSecAccept)
+ textTailErrHandshakeBadSecKey = errorText(ErrHandshakeBadSecKey)
+ textTailErrHandshakeBadSecVersion = errorText(ErrHandshakeBadSecVersion)
+ textTailErrUpgradeRequired = errorText(ErrHandshakeUpgradeRequired)
+)
+
+var (
+ headerHost = "Host"
+ headerUpgrade = "Upgrade"
+ headerConnection = "Connection"
+ headerSecVersion = "Sec-WebSocket-Version"
+ headerSecProtocol = "Sec-WebSocket-Protocol"
+ headerSecExtensions = "Sec-WebSocket-Extensions"
+ headerSecKey = "Sec-WebSocket-Key"
+ headerSecAccept = "Sec-WebSocket-Accept"
+
+ headerHostCanonical = textproto.CanonicalMIMEHeaderKey(headerHost)
+ headerUpgradeCanonical = textproto.CanonicalMIMEHeaderKey(headerUpgrade)
+ headerConnectionCanonical = textproto.CanonicalMIMEHeaderKey(headerConnection)
+ headerSecVersionCanonical = textproto.CanonicalMIMEHeaderKey(headerSecVersion)
+ headerSecProtocolCanonical = textproto.CanonicalMIMEHeaderKey(headerSecProtocol)
+ headerSecExtensionsCanonical = textproto.CanonicalMIMEHeaderKey(headerSecExtensions)
+ headerSecKeyCanonical = textproto.CanonicalMIMEHeaderKey(headerSecKey)
+ headerSecAcceptCanonical = textproto.CanonicalMIMEHeaderKey(headerSecAccept)
+)
+
+var (
+ specHeaderValueUpgrade = []byte("websocket")
+ specHeaderValueConnection = []byte("Upgrade")
+ specHeaderValueConnectionLower = []byte("upgrade")
+ specHeaderValueSecVersion = []byte("13")
+)
+
+var (
+ httpVersion1_0 = []byte("HTTP/1.0")
+ httpVersion1_1 = []byte("HTTP/1.1")
+ httpVersionPrefix = []byte("HTTP/")
+)
+
+type httpRequestLine struct {
+ method, uri []byte
+ major, minor int
+}
+
+type httpResponseLine struct {
+ major, minor int
+ status int
+ reason []byte
+}
+
+// httpParseRequestLine parses http request line like "GET / HTTP/1.0".
+func httpParseRequestLine(line []byte) (req httpRequestLine, err error) {
+ var proto []byte
+ req.method, req.uri, proto = bsplit3(line, ' ')
+
+ var ok bool
+ req.major, req.minor, ok = httpParseVersion(proto)
+ if !ok {
+ err = ErrMalformedRequest
+ return
+ }
+
+ return
+}
+
+func httpParseResponseLine(line []byte) (resp httpResponseLine, err error) {
+ var (
+ proto []byte
+ status []byte
+ )
+ proto, status, resp.reason = bsplit3(line, ' ')
+
+ var ok bool
+ resp.major, resp.minor, ok = httpParseVersion(proto)
+ if !ok {
+ return resp, ErrMalformedResponse
+ }
+
+ var convErr error
+ resp.status, convErr = asciiToInt(status)
+ if convErr != nil {
+ return resp, ErrMalformedResponse
+ }
+
+ return resp, nil
+}
+
+// httpParseVersion parses major and minor version of HTTP protocol. It returns
+// parsed values and true if parse is ok.
+func httpParseVersion(bts []byte) (major, minor int, ok bool) {
+ switch {
+ case bytes.Equal(bts, httpVersion1_0):
+ return 1, 0, true
+ case bytes.Equal(bts, httpVersion1_1):
+ return 1, 1, true
+ case len(bts) < 8:
+ return
+ case !bytes.Equal(bts[:5], httpVersionPrefix):
+ return
+ }
+
+ bts = bts[5:]
+
+ dot := bytes.IndexByte(bts, '.')
+ if dot == -1 {
+ return
+ }
+ var err error
+ major, err = asciiToInt(bts[:dot])
+ if err != nil {
+ return
+ }
+ minor, err = asciiToInt(bts[dot+1:])
+ if err != nil {
+ return
+ }
+
+ return major, minor, true
+}
+
+// httpParseHeaderLine parses HTTP header as key-value pair. It returns parsed
+// values and true if parse is ok.
+func httpParseHeaderLine(line []byte) (k, v []byte, ok bool) {
+ colon := bytes.IndexByte(line, ':')
+ if colon == -1 {
+ return
+ }
+
+ k = btrim(line[:colon])
+ // TODO(gobwas): maybe use just lower here?
+ canonicalizeHeaderKey(k)
+
+ v = btrim(line[colon+1:])
+
+ return k, v, true
+}
+
+// httpGetHeader is the same as textproto.MIMEHeader.Get, except the thing,
+// that key is already canonical. This helps to increase performance.
+func httpGetHeader(h http.Header, key string) string {
+ if h == nil {
+ return ""
+ }
+ v := h[key]
+ if len(v) == 0 {
+ return ""
+ }
+ return v[0]
+}
+
+// The request MAY include a header field with the name
+// |Sec-WebSocket-Protocol|. If present, this value indicates one or more
+// comma-separated subprotocol the client wishes to speak, ordered by
+// preference. The elements that comprise this value MUST be non-empty strings
+// with characters in the range U+0021 to U+007E not including separator
+// characters as defined in [RFC2616] and MUST all be unique strings. The ABNF
+// for the value of this header field is 1#token, where the definitions of
+// constructs and rules are as given in [RFC2616].
+func strSelectProtocol(h string, check func(string) bool) (ret string, ok bool) {
+ ok = httphead.ScanTokens(strToBytes(h), func(v []byte) bool {
+ if check(btsToString(v)) {
+ ret = string(v)
+ return false
+ }
+ return true
+ })
+ return
+}
+func btsSelectProtocol(h []byte, check func([]byte) bool) (ret string, ok bool) {
+ var selected []byte
+ ok = httphead.ScanTokens(h, func(v []byte) bool {
+ if check(v) {
+ selected = v
+ return false
+ }
+ return true
+ })
+ if ok && selected != nil {
+ return string(selected), true
+ }
+ return
+}
+
+func btsSelectExtensions(h []byte, selected []httphead.Option, check func(httphead.Option) bool) ([]httphead.Option, bool) {
+ s := httphead.OptionSelector{
+ Flags: httphead.SelectCopy,
+ Check: check,
+ }
+ return s.Select(h, selected)
+}
+
+func negotiateMaybe(in httphead.Option, dest []httphead.Option, f func(httphead.Option) (httphead.Option, error)) ([]httphead.Option, error) {
+ if in.Size() == 0 {
+ return dest, nil
+ }
+ opt, err := f(in)
+ if err != nil {
+ return nil, err
+ }
+ if opt.Size() > 0 {
+ dest = append(dest, opt)
+ }
+ return dest, nil
+}
+
+func negotiateExtensions(
+ h []byte, dest []httphead.Option,
+ f func(httphead.Option) (httphead.Option, error),
+) (_ []httphead.Option, err error) {
+ index := -1
+ var current httphead.Option
+ ok := httphead.ScanOptions(h, func(i int, name, attr, val []byte) httphead.Control {
+ if i != index {
+ dest, err = negotiateMaybe(current, dest, f)
+ if err != nil {
+ return httphead.ControlBreak
+ }
+ index = i
+ current = httphead.Option{Name: name}
+ }
+ if attr != nil {
+ current.Parameters.Set(attr, val)
+ }
+ return httphead.ControlContinue
+ })
+ if !ok {
+ return nil, ErrMalformedRequest
+ }
+ return negotiateMaybe(current, dest, f)
+}
+
+func httpWriteHeader(bw *bufio.Writer, key, value string) {
+ httpWriteHeaderKey(bw, key)
+ bw.WriteString(value)
+ bw.WriteString(crlf)
+}
+
+func httpWriteHeaderBts(bw *bufio.Writer, key string, value []byte) {
+ httpWriteHeaderKey(bw, key)
+ bw.Write(value)
+ bw.WriteString(crlf)
+}
+
+func httpWriteHeaderKey(bw *bufio.Writer, key string) {
+ bw.WriteString(key)
+ bw.WriteString(colonAndSpace)
+}
+
+func httpWriteUpgradeRequest(
+ bw *bufio.Writer,
+ u *url.URL,
+ nonce []byte,
+ protocols []string,
+ extensions []httphead.Option,
+ header HandshakeHeader,
+) {
+ bw.WriteString("GET ")
+ bw.WriteString(u.RequestURI())
+ bw.WriteString(" HTTP/1.1\r\n")
+
+ httpWriteHeader(bw, headerHost, u.Host)
+
+ httpWriteHeaderBts(bw, headerUpgrade, specHeaderValueUpgrade)
+ httpWriteHeaderBts(bw, headerConnection, specHeaderValueConnection)
+ httpWriteHeaderBts(bw, headerSecVersion, specHeaderValueSecVersion)
+
+ // NOTE: write nonce bytes as a string to prevent heap allocation –
+ // WriteString() copy given string into its inner buffer, unlike Write()
+ // which may write p directly to the underlying io.Writer – which in turn
+ // will lead to p escape.
+ httpWriteHeader(bw, headerSecKey, btsToString(nonce))
+
+ if len(protocols) > 0 {
+ httpWriteHeaderKey(bw, headerSecProtocol)
+ for i, p := range protocols {
+ if i > 0 {
+ bw.WriteString(commaAndSpace)
+ }
+ bw.WriteString(p)
+ }
+ bw.WriteString(crlf)
+ }
+
+ if len(extensions) > 0 {
+ httpWriteHeaderKey(bw, headerSecExtensions)
+ httphead.WriteOptions(bw, extensions)
+ bw.WriteString(crlf)
+ }
+
+ if header != nil {
+ header.WriteTo(bw)
+ }
+
+ bw.WriteString(crlf)
+}
+
+func httpWriteResponseUpgrade(bw *bufio.Writer, nonce []byte, hs Handshake, header HandshakeHeaderFunc) {
+ bw.WriteString(textHeadUpgrade)
+
+ httpWriteHeaderKey(bw, headerSecAccept)
+ writeAccept(bw, nonce)
+ bw.WriteString(crlf)
+
+ if hs.Protocol != "" {
+ httpWriteHeader(bw, headerSecProtocol, hs.Protocol)
+ }
+ if len(hs.Extensions) > 0 {
+ httpWriteHeaderKey(bw, headerSecExtensions)
+ httphead.WriteOptions(bw, hs.Extensions)
+ bw.WriteString(crlf)
+ }
+ if header != nil {
+ header(bw)
+ }
+
+ bw.WriteString(crlf)
+}
+
+func httpWriteResponseError(bw *bufio.Writer, err error, code int, header HandshakeHeaderFunc) {
+ switch code {
+ case http.StatusBadRequest:
+ bw.WriteString(textHeadBadRequest)
+ case http.StatusInternalServerError:
+ bw.WriteString(textHeadInternalServerError)
+ case http.StatusUpgradeRequired:
+ bw.WriteString(textHeadUpgradeRequired)
+ default:
+ writeStatusText(bw, code)
+ }
+
+ // Write custom headers.
+ if header != nil {
+ header(bw)
+ }
+
+ switch err {
+ case ErrHandshakeBadProtocol:
+ bw.WriteString(textTailErrHandshakeBadProtocol)
+ case ErrHandshakeBadMethod:
+ bw.WriteString(textTailErrHandshakeBadMethod)
+ case ErrHandshakeBadHost:
+ bw.WriteString(textTailErrHandshakeBadHost)
+ case ErrHandshakeBadUpgrade:
+ bw.WriteString(textTailErrHandshakeBadUpgrade)
+ case ErrHandshakeBadConnection:
+ bw.WriteString(textTailErrHandshakeBadConnection)
+ case ErrHandshakeBadSecAccept:
+ bw.WriteString(textTailErrHandshakeBadSecAccept)
+ case ErrHandshakeBadSecKey:
+ bw.WriteString(textTailErrHandshakeBadSecKey)
+ case ErrHandshakeBadSecVersion:
+ bw.WriteString(textTailErrHandshakeBadSecVersion)
+ case ErrHandshakeUpgradeRequired:
+ bw.WriteString(textTailErrUpgradeRequired)
+ case nil:
+ bw.WriteString(crlf)
+ default:
+ writeErrorText(bw, err)
+ }
+}
+
+func writeStatusText(bw *bufio.Writer, code int) {
+ bw.WriteString("HTTP/1.1 ")
+ bw.WriteString(strconv.Itoa(code))
+ bw.WriteByte(' ')
+ bw.WriteString(http.StatusText(code))
+ bw.WriteString(crlf)
+ bw.WriteString("Content-Type: text/plain; charset=utf-8")
+ bw.WriteString(crlf)
+}
+
+func writeErrorText(bw *bufio.Writer, err error) {
+ body := err.Error()
+ bw.WriteString("Content-Length: ")
+ bw.WriteString(strconv.Itoa(len(body)))
+ bw.WriteString(crlf)
+ bw.WriteString(crlf)
+ bw.WriteString(body)
+}
+
+// httpError is like the http.Error with WebSocket context exception.
+func httpError(w http.ResponseWriter, body string, code int) {
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("Content-Length", strconv.Itoa(len(body)))
+ w.WriteHeader(code)
+ w.Write([]byte(body))
+}
+
+// statusText is a non-performant status text generator.
+// NOTE: Used only to generate constants.
+func statusText(code int) string {
+ var buf bytes.Buffer
+ bw := bufio.NewWriter(&buf)
+ writeStatusText(bw, code)
+ bw.Flush()
+ return buf.String()
+}
+
+// errorText is a non-performant error text generator.
+// NOTE: Used only to generate constants.
+func errorText(err error) string {
+ var buf bytes.Buffer
+ bw := bufio.NewWriter(&buf)
+ writeErrorText(bw, err)
+ bw.Flush()
+ return buf.String()
+}
+
+// HandshakeHeader is the interface that writes both upgrade request or
+// response headers into a given io.Writer.
+type HandshakeHeader interface {
+ io.WriterTo
+}
+
+// HandshakeHeaderString is an adapter to allow the use of headers represented
+// by ordinary string as HandshakeHeader.
+type HandshakeHeaderString string
+
+// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
+func (s HandshakeHeaderString) WriteTo(w io.Writer) (int64, error) {
+ n, err := io.WriteString(w, string(s))
+ return int64(n), err
+}
+
+// HandshakeHeaderBytes is an adapter to allow the use of headers represented
+// by ordinary slice of bytes as HandshakeHeader.
+type HandshakeHeaderBytes []byte
+
+// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
+func (b HandshakeHeaderBytes) WriteTo(w io.Writer) (int64, error) {
+ n, err := w.Write(b)
+ return int64(n), err
+}
+
+// HandshakeHeaderFunc is an adapter to allow the use of headers represented by
+// ordinary function as HandshakeHeader.
+type HandshakeHeaderFunc func(io.Writer) (int64, error)
+
+// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
+func (f HandshakeHeaderFunc) WriteTo(w io.Writer) (int64, error) {
+ return f(w)
+}
+
+// HandshakeHeaderHTTP is an adapter to allow the use of http.Header as
+// HandshakeHeader.
+type HandshakeHeaderHTTP http.Header
+
+// WriteTo implements HandshakeHeader (and io.WriterTo) interface.
+func (h HandshakeHeaderHTTP) WriteTo(w io.Writer) (int64, error) {
+ wr := writer{w: w}
+ err := http.Header(h).Write(&wr)
+ return wr.n, err
+}
+
+type writer struct {
+ n int64
+ w io.Writer
+}
+
+func (w *writer) WriteString(s string) (int, error) {
+ n, err := io.WriteString(w.w, s)
+ w.n += int64(n)
+ return n, err
+}
+
+func (w *writer) Write(p []byte) (int, error) {
+ n, err := w.w.Write(p)
+ w.n += int64(n)
+ return n, err
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/nonce.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/nonce.go
new file mode 100644
index 0000000000..e694da7c30
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/nonce.go
@@ -0,0 +1,80 @@
+package ws
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/sha1"
+ "encoding/base64"
+ "fmt"
+ "math/rand"
+)
+
+const (
+ // RFC6455: The value of this header field MUST be a nonce consisting of a
+ // randomly selected 16-byte value that has been base64-encoded (see
+ // Section 4 of [RFC4648]). The nonce MUST be selected randomly for each
+ // connection.
+ nonceKeySize = 16
+ nonceSize = 24 // base64.StdEncoding.EncodedLen(nonceKeySize)
+
+ // RFC6455: The value of this header field is constructed by concatenating
+ // /key/, defined above in step 4 in Section 4.2.2, with the string
+ // "258EAFA5- E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
+ // concatenated value to obtain a 20-byte value and base64- encoding (see
+ // Section 4 of [RFC4648]) this 20-byte hash.
+ acceptSize = 28 // base64.StdEncoding.EncodedLen(sha1.Size)
+)
+
+// initNonce fills given slice with random base64-encoded nonce bytes.
+func initNonce(dst []byte) {
+ // NOTE: bts does not escape.
+ bts := make([]byte, nonceKeySize)
+ if _, err := rand.Read(bts); err != nil {
+ panic(fmt.Sprintf("rand read error: %s", err))
+ }
+ base64.StdEncoding.Encode(dst, bts)
+}
+
+// checkAcceptFromNonce reports whether given accept bytes are valid for given
+// nonce bytes.
+func checkAcceptFromNonce(accept, nonce []byte) bool {
+ if len(accept) != acceptSize {
+ return false
+ }
+ // NOTE: expect does not escape.
+ expect := make([]byte, acceptSize)
+ initAcceptFromNonce(expect, nonce)
+ return bytes.Equal(expect, accept)
+}
+
+// initAcceptFromNonce fills given slice with accept bytes generated from given
+// nonce bytes. Given buffer should be exactly acceptSize bytes.
+func initAcceptFromNonce(accept, nonce []byte) {
+ const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+ if len(accept) != acceptSize {
+ panic("accept buffer is invalid")
+ }
+ if len(nonce) != nonceSize {
+ panic("nonce is invalid")
+ }
+
+ p := make([]byte, nonceSize+len(magic))
+ copy(p[:nonceSize], nonce)
+ copy(p[nonceSize:], magic)
+
+ sum := sha1.Sum(p)
+ base64.StdEncoding.Encode(accept, sum[:])
+
+ return
+}
+
+func writeAccept(bw *bufio.Writer, nonce []byte) (int, error) {
+ accept := make([]byte, acceptSize)
+ initAcceptFromNonce(accept, nonce)
+ // NOTE: write accept bytes as a string to prevent heap allocation –
+ // WriteString() copy given string into its inner buffer, unlike Write()
+ // which may write p directly to the underlying io.Writer – which in turn
+ // will lead to p escape.
+ return bw.WriteString(btsToString(accept))
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/read.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/read.go
new file mode 100644
index 0000000000..bc653e4690
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/read.go
@@ -0,0 +1,147 @@
+package ws
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// Errors used by frame reader.
+var (
+ ErrHeaderLengthMSB = fmt.Errorf("header error: the most significant bit must be 0")
+ ErrHeaderLengthUnexpected = fmt.Errorf("header error: unexpected payload length bits")
+)
+
+// ReadHeader reads a frame header from r.
+func ReadHeader(r io.Reader) (h Header, err error) {
+ // Make slice of bytes with capacity 12 that could hold any header.
+ //
+ // The maximum header size is 14, but due to the 2 hop reads,
+ // after first hop that reads first 2 constant bytes, we could reuse 2 bytes.
+ // So 14 - 2 = 12.
+ bts := make([]byte, 2, MaxHeaderSize-2)
+
+ // Prepare to hold first 2 bytes to choose size of next read.
+ _, err = io.ReadFull(r, bts)
+ if err != nil {
+ return
+ }
+
+ h.Fin = bts[0]&bit0 != 0
+ h.Rsv = (bts[0] & 0x70) >> 4
+ h.OpCode = OpCode(bts[0] & 0x0f)
+
+ var extra int
+
+ if bts[1]&bit0 != 0 {
+ h.Masked = true
+ extra += 4
+ }
+
+ length := bts[1] & 0x7f
+ switch {
+ case length < 126:
+ h.Length = int64(length)
+
+ case length == 126:
+ extra += 2
+
+ case length == 127:
+ extra += 8
+
+ default:
+ err = ErrHeaderLengthUnexpected
+ return
+ }
+
+ if extra == 0 {
+ return
+ }
+
+ // Increase len of bts to extra bytes need to read.
+ // Overwrite first 2 bytes that was read before.
+ bts = bts[:extra]
+ _, err = io.ReadFull(r, bts)
+ if err != nil {
+ return
+ }
+
+ switch {
+ case length == 126:
+ h.Length = int64(binary.BigEndian.Uint16(bts[:2]))
+ bts = bts[2:]
+
+ case length == 127:
+ if bts[0]&0x80 != 0 {
+ err = ErrHeaderLengthMSB
+ return
+ }
+ h.Length = int64(binary.BigEndian.Uint64(bts[:8]))
+ bts = bts[8:]
+ }
+
+ if h.Masked {
+ copy(h.Mask[:], bts)
+ }
+
+ return
+}
+
+// ReadFrame reads a frame from r.
+// It is not designed for high optimized use case cause it makes allocation
+// for frame.Header.Length size inside to read frame payload into.
+//
+// Note that ReadFrame does not unmask payload.
+func ReadFrame(r io.Reader) (f Frame, err error) {
+ f.Header, err = ReadHeader(r)
+ if err != nil {
+ return
+ }
+
+ if f.Header.Length > 0 {
+ // int(f.Header.Length) is safe here cause we have
+ // checked it for overflow above in ReadHeader.
+ f.Payload = make([]byte, int(f.Header.Length))
+ _, err = io.ReadFull(r, f.Payload)
+ }
+
+ return
+}
+
+// MustReadFrame is like ReadFrame but panics if frame can not be read.
+func MustReadFrame(r io.Reader) Frame {
+ f, err := ReadFrame(r)
+ if err != nil {
+ panic(err)
+ }
+ return f
+}
+
+// ParseCloseFrameData parses close frame status code and closure reason if any provided.
+// If there is no status code in the payload
+// the empty status code is returned (code.Empty()) with empty string as a reason.
+func ParseCloseFrameData(payload []byte) (code StatusCode, reason string) {
+ if len(payload) < 2 {
+ // We returning empty StatusCode here, preventing the situation
+ // when endpoint really sent code 1005 and we should return ProtocolError on that.
+ //
+ // In other words, we ignoring this rule [RFC6455:7.1.5]:
+ // If this Close control frame contains no status code, _The WebSocket
+ // Connection Close Code_ is considered to be 1005.
+ return
+ }
+ code = StatusCode(binary.BigEndian.Uint16(payload))
+ reason = string(payload[2:])
+ return
+}
+
+// ParseCloseFrameDataUnsafe is like ParseCloseFrameData except the thing
+// that it does not copies payload bytes into reason, but prepares unsafe cast.
+func ParseCloseFrameDataUnsafe(payload []byte) (code StatusCode, reason string) {
+ if len(payload) < 2 {
+ return
+ }
+ code = StatusCode(binary.BigEndian.Uint16(payload))
+ reason = btsToString(payload[2:])
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/server.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/server.go
new file mode 100644
index 0000000000..8c359be461
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/server.go
@@ -0,0 +1,663 @@
+package ws
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/gobwas/httphead"
+ "github.com/gobwas/pool/pbufio"
+)
+
+// Constants used by ConnUpgrader.
+const (
+ DefaultServerReadBufferSize = 4096
+ DefaultServerWriteBufferSize = 512
+)
+
+// Errors used by both client and server when preparing WebSocket handshake.
+var (
+ ErrHandshakeBadProtocol = RejectConnectionError(
+ RejectionStatus(http.StatusHTTPVersionNotSupported),
+ RejectionReason(fmt.Sprintf("handshake error: bad HTTP protocol version")),
+ )
+ ErrHandshakeBadMethod = RejectConnectionError(
+ RejectionStatus(http.StatusMethodNotAllowed),
+ RejectionReason(fmt.Sprintf("handshake error: bad HTTP request method")),
+ )
+ ErrHandshakeBadHost = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerHost)),
+ )
+ ErrHandshakeBadUpgrade = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerUpgrade)),
+ )
+ ErrHandshakeBadConnection = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerConnection)),
+ )
+ ErrHandshakeBadSecAccept = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecAccept)),
+ )
+ ErrHandshakeBadSecKey = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecKey)),
+ )
+ ErrHandshakeBadSecVersion = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)),
+ )
+)
+
+// ErrMalformedResponse is returned by Dialer to indicate that server response
+// can not be parsed.
+var ErrMalformedResponse = fmt.Errorf("malformed HTTP response")
+
+// ErrMalformedRequest is returned when HTTP request can not be parsed.
+var ErrMalformedRequest = RejectConnectionError(
+ RejectionStatus(http.StatusBadRequest),
+ RejectionReason("malformed HTTP request"),
+)
+
+// ErrHandshakeUpgradeRequired is returned by Upgrader to indicate that
+// connection is rejected because given WebSocket version is malformed.
+//
+// According to RFC6455:
+// If this version does not match a version understood by the server, the
+// server MUST abort the WebSocket handshake described in this section and
+// instead send an appropriate HTTP error code (such as 426 Upgrade Required)
+// and a |Sec-WebSocket-Version| header field indicating the version(s) the
+// server is capable of understanding.
+var ErrHandshakeUpgradeRequired = RejectConnectionError(
+ RejectionStatus(http.StatusUpgradeRequired),
+ RejectionHeader(HandshakeHeaderString(headerSecVersion+": 13\r\n")),
+ RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)),
+)
+
+// ErrNotHijacker is an error returned when http.ResponseWriter does not
+// implement http.Hijacker interface.
+var ErrNotHijacker = RejectConnectionError(
+ RejectionStatus(http.StatusInternalServerError),
+ RejectionReason("given http.ResponseWriter is not a http.Hijacker"),
+)
+
+// DefaultHTTPUpgrader is an HTTPUpgrader that holds no options and is used by
+// UpgradeHTTP function.
+var DefaultHTTPUpgrader HTTPUpgrader
+
+// UpgradeHTTP is like HTTPUpgrader{}.Upgrade().
+func UpgradeHTTP(r *http.Request, w http.ResponseWriter) (net.Conn, *bufio.ReadWriter, Handshake, error) {
+ return DefaultHTTPUpgrader.Upgrade(r, w)
+}
+
+// DefaultUpgrader is an Upgrader that holds no options and is used by Upgrade
+// function.
+var DefaultUpgrader Upgrader
+
+// Upgrade is like Upgrader{}.Upgrade().
+func Upgrade(conn io.ReadWriter) (Handshake, error) {
+ return DefaultUpgrader.Upgrade(conn)
+}
+
+// HTTPUpgrader contains options for upgrading connection to websocket from
+// net/http Handler arguments.
+type HTTPUpgrader struct {
+ // Timeout is the maximum amount of time an Upgrade() will spent while
+ // writing handshake response.
+ //
+ // The default is no timeout.
+ Timeout time.Duration
+
+ // Header is an optional http.Header mapping that could be used to
+ // write additional headers to the handshake response.
+ //
+ // Note that if present, it will be written in any result of handshake.
+ Header http.Header
+
+ // Protocol is the select function that is used to select subprotocol from
+ // list requested by client. If this field is set, then the first matched
+ // protocol is sent to a client as negotiated.
+ Protocol func(string) bool
+
+ // Extension is the select function that is used to select extensions from
+ // list requested by client. If this field is set, then the all matched
+ // extensions are sent to a client as negotiated.
+ //
+ // DEPRECATED. Use Negotiate instead.
+ Extension func(httphead.Option) bool
+
+ // Negotiate is the callback that is used to negotiate extensions from
+ // the client's offer. If this field is set, then the returned non-zero
+ // extensions are sent to the client as accepted extensions in the
+ // response.
+ //
+ // The argument is only valid until the Negotiate callback returns.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ Negotiate func(httphead.Option) (httphead.Option, error)
+}
+
+// Upgrade upgrades http connection to the websocket connection.
+//
+// It hijacks net.Conn from w and returns received net.Conn and
+// bufio.ReadWriter. On successful handshake it returns Handshake struct
+// describing handshake info.
+func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.Conn, rw *bufio.ReadWriter, hs Handshake, err error) {
+ // Hijack connection first to get the ability to write rejection errors the
+ // same way as in Upgrader.
+ hj, ok := w.(http.Hijacker)
+ if ok {
+ conn, rw, err = hj.Hijack()
+ } else {
+ err = ErrNotHijacker
+ }
+ if err != nil {
+ httpError(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // See https://tools.ietf.org/html/rfc6455#section-4.1
+ // The method of the request MUST be GET, and the HTTP version MUST be at least 1.1.
+ var nonce string
+ if r.Method != http.MethodGet {
+ err = ErrHandshakeBadMethod
+ } else if r.ProtoMajor < 1 || (r.ProtoMajor == 1 && r.ProtoMinor < 1) {
+ err = ErrHandshakeBadProtocol
+ } else if r.Host == "" {
+ err = ErrHandshakeBadHost
+ } else if u := httpGetHeader(r.Header, headerUpgradeCanonical); u != "websocket" && !strings.EqualFold(u, "websocket") {
+ err = ErrHandshakeBadUpgrade
+ } else if c := httpGetHeader(r.Header, headerConnectionCanonical); c != "Upgrade" && !strHasToken(c, "upgrade") {
+ err = ErrHandshakeBadConnection
+ } else if nonce = httpGetHeader(r.Header, headerSecKeyCanonical); len(nonce) != nonceSize {
+ err = ErrHandshakeBadSecKey
+ } else if v := httpGetHeader(r.Header, headerSecVersionCanonical); v != "13" {
+ // According to RFC6455:
+ //
+ // If this version does not match a version understood by the server,
+ // the server MUST abort the WebSocket handshake described in this
+ // section and instead send an appropriate HTTP error code (such as 426
+ // Upgrade Required) and a |Sec-WebSocket-Version| header field
+ // indicating the version(s) the server is capable of understanding.
+ //
+ // So we branching here cause empty or not present version does not
+ // meet the ABNF rules of RFC6455:
+ //
+ // version = DIGIT | (NZDIGIT DIGIT) |
+ // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT)
+ // ; Limited to 0-255 range, with no leading zeros
+ //
+ // That is, if version is really invalid – we sent 426 status, if it
+ // not present or empty – it is 400.
+ if v != "" {
+ err = ErrHandshakeUpgradeRequired
+ } else {
+ err = ErrHandshakeBadSecVersion
+ }
+ }
+ if check := u.Protocol; err == nil && check != nil {
+ ps := r.Header[headerSecProtocolCanonical]
+ for i := 0; i < len(ps) && err == nil && hs.Protocol == ""; i++ {
+ var ok bool
+ hs.Protocol, ok = strSelectProtocol(ps[i], check)
+ if !ok {
+ err = ErrMalformedRequest
+ }
+ }
+ }
+ if f := u.Negotiate; err == nil && f != nil {
+ for _, h := range r.Header[headerSecExtensionsCanonical] {
+ hs.Extensions, err = negotiateExtensions(strToBytes(h), hs.Extensions, f)
+ if err != nil {
+ break
+ }
+ }
+ }
+ // DEPRECATED path.
+ if check := u.Extension; err == nil && check != nil && u.Negotiate == nil {
+ xs := r.Header[headerSecExtensionsCanonical]
+ for i := 0; i < len(xs) && err == nil; i++ {
+ var ok bool
+ hs.Extensions, ok = btsSelectExtensions(strToBytes(xs[i]), hs.Extensions, check)
+ if !ok {
+ err = ErrMalformedRequest
+ }
+ }
+ }
+
+ // Clear deadlines set by server.
+ conn.SetDeadline(noDeadline)
+ if t := u.Timeout; t != 0 {
+ conn.SetWriteDeadline(time.Now().Add(t))
+ defer conn.SetWriteDeadline(noDeadline)
+ }
+
+ var header handshakeHeader
+ if h := u.Header; h != nil {
+ header[0] = HandshakeHeaderHTTP(h)
+ }
+ if err == nil {
+ httpWriteResponseUpgrade(rw.Writer, strToBytes(nonce), hs, header.WriteTo)
+ err = rw.Writer.Flush()
+ } else {
+ var code int
+ if rej, ok := err.(*rejectConnectionError); ok {
+ code = rej.code
+ header[1] = rej.header
+ }
+ if code == 0 {
+ code = http.StatusInternalServerError
+ }
+ httpWriteResponseError(rw.Writer, err, code, header.WriteTo)
+ // Do not store Flush() error to not override already existing one.
+ rw.Writer.Flush()
+ }
+ return
+}
+
+// Upgrader contains options for upgrading connection to websocket.
+type Upgrader struct {
+ // ReadBufferSize and WriteBufferSize is an I/O buffer sizes.
+ // They used to read and write http data while upgrading to WebSocket.
+ // Allocated buffers are pooled with sync.Pool to avoid extra allocations.
+ //
+ // If a size is zero then default value is used.
+ //
+ // Usually it is useful to set read buffer size bigger than write buffer
+ // size because incoming request could contain long header values, such as
+ // Cookie. Response, in other way, could be big only if user write multiple
+ // custom headers. Usually response takes less than 256 bytes.
+ ReadBufferSize, WriteBufferSize int
+
+ // Protocol is a select function that is used to select subprotocol
+ // from list requested by client. If this field is set, then the first matched
+ // protocol is sent to a client as negotiated.
+ //
+ // The argument is only valid until the callback returns.
+ Protocol func([]byte) bool
+
+ // ProtocolCustrom allow user to parse Sec-WebSocket-Protocol header manually.
+ // Note that returned bytes must be valid until Upgrade returns.
+ // If ProtocolCustom is set, it used instead of Protocol function.
+ ProtocolCustom func([]byte) (string, bool)
+
+ // Extension is a select function that is used to select extensions
+ // from list requested by client. If this field is set, then the all matched
+ // extensions are sent to a client as negotiated.
+ //
+ // Note that Extension may be called multiple times and implementations
+ // must track uniqueness of accepted extensions manually.
+ //
+ // The argument is only valid until the callback returns.
+ //
+ // According to the RFC6455 order of extensions passed by a client is
+ // significant. That is, returning true from this function means that no
+ // other extension with the same name should be checked because server
+ // accepted the most preferable extension right now:
+ // "Note that the order of extensions is significant. Any interactions between
+ // multiple extensions MAY be defined in the documents defining the extensions.
+ // In the absence of such definitions, the interpretation is that the header
+ // fields listed by the client in its request represent a preference of the
+ // header fields it wishes to use, with the first options listed being most
+ // preferable."
+ //
+ // DEPRECATED. Use Negotiate instead.
+ Extension func(httphead.Option) bool
+
+ // ExtensionCustom allow user to parse Sec-WebSocket-Extensions header
+ // manually.
+ //
+ // If ExtensionCustom() decides to accept received extension, it must
+ // append appropriate option to the given slice of httphead.Option.
+ // It returns results of append() to the given slice and a flag that
+ // reports whether given header value is wellformed or not.
+ //
+ // Note that ExtensionCustom may be called multiple times and
+ // implementations must track uniqueness of accepted extensions manually.
+ //
+ // Note that returned options should be valid until Upgrade returns.
+ // If ExtensionCustom is set, it used instead of Extension function.
+ ExtensionCustom func([]byte, []httphead.Option) ([]httphead.Option, bool)
+
+ // Negotiate is the callback that is used to negotiate extensions from
+ // the client's offer. If this field is set, then the returned non-zero
+ // extensions are sent to the client as accepted extensions in the
+ // response.
+ //
+ // The argument is only valid until the Negotiate callback returns.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ Negotiate func(httphead.Option) (httphead.Option, error)
+
+ // Header is an optional HandshakeHeader instance that could be used to
+ // write additional headers to the handshake response.
+ //
+ // It used instead of any key-value mappings to avoid allocations in user
+ // land.
+ //
+ // Note that if present, it will be written in any result of handshake.
+ Header HandshakeHeader
+
+ // OnRequest is a callback that will be called after request line
+ // successful parsing.
+ //
+ // The arguments are only valid until the callback returns.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ OnRequest func(uri []byte) error
+
+ // OnHost is a callback that will be called after "Host" header successful
+ // parsing.
+ //
+ // It is separated from OnHeader callback because the Host header must be
+ // present in each request since HTTP/1.1. Thus Host header is non-optional
+ // and required for every WebSocket handshake.
+ //
+ // The arguments are only valid until the callback returns.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ OnHost func(host []byte) error
+
+ // OnHeader is a callback that will be called after successful parsing of
+ // header, that is not used during WebSocket handshake procedure. That is,
+ // it will be called with non-websocket headers, which could be relevant
+ // for application-level logic.
+ //
+ // The arguments are only valid until the callback returns.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ OnHeader func(key, value []byte) error
+
+ // OnBeforeUpgrade is a callback that will be called before sending
+ // successful upgrade response.
+ //
+ // Setting OnBeforeUpgrade allows user to make final application-level
+ // checks and decide whether this connection is allowed to successfully
+ // upgrade to WebSocket.
+ //
+ // It must return non-nil either HandshakeHeader or error and never both.
+ //
+ // If returned error is non-nil then connection is rejected and response is
+ // sent with appropriate HTTP error code and body set to error message.
+ //
+ // RejectConnectionError could be used to get more control on response.
+ OnBeforeUpgrade func() (header HandshakeHeader, err error)
+}
+
+// Upgrade zero-copy upgrades connection to WebSocket. It interprets given conn
+// as connection with incoming HTTP Upgrade request.
+//
+// It is a caller responsibility to manage i/o timeouts on conn.
+//
+// Non-nil error means that request for the WebSocket upgrade is invalid or
+// malformed and usually connection should be closed.
+// Even when error is non-nil Upgrade will write appropriate response into
+// connection in compliance with RFC.
+func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) {
+ // headerSeen constants helps to report whether or not some header was seen
+ // during reading request bytes.
+ const (
+ headerSeenHost = 1 << iota
+ headerSeenUpgrade
+ headerSeenConnection
+ headerSeenSecVersion
+ headerSeenSecKey
+
+ // headerSeenAll is the value that we expect to receive at the end of
+ // headers read/parse loop.
+ headerSeenAll = 0 |
+ headerSeenHost |
+ headerSeenUpgrade |
+ headerSeenConnection |
+ headerSeenSecVersion |
+ headerSeenSecKey
+ )
+
+ // Prepare I/O buffers.
+ // TODO(gobwas): make it configurable.
+ br := pbufio.GetReader(conn,
+ nonZero(u.ReadBufferSize, DefaultServerReadBufferSize),
+ )
+ bw := pbufio.GetWriter(conn,
+ nonZero(u.WriteBufferSize, DefaultServerWriteBufferSize),
+ )
+ defer func() {
+ pbufio.PutReader(br)
+ pbufio.PutWriter(bw)
+ }()
+
+ // Read HTTP request line like "GET /ws HTTP/1.1".
+ rl, err := readLine(br)
+ if err != nil {
+ return
+ }
+ // Parse request line data like HTTP version, uri and method.
+ req, err := httpParseRequestLine(rl)
+ if err != nil {
+ return
+ }
+
+ // Prepare stack-based handshake header list.
+ header := handshakeHeader{
+ 0: u.Header,
+ }
+
+ // Parse and check HTTP request.
+ // As RFC6455 says:
+ // The client's opening handshake consists of the following parts. If the
+ // server, while reading the handshake, finds that the client did not
+ // send a handshake that matches the description below (note that as per
+ // [RFC2616], the order of the header fields is not important), including
+ // but not limited to any violations of the ABNF grammar specified for
+ // the components of the handshake, the server MUST stop processing the
+ // client's handshake and return an HTTP response with an appropriate
+ // error code (such as 400 Bad Request).
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-4.2.1
+
+ // An HTTP/1.1 or higher GET request, including a "Request-URI".
+ //
+ // Even if RFC says "1.1 or higher" without mentioning the part of the
+ // version, we apply it only to minor part.
+ switch {
+ case req.major != 1 || req.minor < 1:
+ // Abort processing the whole request because we do not even know how
+ // to actually parse it.
+ err = ErrHandshakeBadProtocol
+
+ case btsToString(req.method) != http.MethodGet:
+ err = ErrHandshakeBadMethod
+
+ default:
+ if onRequest := u.OnRequest; onRequest != nil {
+ err = onRequest(req.uri)
+ }
+ }
+ // Start headers read/parse loop.
+ var (
+ // headerSeen reports which header was seen by setting corresponding
+ // bit on.
+ headerSeen byte
+
+ nonce = make([]byte, nonceSize)
+ )
+ for err == nil {
+ line, e := readLine(br)
+ if e != nil {
+ return hs, e
+ }
+ if len(line) == 0 {
+ // Blank line, no more lines to read.
+ break
+ }
+
+ k, v, ok := httpParseHeaderLine(line)
+ if !ok {
+ err = ErrMalformedRequest
+ break
+ }
+
+ switch btsToString(k) {
+ case headerHostCanonical:
+ headerSeen |= headerSeenHost
+ if onHost := u.OnHost; onHost != nil {
+ err = onHost(v)
+ }
+
+ case headerUpgradeCanonical:
+ headerSeen |= headerSeenUpgrade
+ if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) {
+ err = ErrHandshakeBadUpgrade
+ }
+
+ case headerConnectionCanonical:
+ headerSeen |= headerSeenConnection
+ if !bytes.Equal(v, specHeaderValueConnection) && !btsHasToken(v, specHeaderValueConnectionLower) {
+ err = ErrHandshakeBadConnection
+ }
+
+ case headerSecVersionCanonical:
+ headerSeen |= headerSeenSecVersion
+ if !bytes.Equal(v, specHeaderValueSecVersion) {
+ err = ErrHandshakeUpgradeRequired
+ }
+
+ case headerSecKeyCanonical:
+ headerSeen |= headerSeenSecKey
+ if len(v) != nonceSize {
+ err = ErrHandshakeBadSecKey
+ } else {
+ copy(nonce[:], v)
+ }
+
+ case headerSecProtocolCanonical:
+ if custom, check := u.ProtocolCustom, u.Protocol; hs.Protocol == "" && (custom != nil || check != nil) {
+ var ok bool
+ if custom != nil {
+ hs.Protocol, ok = custom(v)
+ } else {
+ hs.Protocol, ok = btsSelectProtocol(v, check)
+ }
+ if !ok {
+ err = ErrMalformedRequest
+ }
+ }
+
+ case headerSecExtensionsCanonical:
+ if f := u.Negotiate; err == nil && f != nil {
+ hs.Extensions, err = negotiateExtensions(v, hs.Extensions, f)
+ }
+ // DEPRECATED path.
+ if custom, check := u.ExtensionCustom, u.Extension; u.Negotiate == nil && (custom != nil || check != nil) {
+ var ok bool
+ if custom != nil {
+ hs.Extensions, ok = custom(v, hs.Extensions)
+ } else {
+ hs.Extensions, ok = btsSelectExtensions(v, hs.Extensions, check)
+ }
+ if !ok {
+ err = ErrMalformedRequest
+ }
+ }
+
+ default:
+ if onHeader := u.OnHeader; onHeader != nil {
+ err = onHeader(k, v)
+ }
+ }
+ }
+ switch {
+ case err == nil && headerSeen != headerSeenAll:
+ switch {
+ case headerSeen&headerSeenHost == 0:
+ // As RFC2616 says:
+ // A client MUST include a Host header field in all HTTP/1.1
+ // request messages. If the requested URI does not include an
+ // Internet host name for the service being requested, then the
+ // Host header field MUST be given with an empty value. An
+ // HTTP/1.1 proxy MUST ensure that any request message it
+ // forwards does contain an appropriate Host header field that
+ // identifies the service being requested by the proxy. All
+ // Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad
+ // Request) status code to any HTTP/1.1 request message which
+ // lacks a Host header field.
+ err = ErrHandshakeBadHost
+ case headerSeen&headerSeenUpgrade == 0:
+ err = ErrHandshakeBadUpgrade
+ case headerSeen&headerSeenConnection == 0:
+ err = ErrHandshakeBadConnection
+ case headerSeen&headerSeenSecVersion == 0:
+ // In case of empty or not present version we do not send 426 status,
+ // because it does not meet the ABNF rules of RFC6455:
+ //
+ // version = DIGIT | (NZDIGIT DIGIT) |
+ // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT)
+ // ; Limited to 0-255 range, with no leading zeros
+ //
+ // That is, if version is really invalid – we sent 426 status as above, if it
+ // not present – it is 400.
+ err = ErrHandshakeBadSecVersion
+ case headerSeen&headerSeenSecKey == 0:
+ err = ErrHandshakeBadSecKey
+ default:
+ panic("unknown headers state")
+ }
+
+ case err == nil && u.OnBeforeUpgrade != nil:
+ header[1], err = u.OnBeforeUpgrade()
+ }
+ if err != nil {
+ var code int
+ if rej, ok := err.(*rejectConnectionError); ok {
+ code = rej.code
+ header[1] = rej.header
+ }
+ if code == 0 {
+ code = http.StatusInternalServerError
+ }
+ httpWriteResponseError(bw, err, code, header.WriteTo)
+ // Do not store Flush() error to not override already existing one.
+ bw.Flush()
+ return
+ }
+
+ httpWriteResponseUpgrade(bw, nonce, hs, header.WriteTo)
+ err = bw.Flush()
+
+ return
+}
+
+type handshakeHeader [2]HandshakeHeader
+
+func (hs handshakeHeader) WriteTo(w io.Writer) (n int64, err error) {
+ for i := 0; i < len(hs) && err == nil; i++ {
+ if h := hs[i]; h != nil {
+ var m int64
+ m, err = h.WriteTo(w)
+ n += m
+ }
+ }
+ return n, err
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/server_test.s b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/server_test.s
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/util.go
new file mode 100644
index 0000000000..67ad906e5d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/util.go
@@ -0,0 +1,214 @@
+package ws
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "reflect"
+ "unsafe"
+
+ "github.com/gobwas/httphead"
+)
+
+// SelectFromSlice creates accept function that could be used as Protocol/Extension
+// select during upgrade.
+func SelectFromSlice(accept []string) func(string) bool {
+ if len(accept) > 16 {
+ mp := make(map[string]struct{}, len(accept))
+ for _, p := range accept {
+ mp[p] = struct{}{}
+ }
+ return func(p string) bool {
+ _, ok := mp[p]
+ return ok
+ }
+ }
+ return func(p string) bool {
+ for _, ok := range accept {
+ if p == ok {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// SelectEqual creates accept function that could be used as Protocol/Extension
+// select during upgrade.
+func SelectEqual(v string) func(string) bool {
+ return func(p string) bool {
+ return v == p
+ }
+}
+
+func strToBytes(str string) (bts []byte) {
+ s := (*reflect.StringHeader)(unsafe.Pointer(&str))
+ b := (*reflect.SliceHeader)(unsafe.Pointer(&bts))
+ b.Data = s.Data
+ b.Len = s.Len
+ b.Cap = s.Len
+ return
+}
+
+func btsToString(bts []byte) (str string) {
+ return *(*string)(unsafe.Pointer(&bts))
+}
+
+// asciiToInt converts bytes to int.
+func asciiToInt(bts []byte) (ret int, err error) {
+ // ASCII numbers all start with the high-order bits 0011.
+ // If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those
+ // bits and interpret them directly as an integer.
+ var n int
+ if n = len(bts); n < 1 {
+ return 0, fmt.Errorf("converting empty bytes to int")
+ }
+ for i := 0; i < n; i++ {
+ if bts[i]&0xf0 != 0x30 {
+ return 0, fmt.Errorf("%s is not a numeric character", string(bts[i]))
+ }
+ ret += int(bts[i]&0xf) * pow(10, n-i-1)
+ }
+ return ret, nil
+}
+
+// pow for integers implementation.
+// See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3
+func pow(a, b int) int {
+ p := 1
+ for b > 0 {
+ if b&1 != 0 {
+ p *= a
+ }
+ b >>= 1
+ a *= a
+ }
+ return p
+}
+
+func bsplit3(bts []byte, sep byte) (b1, b2, b3 []byte) {
+ a := bytes.IndexByte(bts, sep)
+ b := bytes.IndexByte(bts[a+1:], sep)
+ if a == -1 || b == -1 {
+ return bts, nil, nil
+ }
+ b += a + 1
+ return bts[:a], bts[a+1 : b], bts[b+1:]
+}
+
+func btrim(bts []byte) []byte {
+ var i, j int
+ for i = 0; i < len(bts) && (bts[i] == ' ' || bts[i] == '\t'); {
+ i++
+ }
+ for j = len(bts); j > i && (bts[j-1] == ' ' || bts[j-1] == '\t'); {
+ j--
+ }
+ return bts[i:j]
+}
+
+func strHasToken(header, token string) (has bool) {
+ return btsHasToken(strToBytes(header), strToBytes(token))
+}
+
+func btsHasToken(header, token []byte) (has bool) {
+ httphead.ScanTokens(header, func(v []byte) bool {
+ has = bytes.EqualFold(v, token)
+ return !has
+ })
+ return
+}
+
+const (
+ toLower = 'a' - 'A' // for use with OR.
+ toUpper = ^byte(toLower) // for use with AND.
+ toLower8 = uint64(toLower) |
+ uint64(toLower)<<8 |
+ uint64(toLower)<<16 |
+ uint64(toLower)<<24 |
+ uint64(toLower)<<32 |
+ uint64(toLower)<<40 |
+ uint64(toLower)<<48 |
+ uint64(toLower)<<56
+)
+
+// Algorithm below is like standard textproto/CanonicalMIMEHeaderKey, except
+// that it operates with slice of bytes and modifies it inplace without copying.
+func canonicalizeHeaderKey(k []byte) {
+ upper := true
+ for i, c := range k {
+ if upper && 'a' <= c && c <= 'z' {
+ k[i] &= toUpper
+ } else if !upper && 'A' <= c && c <= 'Z' {
+ k[i] |= toLower
+ }
+ upper = c == '-'
+ }
+}
+
+// readLine reads line from br. It reads until '\n' and returns bytes without
+// '\n' or '\r\n' at the end.
+// It returns err if and only if line does not end in '\n'. Note that read
+// bytes returned in any case of error.
+//
+// It is much like the textproto/Reader.ReadLine() except the thing that it
+// returns raw bytes, instead of string. That is, it avoids copying bytes read
+// from br.
+//
+// textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be
+// safe with future I/O operations on br.
+//
+// We could control I/O operations on br and do not need to make additional
+// copy for safety.
+//
+// NOTE: it may return copied flag to notify that returned buffer is safe to
+// use.
+func readLine(br *bufio.Reader) ([]byte, error) {
+ var line []byte
+ for {
+ bts, err := br.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ // Copy bytes because next read will discard them.
+ line = append(line, bts...)
+ continue
+ }
+
+ // Avoid copy of single read.
+ if line == nil {
+ line = bts
+ } else {
+ line = append(line, bts...)
+ }
+
+ if err != nil {
+ return line, err
+ }
+
+ // Size of line is at least 1.
+ // In other case bufio.ReadSlice() returns error.
+ n := len(line)
+
+ // Cut '\n' or '\r\n'.
+ if n > 1 && line[n-2] == '\r' {
+ line = line[:n-2]
+ } else {
+ line = line[:n-1]
+ }
+
+ return line, nil
+ }
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func nonZero(a, b int) int {
+ if a != 0 {
+ return a
+ }
+ return b
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/write.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/write.go
new file mode 100644
index 0000000000..94557c6963
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/write.go
@@ -0,0 +1,104 @@
+package ws
+
+import (
+ "encoding/binary"
+ "io"
+)
+
+// Header size length bounds in bytes.
+const (
+ MaxHeaderSize = 14
+ MinHeaderSize = 2
+)
+
+const (
+ bit0 = 0x80
+ bit1 = 0x40
+ bit2 = 0x20
+ bit3 = 0x10
+ bit4 = 0x08
+ bit5 = 0x04
+ bit6 = 0x02
+ bit7 = 0x01
+
+ len7 = int64(125)
+ len16 = int64(^(uint16(0)))
+ len64 = int64(^(uint64(0)) >> 1)
+)
+
+// HeaderSize returns number of bytes that are needed to encode given header.
+// It returns -1 if header is malformed.
+func HeaderSize(h Header) (n int) {
+ switch {
+ case h.Length < 126:
+ n = 2
+ case h.Length <= len16:
+ n = 4
+ case h.Length <= len64:
+ n = 10
+ default:
+ return -1
+ }
+ if h.Masked {
+ n += len(h.Mask)
+ }
+ return n
+}
+
+// WriteHeader writes header binary representation into w.
+func WriteHeader(w io.Writer, h Header) error {
+ // Make slice of bytes with capacity 14 that could hold any header.
+ bts := make([]byte, MaxHeaderSize)
+
+ if h.Fin {
+ bts[0] |= bit0
+ }
+ bts[0] |= h.Rsv << 4
+ bts[0] |= byte(h.OpCode)
+
+ var n int
+ switch {
+ case h.Length <= len7:
+ bts[1] = byte(h.Length)
+ n = 2
+
+ case h.Length <= len16:
+ bts[1] = 126
+ binary.BigEndian.PutUint16(bts[2:4], uint16(h.Length))
+ n = 4
+
+ case h.Length <= len64:
+ bts[1] = 127
+ binary.BigEndian.PutUint64(bts[2:10], uint64(h.Length))
+ n = 10
+
+ default:
+ return ErrHeaderLengthUnexpected
+ }
+
+ if h.Masked {
+ bts[1] |= bit0
+ n += copy(bts[n:], h.Mask[:])
+ }
+
+ _, err := w.Write(bts[:n])
+
+ return err
+}
+
+// WriteFrame writes frame binary representation into w.
+func WriteFrame(w io.Writer, f Frame) error {
+ err := WriteHeader(w, f.Header)
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(f.Payload)
+ return err
+}
+
+// MustWriteFrame is like WriteFrame but panics if frame can not be read.
+func MustWriteFrame(w io.Writer, f Frame) {
+ if err := WriteFrame(w, f); err != nil {
+ panic(err)
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/cipher.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/cipher.go
new file mode 100644
index 0000000000..f234be7322
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/cipher.go
@@ -0,0 +1,72 @@
+package wsutil
+
+import (
+ "io"
+
+ "github.com/gobwas/pool/pbytes"
+ "github.com/gobwas/ws"
+)
+
+// CipherReader implements io.Reader that applies xor-cipher to the bytes read
+// from source.
+// It could help to unmask WebSocket frame payload on the fly.
+type CipherReader struct {
+ r io.Reader
+ mask [4]byte
+ pos int
+}
+
+// NewCipherReader creates xor-cipher reader from r with given mask.
+func NewCipherReader(r io.Reader, mask [4]byte) *CipherReader {
+ return &CipherReader{r, mask, 0}
+}
+
+// Reset resets CipherReader to read from r with given mask.
+func (c *CipherReader) Reset(r io.Reader, mask [4]byte) {
+ c.r = r
+ c.mask = mask
+ c.pos = 0
+}
+
+// Read implements io.Reader interface. It applies mask given during
+// initialization to every read byte.
+func (c *CipherReader) Read(p []byte) (n int, err error) {
+ n, err = c.r.Read(p)
+ ws.Cipher(p[:n], c.mask, c.pos)
+ c.pos += n
+ return
+}
+
+// CipherWriter implements io.Writer that applies xor-cipher to the bytes
+// written to the destination writer. It does not modify the original bytes.
+type CipherWriter struct {
+ w io.Writer
+ mask [4]byte
+ pos int
+}
+
+// NewCipherWriter creates xor-cipher writer to w with given mask.
+func NewCipherWriter(w io.Writer, mask [4]byte) *CipherWriter {
+ return &CipherWriter{w, mask, 0}
+}
+
+// Reset reset CipherWriter to write to w with given mask.
+func (c *CipherWriter) Reset(w io.Writer, mask [4]byte) {
+ c.w = w
+ c.mask = mask
+ c.pos = 0
+}
+
+// Write implements io.Writer interface. It applies masking during
+// initialization to every sent byte. It does not modify original slice.
+func (c *CipherWriter) Write(p []byte) (n int, err error) {
+ cp := pbytes.GetLen(len(p))
+ defer pbytes.Put(cp)
+
+ copy(cp, p)
+ ws.Cipher(cp, c.mask, c.pos)
+ n, err = c.w.Write(cp)
+ c.pos += n
+
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/dialer.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/dialer.go
new file mode 100644
index 0000000000..91c03d512b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/dialer.go
@@ -0,0 +1,146 @@
+package wsutil
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+
+ "github.com/gobwas/ws"
+)
+
+// DebugDialer is a wrapper around ws.Dialer. It tracks i/o of WebSocket
+// handshake. That is, it gives ability to receive copied HTTP request and
+// response bytes that made inside Dialer.Dial().
+//
+// Note that it must not be used in production applications that requires
+// Dial() to be efficient.
+type DebugDialer struct {
+ // Dialer contains WebSocket connection establishment options.
+ Dialer ws.Dialer
+
+ // OnRequest and OnResponse are the callbacks that will be called with the
+ // HTTP request and response respectively.
+ OnRequest, OnResponse func([]byte)
+}
+
+// Dial connects to the url host and upgrades connection to WebSocket. It makes
+// it by calling d.Dialer.Dial().
+func (d *DebugDialer) Dial(ctx context.Context, urlstr string) (conn net.Conn, br *bufio.Reader, hs ws.Handshake, err error) {
+ // Need to copy Dialer to prevent original object mutation.
+ dialer := d.Dialer
+ var (
+ reqBuf bytes.Buffer
+ resBuf bytes.Buffer
+
+ resContentLength int64
+ )
+ userWrap := dialer.WrapConn
+ dialer.WrapConn = func(c net.Conn) net.Conn {
+ if userWrap != nil {
+ c = userWrap(c)
+ }
+
+ // Save the pointer to the raw connection.
+ conn = c
+
+ var (
+ r io.Reader = conn
+ w io.Writer = conn
+ )
+ if d.OnResponse != nil {
+ r = &prefetchResponseReader{
+ source: conn,
+ buffer: &resBuf,
+ contentLength: &resContentLength,
+ }
+ }
+ if d.OnRequest != nil {
+ w = io.MultiWriter(conn, &reqBuf)
+ }
+ return rwConn{conn, r, w}
+ }
+
+ _, br, hs, err = dialer.Dial(ctx, urlstr)
+
+ if onRequest := d.OnRequest; onRequest != nil {
+ onRequest(reqBuf.Bytes())
+ }
+ if onResponse := d.OnResponse; onResponse != nil {
+ // We must split response inside buffered bytes from other received
+ // bytes from server.
+ p := resBuf.Bytes()
+ n := bytes.Index(p, headEnd)
+ h := n + len(headEnd) // Head end index.
+ n = h + int(resContentLength) // Body end index.
+
+ onResponse(p[:n])
+
+ if br != nil {
+ // If br is non-nil, then it mean two things. First is that
+ // handshake is OK and server has sent additional bytes – probably
+ // immediate sent frames (or weird but possible response body).
+ // Second, the bad one, is that br buffer's source is now rwConn
+ // instance from above WrapConn call. It is incorrect, so we must
+ // fix it.
+ var r io.Reader = conn
+ if len(p) > h {
+ // Buffer contains more than just HTTP headers bytes.
+ r = io.MultiReader(
+ bytes.NewReader(p[h:]),
+ conn,
+ )
+ }
+ br.Reset(r)
+ // Must make br.Buffered() to be non-zero.
+ br.Peek(len(p[h:]))
+ }
+ }
+
+ return conn, br, hs, err
+}
+
+type rwConn struct {
+ net.Conn
+
+ r io.Reader
+ w io.Writer
+}
+
+func (rwc rwConn) Read(p []byte) (int, error) {
+ return rwc.r.Read(p)
+}
+func (rwc rwConn) Write(p []byte) (int, error) {
+ return rwc.w.Write(p)
+}
+
+var headEnd = []byte("\r\n\r\n")
+
+type prefetchResponseReader struct {
+ source io.Reader // Original connection source.
+ reader io.Reader // Wrapped reader used to read from by clients.
+ buffer *bytes.Buffer
+
+ contentLength *int64
+}
+
+func (r *prefetchResponseReader) Read(p []byte) (int, error) {
+ if r.reader == nil {
+ resp, err := http.ReadResponse(bufio.NewReader(
+ io.TeeReader(r.source, r.buffer),
+ ), nil)
+ if err == nil {
+ *r.contentLength, _ = io.Copy(ioutil.Discard, resp.Body)
+ resp.Body.Close()
+ }
+ bts := r.buffer.Bytes()
+ r.reader = io.MultiReader(
+ bytes.NewReader(bts),
+ r.source,
+ )
+ }
+ return r.reader.Read(p)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/extenstion.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/extenstion.go
new file mode 100644
index 0000000000..8c2a4fd7df
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/extenstion.go
@@ -0,0 +1,29 @@
+package wsutil
+
+// RecvExtension is an interface for clearing fragment header RSV bits.
+type RecvExtension interface {
+ BitsRecv(seq int, rsv byte) (byte, error)
+}
+
+// RecvExtensionFunc is an adapter to allow the use of ordinary functions as
+// RecvExtension.
+type RecvExtensionFunc func(int, byte) (byte, error)
+
+// BitsRecv implements RecvExtension.
+func (fn RecvExtensionFunc) BitsRecv(seq int, rsv byte) (byte, error) {
+ return fn(seq, rsv)
+}
+
+// SendExtension is an interface for setting fragment header RSV bits.
+type SendExtension interface {
+ BitsSend(seq int, rsv byte) (byte, error)
+}
+
+// SendExtensionFunc is an adapter to allow the use of ordinary functions as
+// SendExtension.
+type SendExtensionFunc func(int, byte) (byte, error)
+
+// BitsSend implements SendExtension.
+func (fn SendExtensionFunc) BitsSend(seq int, rsv byte) (byte, error) {
+ return fn(seq, rsv)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/handler.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/handler.go
new file mode 100644
index 0000000000..abb7cb73d1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/handler.go
@@ -0,0 +1,219 @@
+package wsutil
+
+import (
+ "errors"
+ "io"
+ "io/ioutil"
+ "strconv"
+
+ "github.com/gobwas/pool/pbytes"
+ "github.com/gobwas/ws"
+)
+
+// ClosedError returned when peer has closed the connection with appropriate
+// code and a textual reason.
+type ClosedError struct {
+ Code ws.StatusCode
+ Reason string
+}
+
+// Error implements error interface.
+func (err ClosedError) Error() string {
+ return "ws closed: " + strconv.FormatUint(uint64(err.Code), 10) + " " + err.Reason
+}
+
+// ControlHandler contains logic of handling control frames.
+//
+// The intentional way to use it is to read the next frame header from the
+// connection, optionally check its validity via ws.CheckHeader() and if it is
+// not a ws.OpText of ws.OpBinary (or ws.OpContinuation) – pass it to Handle()
+// method.
+//
+// That is, passed header should be checked to get rid of unexpected errors.
+//
+// The Handle() method will read out all control frame payload (if any) and
+// write necessary bytes as a rfc compatible response.
+type ControlHandler struct {
+ Src io.Reader
+ Dst io.Writer
+ State ws.State
+
+ // DisableSrcCiphering disables unmasking payload data read from Src.
+ // It is useful when wsutil.Reader is used or when frame payload already
+ // pulled and ciphered out from the connection (and introduced by
+ // bytes.Reader, for example).
+ DisableSrcCiphering bool
+}
+
+// ErrNotControlFrame is returned by ControlHandler to indicate that given
+// header could not be handled.
+var ErrNotControlFrame = errors.New("not a control frame")
+
+// Handle handles control frames regarding to the c.State and writes responses
+// to the c.Dst when needed.
+//
+// It returns ErrNotControlFrame when given header is not of ws.OpClose,
+// ws.OpPing or ws.OpPong operation code.
+func (c ControlHandler) Handle(h ws.Header) error {
+ switch h.OpCode {
+ case ws.OpPing:
+ return c.HandlePing(h)
+ case ws.OpPong:
+ return c.HandlePong(h)
+ case ws.OpClose:
+ return c.HandleClose(h)
+ }
+ return ErrNotControlFrame
+}
+
+// HandlePing handles ping frame and writes specification compatible response
+// to the c.Dst.
+func (c ControlHandler) HandlePing(h ws.Header) error {
+ if h.Length == 0 {
+ // The most common case when ping is empty.
+ // Note that when sending masked frame the mask for empty payload is
+ // just four zero bytes.
+ return ws.WriteHeader(c.Dst, ws.Header{
+ Fin: true,
+ OpCode: ws.OpPong,
+ Masked: c.State.ClientSide(),
+ })
+ }
+
+ // In other way reply with Pong frame with copied payload.
+ p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{
+ Length: h.Length,
+ Masked: c.State.ClientSide(),
+ }))
+ defer pbytes.Put(p)
+
+ // Deal with ciphering i/o:
+ // Masking key is used to mask the "Payload data" defined in the same
+ // section as frame-payload-data, which includes "Extension data" and
+ // "Application data".
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-5.3
+ //
+ // NOTE: We prefer ControlWriter with preallocated buffer to
+ // ws.WriteHeader because it performs one syscall instead of two.
+ w := NewControlWriterBuffer(c.Dst, c.State, ws.OpPong, p)
+ r := c.Src
+ if c.State.ServerSide() && !c.DisableSrcCiphering {
+ r = NewCipherReader(r, h.Mask)
+ }
+
+ _, err := io.Copy(w, r)
+ if err == nil {
+ err = w.Flush()
+ }
+
+ return err
+}
+
+// HandlePong handles pong frame by discarding it.
+func (c ControlHandler) HandlePong(h ws.Header) error {
+ if h.Length == 0 {
+ return nil
+ }
+
+ buf := pbytes.GetLen(int(h.Length))
+ defer pbytes.Put(buf)
+
+ // Discard pong message according to the RFC6455:
+ // A Pong frame MAY be sent unsolicited. This serves as a
+ // unidirectional heartbeat. A response to an unsolicited Pong frame
+ // is not expected.
+ _, err := io.CopyBuffer(ioutil.Discard, c.Src, buf)
+
+ return err
+}
+
+// HandleClose handles close frame, makes protocol validity checks and writes
+// specification compatible response to the c.Dst.
+func (c ControlHandler) HandleClose(h ws.Header) error {
+ if h.Length == 0 {
+ err := ws.WriteHeader(c.Dst, ws.Header{
+ Fin: true,
+ OpCode: ws.OpClose,
+ Masked: c.State.ClientSide(),
+ })
+ if err != nil {
+ return err
+ }
+
+ // Due to RFC, we should interpret the code as no status code
+ // received:
+ // If this Close control frame contains no status code, _The WebSocket
+ // Connection Close Code_ is considered to be 1005.
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-7.1.5
+ return ClosedError{
+ Code: ws.StatusNoStatusRcvd,
+ }
+ }
+
+ // Prepare bytes both for reading reason and sending response.
+ p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{
+ Length: h.Length,
+ Masked: c.State.ClientSide(),
+ }))
+ defer pbytes.Put(p)
+
+ // Get the subslice to read the frame payload out.
+ subp := p[:h.Length]
+
+ r := c.Src
+ if c.State.ServerSide() && !c.DisableSrcCiphering {
+ r = NewCipherReader(r, h.Mask)
+ }
+ if _, err := io.ReadFull(r, subp); err != nil {
+ return err
+ }
+
+ code, reason := ws.ParseCloseFrameData(subp)
+ if err := ws.CheckCloseFrameData(code, reason); err != nil {
+ // Here we could not use the prepared bytes because there is no
+ // guarantee that it may fit our protocol error closure code and a
+ // reason.
+ c.closeWithProtocolError(err)
+ return err
+ }
+
+ // Deal with ciphering i/o:
+ // Masking key is used to mask the "Payload data" defined in the same
+ // section as frame-payload-data, which includes "Extension data" and
+ // "Application data".
+ //
+ // See https://tools.ietf.org/html/rfc6455#section-5.3
+ //
+ // NOTE: We prefer ControlWriter with preallocated buffer to
+ // ws.WriteHeader because it performs one syscall instead of two.
+ w := NewControlWriterBuffer(c.Dst, c.State, ws.OpClose, p)
+
+ // RFC6455#5.5.1:
+ // If an endpoint receives a Close frame and did not previously
+ // send a Close frame, the endpoint MUST send a Close frame in
+ // response. (When sending a Close frame in response, the endpoint
+ // typically echoes the status code it received.)
+ _, err := w.Write(p[:2])
+ if err != nil {
+ return err
+ }
+ if err = w.Flush(); err != nil {
+ return err
+ }
+ return ClosedError{
+ Code: code,
+ Reason: reason,
+ }
+}
+
+func (c ControlHandler) closeWithProtocolError(reason error) error {
+ f := ws.NewCloseFrame(ws.NewCloseFrameBody(
+ ws.StatusProtocolError, reason.Error(),
+ ))
+ if c.State.ClientSide() {
+ ws.MaskFrameInPlace(f)
+ }
+ return ws.WriteFrame(c.Dst, f)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/helper.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/helper.go
new file mode 100644
index 0000000000..411d68d7e1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/helper.go
@@ -0,0 +1,279 @@
+package wsutil
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+
+ "github.com/gobwas/ws"
+)
+
+// Message represents a message from peer, that could be presented in one or
+// more frames. That is, it contains payload of all message fragments and
+// operation code of initial frame for this message.
+type Message struct {
+ OpCode ws.OpCode
+ Payload []byte
+}
+
+// ReadMessage is a helper function that reads next message from r. It appends
+// received message(s) to the third argument and returns the result of it and
+// an error if some failure happened. That is, it probably could receive more
+// than one message when peer sending fragmented message in multiple frames and
+// want to send some control frame between fragments. Then returned slice will
+// contain those control frames at first, and then result of gluing fragments.
+//
+// TODO(gobwas): add DefaultReader with buffer size options.
+func ReadMessage(r io.Reader, s ws.State, m []Message) ([]Message, error) {
+ rd := Reader{
+ Source: r,
+ State: s,
+ CheckUTF8: true,
+ OnIntermediate: func(hdr ws.Header, src io.Reader) error {
+ bts, err := ioutil.ReadAll(src)
+ if err != nil {
+ return err
+ }
+ m = append(m, Message{hdr.OpCode, bts})
+ return nil
+ },
+ }
+ h, err := rd.NextFrame()
+ if err != nil {
+ return m, err
+ }
+ var p []byte
+ if h.Fin {
+ // No more frames will be read. Use fixed sized buffer to read payload.
+ p = make([]byte, h.Length)
+ // It is not possible to receive io.EOF here because Reader does not
+ // return EOF if frame payload was successfully fetched.
+ // Thus we consistent here with io.Reader behavior.
+ _, err = io.ReadFull(&rd, p)
+ } else {
+ // Frame is fragmented, thus use ioutil.ReadAll behavior.
+ var buf bytes.Buffer
+ _, err = buf.ReadFrom(&rd)
+ p = buf.Bytes()
+ }
+ if err != nil {
+ return m, err
+ }
+ return append(m, Message{h.OpCode, p}), nil
+}
+
+// ReadClientMessage reads next message from r, considering that caller
+// represents server side.
+// It is a shortcut for ReadMessage(r, ws.StateServerSide, m)
+func ReadClientMessage(r io.Reader, m []Message) ([]Message, error) {
+ return ReadMessage(r, ws.StateServerSide, m)
+}
+
+// ReadServerMessage reads next message from r, considering that caller
+// represents client side.
+// It is a shortcut for ReadMessage(r, ws.StateClientSide, m)
+func ReadServerMessage(r io.Reader, m []Message) ([]Message, error) {
+ return ReadMessage(r, ws.StateClientSide, m)
+}
+
+// ReadData is a helper function that reads next data (non-control) message
+// from rw.
+// It takes care on handling all control frames. It will write response on
+// control frames to the write part of rw. It blocks until some data frame
+// will be received.
+//
+// Note this may handle and write control frames into the writer part of a
+// given io.ReadWriter.
+func ReadData(rw io.ReadWriter, s ws.State) ([]byte, ws.OpCode, error) {
+ return readData(rw, s, ws.OpText|ws.OpBinary)
+}
+
+// ReadClientData reads next data message from rw, considering that caller
+// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
+//
+// Note this may handle and write control frames into the writer part of a
+// given io.ReadWriter.
+func ReadClientData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
+ return ReadData(rw, ws.StateServerSide)
+}
+
+// ReadClientText reads next text message from rw, considering that caller
+// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
+// It discards received binary messages.
+//
+// Note this may handle and write control frames into the writer part of a
+// given io.ReadWriter.
+func ReadClientText(rw io.ReadWriter) ([]byte, error) {
+ p, _, err := readData(rw, ws.StateServerSide, ws.OpText)
+ return p, err
+}
+
+// ReadClientBinary reads next binary message from rw, considering that caller
+// represents server side. It is a shortcut for ReadData(rw, ws.StateServerSide).
+// It discards received text messages.
+//
+// Note this may handle and write control frames into the writer part of a given
+// io.ReadWriter.
+func ReadClientBinary(rw io.ReadWriter) ([]byte, error) {
+ p, _, err := readData(rw, ws.StateServerSide, ws.OpBinary)
+ return p, err
+}
+
+// ReadServerData reads next data message from rw, considering that caller
+// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
+//
+// Note this may handle and write control frames into the writer part of a
+// given io.ReadWriter.
+func ReadServerData(rw io.ReadWriter) ([]byte, ws.OpCode, error) {
+ return ReadData(rw, ws.StateClientSide)
+}
+
+// ReadServerText reads next text message from rw, considering that caller
+// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
+// It discards received binary messages.
+//
+// Note this may handle and write control frames into the writer part of a given
+// io.ReadWriter.
+func ReadServerText(rw io.ReadWriter) ([]byte, error) {
+ p, _, err := readData(rw, ws.StateClientSide, ws.OpText)
+ return p, err
+}
+
+// ReadServerBinary reads next binary message from rw, considering that caller
+// represents client side. It is a shortcut for ReadData(rw, ws.StateClientSide).
+// It discards received text messages.
+//
+// Note this may handle and write control frames into the writer part of a
+// given io.ReadWriter.
+func ReadServerBinary(rw io.ReadWriter) ([]byte, error) {
+ p, _, err := readData(rw, ws.StateClientSide, ws.OpBinary)
+ return p, err
+}
+
+// WriteMessage is a helper function that writes message to the w. It
+// constructs single frame with given operation code and payload.
+// It uses given state to prepare side-dependent things, like cipher
+// payload bytes from client to server. It will not mutate p bytes if
+// cipher must be made.
+//
+// If you want to write message in fragmented frames, use Writer instead.
+func WriteMessage(w io.Writer, s ws.State, op ws.OpCode, p []byte) error {
+ return writeFrame(w, s, op, true, p)
+}
+
+// WriteServerMessage writes message to w, considering that caller
+// represents server side.
+func WriteServerMessage(w io.Writer, op ws.OpCode, p []byte) error {
+ return WriteMessage(w, ws.StateServerSide, op, p)
+}
+
+// WriteServerText is the same as WriteServerMessage with
+// ws.OpText.
+func WriteServerText(w io.Writer, p []byte) error {
+ return WriteServerMessage(w, ws.OpText, p)
+}
+
+// WriteServerBinary is the same as WriteServerMessage with
+// ws.OpBinary.
+func WriteServerBinary(w io.Writer, p []byte) error {
+ return WriteServerMessage(w, ws.OpBinary, p)
+}
+
+// WriteClientMessage writes message to w, considering that caller
+// represents client side.
+func WriteClientMessage(w io.Writer, op ws.OpCode, p []byte) error {
+ return WriteMessage(w, ws.StateClientSide, op, p)
+}
+
+// WriteClientText is the same as WriteClientMessage with
+// ws.OpText.
+func WriteClientText(w io.Writer, p []byte) error {
+ return WriteClientMessage(w, ws.OpText, p)
+}
+
+// WriteClientBinary is the same as WriteClientMessage with
+// ws.OpBinary.
+func WriteClientBinary(w io.Writer, p []byte) error {
+ return WriteClientMessage(w, ws.OpBinary, p)
+}
+
+// HandleClientControlMessage handles control frame from conn and writes
+// response when needed.
+//
+// It considers that caller represents server side.
+func HandleClientControlMessage(conn io.Writer, msg Message) error {
+ return HandleControlMessage(conn, ws.StateServerSide, msg)
+}
+
+// HandleServerControlMessage handles control frame from conn and writes
+// response when needed.
+//
+// It considers that caller represents client side.
+func HandleServerControlMessage(conn io.Writer, msg Message) error {
+ return HandleControlMessage(conn, ws.StateClientSide, msg)
+}
+
+// HandleControlMessage handles message which was read by ReadMessage()
+// functions.
+//
+// That is, it is expected, that payload is already unmasked and frame header
+// were checked by ws.CheckHeader() call.
+func HandleControlMessage(conn io.Writer, state ws.State, msg Message) error {
+ return (ControlHandler{
+ DisableSrcCiphering: true,
+ Src: bytes.NewReader(msg.Payload),
+ Dst: conn,
+ State: state,
+ }).Handle(ws.Header{
+ Length: int64(len(msg.Payload)),
+ OpCode: msg.OpCode,
+ Fin: true,
+ Masked: state.ServerSide(),
+ })
+}
+
+// ControlFrameHandler returns FrameHandlerFunc for handling control frames.
+// For more info see ControlHandler docs.
+func ControlFrameHandler(w io.Writer, state ws.State) FrameHandlerFunc {
+ return func(h ws.Header, r io.Reader) error {
+ return (ControlHandler{
+ DisableSrcCiphering: true,
+ Src: r,
+ Dst: w,
+ State: state,
+ }).Handle(h)
+ }
+}
+
+func readData(rw io.ReadWriter, s ws.State, want ws.OpCode) ([]byte, ws.OpCode, error) {
+ controlHandler := ControlFrameHandler(rw, s)
+ rd := Reader{
+ Source: rw,
+ State: s,
+ CheckUTF8: true,
+ SkipHeaderCheck: false,
+ OnIntermediate: controlHandler,
+ }
+ for {
+ hdr, err := rd.NextFrame()
+ if err != nil {
+ return nil, 0, err
+ }
+ if hdr.OpCode.IsControl() {
+ if err := controlHandler(hdr, &rd); err != nil {
+ return nil, 0, err
+ }
+ continue
+ }
+ if hdr.OpCode&want == 0 {
+ if err := rd.Discard(); err != nil {
+ return nil, 0, err
+ }
+ continue
+ }
+
+ bts, err := ioutil.ReadAll(&rd)
+
+ return bts, hdr.OpCode, err
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/reader.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/reader.go
new file mode 100644
index 0000000000..c19e0f2d7d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/reader.go
@@ -0,0 +1,280 @@
+package wsutil
+
+import (
+ "errors"
+ "io"
+ "io/ioutil"
+
+ "github.com/gobwas/ws"
+)
+
+// ErrNoFrameAdvance means that Reader's Read() method was called without
+// preceding NextFrame() call.
+var ErrNoFrameAdvance = errors.New("no frame advance")
+
+// FrameHandlerFunc handles parsed frame header and its body represented by
+// io.Reader.
+//
+// Note that reader represents already unmasked body.
+type FrameHandlerFunc func(ws.Header, io.Reader) error
+
+// Reader is a wrapper around source io.Reader which represents WebSocket
+// connection. It contains options for reading messages from source.
+//
+// Reader implements io.Reader, which Read() method reads payload of incoming
+// WebSocket frames. It also takes care on fragmented frames and possibly
+// intermediate control frames between them.
+//
+// Note that Reader's methods are not goroutine safe.
+type Reader struct {
+ Source io.Reader
+ State ws.State
+
+ // SkipHeaderCheck disables checking header bits to be RFC6455 compliant.
+ SkipHeaderCheck bool
+
+ // CheckUTF8 enables UTF-8 checks for text frames payload. If incoming
+ // bytes are not valid UTF-8 sequence, ErrInvalidUTF8 returned.
+ CheckUTF8 bool
+
+ // Extensions is a list of negotiated extensions for reader Source.
+ // It is used to meet the specs and clear appropriate bits in fragment
+ // header RSV segment.
+ Extensions []RecvExtension
+
+ // TODO(gobwas): add max frame size limit here.
+
+ OnContinuation FrameHandlerFunc
+ OnIntermediate FrameHandlerFunc
+
+ opCode ws.OpCode // Used to store message op code on fragmentation.
+ frame io.Reader // Used to as frame reader.
+ raw io.LimitedReader // Used to discard frames without cipher.
+ utf8 UTF8Reader // Used to check UTF8 sequences if CheckUTF8 is true.
+ fseq int // Fragment sequence in message counter.
+}
+
+// NewReader creates new frame reader that reads from r keeping given state to
+// make some protocol validity checks when it needed.
+func NewReader(r io.Reader, s ws.State) *Reader {
+ return &Reader{
+ Source: r,
+ State: s,
+ }
+}
+
+// NewClientSideReader is a helper function that calls NewReader with r and
+// ws.StateClientSide.
+func NewClientSideReader(r io.Reader) *Reader {
+ return NewReader(r, ws.StateClientSide)
+}
+
+// NewServerSideReader is a helper function that calls NewReader with r and
+// ws.StateServerSide.
+func NewServerSideReader(r io.Reader) *Reader {
+ return NewReader(r, ws.StateServerSide)
+}
+
+// Read implements io.Reader. It reads the next message payload into p.
+// It takes care on fragmented messages.
+//
+// The error is io.EOF only if all of message bytes were read.
+// If an io.EOF happens during reading some but not all the message bytes
+// Read() returns io.ErrUnexpectedEOF.
+//
+// The error is ErrNoFrameAdvance if no NextFrame() call was made before
+// reading next message bytes.
+func (r *Reader) Read(p []byte) (n int, err error) {
+ if r.frame == nil {
+ if !r.fragmented() {
+ // Every new Read() must be preceded by NextFrame() call.
+ return 0, ErrNoFrameAdvance
+ }
+ // Read next continuation or intermediate control frame.
+ _, err := r.NextFrame()
+ if err != nil {
+ return 0, err
+ }
+ if r.frame == nil {
+ // We handled intermediate control and now got nothing to read.
+ return 0, nil
+ }
+ }
+
+ n, err = r.frame.Read(p)
+ if err != nil && err != io.EOF {
+ return
+ }
+ if err == nil && r.raw.N != 0 {
+ return n, nil
+ }
+
+ // EOF condition (either err is io.EOF or r.raw.N is zero).
+ switch {
+ case r.raw.N != 0:
+ err = io.ErrUnexpectedEOF
+
+ case r.fragmented():
+ err = nil
+ r.resetFragment()
+
+ case r.CheckUTF8 && !r.utf8.Valid():
+ // NOTE: check utf8 only when full message received, since partial
+ // reads may be invalid.
+ n = r.utf8.Accepted()
+ err = ErrInvalidUTF8
+
+ default:
+ r.reset()
+ err = io.EOF
+ }
+
+ return
+}
+
+// Discard discards current message unread bytes.
+// It discards all frames of fragmented message.
+func (r *Reader) Discard() (err error) {
+ for {
+ _, err = io.Copy(ioutil.Discard, &r.raw)
+ if err != nil {
+ break
+ }
+ if !r.fragmented() {
+ break
+ }
+ if _, err = r.NextFrame(); err != nil {
+ break
+ }
+ }
+ r.reset()
+ return err
+}
+
+// NextFrame prepares r to read next message. It returns received frame header
+// and non-nil error on failure.
+//
+// Note that next NextFrame() call must be done after receiving or discarding
+// all current message bytes.
+func (r *Reader) NextFrame() (hdr ws.Header, err error) {
+ hdr, err = ws.ReadHeader(r.Source)
+ if err == io.EOF && r.fragmented() {
+ // If we are in fragmented state EOF means that is was totally
+ // unexpected.
+ //
+ // NOTE: This is necessary to prevent callers such that
+ // ioutil.ReadAll to receive some amount of bytes without an error.
+ // ReadAll() ignores an io.EOF error, thus caller may think that
+ // whole message fetched, but actually only part of it.
+ err = io.ErrUnexpectedEOF
+ }
+ if err == nil && !r.SkipHeaderCheck {
+ err = ws.CheckHeader(hdr, r.State)
+ }
+ if err != nil {
+ return hdr, err
+ }
+
+ // Save raw reader to use it on discarding frame without ciphering and
+ // other streaming checks.
+ r.raw = io.LimitedReader{
+ R: r.Source,
+ N: hdr.Length,
+ }
+
+ frame := io.Reader(&r.raw)
+ if hdr.Masked {
+ frame = NewCipherReader(frame, hdr.Mask)
+ }
+
+ for _, ext := range r.Extensions {
+ hdr.Rsv, err = ext.BitsRecv(r.fseq, hdr.Rsv)
+ if err != nil {
+ return hdr, err
+ }
+ }
+
+ if r.fragmented() {
+ if hdr.OpCode.IsControl() {
+ if cb := r.OnIntermediate; cb != nil {
+ err = cb(hdr, frame)
+ }
+ if err == nil {
+ // Ensure that src is empty.
+ _, err = io.Copy(ioutil.Discard, &r.raw)
+ }
+ return
+ }
+ } else {
+ r.opCode = hdr.OpCode
+ }
+ if r.CheckUTF8 && (hdr.OpCode == ws.OpText || (r.fragmented() && r.opCode == ws.OpText)) {
+ r.utf8.Source = frame
+ frame = &r.utf8
+ }
+
+ // Save reader with ciphering and other streaming checks.
+ r.frame = frame
+
+ if hdr.OpCode == ws.OpContinuation {
+ if cb := r.OnContinuation; cb != nil {
+ err = cb(hdr, frame)
+ }
+ }
+
+ if hdr.Fin {
+ r.State = r.State.Clear(ws.StateFragmented)
+ r.fseq = 0
+ } else {
+ r.State = r.State.Set(ws.StateFragmented)
+ r.fseq++
+ }
+
+ return
+}
+
+func (r *Reader) fragmented() bool {
+ return r.State.Fragmented()
+}
+
+func (r *Reader) resetFragment() {
+ r.raw = io.LimitedReader{}
+ r.frame = nil
+ // Reset source of the UTF8Reader, but not the state.
+ r.utf8.Source = nil
+}
+
+func (r *Reader) reset() {
+ r.raw = io.LimitedReader{}
+ r.frame = nil
+ r.utf8 = UTF8Reader{}
+ r.fseq = 0
+ r.opCode = 0
+}
+
+// NextReader prepares next message read from r. It returns header that
+// describes the message and io.Reader to read message's payload. It returns
+// non-nil error when it is not possible to read message's initial frame.
+//
+// Note that next NextReader() on the same r should be done after reading all
+// bytes from previously returned io.Reader. For more performant way to discard
+// message use Reader and its Discard() method.
+//
+// Note that it will not handle any "intermediate" frames, that possibly could
+// be received between text/binary continuation frames. That is, if peer sent
+// text/binary frame with fin flag "false", then it could send ping frame, and
+// eventually remaining part of text/binary frame with fin "true" – with
+// NextReader() the ping frame will be dropped without any notice. To handle
+// this rare, but possible situation (and if you do not know exactly which
+// frames peer could send), you could use Reader with OnIntermediate field set.
+func NextReader(r io.Reader, s ws.State) (ws.Header, io.Reader, error) {
+ rd := &Reader{
+ Source: r,
+ State: s,
+ }
+ header, err := rd.NextFrame()
+ if err != nil {
+ return header, nil, err
+ }
+ return header, rd, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/upgrader.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/upgrader.go
new file mode 100644
index 0000000000..2ed351e088
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/upgrader.go
@@ -0,0 +1,68 @@
+package wsutil
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/gobwas/ws"
+)
+
+// DebugUpgrader is a wrapper around ws.Upgrader. It tracks I/O of a
+// WebSocket handshake.
+//
+// Note that it must not be used in production applications that requires
+// Upgrade() to be efficient.
+type DebugUpgrader struct {
+ // Upgrader contains upgrade to WebSocket options.
+ Upgrader ws.Upgrader
+
+ // OnRequest and OnResponse are the callbacks that will be called with the
+ // HTTP request and response respectively.
+ OnRequest, OnResponse func([]byte)
+}
+
+// Upgrade calls Upgrade() on underlying ws.Upgrader and tracks I/O on conn.
+func (d *DebugUpgrader) Upgrade(conn io.ReadWriter) (hs ws.Handshake, err error) {
+ var (
+ // Take the Reader and Writer parts from conn to be probably replaced
+ // below.
+ r io.Reader = conn
+ w io.Writer = conn
+ )
+ if onRequest := d.OnRequest; onRequest != nil {
+ var buf bytes.Buffer
+ // First, we must read the entire request.
+ req, err := http.ReadRequest(bufio.NewReader(
+ io.TeeReader(conn, &buf),
+ ))
+ if err == nil {
+ // Fulfill the buffer with the response body.
+ io.Copy(ioutil.Discard, req.Body)
+ req.Body.Close()
+ }
+ onRequest(buf.Bytes())
+
+ r = io.MultiReader(
+ &buf, conn,
+ )
+ }
+
+ if onResponse := d.OnResponse; onResponse != nil {
+ var buf bytes.Buffer
+ // Intercept the response stream written by the Upgrade().
+ w = io.MultiWriter(
+ conn, &buf,
+ )
+ defer func() {
+ onResponse(buf.Bytes())
+ }()
+ }
+
+ return d.Upgrader.Upgrade(struct {
+ io.Reader
+ io.Writer
+ }{r, w})
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/utf8.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/utf8.go
new file mode 100644
index 0000000000..d877be0b1d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/utf8.go
@@ -0,0 +1,140 @@
+package wsutil
+
+import (
+ "fmt"
+ "io"
+)
+
+// ErrInvalidUTF8 is returned by UTF8 reader on invalid utf8 sequence.
+var ErrInvalidUTF8 = fmt.Errorf("invalid utf8")
+
+// UTF8Reader implements io.Reader that calculates utf8 validity state after
+// every read byte from Source.
+//
+// Note that in some cases client must call r.Valid() after all bytes are read
+// to ensure that all of them are valid utf8 sequences. That is, some io helper
+// functions such io.ReadAtLeast or io.ReadFull could discard the error
+// information returned by the reader when they receive all of requested bytes.
+// For example, the last read sequence is invalid and UTF8Reader returns number
+// of bytes read and an error. But helper function decides to discard received
+// error due to all requested bytes are completely read from the source.
+//
+// Another possible case is when some valid sequence become split by the read
+// bound. Then UTF8Reader can not make decision about validity of the last
+// sequence cause it is not fully read yet. And if the read stops, Valid() will
+// return false, even if Read() by itself dit not.
+type UTF8Reader struct {
+ Source io.Reader
+
+ accepted int
+
+ state uint32
+ codep uint32
+}
+
+// NewUTF8Reader creates utf8 reader that reads from r.
+func NewUTF8Reader(r io.Reader) *UTF8Reader {
+ return &UTF8Reader{
+ Source: r,
+ }
+}
+
+// Reset resets utf8 reader to read from r.
+func (u *UTF8Reader) Reset(r io.Reader) {
+ u.Source = r
+ u.state = 0
+ u.codep = 0
+}
+
+// Read implements io.Reader.
+func (u *UTF8Reader) Read(p []byte) (n int, err error) {
+ n, err = u.Source.Read(p)
+
+ accepted := 0
+ s, c := u.state, u.codep
+ for i := 0; i < n; i++ {
+ c, s = decode(s, c, p[i])
+ if s == utf8Reject {
+ u.state = s
+ return accepted, ErrInvalidUTF8
+ }
+ if s == utf8Accept {
+ accepted = i + 1
+ }
+ }
+ u.state, u.codep = s, c
+ u.accepted = accepted
+
+ return
+}
+
+// Valid checks current reader state. It returns true if all read bytes are
+// valid UTF-8 sequences, and false if not.
+func (u *UTF8Reader) Valid() bool {
+ return u.state == utf8Accept
+}
+
+// Accepted returns number of valid bytes in last Read().
+func (u *UTF8Reader) Accepted() int {
+ return u.accepted
+}
+
+// Below is port of UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+//
+// Copyright (c) 2008-2009 Bjoern Hoehrmann
+//
+// 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.
+
+const (
+ utf8Accept = 0
+ utf8Reject = 12
+)
+
+var utf8d = [...]byte{
+ // The first part of the table maps bytes to character classes that
+ // to reduce the size of the transition table and create bitmasks.
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+
+ // The second part is a transition table that maps a combination
+ // of a state of the automaton and a character class to a state.
+ 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12,
+ 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+}
+
+func decode(state, codep uint32, b byte) (uint32, uint32) {
+ t := uint32(utf8d[b])
+
+ if state != utf8Accept {
+ codep = (uint32(b) & 0x3f) | (codep << 6)
+ } else {
+ codep = (0xff >> t) & uint32(b)
+ }
+
+ return codep, uint32(utf8d[256+state+t])
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/writer.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/writer.go
new file mode 100644
index 0000000000..ae3c1f201e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/writer.go
@@ -0,0 +1,572 @@
+package wsutil
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/gobwas/pool"
+ "github.com/gobwas/pool/pbytes"
+ "github.com/gobwas/ws"
+)
+
+// DefaultWriteBuffer contains size of Writer's default buffer. It used by
+// Writer constructor functions.
+var DefaultWriteBuffer = 4096
+
+var (
+ // ErrNotEmpty is returned by Writer.WriteThrough() to indicate that buffer is
+ // not empty and write through could not be done. That is, caller should call
+ // Writer.FlushFragment() to make buffer empty.
+ ErrNotEmpty = fmt.Errorf("writer not empty")
+
+ // ErrControlOverflow is returned by ControlWriter.Write() to indicate that
+ // no more data could be written to the underlying io.Writer because
+ // MaxControlFramePayloadSize limit is reached.
+ ErrControlOverflow = fmt.Errorf("control frame payload overflow")
+)
+
+// Constants which are represent frame length ranges.
+const (
+ len7 = int64(125) // 126 and 127 are reserved values
+ len16 = int64(^uint16(0))
+ len64 = int64((^uint64(0)) >> 1)
+)
+
+// ControlWriter is a wrapper around Writer that contains some guards for
+// buffered writes of control frames.
+type ControlWriter struct {
+ w *Writer
+ limit int
+ n int
+}
+
+// NewControlWriter contains ControlWriter with Writer inside whose buffer size
+// is at most ws.MaxControlFramePayloadSize + ws.MaxHeaderSize.
+func NewControlWriter(dest io.Writer, state ws.State, op ws.OpCode) *ControlWriter {
+ return &ControlWriter{
+ w: NewWriterSize(dest, state, op, ws.MaxControlFramePayloadSize),
+ limit: ws.MaxControlFramePayloadSize,
+ }
+}
+
+// NewControlWriterBuffer returns a new ControlWriter with buf as a buffer.
+//
+// Note that it reserves x bytes of buf for header data, where x could be
+// ws.MinHeaderSize or ws.MinHeaderSize+4 (depending on state). At most
+// (ws.MaxControlFramePayloadSize + x) bytes of buf will be used.
+//
+// It panics if len(buf) <= ws.MinHeaderSize + x.
+func NewControlWriterBuffer(dest io.Writer, state ws.State, op ws.OpCode, buf []byte) *ControlWriter {
+ max := ws.MaxControlFramePayloadSize + headerSize(state, ws.MaxControlFramePayloadSize)
+ if len(buf) > max {
+ buf = buf[:max]
+ }
+
+ w := NewWriterBuffer(dest, state, op, buf)
+
+ return &ControlWriter{
+ w: w,
+ limit: len(w.buf),
+ }
+}
+
+// Write implements io.Writer. It writes to the underlying Writer until it
+// returns error or until ControlWriter write limit will be exceeded.
+func (c *ControlWriter) Write(p []byte) (n int, err error) {
+ if c.n+len(p) > c.limit {
+ return 0, ErrControlOverflow
+ }
+ return c.w.Write(p)
+}
+
+// Flush flushes all buffered data to the underlying io.Writer.
+func (c *ControlWriter) Flush() error {
+ return c.w.Flush()
+}
+
+var writers = pool.New(128, 65536)
+
+// GetWriter tries to reuse Writer getting it from the pool.
+//
+// This function is intended for memory consumption optimizations, because
+// NewWriter*() functions make allocations for inner buffer.
+//
+// Note the it ceils n to the power of two.
+//
+// If you have your own bytes buffer pool you could use NewWriterBuffer to use
+// pooled bytes in writer.
+func GetWriter(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer {
+ x, m := writers.Get(n)
+ if x != nil {
+ w := x.(*Writer)
+ w.Reset(dest, state, op)
+ return w
+ }
+ // NOTE: we use m instead of n, because m is an attempt to reuse w of such
+ // size in the future.
+ return NewWriterBufferSize(dest, state, op, m)
+}
+
+// PutWriter puts w for future reuse by GetWriter().
+func PutWriter(w *Writer) {
+ w.Reset(nil, 0, 0)
+ writers.Put(w, w.Size())
+}
+
+// Writer contains logic of buffering output data into a WebSocket fragments.
+// It is much the same as bufio.Writer, except the thing that it works with
+// WebSocket frames, not the raw data.
+//
+// Writer writes frames with specified OpCode.
+// It uses ws.State to decide whether the output frames must be masked.
+//
+// Note that it does not check control frame size or other RFC rules.
+// That is, it must be used with special care to write control frames without
+// violation of RFC. You could use ControlWriter that wraps Writer and contains
+// some guards for writing control frames.
+//
+// If an error occurs writing to a Writer, no more data will be accepted and
+// all subsequent writes will return the error.
+//
+// After all data has been written, the client should call the Flush() method
+// to guarantee all data has been forwarded to the underlying io.Writer.
+type Writer struct {
+ // dest specifies a destination of buffer flushes.
+ dest io.Writer
+
+ // op specifies the WebSocket operation code used in flushed frames.
+ op ws.OpCode
+
+ // state specifies the state of the Writer.
+ state ws.State
+
+ // extensions is a list of negotiated extensions for writer Dest.
+ // It is used to meet the specs and set appropriate bits in fragment
+ // header RSV segment.
+ extensions []SendExtension
+
+ // noFlush reports whether buffer must grow instead of being flushed.
+ noFlush bool
+
+ // Raw representation of the buffer, including reserved header bytes.
+ raw []byte
+
+ // Writeable part of buffer, without reserved header bytes.
+ // Resetting this to nil will not result in reallocation if raw is not nil.
+ // And vice versa: if buf is not nil, then Writer is assumed as ready and
+ // initialized.
+ buf []byte
+
+ // Buffered bytes counter.
+ n int
+
+ dirty bool
+ fseq int
+ err error
+}
+
+// NewWriter returns a new Writer whose buffer has the DefaultWriteBuffer size.
+func NewWriter(dest io.Writer, state ws.State, op ws.OpCode) *Writer {
+ return NewWriterBufferSize(dest, state, op, 0)
+}
+
+// NewWriterSize returns a new Writer whose buffer size is at most n + ws.MaxHeaderSize.
+// That is, output frames payload length could be up to n, except the case when
+// Write() is called on empty Writer with len(p) > n.
+//
+// If n <= 0 then the default buffer size is used as Writer's buffer size.
+func NewWriterSize(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer {
+ if n > 0 {
+ n += headerSize(state, n)
+ }
+ return NewWriterBufferSize(dest, state, op, n)
+}
+
+// NewWriterBufferSize returns a new Writer whose buffer size is equal to n.
+// If n <= ws.MinHeaderSize then the default buffer size is used.
+//
+// Note that Writer will reserve x bytes for header data, where x is in range
+// [ws.MinHeaderSize,ws.MaxHeaderSize]. That is, frames flushed by Writer
+// will not have payload length equal to n, except the case when Write() is
+// called on empty Writer with len(p) > n.
+func NewWriterBufferSize(dest io.Writer, state ws.State, op ws.OpCode, n int) *Writer {
+ if n <= ws.MinHeaderSize {
+ n = DefaultWriteBuffer
+ }
+ return NewWriterBuffer(dest, state, op, make([]byte, n))
+}
+
+// NewWriterBuffer returns a new Writer with buf as a buffer.
+//
+// Note that it reserves x bytes of buf for header data, where x is in range
+// [ws.MinHeaderSize,ws.MaxHeaderSize] (depending on state and buf size).
+//
+// You could use ws.HeaderSize() to calculate number of bytes needed to store
+// header data.
+//
+// It panics if len(buf) is too small to fit header and payload data.
+func NewWriterBuffer(dest io.Writer, state ws.State, op ws.OpCode, buf []byte) *Writer {
+ w := &Writer{
+ dest: dest,
+ state: state,
+ op: op,
+ raw: buf,
+ }
+ w.initBuf()
+ return w
+}
+
+func (w *Writer) initBuf() {
+ offset := reserve(w.state, len(w.raw))
+ if len(w.raw) <= offset {
+ panic("wsutil: writer buffer is too small")
+ }
+ w.buf = w.raw[offset:]
+}
+
+// Reset resets Writer as it was created by New() methods.
+// Note that Reset does reset extenstions and other options was set after
+// Writer initialization.
+func (w *Writer) Reset(dest io.Writer, state ws.State, op ws.OpCode) {
+ w.dest = dest
+ w.state = state
+ w.op = op
+
+ w.initBuf()
+
+ w.n = 0
+ w.dirty = false
+ w.fseq = 0
+ w.extensions = w.extensions[:0]
+ w.noFlush = false
+}
+
+// ResetOp is an quick version of Reset().
+// ResetOp does reset unwritten fragments and does not reset results of
+// SetExtensions() or DisableFlush() methods.
+func (w *Writer) ResetOp(op ws.OpCode) {
+ w.op = op
+ w.n = 0
+ w.dirty = false
+ w.fseq = 0
+}
+
+// SetExtensions adds xs as extenstions to be used during writes.
+func (w *Writer) SetExtensions(xs ...SendExtension) {
+ w.extensions = xs
+}
+
+// DisableFlush denies Writer to write fragments.
+func (w *Writer) DisableFlush() {
+ w.noFlush = true
+}
+
+// Size returns the size of the underlying buffer in bytes (not including
+// WebSocket header bytes).
+func (w *Writer) Size() int {
+ return len(w.buf)
+}
+
+// Available returns how many bytes are unused in the buffer.
+func (w *Writer) Available() int {
+ return len(w.buf) - w.n
+}
+
+// Buffered returns the number of bytes that have been written into the current
+// buffer.
+func (w *Writer) Buffered() int {
+ return w.n
+}
+
+// Write implements io.Writer.
+//
+// Note that even if the Writer was created to have N-sized buffer, Write()
+// with payload of N bytes will not fit into that buffer. Writer reserves some
+// space to fit WebSocket header data.
+func (w *Writer) Write(p []byte) (n int, err error) {
+ // Even empty p may make a sense.
+ w.dirty = true
+
+ var nn int
+ for len(p) > w.Available() && w.err == nil {
+ if w.noFlush {
+ w.Grow(len(p) - w.Available())
+ continue
+ }
+ if w.Buffered() == 0 {
+ // Large write, empty buffer. Write directly from p to avoid copy.
+ // Trade off here is that we make additional Write() to underlying
+ // io.Writer when writing frame header.
+ //
+ // On large buffers additional write is better than copying.
+ nn, _ = w.WriteThrough(p)
+ } else {
+ nn = copy(w.buf[w.n:], p)
+ w.n += nn
+ w.FlushFragment()
+ }
+ n += nn
+ p = p[nn:]
+ }
+ if w.err != nil {
+ return n, w.err
+ }
+ nn = copy(w.buf[w.n:], p)
+ w.n += nn
+ n += nn
+
+ // Even if w.Available() == 0 we will not flush buffer preventively because
+ // this could bring unwanted fragmentation. That is, user could create
+ // buffer with size that fits exactly all further Write() call, and then
+ // call Flush(), excepting that single and not fragmented frame will be
+ // sent. With preemptive flush this case will produce two frames – last one
+ // will be empty and just to set fin = true.
+
+ return n, w.err
+}
+
+func ceilPowerOfTwo(n int) int {
+ n |= n >> 1
+ n |= n >> 2
+ n |= n >> 4
+ n |= n >> 8
+ n |= n >> 16
+ n |= n >> 32
+ n++
+ return n
+}
+
+func (w *Writer) Grow(n int) {
+ var (
+ offset = len(w.raw) - len(w.buf)
+ size = ceilPowerOfTwo(offset + w.n + n)
+ )
+ if size <= len(w.raw) {
+ panic("wsutil: buffer grow leads to its reduce")
+ }
+ p := make([]byte, size)
+ copy(p, w.raw[:offset+w.n])
+ w.raw = p
+ w.buf = w.raw[offset:]
+}
+
+// WriteThrough writes data bypassing the buffer.
+// Note that Writer's buffer must be empty before calling WriteThrough().
+func (w *Writer) WriteThrough(p []byte) (n int, err error) {
+ if w.err != nil {
+ return 0, w.err
+ }
+ if w.Buffered() != 0 {
+ return 0, ErrNotEmpty
+ }
+
+ var frame ws.Frame
+ frame.Header = ws.Header{
+ OpCode: w.opCode(),
+ Fin: false,
+ Length: int64(len(p)),
+ }
+ for _, ext := range w.extensions {
+ frame.Header.Rsv, err = ext.BitsSend(w.fseq, frame.Header.Rsv)
+ if err != nil {
+ return 0, err
+ }
+ }
+ if w.state.ClientSide() {
+ // Should copy bytes to prevent corruption of caller data.
+ payload := pbytes.GetLen(len(p))
+ defer pbytes.Put(payload)
+ copy(payload, p)
+
+ frame.Payload = payload
+ frame = ws.MaskFrameInPlace(frame)
+ } else {
+ frame.Payload = p
+ }
+
+ w.err = ws.WriteFrame(w.dest, frame)
+ if w.err == nil {
+ n = len(p)
+ }
+
+ w.dirty = true
+ w.fseq++
+
+ return n, w.err
+}
+
+// ReadFrom implements io.ReaderFrom.
+func (w *Writer) ReadFrom(src io.Reader) (n int64, err error) {
+ var nn int
+ for err == nil {
+ if w.Available() == 0 {
+ if w.noFlush {
+ w.Grow(w.Buffered()) // Twice bigger.
+ } else {
+ err = w.FlushFragment()
+ }
+ continue
+ }
+
+ // We copy the behavior of bufio.Writer here.
+ // Also, from the docs on io.ReaderFrom:
+ // ReadFrom reads data from r until EOF or error.
+ //
+ // See https://codereview.appspot.com/76400048/#ps1
+ const maxEmptyReads = 100
+ var nr int
+ for nr < maxEmptyReads {
+ nn, err = src.Read(w.buf[w.n:])
+ if nn != 0 || err != nil {
+ break
+ }
+ nr++
+ }
+ if nr == maxEmptyReads {
+ return n, io.ErrNoProgress
+ }
+
+ w.n += nn
+ n += int64(nn)
+ }
+ if err == io.EOF {
+ // NOTE: Do not flush preemptively.
+ // See the Write() sources for more info.
+ err = nil
+ w.dirty = true
+ }
+ return n, err
+}
+
+// Flush writes any buffered data to the underlying io.Writer.
+// It sends the frame with "fin" flag set to true.
+//
+// If no Write() or ReadFrom() was made, then Flush() does nothing.
+func (w *Writer) Flush() error {
+ if (!w.dirty && w.Buffered() == 0) || w.err != nil {
+ return w.err
+ }
+
+ w.err = w.flushFragment(true)
+ w.n = 0
+ w.dirty = false
+ w.fseq = 0
+
+ return w.err
+}
+
+// FlushFragment writes any buffered data to the underlying io.Writer.
+// It sends the frame with "fin" flag set to false.
+func (w *Writer) FlushFragment() error {
+ if w.Buffered() == 0 || w.err != nil {
+ return w.err
+ }
+
+ w.err = w.flushFragment(false)
+ w.n = 0
+ w.fseq++
+
+ return w.err
+}
+
+func (w *Writer) flushFragment(fin bool) (err error) {
+ var (
+ payload = w.buf[:w.n]
+ header = ws.Header{
+ OpCode: w.opCode(),
+ Fin: fin,
+ Length: int64(len(payload)),
+ }
+ )
+ for _, ext := range w.extensions {
+ header.Rsv, err = ext.BitsSend(w.fseq, header.Rsv)
+ if err != nil {
+ return err
+ }
+ }
+ if w.state.ClientSide() {
+ header.Masked = true
+ header.Mask = ws.NewMask()
+ ws.Cipher(payload, header.Mask, 0)
+ }
+ // Write header to the header segment of the raw buffer.
+ var (
+ offset = len(w.raw) - len(w.buf)
+ skip = offset - ws.HeaderSize(header)
+ )
+ buf := bytesWriter{
+ buf: w.raw[skip:offset],
+ }
+ if err := ws.WriteHeader(&buf, header); err != nil {
+ // Must never be reached.
+ panic("dump header error: " + err.Error())
+ }
+ _, err = w.dest.Write(w.raw[skip : offset+w.n])
+ return err
+}
+
+func (w *Writer) opCode() ws.OpCode {
+ if w.fseq > 0 {
+ return ws.OpContinuation
+ }
+ return w.op
+}
+
+var errNoSpace = fmt.Errorf("not enough buffer space")
+
+type bytesWriter struct {
+ buf []byte
+ pos int
+}
+
+func (w *bytesWriter) Write(p []byte) (int, error) {
+ n := copy(w.buf[w.pos:], p)
+ w.pos += n
+ if n != len(p) {
+ return n, errNoSpace
+ }
+ return n, nil
+}
+
+func writeFrame(w io.Writer, s ws.State, op ws.OpCode, fin bool, p []byte) error {
+ var frame ws.Frame
+ if s.ClientSide() {
+ // Should copy bytes to prevent corruption of caller data.
+ payload := pbytes.GetLen(len(p))
+ defer pbytes.Put(payload)
+
+ copy(payload, p)
+
+ frame = ws.NewFrame(op, fin, payload)
+ frame = ws.MaskFrameInPlace(frame)
+ } else {
+ frame = ws.NewFrame(op, fin, p)
+ }
+
+ return ws.WriteFrame(w, frame)
+}
+
+func reserve(state ws.State, n int) (offset int) {
+ var mask int
+ if state.ClientSide() {
+ mask = 4
+ }
+
+ switch {
+ case n <= int(len7)+mask+2:
+ return mask + 2
+ case n <= int(len16)+mask+4:
+ return mask + 4
+ default:
+ return mask + 10
+ }
+}
+
+// headerSize returns number of bytes needed to encode header of a frame with
+// given state and length.
+func headerSize(s ws.State, n int) int {
+ return ws.HeaderSize(ws.Header{
+ Length: int64(n),
+ Masked: s.ClientSide(),
+ })
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/wsutil.go b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/wsutil.go
new file mode 100644
index 0000000000..ffd43367de
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/gobwas/ws/wsutil/wsutil.go
@@ -0,0 +1,57 @@
+/*
+Package wsutil provides utilities for working with WebSocket protocol.
+
+Overview:
+
+ // Read masked text message from peer and check utf8 encoding.
+ header, err := ws.ReadHeader(conn)
+ if err != nil {
+ // handle err
+ }
+
+ // Prepare to read payload.
+ r := io.LimitReader(conn, header.Length)
+ r = wsutil.NewCipherReader(r, header.Mask)
+ r = wsutil.NewUTF8Reader(r)
+
+ payload, err := ioutil.ReadAll(r)
+ if err != nil {
+ // handle err
+ }
+
+You could get the same behavior using just `wsutil.Reader`:
+
+ r := wsutil.Reader{
+ Source: conn,
+ CheckUTF8: true,
+ }
+
+ payload, err := ioutil.ReadAll(r)
+ if err != nil {
+ // handle err
+ }
+
+Or even simplest:
+
+ payload, err := wsutil.ReadClientText(conn)
+ if err != nil {
+ // handle err
+ }
+
+Package is also exports tools for buffered writing:
+
+ // Create buffered writer, that will buffer output bytes and send them as
+ // 128-length fragments (with exception on large writes, see the doc).
+ writer := wsutil.NewWriterSize(conn, ws.StateServerSide, ws.OpText, 128)
+
+ _, err := io.CopyN(writer, rand.Reader, 100)
+ if err == nil {
+ err = writer.Flush()
+ }
+ if err != nil {
+ // handle error
+ }
+
+For more utils and helpers see the documentation.
+*/
+package wsutil
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE
new file mode 100644
index 0000000000..14127cd831
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE
@@ -0,0 +1,9 @@
+(The MIT License)
+
+Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de)
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
new file mode 100644
index 0000000000..195333e51d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
@@ -0,0 +1,41 @@
+# Windows Terminal Sequences
+
+This library allow for enabling Windows terminal color support for Go.
+
+See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details.
+
+## Usage
+
+```go
+import (
+ "syscall"
+
+ sequences "github.com/konsorten/go-windows-terminal-sequences"
+)
+
+func main() {
+ sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true)
+}
+
+```
+
+## Authors
+
+The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
+
+We thank all the authors who provided code to this library:
+
+* Felix Kollmann
+* Nicolas Perraut
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de)
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod
new file mode 100644
index 0000000000..716c613125
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod
@@ -0,0 +1 @@
+module github.com/konsorten/go-windows-terminal-sequences
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
new file mode 100644
index 0000000000..ef18d8f978
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
@@ -0,0 +1,36 @@
+// +build windows
+
+package sequences
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+var (
+ kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll")
+ setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode")
+)
+
+func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error {
+ const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4
+
+ var mode uint32
+ err := syscall.GetConsoleMode(syscall.Stdout, &mode)
+ if err != nil {
+ return err
+ }
+
+ if enable {
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ } else {
+ mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING
+ }
+
+ ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode))
+ if ret == 0 {
+ return err
+ }
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go
new file mode 100644
index 0000000000..df61a6f2f6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go
@@ -0,0 +1,11 @@
+// +build linux darwin
+
+package sequences
+
+import (
+ "fmt"
+)
+
+func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error {
+ return fmt.Errorf("windows only package")
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/.travis.yml
new file mode 100644
index 0000000000..98db8f060b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/.travis.yml
@@ -0,0 +1,9 @@
+language: go
+go:
+ - tip
+
+before_install:
+ - go get github.com/mattn/goveralls
+ - go get golang.org/x/tools/cmd/cover
+script:
+ - $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/LICENSE
new file mode 100644
index 0000000000..91b5cef30e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Yasuhiro Matsumoto
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/README.md
new file mode 100644
index 0000000000..56729a92ca
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/README.md
@@ -0,0 +1,48 @@
+# go-colorable
+
+[](http://godoc.org/github.com/mattn/go-colorable)
+[](https://travis-ci.org/mattn/go-colorable)
+[](https://coveralls.io/github/mattn/go-colorable?branch=master)
+[](https://goreportcard.com/report/mattn/go-colorable)
+
+Colorable writer for windows.
+
+For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
+This package is possible to handle escape sequence for ansi color on windows.
+
+## Too Bad!
+
+
+
+
+## So Good!
+
+
+
+## Usage
+
+```go
+logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
+logrus.SetOutput(colorable.NewColorableStdout())
+
+logrus.Info("succeeded")
+logrus.Warn("not correct")
+logrus.Error("something error")
+logrus.Fatal("panic")
+```
+
+You can compile above code on non-windows OSs.
+
+## Installation
+
+```
+$ go get github.com/mattn/go-colorable
+```
+
+# License
+
+MIT
+
+# Author
+
+Yasuhiro Matsumoto (a.k.a mattn)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_appengine.go
new file mode 100644
index 0000000000..0b0aef8370
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_appengine.go
@@ -0,0 +1,29 @@
+// +build appengine
+
+package colorable
+
+import (
+ "io"
+ "os"
+
+ _ "github.com/mattn/go-isatty"
+)
+
+// NewColorable returns new instance of Writer which handles escape sequence.
+func NewColorable(file *os.File) io.Writer {
+ if file == nil {
+ panic("nil passed instead of *os.File to NewColorable()")
+ }
+
+ return file
+}
+
+// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
+func NewColorableStdout() io.Writer {
+ return os.Stdout
+}
+
+// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
+func NewColorableStderr() io.Writer {
+ return os.Stderr
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_others.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_others.go
new file mode 100644
index 0000000000..3fb771dcca
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_others.go
@@ -0,0 +1,30 @@
+// +build !windows
+// +build !appengine
+
+package colorable
+
+import (
+ "io"
+ "os"
+
+ _ "github.com/mattn/go-isatty"
+)
+
+// NewColorable returns new instance of Writer which handles escape sequence.
+func NewColorable(file *os.File) io.Writer {
+ if file == nil {
+ panic("nil passed instead of *os.File to NewColorable()")
+ }
+
+ return file
+}
+
+// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
+func NewColorableStdout() io.Writer {
+ return os.Stdout
+}
+
+// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
+func NewColorableStderr() io.Writer {
+ return os.Stderr
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_windows.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_windows.go
new file mode 100644
index 0000000000..1bd628f25c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/colorable_windows.go
@@ -0,0 +1,1005 @@
+// +build windows
+// +build !appengine
+
+package colorable
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "os"
+ "strconv"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "github.com/mattn/go-isatty"
+)
+
+const (
+ foregroundBlue = 0x1
+ foregroundGreen = 0x2
+ foregroundRed = 0x4
+ foregroundIntensity = 0x8
+ foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
+ backgroundBlue = 0x10
+ backgroundGreen = 0x20
+ backgroundRed = 0x40
+ backgroundIntensity = 0x80
+ backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
+)
+
+const (
+ genericRead = 0x80000000
+ genericWrite = 0x40000000
+)
+
+const (
+ consoleTextmodeBuffer = 0x1
+)
+
+type wchar uint16
+type short int16
+type dword uint32
+type word uint16
+
+type coord struct {
+ x short
+ y short
+}
+
+type smallRect struct {
+ left short
+ top short
+ right short
+ bottom short
+}
+
+type consoleScreenBufferInfo struct {
+ size coord
+ cursorPosition coord
+ attributes word
+ window smallRect
+ maximumWindowSize coord
+}
+
+type consoleCursorInfo struct {
+ size dword
+ visible int32
+}
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
+ procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+ procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
+ procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
+ procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
+ procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
+ procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
+ procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
+ procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
+ procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
+)
+
+// Writer provides colorable Writer to the console
+type Writer struct {
+ out io.Writer
+ handle syscall.Handle
+ althandle syscall.Handle
+ oldattr word
+ oldpos coord
+ rest bytes.Buffer
+}
+
+// NewColorable returns new instance of Writer which handles escape sequence from File.
+func NewColorable(file *os.File) io.Writer {
+ if file == nil {
+ panic("nil passed instead of *os.File to NewColorable()")
+ }
+
+ if isatty.IsTerminal(file.Fd()) {
+ var csbi consoleScreenBufferInfo
+ handle := syscall.Handle(file.Fd())
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
+ }
+ return file
+}
+
+// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
+func NewColorableStdout() io.Writer {
+ return NewColorable(os.Stdout)
+}
+
+// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
+func NewColorableStderr() io.Writer {
+ return NewColorable(os.Stderr)
+}
+
+var color256 = map[int]int{
+ 0: 0x000000,
+ 1: 0x800000,
+ 2: 0x008000,
+ 3: 0x808000,
+ 4: 0x000080,
+ 5: 0x800080,
+ 6: 0x008080,
+ 7: 0xc0c0c0,
+ 8: 0x808080,
+ 9: 0xff0000,
+ 10: 0x00ff00,
+ 11: 0xffff00,
+ 12: 0x0000ff,
+ 13: 0xff00ff,
+ 14: 0x00ffff,
+ 15: 0xffffff,
+ 16: 0x000000,
+ 17: 0x00005f,
+ 18: 0x000087,
+ 19: 0x0000af,
+ 20: 0x0000d7,
+ 21: 0x0000ff,
+ 22: 0x005f00,
+ 23: 0x005f5f,
+ 24: 0x005f87,
+ 25: 0x005faf,
+ 26: 0x005fd7,
+ 27: 0x005fff,
+ 28: 0x008700,
+ 29: 0x00875f,
+ 30: 0x008787,
+ 31: 0x0087af,
+ 32: 0x0087d7,
+ 33: 0x0087ff,
+ 34: 0x00af00,
+ 35: 0x00af5f,
+ 36: 0x00af87,
+ 37: 0x00afaf,
+ 38: 0x00afd7,
+ 39: 0x00afff,
+ 40: 0x00d700,
+ 41: 0x00d75f,
+ 42: 0x00d787,
+ 43: 0x00d7af,
+ 44: 0x00d7d7,
+ 45: 0x00d7ff,
+ 46: 0x00ff00,
+ 47: 0x00ff5f,
+ 48: 0x00ff87,
+ 49: 0x00ffaf,
+ 50: 0x00ffd7,
+ 51: 0x00ffff,
+ 52: 0x5f0000,
+ 53: 0x5f005f,
+ 54: 0x5f0087,
+ 55: 0x5f00af,
+ 56: 0x5f00d7,
+ 57: 0x5f00ff,
+ 58: 0x5f5f00,
+ 59: 0x5f5f5f,
+ 60: 0x5f5f87,
+ 61: 0x5f5faf,
+ 62: 0x5f5fd7,
+ 63: 0x5f5fff,
+ 64: 0x5f8700,
+ 65: 0x5f875f,
+ 66: 0x5f8787,
+ 67: 0x5f87af,
+ 68: 0x5f87d7,
+ 69: 0x5f87ff,
+ 70: 0x5faf00,
+ 71: 0x5faf5f,
+ 72: 0x5faf87,
+ 73: 0x5fafaf,
+ 74: 0x5fafd7,
+ 75: 0x5fafff,
+ 76: 0x5fd700,
+ 77: 0x5fd75f,
+ 78: 0x5fd787,
+ 79: 0x5fd7af,
+ 80: 0x5fd7d7,
+ 81: 0x5fd7ff,
+ 82: 0x5fff00,
+ 83: 0x5fff5f,
+ 84: 0x5fff87,
+ 85: 0x5fffaf,
+ 86: 0x5fffd7,
+ 87: 0x5fffff,
+ 88: 0x870000,
+ 89: 0x87005f,
+ 90: 0x870087,
+ 91: 0x8700af,
+ 92: 0x8700d7,
+ 93: 0x8700ff,
+ 94: 0x875f00,
+ 95: 0x875f5f,
+ 96: 0x875f87,
+ 97: 0x875faf,
+ 98: 0x875fd7,
+ 99: 0x875fff,
+ 100: 0x878700,
+ 101: 0x87875f,
+ 102: 0x878787,
+ 103: 0x8787af,
+ 104: 0x8787d7,
+ 105: 0x8787ff,
+ 106: 0x87af00,
+ 107: 0x87af5f,
+ 108: 0x87af87,
+ 109: 0x87afaf,
+ 110: 0x87afd7,
+ 111: 0x87afff,
+ 112: 0x87d700,
+ 113: 0x87d75f,
+ 114: 0x87d787,
+ 115: 0x87d7af,
+ 116: 0x87d7d7,
+ 117: 0x87d7ff,
+ 118: 0x87ff00,
+ 119: 0x87ff5f,
+ 120: 0x87ff87,
+ 121: 0x87ffaf,
+ 122: 0x87ffd7,
+ 123: 0x87ffff,
+ 124: 0xaf0000,
+ 125: 0xaf005f,
+ 126: 0xaf0087,
+ 127: 0xaf00af,
+ 128: 0xaf00d7,
+ 129: 0xaf00ff,
+ 130: 0xaf5f00,
+ 131: 0xaf5f5f,
+ 132: 0xaf5f87,
+ 133: 0xaf5faf,
+ 134: 0xaf5fd7,
+ 135: 0xaf5fff,
+ 136: 0xaf8700,
+ 137: 0xaf875f,
+ 138: 0xaf8787,
+ 139: 0xaf87af,
+ 140: 0xaf87d7,
+ 141: 0xaf87ff,
+ 142: 0xafaf00,
+ 143: 0xafaf5f,
+ 144: 0xafaf87,
+ 145: 0xafafaf,
+ 146: 0xafafd7,
+ 147: 0xafafff,
+ 148: 0xafd700,
+ 149: 0xafd75f,
+ 150: 0xafd787,
+ 151: 0xafd7af,
+ 152: 0xafd7d7,
+ 153: 0xafd7ff,
+ 154: 0xafff00,
+ 155: 0xafff5f,
+ 156: 0xafff87,
+ 157: 0xafffaf,
+ 158: 0xafffd7,
+ 159: 0xafffff,
+ 160: 0xd70000,
+ 161: 0xd7005f,
+ 162: 0xd70087,
+ 163: 0xd700af,
+ 164: 0xd700d7,
+ 165: 0xd700ff,
+ 166: 0xd75f00,
+ 167: 0xd75f5f,
+ 168: 0xd75f87,
+ 169: 0xd75faf,
+ 170: 0xd75fd7,
+ 171: 0xd75fff,
+ 172: 0xd78700,
+ 173: 0xd7875f,
+ 174: 0xd78787,
+ 175: 0xd787af,
+ 176: 0xd787d7,
+ 177: 0xd787ff,
+ 178: 0xd7af00,
+ 179: 0xd7af5f,
+ 180: 0xd7af87,
+ 181: 0xd7afaf,
+ 182: 0xd7afd7,
+ 183: 0xd7afff,
+ 184: 0xd7d700,
+ 185: 0xd7d75f,
+ 186: 0xd7d787,
+ 187: 0xd7d7af,
+ 188: 0xd7d7d7,
+ 189: 0xd7d7ff,
+ 190: 0xd7ff00,
+ 191: 0xd7ff5f,
+ 192: 0xd7ff87,
+ 193: 0xd7ffaf,
+ 194: 0xd7ffd7,
+ 195: 0xd7ffff,
+ 196: 0xff0000,
+ 197: 0xff005f,
+ 198: 0xff0087,
+ 199: 0xff00af,
+ 200: 0xff00d7,
+ 201: 0xff00ff,
+ 202: 0xff5f00,
+ 203: 0xff5f5f,
+ 204: 0xff5f87,
+ 205: 0xff5faf,
+ 206: 0xff5fd7,
+ 207: 0xff5fff,
+ 208: 0xff8700,
+ 209: 0xff875f,
+ 210: 0xff8787,
+ 211: 0xff87af,
+ 212: 0xff87d7,
+ 213: 0xff87ff,
+ 214: 0xffaf00,
+ 215: 0xffaf5f,
+ 216: 0xffaf87,
+ 217: 0xffafaf,
+ 218: 0xffafd7,
+ 219: 0xffafff,
+ 220: 0xffd700,
+ 221: 0xffd75f,
+ 222: 0xffd787,
+ 223: 0xffd7af,
+ 224: 0xffd7d7,
+ 225: 0xffd7ff,
+ 226: 0xffff00,
+ 227: 0xffff5f,
+ 228: 0xffff87,
+ 229: 0xffffaf,
+ 230: 0xffffd7,
+ 231: 0xffffff,
+ 232: 0x080808,
+ 233: 0x121212,
+ 234: 0x1c1c1c,
+ 235: 0x262626,
+ 236: 0x303030,
+ 237: 0x3a3a3a,
+ 238: 0x444444,
+ 239: 0x4e4e4e,
+ 240: 0x585858,
+ 241: 0x626262,
+ 242: 0x6c6c6c,
+ 243: 0x767676,
+ 244: 0x808080,
+ 245: 0x8a8a8a,
+ 246: 0x949494,
+ 247: 0x9e9e9e,
+ 248: 0xa8a8a8,
+ 249: 0xb2b2b2,
+ 250: 0xbcbcbc,
+ 251: 0xc6c6c6,
+ 252: 0xd0d0d0,
+ 253: 0xdadada,
+ 254: 0xe4e4e4,
+ 255: 0xeeeeee,
+}
+
+// `\033]0;TITLESTR\007`
+func doTitleSequence(er *bytes.Reader) error {
+ var c byte
+ var err error
+
+ c, err = er.ReadByte()
+ if err != nil {
+ return err
+ }
+ if c != '0' && c != '2' {
+ return nil
+ }
+ c, err = er.ReadByte()
+ if err != nil {
+ return err
+ }
+ if c != ';' {
+ return nil
+ }
+ title := make([]byte, 0, 80)
+ for {
+ c, err = er.ReadByte()
+ if err != nil {
+ return err
+ }
+ if c == 0x07 || c == '\n' {
+ break
+ }
+ title = append(title, c)
+ }
+ if len(title) > 0 {
+ title8, err := syscall.UTF16PtrFromString(string(title))
+ if err == nil {
+ procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
+ }
+ }
+ return nil
+}
+
+// returns Atoi(s) unless s == "" in which case it returns def
+func atoiWithDefault(s string, def int) (int, error) {
+ if s == "" {
+ return def, nil
+ }
+ return strconv.Atoi(s)
+}
+
+// Write writes data on console
+func (w *Writer) Write(data []byte) (n int, err error) {
+ var csbi consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
+
+ handle := w.handle
+
+ var er *bytes.Reader
+ if w.rest.Len() > 0 {
+ var rest bytes.Buffer
+ w.rest.WriteTo(&rest)
+ w.rest.Reset()
+ rest.Write(data)
+ er = bytes.NewReader(rest.Bytes())
+ } else {
+ er = bytes.NewReader(data)
+ }
+ var bw [1]byte
+loop:
+ for {
+ c1, err := er.ReadByte()
+ if err != nil {
+ break loop
+ }
+ if c1 != 0x1b {
+ bw[0] = c1
+ w.out.Write(bw[:])
+ continue
+ }
+ c2, err := er.ReadByte()
+ if err != nil {
+ break loop
+ }
+
+ switch c2 {
+ case '>':
+ continue
+ case ']':
+ w.rest.WriteByte(c1)
+ w.rest.WriteByte(c2)
+ er.WriteTo(&w.rest)
+ if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 {
+ break loop
+ }
+ er = bytes.NewReader(w.rest.Bytes()[2:])
+ err := doTitleSequence(er)
+ if err != nil {
+ break loop
+ }
+ w.rest.Reset()
+ continue
+ // https://github.com/mattn/go-colorable/issues/27
+ case '7':
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ w.oldpos = csbi.cursorPosition
+ continue
+ case '8':
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
+ continue
+ case 0x5b:
+ // execute part after switch
+ default:
+ continue
+ }
+
+ w.rest.WriteByte(c1)
+ w.rest.WriteByte(c2)
+ er.WriteTo(&w.rest)
+
+ var buf bytes.Buffer
+ var m byte
+ for i, c := range w.rest.Bytes()[2:] {
+ if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
+ m = c
+ er = bytes.NewReader(w.rest.Bytes()[2+i+1:])
+ w.rest.Reset()
+ break
+ }
+ buf.Write([]byte(string(c)))
+ }
+ if m == 0 {
+ break loop
+ }
+
+ switch m {
+ case 'A':
+ n, err = atoiWithDefault(buf.String(), 1)
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.y -= short(n)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'B':
+ n, err = atoiWithDefault(buf.String(), 1)
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.y += short(n)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'C':
+ n, err = atoiWithDefault(buf.String(), 1)
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.x += short(n)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'D':
+ n, err = atoiWithDefault(buf.String(), 1)
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.x -= short(n)
+ if csbi.cursorPosition.x < 0 {
+ csbi.cursorPosition.x = 0
+ }
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'E':
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.x = 0
+ csbi.cursorPosition.y += short(n)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'F':
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.x = 0
+ csbi.cursorPosition.y -= short(n)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'G':
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ if n < 1 {
+ n = 1
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ csbi.cursorPosition.x = short(n - 1)
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'H', 'f':
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ if buf.Len() > 0 {
+ token := strings.Split(buf.String(), ";")
+ switch len(token) {
+ case 1:
+ n1, err := strconv.Atoi(token[0])
+ if err != nil {
+ continue
+ }
+ csbi.cursorPosition.y = short(n1 - 1)
+ case 2:
+ n1, err := strconv.Atoi(token[0])
+ if err != nil {
+ continue
+ }
+ n2, err := strconv.Atoi(token[1])
+ if err != nil {
+ continue
+ }
+ csbi.cursorPosition.x = short(n2 - 1)
+ csbi.cursorPosition.y = short(n1 - 1)
+ }
+ } else {
+ csbi.cursorPosition.y = 0
+ }
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
+ case 'J':
+ n := 0
+ if buf.Len() > 0 {
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ }
+ var count, written dword
+ var cursor coord
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ switch n {
+ case 0:
+ cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
+ count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
+ case 1:
+ cursor = coord{x: csbi.window.left, y: csbi.window.top}
+ count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x)
+ case 2:
+ cursor = coord{x: csbi.window.left, y: csbi.window.top}
+ count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
+ }
+ procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ case 'K':
+ n := 0
+ if buf.Len() > 0 {
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ var cursor coord
+ var count, written dword
+ switch n {
+ case 0:
+ cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
+ count = dword(csbi.size.x - csbi.cursorPosition.x)
+ case 1:
+ cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
+ count = dword(csbi.size.x - csbi.cursorPosition.x)
+ case 2:
+ cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
+ count = dword(csbi.size.x)
+ }
+ procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ case 'X':
+ n := 0
+ if buf.Len() > 0 {
+ n, err = strconv.Atoi(buf.String())
+ if err != nil {
+ continue
+ }
+ }
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ var cursor coord
+ var written dword
+ cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
+ procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
+ case 'm':
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ attr := csbi.attributes
+ cs := buf.String()
+ if cs == "" {
+ procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr))
+ continue
+ }
+ token := strings.Split(cs, ";")
+ for i := 0; i < len(token); i++ {
+ ns := token[i]
+ if n, err = strconv.Atoi(ns); err == nil {
+ switch {
+ case n == 0 || n == 100:
+ attr = w.oldattr
+ case 1 <= n && n <= 5:
+ attr |= foregroundIntensity
+ case n == 7:
+ attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
+ case n == 22 || n == 25:
+ attr |= foregroundIntensity
+ case n == 27:
+ attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
+ case 30 <= n && n <= 37:
+ attr &= backgroundMask
+ if (n-30)&1 != 0 {
+ attr |= foregroundRed
+ }
+ if (n-30)&2 != 0 {
+ attr |= foregroundGreen
+ }
+ if (n-30)&4 != 0 {
+ attr |= foregroundBlue
+ }
+ case n == 38: // set foreground color.
+ if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
+ if n256, err := strconv.Atoi(token[i+2]); err == nil {
+ if n256foreAttr == nil {
+ n256setup()
+ }
+ attr &= backgroundMask
+ attr |= n256foreAttr[n256]
+ i += 2
+ }
+ } else if len(token) == 5 && token[i+1] == "2" {
+ var r, g, b int
+ r, _ = strconv.Atoi(token[i+2])
+ g, _ = strconv.Atoi(token[i+3])
+ b, _ = strconv.Atoi(token[i+4])
+ i += 4
+ if r > 127 {
+ attr |= foregroundRed
+ }
+ if g > 127 {
+ attr |= foregroundGreen
+ }
+ if b > 127 {
+ attr |= foregroundBlue
+ }
+ } else {
+ attr = attr & (w.oldattr & backgroundMask)
+ }
+ case n == 39: // reset foreground color.
+ attr &= backgroundMask
+ attr |= w.oldattr & foregroundMask
+ case 40 <= n && n <= 47:
+ attr &= foregroundMask
+ if (n-40)&1 != 0 {
+ attr |= backgroundRed
+ }
+ if (n-40)&2 != 0 {
+ attr |= backgroundGreen
+ }
+ if (n-40)&4 != 0 {
+ attr |= backgroundBlue
+ }
+ case n == 48: // set background color.
+ if i < len(token)-2 && token[i+1] == "5" {
+ if n256, err := strconv.Atoi(token[i+2]); err == nil {
+ if n256backAttr == nil {
+ n256setup()
+ }
+ attr &= foregroundMask
+ attr |= n256backAttr[n256]
+ i += 2
+ }
+ } else if len(token) == 5 && token[i+1] == "2" {
+ var r, g, b int
+ r, _ = strconv.Atoi(token[i+2])
+ g, _ = strconv.Atoi(token[i+3])
+ b, _ = strconv.Atoi(token[i+4])
+ i += 4
+ if r > 127 {
+ attr |= backgroundRed
+ }
+ if g > 127 {
+ attr |= backgroundGreen
+ }
+ if b > 127 {
+ attr |= backgroundBlue
+ }
+ } else {
+ attr = attr & (w.oldattr & foregroundMask)
+ }
+ case n == 49: // reset foreground color.
+ attr &= foregroundMask
+ attr |= w.oldattr & backgroundMask
+ case 90 <= n && n <= 97:
+ attr = (attr & backgroundMask)
+ attr |= foregroundIntensity
+ if (n-90)&1 != 0 {
+ attr |= foregroundRed
+ }
+ if (n-90)&2 != 0 {
+ attr |= foregroundGreen
+ }
+ if (n-90)&4 != 0 {
+ attr |= foregroundBlue
+ }
+ case 100 <= n && n <= 107:
+ attr = (attr & foregroundMask)
+ attr |= backgroundIntensity
+ if (n-100)&1 != 0 {
+ attr |= backgroundRed
+ }
+ if (n-100)&2 != 0 {
+ attr |= backgroundGreen
+ }
+ if (n-100)&4 != 0 {
+ attr |= backgroundBlue
+ }
+ }
+ procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
+ }
+ }
+ case 'h':
+ var ci consoleCursorInfo
+ cs := buf.String()
+ if cs == "5>" {
+ procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ ci.visible = 0
+ procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ } else if cs == "?25" {
+ procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ ci.visible = 1
+ procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ } else if cs == "?1049" {
+ if w.althandle == 0 {
+ h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0)
+ w.althandle = syscall.Handle(h)
+ if w.althandle != 0 {
+ handle = w.althandle
+ }
+ }
+ }
+ case 'l':
+ var ci consoleCursorInfo
+ cs := buf.String()
+ if cs == "5>" {
+ procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ ci.visible = 1
+ procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ } else if cs == "?25" {
+ procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ ci.visible = 0
+ procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
+ } else if cs == "?1049" {
+ if w.althandle != 0 {
+ syscall.CloseHandle(w.althandle)
+ w.althandle = 0
+ handle = w.handle
+ }
+ }
+ case 's':
+ procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
+ w.oldpos = csbi.cursorPosition
+ case 'u':
+ procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
+ }
+ }
+
+ return len(data), nil
+}
+
+type consoleColor struct {
+ rgb int
+ red bool
+ green bool
+ blue bool
+ intensity bool
+}
+
+func (c consoleColor) foregroundAttr() (attr word) {
+ if c.red {
+ attr |= foregroundRed
+ }
+ if c.green {
+ attr |= foregroundGreen
+ }
+ if c.blue {
+ attr |= foregroundBlue
+ }
+ if c.intensity {
+ attr |= foregroundIntensity
+ }
+ return
+}
+
+func (c consoleColor) backgroundAttr() (attr word) {
+ if c.red {
+ attr |= backgroundRed
+ }
+ if c.green {
+ attr |= backgroundGreen
+ }
+ if c.blue {
+ attr |= backgroundBlue
+ }
+ if c.intensity {
+ attr |= backgroundIntensity
+ }
+ return
+}
+
+var color16 = []consoleColor{
+ {0x000000, false, false, false, false},
+ {0x000080, false, false, true, false},
+ {0x008000, false, true, false, false},
+ {0x008080, false, true, true, false},
+ {0x800000, true, false, false, false},
+ {0x800080, true, false, true, false},
+ {0x808000, true, true, false, false},
+ {0xc0c0c0, true, true, true, false},
+ {0x808080, false, false, false, true},
+ {0x0000ff, false, false, true, true},
+ {0x00ff00, false, true, false, true},
+ {0x00ffff, false, true, true, true},
+ {0xff0000, true, false, false, true},
+ {0xff00ff, true, false, true, true},
+ {0xffff00, true, true, false, true},
+ {0xffffff, true, true, true, true},
+}
+
+type hsv struct {
+ h, s, v float32
+}
+
+func (a hsv) dist(b hsv) float32 {
+ dh := a.h - b.h
+ switch {
+ case dh > 0.5:
+ dh = 1 - dh
+ case dh < -0.5:
+ dh = -1 - dh
+ }
+ ds := a.s - b.s
+ dv := a.v - b.v
+ return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
+}
+
+func toHSV(rgb int) hsv {
+ r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
+ float32((rgb&0x00FF00)>>8)/256.0,
+ float32(rgb&0x0000FF)/256.0
+ min, max := minmax3f(r, g, b)
+ h := max - min
+ if h > 0 {
+ if max == r {
+ h = (g - b) / h
+ if h < 0 {
+ h += 6
+ }
+ } else if max == g {
+ h = 2 + (b-r)/h
+ } else {
+ h = 4 + (r-g)/h
+ }
+ }
+ h /= 6.0
+ s := max - min
+ if max != 0 {
+ s /= max
+ }
+ v := max
+ return hsv{h: h, s: s, v: v}
+}
+
+type hsvTable []hsv
+
+func toHSVTable(rgbTable []consoleColor) hsvTable {
+ t := make(hsvTable, len(rgbTable))
+ for i, c := range rgbTable {
+ t[i] = toHSV(c.rgb)
+ }
+ return t
+}
+
+func (t hsvTable) find(rgb int) consoleColor {
+ hsv := toHSV(rgb)
+ n := 7
+ l := float32(5.0)
+ for i, p := range t {
+ d := hsv.dist(p)
+ if d < l {
+ l, n = d, i
+ }
+ }
+ return color16[n]
+}
+
+func minmax3f(a, b, c float32) (min, max float32) {
+ if a < b {
+ if b < c {
+ return a, c
+ } else if a < c {
+ return a, b
+ } else {
+ return c, b
+ }
+ } else {
+ if a < c {
+ return b, c
+ } else if b < c {
+ return b, a
+ } else {
+ return c, a
+ }
+ }
+}
+
+var n256foreAttr []word
+var n256backAttr []word
+
+func n256setup() {
+ n256foreAttr = make([]word, 256)
+ n256backAttr = make([]word, 256)
+ t := toHSVTable(color16)
+ for i, rgb := range color256 {
+ c := t.find(rgb)
+ n256foreAttr[i] = c.foregroundAttr()
+ n256backAttr[i] = c.backgroundAttr()
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.mod
new file mode 100644
index 0000000000..ef3ca9d4c3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.mod
@@ -0,0 +1,3 @@
+module github.com/mattn/go-colorable
+
+require github.com/mattn/go-isatty v0.0.8
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.sum b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.sum
new file mode 100644
index 0000000000..2c12960ec7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/go.sum
@@ -0,0 +1,4 @@
+github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/noncolorable.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/noncolorable.go
new file mode 100644
index 0000000000..95f2c6be25
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-colorable/noncolorable.go
@@ -0,0 +1,55 @@
+package colorable
+
+import (
+ "bytes"
+ "io"
+)
+
+// NonColorable holds writer but removes escape sequence.
+type NonColorable struct {
+ out io.Writer
+}
+
+// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
+func NewNonColorable(w io.Writer) io.Writer {
+ return &NonColorable{out: w}
+}
+
+// Write writes data on console
+func (w *NonColorable) Write(data []byte) (n int, err error) {
+ er := bytes.NewReader(data)
+ var bw [1]byte
+loop:
+ for {
+ c1, err := er.ReadByte()
+ if err != nil {
+ break loop
+ }
+ if c1 != 0x1b {
+ bw[0] = c1
+ w.out.Write(bw[:])
+ continue
+ }
+ c2, err := er.ReadByte()
+ if err != nil {
+ break loop
+ }
+ if c2 != 0x5b {
+ continue
+ }
+
+ var buf bytes.Buffer
+ for {
+ c, err := er.ReadByte()
+ if err != nil {
+ break loop
+ }
+ if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
+ break
+ }
+ buf.Write([]byte(string(c)))
+ }
+ }
+
+ return len(data), nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/.travis.yml
new file mode 100644
index 0000000000..5597e026dd
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+go:
+ - tip
+
+os:
+ - linux
+ - osx
+
+before_install:
+ - go get github.com/mattn/goveralls
+ - go get golang.org/x/tools/cmd/cover
+script:
+ - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/LICENSE
new file mode 100644
index 0000000000..65dc692b6b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) Yasuhiro MATSUMOTO
+
+MIT License (Expat)
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/README.md
new file mode 100644
index 0000000000..1e69004bb0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/README.md
@@ -0,0 +1,50 @@
+# go-isatty
+
+[](http://godoc.org/github.com/mattn/go-isatty)
+[](https://travis-ci.org/mattn/go-isatty)
+[](https://coveralls.io/github/mattn/go-isatty?branch=master)
+[](https://goreportcard.com/report/mattn/go-isatty)
+
+isatty for golang
+
+## Usage
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/mattn/go-isatty"
+ "os"
+)
+
+func main() {
+ if isatty.IsTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Terminal")
+ } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Cygwin/MSYS2 Terminal")
+ } else {
+ fmt.Println("Is Not Terminal")
+ }
+}
+```
+
+## Installation
+
+```
+$ go get github.com/mattn/go-isatty
+```
+
+## License
+
+MIT
+
+## Author
+
+Yasuhiro Matsumoto (a.k.a mattn)
+
+## Thanks
+
+* k-takata: base idea for IsCygwinTerminal
+
+ https://github.com/k-takata/go-iscygpty
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/doc.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/doc.go
new file mode 100644
index 0000000000..17d4f90ebc
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/doc.go
@@ -0,0 +1,2 @@
+// Package isatty implements interface to isatty
+package isatty
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.mod
new file mode 100644
index 0000000000..f310320c33
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.mod
@@ -0,0 +1,3 @@
+module github.com/mattn/go-isatty
+
+require golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.sum b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.sum
new file mode 100644
index 0000000000..426c8973c0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_android.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_android.go
new file mode 100644
index 0000000000..d3567cb5bf
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_android.go
@@ -0,0 +1,23 @@
+// +build android
+
+package isatty
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const ioctlReadTermios = syscall.TCGETS
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_bsd.go
new file mode 100644
index 0000000000..07e93039db
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_bsd.go
@@ -0,0 +1,24 @@
+// +build darwin freebsd openbsd netbsd dragonfly
+// +build !appengine
+
+package isatty
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_others.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_others.go
new file mode 100644
index 0000000000..ff714a3761
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_others.go
@@ -0,0 +1,15 @@
+// +build appengine js nacl
+
+package isatty
+
+// IsTerminal returns true if the file descriptor is terminal which
+// is always false on js and appengine classic which is a sandboxed PaaS.
+func IsTerminal(fd uintptr) bool {
+ return false
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_solaris.go
new file mode 100644
index 0000000000..bdd5c79a07
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_solaris.go
@@ -0,0 +1,22 @@
+// +build solaris
+// +build !appengine
+
+package isatty
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
+func IsTerminal(fd uintptr) bool {
+ var termio unix.Termio
+ err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
+ return err == nil
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
new file mode 100644
index 0000000000..453b025d0d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
@@ -0,0 +1,19 @@
+// +build linux aix
+// +build !appengine
+// +build !android
+
+package isatty
+
+import "golang.org/x/sys/unix"
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
+ return err == nil
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_windows.go b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_windows.go
new file mode 100644
index 0000000000..af51cbcaa4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mattn/go-isatty/isatty_windows.go
@@ -0,0 +1,94 @@
+// +build windows
+// +build !appengine
+
+package isatty
+
+import (
+ "strings"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+const (
+ fileNameInfo uintptr = 2
+ fileTypePipe = 3
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
+ procGetFileType = kernel32.NewProc("GetFileType")
+)
+
+func init() {
+ // Check if GetFileInformationByHandleEx is available.
+ if procGetFileInformationByHandleEx.Find() != nil {
+ procGetFileInformationByHandleEx = nil
+ }
+}
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
+
+// Check pipe name is used for cygwin/msys2 pty.
+// Cygwin/MSYS2 PTY has a name like:
+// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
+func isCygwinPipeName(name string) bool {
+ token := strings.Split(name, "-")
+ if len(token) < 5 {
+ return false
+ }
+
+ if token[0] != `\msys` && token[0] != `\cygwin` {
+ return false
+ }
+
+ if token[1] == "" {
+ return false
+ }
+
+ if !strings.HasPrefix(token[2], "pty") {
+ return false
+ }
+
+ if token[3] != `from` && token[3] != `to` {
+ return false
+ }
+
+ if token[4] != "master" {
+ return false
+ }
+
+ return true
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal.
+func IsCygwinTerminal(fd uintptr) bool {
+ if procGetFileInformationByHandleEx == nil {
+ return false
+ }
+
+ // Cygwin/msys's pty is a pipe.
+ ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
+ if ft != fileTypePipe || e != 0 {
+ return false
+ }
+
+ var buf [2 + syscall.MAX_PATH]uint16
+ r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
+ 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
+ uintptr(len(buf)*2), 0, 0)
+ if r == 0 || e != 0 {
+ return false
+ }
+
+ l := *(*uint32)(unsafe.Pointer(&buf))
+ return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/.gitignore
new file mode 100644
index 0000000000..9ed3b07cef
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/.gitignore
@@ -0,0 +1 @@
+*.test
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/LICENSE
new file mode 100644
index 0000000000..06ce0c3b51
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/LICENSE
@@ -0,0 +1,9 @@
+The MIT License (MIT)
+Copyright (c) 2013 Mario L. Gutierrez
+
+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.
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/README.md
new file mode 100644
index 0000000000..8f8e20b7e4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/README.md
@@ -0,0 +1,121 @@
+# ansi
+
+Package ansi is a small, fast library to create ANSI colored strings and codes.
+
+## Install
+
+Get it
+
+```sh
+go get -u github.com/mgutz/ansi
+```
+
+## Example
+
+```go
+import "github.com/mgutz/ansi"
+
+// colorize a string, SLOW
+msg := ansi.Color("foo", "red+b:white")
+
+// create a FAST closure function to avoid computation of ANSI code
+phosphorize := ansi.ColorFunc("green+h:black")
+msg = phosphorize("Bring back the 80s!")
+msg2 := phospohorize("Look, I'm a CRT!")
+
+// cache escape codes and build strings manually
+lime := ansi.ColorCode("green+h:black")
+reset := ansi.ColorCode("reset")
+
+fmt.Println(lime, "Bring back the 80s!", reset)
+```
+
+Other examples
+
+```go
+Color(s, "red") // red
+Color(s, "red+b") // red bold
+Color(s, "red+B") // red blinking
+Color(s, "red+u") // red underline
+Color(s, "red+bh") // red bold bright
+Color(s, "red:white") // red on white
+Color(s, "red+b:white+h") // red bold on white bright
+Color(s, "red+B:white+h") // red blink on white bright
+Color(s, "off") // turn off ansi codes
+```
+
+To view color combinations, from project directory in terminal.
+
+```sh
+go test
+```
+
+## Style format
+
+```go
+"foregroundColor+attributes:backgroundColor+attributes"
+```
+
+Colors
+
+* black
+* red
+* green
+* yellow
+* blue
+* magenta
+* cyan
+* white
+* 0...255 (256 colors)
+
+Foreground Attributes
+
+* B = Blink
+* b = bold
+* h = high intensity (bright)
+* i = inverse
+* s = strikethrough
+* u = underline
+
+Background Attributes
+
+* h = high intensity (bright)
+
+## Constants
+
+* ansi.Reset
+* ansi.DefaultBG
+* ansi.DefaultFG
+* ansi.Black
+* ansi.Red
+* ansi.Green
+* ansi.Yellow
+* ansi.Blue
+* ansi.Magenta
+* ansi.Cyan
+* ansi.White
+* ansi.LightBlack
+* ansi.LightRed
+* ansi.LightGreen
+* ansi.LightYellow
+* ansi.LightBlue
+* ansi.LightMagenta
+* ansi.LightCyan
+* ansi.LightWhite
+
+## References
+
+Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
+
+General [tips and formatting](http://misc.flogisoft.com/bash/tip_colors_and_formatting)
+
+What about support on Windows? Use [colorable by mattn](https://github.com/mattn/go-colorable).
+Ansi and colorable are used by [logxi](https://github.com/mgutz/logxi) to support logging in
+color on Windows.
+
+## MIT License
+
+Copyright (c) 2013 Mario Gutierrez mario@mgutz.com
+
+See the file LICENSE for copying permission.
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/ansi.go b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/ansi.go
new file mode 100644
index 0000000000..dc0413649e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/ansi.go
@@ -0,0 +1,285 @@
+package ansi
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+const (
+ black = iota
+ red
+ green
+ yellow
+ blue
+ magenta
+ cyan
+ white
+ defaultt = 9
+
+ normalIntensityFG = 30
+ highIntensityFG = 90
+ normalIntensityBG = 40
+ highIntensityBG = 100
+
+ start = "\033["
+ bold = "1;"
+ blink = "5;"
+ underline = "4;"
+ inverse = "7;"
+ strikethrough = "9;"
+
+ // Reset is the ANSI reset escape sequence
+ Reset = "\033[0m"
+ // DefaultBG is the default background
+ DefaultBG = "\033[49m"
+ // DefaultFG is the default foreground
+ DefaultFG = "\033[39m"
+)
+
+// Black FG
+var Black string
+
+// Red FG
+var Red string
+
+// Green FG
+var Green string
+
+// Yellow FG
+var Yellow string
+
+// Blue FG
+var Blue string
+
+// Magenta FG
+var Magenta string
+
+// Cyan FG
+var Cyan string
+
+// White FG
+var White string
+
+// LightBlack FG
+var LightBlack string
+
+// LightRed FG
+var LightRed string
+
+// LightGreen FG
+var LightGreen string
+
+// LightYellow FG
+var LightYellow string
+
+// LightBlue FG
+var LightBlue string
+
+// LightMagenta FG
+var LightMagenta string
+
+// LightCyan FG
+var LightCyan string
+
+// LightWhite FG
+var LightWhite string
+
+var (
+ plain = false
+ // Colors maps common color names to their ANSI color code.
+ Colors = map[string]int{
+ "black": black,
+ "red": red,
+ "green": green,
+ "yellow": yellow,
+ "blue": blue,
+ "magenta": magenta,
+ "cyan": cyan,
+ "white": white,
+ "default": defaultt,
+ }
+)
+
+func init() {
+ for i := 0; i < 256; i++ {
+ Colors[strconv.Itoa(i)] = i
+ }
+
+ Black = ColorCode("black")
+ Red = ColorCode("red")
+ Green = ColorCode("green")
+ Yellow = ColorCode("yellow")
+ Blue = ColorCode("blue")
+ Magenta = ColorCode("magenta")
+ Cyan = ColorCode("cyan")
+ White = ColorCode("white")
+ LightBlack = ColorCode("black+h")
+ LightRed = ColorCode("red+h")
+ LightGreen = ColorCode("green+h")
+ LightYellow = ColorCode("yellow+h")
+ LightBlue = ColorCode("blue+h")
+ LightMagenta = ColorCode("magenta+h")
+ LightCyan = ColorCode("cyan+h")
+ LightWhite = ColorCode("white+h")
+}
+
+// ColorCode returns the ANSI color color code for style.
+func ColorCode(style string) string {
+ return colorCode(style).String()
+}
+
+// Gets the ANSI color code for a style.
+func colorCode(style string) *bytes.Buffer {
+ buf := bytes.NewBufferString("")
+ if plain || style == "" {
+ return buf
+ }
+ if style == "reset" {
+ buf.WriteString(Reset)
+ return buf
+ } else if style == "off" {
+ return buf
+ }
+
+ foregroundBackground := strings.Split(style, ":")
+ foreground := strings.Split(foregroundBackground[0], "+")
+ fgKey := foreground[0]
+ fg := Colors[fgKey]
+ fgStyle := ""
+ if len(foreground) > 1 {
+ fgStyle = foreground[1]
+ }
+
+ bg, bgStyle := "", ""
+
+ if len(foregroundBackground) > 1 {
+ background := strings.Split(foregroundBackground[1], "+")
+ bg = background[0]
+ if len(background) > 1 {
+ bgStyle = background[1]
+ }
+ }
+
+ buf.WriteString(start)
+ base := normalIntensityFG
+ if len(fgStyle) > 0 {
+ if strings.Contains(fgStyle, "b") {
+ buf.WriteString(bold)
+ }
+ if strings.Contains(fgStyle, "B") {
+ buf.WriteString(blink)
+ }
+ if strings.Contains(fgStyle, "u") {
+ buf.WriteString(underline)
+ }
+ if strings.Contains(fgStyle, "i") {
+ buf.WriteString(inverse)
+ }
+ if strings.Contains(fgStyle, "s") {
+ buf.WriteString(strikethrough)
+ }
+ if strings.Contains(fgStyle, "h") {
+ base = highIntensityFG
+ }
+ }
+
+ // if 256-color
+ n, err := strconv.Atoi(fgKey)
+ if err == nil {
+ fmt.Fprintf(buf, "38;5;%d;", n)
+ } else {
+ fmt.Fprintf(buf, "%d;", base+fg)
+ }
+
+ base = normalIntensityBG
+ if len(bg) > 0 {
+ if strings.Contains(bgStyle, "h") {
+ base = highIntensityBG
+ }
+ // if 256-color
+ n, err := strconv.Atoi(bg)
+ if err == nil {
+ fmt.Fprintf(buf, "48;5;%d;", n)
+ } else {
+ fmt.Fprintf(buf, "%d;", base+Colors[bg])
+ }
+ }
+
+ // remove last ";"
+ buf.Truncate(buf.Len() - 1)
+ buf.WriteRune('m')
+ return buf
+}
+
+// Color colors a string based on the ANSI color code for style.
+func Color(s, style string) string {
+ if plain || len(style) < 1 {
+ return s
+ }
+ buf := colorCode(style)
+ buf.WriteString(s)
+ buf.WriteString(Reset)
+ return buf.String()
+}
+
+// ColorFunc creates a closure to avoid computation ANSI color code.
+func ColorFunc(style string) func(string) string {
+ if style == "" {
+ return func(s string) string {
+ return s
+ }
+ }
+ color := ColorCode(style)
+ return func(s string) string {
+ if plain || s == "" {
+ return s
+ }
+ buf := bytes.NewBufferString(color)
+ buf.WriteString(s)
+ buf.WriteString(Reset)
+ result := buf.String()
+ return result
+ }
+}
+
+// DisableColors disables ANSI color codes. The default is false (colors are on).
+func DisableColors(disable bool) {
+ plain = disable
+ if plain {
+ Black = ""
+ Red = ""
+ Green = ""
+ Yellow = ""
+ Blue = ""
+ Magenta = ""
+ Cyan = ""
+ White = ""
+ LightBlack = ""
+ LightRed = ""
+ LightGreen = ""
+ LightYellow = ""
+ LightBlue = ""
+ LightMagenta = ""
+ LightCyan = ""
+ LightWhite = ""
+ } else {
+ Black = ColorCode("black")
+ Red = ColorCode("red")
+ Green = ColorCode("green")
+ Yellow = ColorCode("yellow")
+ Blue = ColorCode("blue")
+ Magenta = ColorCode("magenta")
+ Cyan = ColorCode("cyan")
+ White = ColorCode("white")
+ LightBlack = ColorCode("black+h")
+ LightRed = ColorCode("red+h")
+ LightGreen = ColorCode("green+h")
+ LightYellow = ColorCode("yellow+h")
+ LightBlue = ColorCode("blue+h")
+ LightMagenta = ColorCode("magenta+h")
+ LightCyan = ColorCode("cyan+h")
+ LightWhite = ColorCode("white+h")
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/doc.go b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/doc.go
new file mode 100644
index 0000000000..43c217e11d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/doc.go
@@ -0,0 +1,65 @@
+/*
+Package ansi is a small, fast library to create ANSI colored strings and codes.
+
+Installation
+
+ # this installs the color viewer and the package
+ go get -u github.com/mgutz/ansi/cmd/ansi-mgutz
+
+Example
+
+ // colorize a string, SLOW
+ msg := ansi.Color("foo", "red+b:white")
+
+ // create a closure to avoid recalculating ANSI code compilation
+ phosphorize := ansi.ColorFunc("green+h:black")
+ msg = phosphorize("Bring back the 80s!")
+ msg2 := phospohorize("Look, I'm a CRT!")
+
+ // cache escape codes and build strings manually
+ lime := ansi.ColorCode("green+h:black")
+ reset := ansi.ColorCode("reset")
+
+ fmt.Println(lime, "Bring back the 80s!", reset)
+
+Other examples
+
+ Color(s, "red") // red
+ Color(s, "red+b") // red bold
+ Color(s, "red+B") // red blinking
+ Color(s, "red+u") // red underline
+ Color(s, "red+bh") // red bold bright
+ Color(s, "red:white") // red on white
+ Color(s, "red+b:white+h") // red bold on white bright
+ Color(s, "red+B:white+h") // red blink on white bright
+
+To view color combinations, from terminal
+
+ ansi-mgutz
+
+Style format
+
+ "foregroundColor+attributes:backgroundColor+attributes"
+
+Colors
+
+ black
+ red
+ green
+ yellow
+ blue
+ magenta
+ cyan
+ white
+
+Attributes
+
+ b = bold foreground
+ B = Blink foreground
+ u = underline foreground
+ h = high intensity (bright) foreground, background
+ i = inverse
+
+Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
+*/
+package ansi
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/print.go b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/print.go
new file mode 100644
index 0000000000..806f436bb3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/mgutz/ansi/print.go
@@ -0,0 +1,57 @@
+package ansi
+
+import (
+ "fmt"
+ "sort"
+
+ colorable "github.com/mattn/go-colorable"
+)
+
+// PrintStyles prints all style combinations to the terminal.
+func PrintStyles() {
+ // for compatibility with Windows, not needed for *nix
+ stdout := colorable.NewColorableStdout()
+
+ bgColors := []string{
+ "",
+ ":black",
+ ":red",
+ ":green",
+ ":yellow",
+ ":blue",
+ ":magenta",
+ ":cyan",
+ ":white",
+ }
+
+ keys := make([]string, 0, len(Colors))
+ for k := range Colors {
+ keys = append(keys, k)
+ }
+
+ sort.Sort(sort.StringSlice(keys))
+
+ for _, fg := range keys {
+ for _, bg := range bgColors {
+ fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg}))
+ fmt.Fprintln(stdout, padColor(fg, []string{"+s" + bg, "+i" + bg}))
+ fmt.Fprintln(stdout, padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h"}))
+ fmt.Fprintln(stdout, padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"}))
+ }
+ }
+}
+
+func pad(s string, length int) string {
+ for len(s) < length {
+ s += " "
+ }
+ return s
+}
+
+func padColor(color string, styles []string) string {
+ buffer := ""
+ for _, style := range styles {
+ buffer += Color(pad(color+style, 20), color+style)
+ }
+ return buffer
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/.travis.yml
new file mode 100644
index 0000000000..acf8201537
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/.travis.yml
@@ -0,0 +1,21 @@
+language: go
+sudo: false
+go:
+ - 1.6.x
+ - 1.7.x
+ - 1.8.x
+ - 1.9.x
+ - 1.10.x
+ - 1.11.x
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
+ fast_finish: true
+before_install:
+ - go get github.com/mattn/goveralls
+ - go get golang.org/x/tools/cmd/cover
+script:
+ - $HOME/gopath/bin/goveralls -service=travis-ci
+notifications:
+ email: false
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/LICENSE
new file mode 100644
index 0000000000..926d549870
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/LICENSE
@@ -0,0 +1,20 @@
+Copyright (C) 2013-2018 by Maxim Bublis
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/README.md
new file mode 100644
index 0000000000..362b270673
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/README.md
@@ -0,0 +1,75 @@
+# UUID package for Go language
+
+[](https://travis-ci.org/satori/go.uuid)
+[](https://coveralls.io/github/satori/go.uuid)
+[](http://godoc.org/github.com/satori/go.uuid)
+
+This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs.
+
+With 100% test coverage and benchmarks out of box.
+
+Supported versions:
+* Version 1, based on timestamp and MAC address (RFC 4122)
+* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
+* Version 3, based on MD5 hashing (RFC 4122)
+* Version 4, based on random numbers (RFC 4122)
+* Version 5, based on SHA-1 hashing (RFC 4122)
+
+## Installation
+
+Use the `go` command:
+
+ $ go get github.com/satori/go.uuid
+
+## Requirements
+
+UUID package tested against Go >= 1.6.
+
+## Example
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/satori/go.uuid"
+)
+
+func main() {
+ // Creating UUID Version 4
+ // panic on error
+ u1 := uuid.Must(uuid.NewV4())
+ fmt.Printf("UUIDv4: %s\n", u1)
+
+ // or error handling
+ u2, err := uuid.NewV4()
+ if err != nil {
+ fmt.Printf("Something went wrong: %s", err)
+ return
+ }
+ fmt.Printf("UUIDv4: %s\n", u2)
+
+ // Parsing UUID from string input
+ u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
+ if err != nil {
+ fmt.Printf("Something went wrong: %s", err)
+ return
+ }
+ fmt.Printf("Successfully parsed: %s", u2)
+}
+```
+
+## Documentation
+
+[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project.
+
+## Links
+* [RFC 4122](http://tools.ietf.org/html/rfc4122)
+* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
+
+## Copyright
+
+Copyright (C) 2013-2018 by Maxim Bublis .
+
+UUID package released under MIT License.
+See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/codec.go b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/codec.go
new file mode 100644
index 0000000000..656892c53e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/codec.go
@@ -0,0 +1,206 @@
+// Copyright (C) 2013-2018 by Maxim Bublis
+//
+// 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 uuid
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+)
+
+// FromBytes returns UUID converted from raw byte slice input.
+// It will return error if the slice isn't 16 bytes long.
+func FromBytes(input []byte) (u UUID, err error) {
+ err = u.UnmarshalBinary(input)
+ return
+}
+
+// FromBytesOrNil returns UUID converted from raw byte slice input.
+// Same behavior as FromBytes, but returns a Nil UUID on error.
+func FromBytesOrNil(input []byte) UUID {
+ uuid, err := FromBytes(input)
+ if err != nil {
+ return Nil
+ }
+ return uuid
+}
+
+// FromString returns UUID parsed from string input.
+// Input is expected in a form accepted by UnmarshalText.
+func FromString(input string) (u UUID, err error) {
+ err = u.UnmarshalText([]byte(input))
+ return
+}
+
+// FromStringOrNil returns UUID parsed from string input.
+// Same behavior as FromString, but returns a Nil UUID on error.
+func FromStringOrNil(input string) UUID {
+ uuid, err := FromString(input)
+ if err != nil {
+ return Nil
+ }
+ return uuid
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+// The encoding is the same as returned by String.
+func (u UUID) MarshalText() (text []byte, err error) {
+ text = []byte(u.String())
+ return
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// Following formats are supported:
+// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
+// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
+// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
+// "6ba7b8109dad11d180b400c04fd430c8"
+// ABNF for supported UUID text representation follows:
+// uuid := canonical | hashlike | braced | urn
+// plain := canonical | hashlike
+// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct
+// hashlike := 12hexoct
+// braced := '{' plain '}'
+// urn := URN ':' UUID-NID ':' plain
+// URN := 'urn'
+// UUID-NID := 'uuid'
+// 12hexoct := 6hexoct 6hexoct
+// 6hexoct := 4hexoct 2hexoct
+// 4hexoct := 2hexoct 2hexoct
+// 2hexoct := hexoct hexoct
+// hexoct := hexdig hexdig
+// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
+// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' |
+// 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
+func (u *UUID) UnmarshalText(text []byte) (err error) {
+ switch len(text) {
+ case 32:
+ return u.decodeHashLike(text)
+ case 36:
+ return u.decodeCanonical(text)
+ case 38:
+ return u.decodeBraced(text)
+ case 41:
+ fallthrough
+ case 45:
+ return u.decodeURN(text)
+ default:
+ return fmt.Errorf("uuid: incorrect UUID length: %s", text)
+ }
+}
+
+// decodeCanonical decodes UUID string in format
+// "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
+func (u *UUID) decodeCanonical(t []byte) (err error) {
+ if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
+ return fmt.Errorf("uuid: incorrect UUID format %s", t)
+ }
+
+ src := t[:]
+ dst := u[:]
+
+ for i, byteGroup := range byteGroups {
+ if i > 0 {
+ src = src[1:] // skip dash
+ }
+ _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup])
+ if err != nil {
+ return
+ }
+ src = src[byteGroup:]
+ dst = dst[byteGroup/2:]
+ }
+
+ return
+}
+
+// decodeHashLike decodes UUID string in format
+// "6ba7b8109dad11d180b400c04fd430c8".
+func (u *UUID) decodeHashLike(t []byte) (err error) {
+ src := t[:]
+ dst := u[:]
+
+ if _, err = hex.Decode(dst, src); err != nil {
+ return err
+ }
+ return
+}
+
+// decodeBraced decodes UUID string in format
+// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format
+// "{6ba7b8109dad11d180b400c04fd430c8}".
+func (u *UUID) decodeBraced(t []byte) (err error) {
+ l := len(t)
+
+ if t[0] != '{' || t[l-1] != '}' {
+ return fmt.Errorf("uuid: incorrect UUID format %s", t)
+ }
+
+ return u.decodePlain(t[1 : l-1])
+}
+
+// decodeURN decodes UUID string in format
+// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format
+// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8".
+func (u *UUID) decodeURN(t []byte) (err error) {
+ total := len(t)
+
+ urn_uuid_prefix := t[:9]
+
+ if !bytes.Equal(urn_uuid_prefix, urnPrefix) {
+ return fmt.Errorf("uuid: incorrect UUID format: %s", t)
+ }
+
+ return u.decodePlain(t[9:total])
+}
+
+// decodePlain decodes UUID string in canonical format
+// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format
+// "6ba7b8109dad11d180b400c04fd430c8".
+func (u *UUID) decodePlain(t []byte) (err error) {
+ switch len(t) {
+ case 32:
+ return u.decodeHashLike(t)
+ case 36:
+ return u.decodeCanonical(t)
+ default:
+ return fmt.Errorf("uuid: incorrrect UUID length: %s", t)
+ }
+}
+
+// MarshalBinary implements the encoding.BinaryMarshaler interface.
+func (u UUID) MarshalBinary() (data []byte, err error) {
+ data = u.Bytes()
+ return
+}
+
+// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
+// It will return error if the slice isn't 16 bytes long.
+func (u *UUID) UnmarshalBinary(data []byte) (err error) {
+ if len(data) != Size {
+ err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
+ return
+ }
+ copy(u[:], data)
+
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/generator.go b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/generator.go
new file mode 100644
index 0000000000..c50d33cab4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/generator.go
@@ -0,0 +1,265 @@
+// Copyright (C) 2013-2018 by Maxim Bublis
+//
+// 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 uuid
+
+import (
+ "crypto/md5"
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "os"
+ "sync"
+ "time"
+)
+
+// Difference in 100-nanosecond intervals between
+// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
+const epochStart = 122192928000000000
+
+type epochFunc func() time.Time
+type hwAddrFunc func() (net.HardwareAddr, error)
+
+var (
+ global = newRFC4122Generator()
+
+ posixUID = uint32(os.Getuid())
+ posixGID = uint32(os.Getgid())
+)
+
+// NewV1 returns UUID based on current timestamp and MAC address.
+func NewV1() (UUID, error) {
+ return global.NewV1()
+}
+
+// NewV2 returns DCE Security UUID based on POSIX UID/GID.
+func NewV2(domain byte) (UUID, error) {
+ return global.NewV2(domain)
+}
+
+// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
+func NewV3(ns UUID, name string) UUID {
+ return global.NewV3(ns, name)
+}
+
+// NewV4 returns random generated UUID.
+func NewV4() (UUID, error) {
+ return global.NewV4()
+}
+
+// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
+func NewV5(ns UUID, name string) UUID {
+ return global.NewV5(ns, name)
+}
+
+// Generator provides interface for generating UUIDs.
+type Generator interface {
+ NewV1() (UUID, error)
+ NewV2(domain byte) (UUID, error)
+ NewV3(ns UUID, name string) UUID
+ NewV4() (UUID, error)
+ NewV5(ns UUID, name string) UUID
+}
+
+// Default generator implementation.
+type rfc4122Generator struct {
+ clockSequenceOnce sync.Once
+ hardwareAddrOnce sync.Once
+ storageMutex sync.Mutex
+
+ rand io.Reader
+
+ epochFunc epochFunc
+ hwAddrFunc hwAddrFunc
+ lastTime uint64
+ clockSequence uint16
+ hardwareAddr [6]byte
+}
+
+func newRFC4122Generator() Generator {
+ return &rfc4122Generator{
+ epochFunc: time.Now,
+ hwAddrFunc: defaultHWAddrFunc,
+ rand: rand.Reader,
+ }
+}
+
+// NewV1 returns UUID based on current timestamp and MAC address.
+func (g *rfc4122Generator) NewV1() (UUID, error) {
+ u := UUID{}
+
+ timeNow, clockSeq, err := g.getClockSequence()
+ if err != nil {
+ return Nil, err
+ }
+ binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
+ binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
+ binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
+ binary.BigEndian.PutUint16(u[8:], clockSeq)
+
+ hardwareAddr, err := g.getHardwareAddr()
+ if err != nil {
+ return Nil, err
+ }
+ copy(u[10:], hardwareAddr)
+
+ u.SetVersion(V1)
+ u.SetVariant(VariantRFC4122)
+
+ return u, nil
+}
+
+// NewV2 returns DCE Security UUID based on POSIX UID/GID.
+func (g *rfc4122Generator) NewV2(domain byte) (UUID, error) {
+ u, err := g.NewV1()
+ if err != nil {
+ return Nil, err
+ }
+
+ switch domain {
+ case DomainPerson:
+ binary.BigEndian.PutUint32(u[:], posixUID)
+ case DomainGroup:
+ binary.BigEndian.PutUint32(u[:], posixGID)
+ }
+
+ u[9] = domain
+
+ u.SetVersion(V2)
+ u.SetVariant(VariantRFC4122)
+
+ return u, nil
+}
+
+// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
+func (g *rfc4122Generator) NewV3(ns UUID, name string) UUID {
+ u := newFromHash(md5.New(), ns, name)
+ u.SetVersion(V3)
+ u.SetVariant(VariantRFC4122)
+
+ return u
+}
+
+// NewV4 returns random generated UUID.
+func (g *rfc4122Generator) NewV4() (UUID, error) {
+ u := UUID{}
+ if _, err := io.ReadFull(g.rand, u[:]); err != nil {
+ return Nil, err
+ }
+ u.SetVersion(V4)
+ u.SetVariant(VariantRFC4122)
+
+ return u, nil
+}
+
+// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
+func (g *rfc4122Generator) NewV5(ns UUID, name string) UUID {
+ u := newFromHash(sha1.New(), ns, name)
+ u.SetVersion(V5)
+ u.SetVariant(VariantRFC4122)
+
+ return u
+}
+
+// Returns epoch and clock sequence.
+func (g *rfc4122Generator) getClockSequence() (uint64, uint16, error) {
+ var err error
+ g.clockSequenceOnce.Do(func() {
+ buf := make([]byte, 2)
+ if _, err = io.ReadFull(g.rand, buf); err != nil {
+ return
+ }
+ g.clockSequence = binary.BigEndian.Uint16(buf)
+ })
+ if err != nil {
+ return 0, 0, err
+ }
+
+ g.storageMutex.Lock()
+ defer g.storageMutex.Unlock()
+
+ timeNow := g.getEpoch()
+ // Clock didn't change since last UUID generation.
+ // Should increase clock sequence.
+ if timeNow <= g.lastTime {
+ g.clockSequence++
+ }
+ g.lastTime = timeNow
+
+ return timeNow, g.clockSequence, nil
+}
+
+// Returns hardware address.
+func (g *rfc4122Generator) getHardwareAddr() ([]byte, error) {
+ var err error
+ g.hardwareAddrOnce.Do(func() {
+ if hwAddr, err := g.hwAddrFunc(); err == nil {
+ copy(g.hardwareAddr[:], hwAddr)
+ return
+ }
+
+ // Initialize hardwareAddr randomly in case
+ // of real network interfaces absence.
+ if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil {
+ return
+ }
+ // Set multicast bit as recommended by RFC 4122
+ g.hardwareAddr[0] |= 0x01
+ })
+ if err != nil {
+ return []byte{}, err
+ }
+ return g.hardwareAddr[:], nil
+}
+
+// Returns difference in 100-nanosecond intervals between
+// UUID epoch (October 15, 1582) and current time.
+func (g *rfc4122Generator) getEpoch() uint64 {
+ return epochStart + uint64(g.epochFunc().UnixNano()/100)
+}
+
+// Returns UUID based on hashing of namespace UUID and name.
+func newFromHash(h hash.Hash, ns UUID, name string) UUID {
+ u := UUID{}
+ h.Write(ns[:])
+ h.Write([]byte(name))
+ copy(u[:], h.Sum(nil))
+
+ return u
+}
+
+// Returns hardware address.
+func defaultHWAddrFunc() (net.HardwareAddr, error) {
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ return []byte{}, err
+ }
+ for _, iface := range ifaces {
+ if len(iface.HardwareAddr) >= 6 {
+ return iface.HardwareAddr, nil
+ }
+ }
+ return []byte{}, fmt.Errorf("uuid: no HW address found")
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/sql.go b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/sql.go
new file mode 100644
index 0000000000..56759d3905
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/sql.go
@@ -0,0 +1,78 @@
+// Copyright (C) 2013-2018 by Maxim Bublis
+//
+// 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 uuid
+
+import (
+ "database/sql/driver"
+ "fmt"
+)
+
+// Value implements the driver.Valuer interface.
+func (u UUID) Value() (driver.Value, error) {
+ return u.String(), nil
+}
+
+// Scan implements the sql.Scanner interface.
+// A 16-byte slice is handled by UnmarshalBinary, while
+// a longer byte slice or a string is handled by UnmarshalText.
+func (u *UUID) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ if len(src) == Size {
+ return u.UnmarshalBinary(src)
+ }
+ return u.UnmarshalText(src)
+
+ case string:
+ return u.UnmarshalText([]byte(src))
+ }
+
+ return fmt.Errorf("uuid: cannot convert %T to UUID", src)
+}
+
+// NullUUID can be used with the standard sql package to represent a
+// UUID value that can be NULL in the database
+type NullUUID struct {
+ UUID UUID
+ Valid bool
+}
+
+// Value implements the driver.Valuer interface.
+func (u NullUUID) Value() (driver.Value, error) {
+ if !u.Valid {
+ return nil, nil
+ }
+ // Delegate to UUID Value function
+ return u.UUID.Value()
+}
+
+// Scan implements the sql.Scanner interface.
+func (u *NullUUID) Scan(src interface{}) error {
+ if src == nil {
+ u.UUID, u.Valid = Nil, false
+ return nil
+ }
+
+ // Delegate to UUID Scan function
+ u.Valid = true
+ return u.UUID.Scan(src)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/uuid.go b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/uuid.go
new file mode 100644
index 0000000000..a2b8e2ca2a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/satori/go.uuid/uuid.go
@@ -0,0 +1,161 @@
+// Copyright (C) 2013-2018 by Maxim Bublis
+//
+// 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 uuid provides implementation of Universally Unique Identifier (UUID).
+// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
+// version 2 (as specified in DCE 1.1).
+package uuid
+
+import (
+ "bytes"
+ "encoding/hex"
+)
+
+// Size of a UUID in bytes.
+const Size = 16
+
+// UUID representation compliant with specification
+// described in RFC 4122.
+type UUID [Size]byte
+
+// UUID versions
+const (
+ _ byte = iota
+ V1
+ V2
+ V3
+ V4
+ V5
+)
+
+// UUID layout variants.
+const (
+ VariantNCS byte = iota
+ VariantRFC4122
+ VariantMicrosoft
+ VariantFuture
+)
+
+// UUID DCE domains.
+const (
+ DomainPerson = iota
+ DomainGroup
+ DomainOrg
+)
+
+// String parse helpers.
+var (
+ urnPrefix = []byte("urn:uuid:")
+ byteGroups = []int{8, 4, 4, 4, 12}
+)
+
+// Nil is special form of UUID that is specified to have all
+// 128 bits set to zero.
+var Nil = UUID{}
+
+// Predefined namespace UUIDs.
+var (
+ NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
+ NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
+ NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
+ NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
+)
+
+// Equal returns true if u1 and u2 equals, otherwise returns false.
+func Equal(u1 UUID, u2 UUID) bool {
+ return bytes.Equal(u1[:], u2[:])
+}
+
+// Version returns algorithm version used to generate UUID.
+func (u UUID) Version() byte {
+ return u[6] >> 4
+}
+
+// Variant returns UUID layout variant.
+func (u UUID) Variant() byte {
+ switch {
+ case (u[8] >> 7) == 0x00:
+ return VariantNCS
+ case (u[8] >> 6) == 0x02:
+ return VariantRFC4122
+ case (u[8] >> 5) == 0x06:
+ return VariantMicrosoft
+ case (u[8] >> 5) == 0x07:
+ fallthrough
+ default:
+ return VariantFuture
+ }
+}
+
+// Bytes returns bytes slice representation of UUID.
+func (u UUID) Bytes() []byte {
+ return u[:]
+}
+
+// Returns canonical string representation of UUID:
+// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
+func (u UUID) String() string {
+ buf := make([]byte, 36)
+
+ hex.Encode(buf[0:8], u[0:4])
+ buf[8] = '-'
+ hex.Encode(buf[9:13], u[4:6])
+ buf[13] = '-'
+ hex.Encode(buf[14:18], u[6:8])
+ buf[18] = '-'
+ hex.Encode(buf[19:23], u[8:10])
+ buf[23] = '-'
+ hex.Encode(buf[24:], u[10:])
+
+ return string(buf)
+}
+
+// SetVersion sets version bits.
+func (u *UUID) SetVersion(v byte) {
+ u[6] = (u[6] & 0x0f) | (v << 4)
+}
+
+// SetVariant sets variant bits.
+func (u *UUID) SetVariant(v byte) {
+ switch v {
+ case VariantNCS:
+ u[8] = (u[8]&(0xff>>1) | (0x00 << 7))
+ case VariantRFC4122:
+ u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
+ case VariantMicrosoft:
+ u[8] = (u[8]&(0xff>>3) | (0x06 << 5))
+ case VariantFuture:
+ fallthrough
+ default:
+ u[8] = (u[8]&(0xff>>3) | (0x07 << 5))
+ }
+}
+
+// Must is a helper that wraps a call to a function returning (UUID, error)
+// and panics if the error is non-nil. It is intended for use in variable
+// initializations such as
+// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000"));
+func Must(u UUID, err error) UUID {
+ if err != nil {
+ panic(err)
+ }
+ return u
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.gitignore
new file mode 100644
index 0000000000..6b7d7d1e8b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.gitignore
@@ -0,0 +1,2 @@
+logrus
+vendor
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.travis.yml
new file mode 100644
index 0000000000..848938a6d4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/.travis.yml
@@ -0,0 +1,25 @@
+language: go
+go_import_path: github.com/sirupsen/logrus
+git:
+ depth: 1
+env:
+ - GO111MODULE=on
+ - GO111MODULE=off
+go: [ 1.11.x, 1.12.x ]
+os: [ linux, osx ]
+matrix:
+ exclude:
+ - go: 1.12.x
+ env: GO111MODULE=off
+ - go: 1.11.x
+ os: osx
+install:
+ - ./travis/install.sh
+ - if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi
+ - if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi
+script:
+ - ./travis/cross_build.sh
+ - export GOMAXPROCS=4
+ - export GORACE=halt_on_error=1
+ - go test -race -v ./...
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/CHANGELOG.md
new file mode 100644
index 0000000000..51a7ab0cab
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/CHANGELOG.md
@@ -0,0 +1,200 @@
+# 1.4.2
+ * Fixes build break for plan9, nacl, solaris
+# 1.4.1
+This new release introduces:
+ * Enhance TextFormatter to not print caller information when they are empty (#944)
+ * Remove dependency on golang.org/x/crypto (#932, #943)
+
+Fixes:
+ * Fix Entry.WithContext method to return a copy of the initial entry (#941)
+
+# 1.4.0
+This new release introduces:
+ * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
+ * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
+ * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
+
+Fixes:
+ * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
+ * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
+ * Fix infinite recursion on unknown `Level.String()` (#907)
+ * Fix race condition in `getCaller` (#916).
+
+
+# 1.3.0
+This new release introduces:
+ * Log, Logf, Logln functions for Logger and Entry that take a Level
+
+Fixes:
+ * Building prometheus node_exporter on AIX (#840)
+ * Race condition in TextFormatter (#468)
+ * Travis CI import path (#868)
+ * Remove coloured output on Windows (#862)
+ * Pointer to func as field in JSONFormatter (#870)
+ * Properly marshal Levels (#873)
+
+# 1.2.0
+This new release introduces:
+ * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
+ * A new trace level named `Trace` whose level is below `Debug`
+ * A configurable exit function to be called upon a Fatal trace
+ * The `Level` object now implements `encoding.TextUnmarshaler` interface
+
+# 1.1.1
+This is a bug fix release.
+ * fix the build break on Solaris
+ * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
+
+# 1.1.0
+This new release introduces:
+ * several fixes:
+ * a fix for a race condition on entry formatting
+ * proper cleanup of previously used entries before putting them back in the pool
+ * the extra new line at the end of message in text formatter has been removed
+ * a new global public API to check if a level is activated: IsLevelEnabled
+ * the following methods have been added to the Logger object
+ * IsLevelEnabled
+ * SetFormatter
+ * SetOutput
+ * ReplaceHooks
+ * introduction of go module
+ * an indent configuration for the json formatter
+ * output colour support for windows
+ * the field sort function is now configurable for text formatter
+ * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
+
+# 1.0.6
+
+This new release introduces:
+ * a new api WithTime which allows to easily force the time of the log entry
+ which is mostly useful for logger wrapper
+ * a fix reverting the immutability of the entry given as parameter to the hooks
+ a new configuration field of the json formatter in order to put all the fields
+ in a nested dictionnary
+ * a new SetOutput method in the Logger
+ * a new configuration of the textformatter to configure the name of the default keys
+ * a new configuration of the text formatter to disable the level truncation
+
+# 1.0.5
+
+* Fix hooks race (#707)
+* Fix panic deadlock (#695)
+
+# 1.0.4
+
+* Fix race when adding hooks (#612)
+* Fix terminal check in AppEngine (#635)
+
+# 1.0.3
+
+* Replace example files with testable examples
+
+# 1.0.2
+
+* bug: quote non-string values in text formatter (#583)
+* Make (*Logger) SetLevel a public method
+
+# 1.0.1
+
+* bug: fix escaping in text formatter (#575)
+
+# 1.0.0
+
+* Officially changed name to lower-case
+* bug: colors on Windows 10 (#541)
+* bug: fix race in accessing level (#512)
+
+# 0.11.5
+
+* feature: add writer and writerlevel to entry (#372)
+
+# 0.11.4
+
+* bug: fix undefined variable on solaris (#493)
+
+# 0.11.3
+
+* formatter: configure quoting of empty values (#484)
+* formatter: configure quoting character (default is `"`) (#484)
+* bug: fix not importing io correctly in non-linux environments (#481)
+
+# 0.11.2
+
+* bug: fix windows terminal detection (#476)
+
+# 0.11.1
+
+* bug: fix tty detection with custom out (#471)
+
+# 0.11.0
+
+* performance: Use bufferpool to allocate (#370)
+* terminal: terminal detection for app-engine (#343)
+* feature: exit handler (#375)
+
+# 0.10.0
+
+* feature: Add a test hook (#180)
+* feature: `ParseLevel` is now case-insensitive (#326)
+* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
+* performance: avoid re-allocations on `WithFields` (#335)
+
+# 0.9.0
+
+* logrus/text_formatter: don't emit empty msg
+* logrus/hooks/airbrake: move out of main repository
+* logrus/hooks/sentry: move out of main repository
+* logrus/hooks/papertrail: move out of main repository
+* logrus/hooks/bugsnag: move out of main repository
+* logrus/core: run tests with `-race`
+* logrus/core: detect TTY based on `stderr`
+* logrus/core: support `WithError` on logger
+* logrus/core: Solaris support
+
+# 0.8.7
+
+* logrus/core: fix possible race (#216)
+* logrus/doc: small typo fixes and doc improvements
+
+
+# 0.8.6
+
+* hooks/raven: allow passing an initialized client
+
+# 0.8.5
+
+* logrus/core: revert #208
+
+# 0.8.4
+
+* formatter/text: fix data race (#218)
+
+# 0.8.3
+
+* logrus/core: fix entry log level (#208)
+* logrus/core: improve performance of text formatter by 40%
+* logrus/core: expose `LevelHooks` type
+* logrus/core: add support for DragonflyBSD and NetBSD
+* formatter/text: print structs more verbosely
+
+# 0.8.2
+
+* logrus: fix more Fatal family functions
+
+# 0.8.1
+
+* logrus: fix not exiting on `Fatalf` and `Fatalln`
+
+# 0.8.0
+
+* logrus: defaults to stderr instead of stdout
+* hooks/sentry: add special field for `*http.Request`
+* formatter/text: ignore Windows for colors
+
+# 0.7.3
+
+* formatter/\*: allow configuration of timestamp layout
+
+# 0.7.2
+
+* formatter/text: Add configuration option for time format (#158)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/LICENSE
new file mode 100644
index 0000000000..f090cb42f3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Simon Eskildsen
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/README.md
new file mode 100644
index 0000000000..a4796eb07d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/README.md
@@ -0,0 +1,495 @@
+# Logrus
[](https://travis-ci.org/sirupsen/logrus) [](https://godoc.org/github.com/sirupsen/logrus)
+
+Logrus is a structured logger for Go (golang), completely API compatible with
+the standard library logger.
+
+**Seeing weird case-sensitive problems?** It's in the past been possible to
+import Logrus as both upper- and lower-case. Due to the Go package environment,
+this caused issues in the community and we needed a standard. Some environments
+experienced problems with the upper-case variant, so the lower-case was decided.
+Everything using `logrus` will need to use the lower-case:
+`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
+
+To fix Glide, see [these
+comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
+For an in-depth explanation of the casing issue, see [this
+comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
+
+**Are you interested in assisting in maintaining Logrus?** Currently I have a
+lot of obligations, and I am unable to provide Logrus with the maintainership it
+needs. If you'd like to help, please reach out to me at `simon at author's
+username dot com`.
+
+Nicely color-coded in development (when a TTY is attached, otherwise just
+plain text):
+
+
+
+With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
+or Splunk:
+
+```json
+{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
+ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
+
+{"level":"warning","msg":"The group's number increased tremendously!",
+"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
+"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
+
+{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
+"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
+
+{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
+"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
+```
+
+With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
+attached, the output is compatible with the
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+
+```text
+time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
+time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
+time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
+time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
+time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
+```
+To ensure this behaviour even if a TTY is attached, set your formatter as follows:
+
+```go
+ log.SetFormatter(&log.TextFormatter{
+ DisableColors: true,
+ FullTimestamp: true,
+ })
+```
+
+#### Logging Method Name
+
+If you wish to add the calling method as a field, instruct the logger via:
+```go
+log.SetReportCaller(true)
+```
+This adds the caller as 'method' like so:
+
+```json
+{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
+"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
+```
+
+```text
+time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
+```
+Note that this does add measurable overhead - the cost will depend on the version of Go, but is
+between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
+environment via benchmarks:
+```
+go test -bench=.*CallerTracing
+```
+
+
+#### Case-sensitivity
+
+The organization's name was changed to lower-case--and this will not be changed
+back. If you are getting import conflicts due to case sensitivity, please use
+the lower-case import: `github.com/sirupsen/logrus`.
+
+#### Example
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+```go
+package main
+
+import (
+ log "github.com/sirupsen/logrus"
+)
+
+func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ }).Info("A walrus appears")
+}
+```
+
+Note that it's completely api-compatible with the stdlib logger, so you can
+replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
+and you'll now have the flexibility of Logrus. You can customize it all you
+want:
+
+```go
+package main
+
+import (
+ "os"
+ log "github.com/sirupsen/logrus"
+)
+
+func init() {
+ // Log as JSON instead of the default ASCII formatter.
+ log.SetFormatter(&log.JSONFormatter{})
+
+ // Output to stdout instead of the default stderr
+ // Can be any io.Writer, see below for File example
+ log.SetOutput(os.Stdout)
+
+ // Only log the warning severity or above.
+ log.SetLevel(log.WarnLevel)
+}
+
+func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(log.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(log.Fields{
+ "omg": true,
+ "number": 100,
+ }).Fatal("The ice breaks!")
+
+ // A common pattern is to re-use fields between logging statements by re-using
+ // the logrus.Entry returned from WithFields()
+ contextLogger := log.WithFields(log.Fields{
+ "common": "this is a common field",
+ "other": "I also should be logged always",
+ })
+
+ contextLogger.Info("I'll be logged with common and other field")
+ contextLogger.Info("Me too")
+}
+```
+
+For more advanced usage such as logging to multiple locations from the same
+application, you can also create an instance of the `logrus` Logger:
+
+```go
+package main
+
+import (
+ "os"
+ "github.com/sirupsen/logrus"
+)
+
+// Create a new instance of the logger. You can have any number of instances.
+var log = logrus.New()
+
+func main() {
+ // The API for setting attributes is a little different than the package level
+ // exported logger. See Godoc.
+ log.Out = os.Stdout
+
+ // You could set this to any `io.Writer` such as a file
+ // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
+ // if err == nil {
+ // log.Out = file
+ // } else {
+ // log.Info("Failed to log to file, using default stderr")
+ // }
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+}
+```
+
+#### Fields
+
+Logrus encourages careful, structured logging through logging fields instead of
+long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
+to send event %s to topic %s with key %d")`, you should log the much more
+discoverable:
+
+```go
+log.WithFields(log.Fields{
+ "event": event,
+ "topic": topic,
+ "key": key,
+}).Fatal("Failed to send event")
+```
+
+We've found this API forces you to think about logging in a way that produces
+much more useful logging messages. We've been in countless situations where just
+a single added field to a log statement that was already there would've saved us
+hours. The `WithFields` call is optional.
+
+In general, with Logrus using any of the `printf`-family functions should be
+seen as a hint you should add a field, however, you can still use the
+`printf`-family functions with Logrus.
+
+#### Default Fields
+
+Often it's helpful to have fields _always_ attached to log statements in an
+application or parts of one. For example, you may want to always log the
+`request_id` and `user_ip` in the context of a request. Instead of writing
+`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
+every line, you can create a `logrus.Entry` to pass around instead:
+
+```go
+requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
+requestLogger.Info("something happened on that request") # will log request_id and user_ip
+requestLogger.Warn("something not great happened")
+```
+
+#### Hooks
+
+You can add hooks for logging levels. For example to send errors to an exception
+tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
+multiple places simultaneously, e.g. syslog.
+
+Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
+`init`:
+
+```go
+import (
+ log "github.com/sirupsen/logrus"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
+ logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
+ "log/syslog"
+)
+
+func init() {
+
+ // Use the Airbrake hook to report errors that have Error severity or above to
+ // an exception tracker. You can create custom hooks, see the Hooks section.
+ log.AddHook(airbrake.NewHook(123, "xyz", "production"))
+
+ hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+ if err != nil {
+ log.Error("Unable to connect to local syslog daemon")
+ } else {
+ log.AddHook(hook)
+ }
+}
+```
+Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
+
+A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
+
+
+#### Level logging
+
+Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
+
+```go
+log.Trace("Something very low level.")
+log.Debug("Useful debugging information.")
+log.Info("Something noteworthy happened!")
+log.Warn("You should probably take a look at this.")
+log.Error("Something failed but I'm not quitting.")
+// Calls os.Exit(1) after logging
+log.Fatal("Bye.")
+// Calls panic() after logging
+log.Panic("I'm bailing.")
+```
+
+You can set the logging level on a `Logger`, then it will only log entries with
+that severity or anything above it:
+
+```go
+// Will log anything that is info or above (warn, error, fatal, panic). Default.
+log.SetLevel(log.InfoLevel)
+```
+
+It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
+environment if your application has that.
+
+#### Entries
+
+Besides the fields added with `WithField` or `WithFields` some fields are
+automatically added to all logging events:
+
+1. `time`. The timestamp when the entry was created.
+2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
+ the `AddFields` call. E.g. `Failed to send event.`
+3. `level`. The logging level. E.g. `info`.
+
+#### Environments
+
+Logrus has no notion of environment.
+
+If you wish for hooks and formatters to only be used in specific environments,
+you should handle that yourself. For example, if your application has a global
+variable `Environment`, which is a string representation of the environment you
+could do:
+
+```go
+import (
+ log "github.com/sirupsen/logrus"
+)
+
+init() {
+ // do something here to set environment depending on an environment variable
+ // or command-line flag
+ if Environment == "production" {
+ log.SetFormatter(&log.JSONFormatter{})
+ } else {
+ // The TextFormatter is default, you don't actually have to do this.
+ log.SetFormatter(&log.TextFormatter{})
+ }
+}
+```
+
+This configuration is how `logrus` was intended to be used, but JSON in
+production is mostly only useful if you do log aggregation with tools like
+Splunk or Logstash.
+
+#### Formatters
+
+The built-in logging formatters are:
+
+* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
+ without colors.
+ * *Note:* to force colored output when there is no TTY, set the `ForceColors`
+ field to `true`. To force no colored output even if there is a TTY set the
+ `DisableColors` field to `true`. For Windows, see
+ [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
+ * When colors are enabled, levels are truncated to 4 characters by default. To disable
+ truncation set the `DisableLevelTruncation` field to `true`.
+ * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
+* `logrus.JSONFormatter`. Logs fields as JSON.
+ * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
+
+Third party logging formatters:
+
+* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
+* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
+* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
+* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
+* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
+* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
+
+You can define your formatter by implementing the `Formatter` interface,
+requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
+`Fields` type (`map[string]interface{}`) with all your fields as well as the
+default ones (see Entries section above):
+
+```go
+type MyJSONFormatter struct {
+}
+
+log.SetFormatter(new(MyJSONFormatter))
+
+func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
+ // Note this doesn't include Time, Level and Message which are available on
+ // the Entry. Consult `godoc` on information about those fields or read the
+ // source of the official loggers.
+ serialized, err := json.Marshal(entry.Data)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+ }
+ return append(serialized, '\n'), nil
+}
+```
+
+#### Logger as an `io.Writer`
+
+Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
+
+```go
+w := logger.Writer()
+defer w.Close()
+
+srv := http.Server{
+ // create a stdlib log.Logger that writes to
+ // logrus.Logger.
+ ErrorLog: log.New(w, "", 0),
+}
+```
+
+Each line written to that writer will be printed the usual way, using formatters
+and hooks. The level for those entries is `info`.
+
+This means that we can override the standard library logger easily:
+
+```go
+logger := logrus.New()
+logger.Formatter = &logrus.JSONFormatter{}
+
+// Use logrus for standard log output
+// Note that `log` here references stdlib's log
+// Not logrus imported under the name `log`.
+log.SetOutput(logger.Writer())
+```
+
+#### Rotation
+
+Log rotation is not provided with Logrus. Log rotation should be done by an
+external program (like `logrotate(8)`) that can compress and delete old log
+entries. It should not be a feature of the application-level logger.
+
+#### Tools
+
+| Tool | Description |
+| ---- | ----------- |
+|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
+|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
+
+#### Testing
+
+Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
+
+* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
+* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
+
+```go
+import(
+ "github.com/sirupsen/logrus"
+ "github.com/sirupsen/logrus/hooks/test"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestSomething(t*testing.T){
+ logger, hook := test.NewNullLogger()
+ logger.Error("Helloerror")
+
+ assert.Equal(t, 1, len(hook.Entries))
+ assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
+ assert.Equal(t, "Helloerror", hook.LastEntry().Message)
+
+ hook.Reset()
+ assert.Nil(t, hook.LastEntry())
+}
+```
+
+#### Fatal handlers
+
+Logrus can register one or more functions that will be called when any `fatal`
+level message is logged. The registered handlers will be executed before
+logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
+to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
+
+```
+...
+handler := func() {
+ // gracefully shutdown something...
+}
+logrus.RegisterExitHandler(handler)
+...
+```
+
+#### Thread safety
+
+By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
+If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
+
+Situation when locking is not needed includes:
+
+* You have no hooks registered, or hooks calling is already thread-safe.
+
+* Writing to logger.Out is already thread-safe, for example:
+
+ 1) logger.Out is protected by locks.
+
+ 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
+
+ (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/alt_exit.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/alt_exit.go
new file mode 100644
index 0000000000..8fd189e1cc
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/alt_exit.go
@@ -0,0 +1,76 @@
+package logrus
+
+// The following code was sourced and modified from the
+// https://github.com/tebeka/atexit package governed by the following license:
+//
+// Copyright (c) 2012 Miki Tebeka .
+//
+// 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.
+
+import (
+ "fmt"
+ "os"
+)
+
+var handlers = []func(){}
+
+func runHandler(handler func()) {
+ defer func() {
+ if err := recover(); err != nil {
+ fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
+ }
+ }()
+
+ handler()
+}
+
+func runHandlers() {
+ for _, handler := range handlers {
+ runHandler(handler)
+ }
+}
+
+// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
+func Exit(code int) {
+ runHandlers()
+ os.Exit(code)
+}
+
+// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
+// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
+// any Fatal log entry is made.
+//
+// This method is useful when a caller wishes to use logrus to log a fatal
+// message but also needs to gracefully shutdown. An example usecase could be
+// closing database connections, or sending a alert that the application is
+// closing.
+func RegisterExitHandler(handler func()) {
+ handlers = append(handlers, handler)
+}
+
+// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
+// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
+// any Fatal log entry is made.
+//
+// This method is useful when a caller wishes to use logrus to log a fatal
+// message but also needs to gracefully shutdown. An example usecase could be
+// closing database connections, or sending a alert that the application is
+// closing.
+func DeferExitHandler(handler func()) {
+ handlers = append([]func(){handler}, handlers...)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/appveyor.yml b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/appveyor.yml
new file mode 100644
index 0000000000..96c2ce15f8
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/appveyor.yml
@@ -0,0 +1,14 @@
+version: "{build}"
+platform: x64
+clone_folder: c:\gopath\src\github.com\sirupsen\logrus
+environment:
+ GOPATH: c:\gopath
+branches:
+ only:
+ - master
+install:
+ - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
+ - go version
+build_script:
+ - go get -t
+ - go test
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/doc.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/doc.go
new file mode 100644
index 0000000000..da67aba06d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/doc.go
@@ -0,0 +1,26 @@
+/*
+Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
+
+
+The simplest way to use Logrus is simply the package-level exported logger:
+
+ package main
+
+ import (
+ log "github.com/sirupsen/logrus"
+ )
+
+ func main() {
+ log.WithFields(log.Fields{
+ "animal": "walrus",
+ "number": 1,
+ "size": 10,
+ }).Info("A walrus appears")
+ }
+
+Output:
+ time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
+
+For a full guide visit https://github.com/sirupsen/logrus
+*/
+package logrus
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/entry.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/entry.go
new file mode 100644
index 0000000000..63e25583cb
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/entry.go
@@ -0,0 +1,407 @@
+package logrus
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "os"
+ "reflect"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ bufferPool *sync.Pool
+
+ // qualified package name, cached at first use
+ logrusPackage string
+
+ // Positions in the call stack when tracing to report the calling method
+ minimumCallerDepth int
+
+ // Used for caller information initialisation
+ callerInitOnce sync.Once
+)
+
+const (
+ maximumCallerDepth int = 25
+ knownLogrusFrames int = 4
+)
+
+func init() {
+ bufferPool = &sync.Pool{
+ New: func() interface{} {
+ return new(bytes.Buffer)
+ },
+ }
+
+ // start at the bottom of the stack before the package-name cache is primed
+ minimumCallerDepth = 1
+}
+
+// Defines the key when adding errors using WithError.
+var ErrorKey = "error"
+
+// An entry is the final or intermediate Logrus logging entry. It contains all
+// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
+// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
+// reused and passed around as much as you wish to avoid field duplication.
+type Entry struct {
+ Logger *Logger
+
+ // Contains all the fields set by the user.
+ Data Fields
+
+ // Time at which the log entry was created
+ Time time.Time
+
+ // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
+ // This field will be set on entry firing and the value will be equal to the one in Logger struct field.
+ Level Level
+
+ // Calling method, with package name
+ Caller *runtime.Frame
+
+ // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
+ Message string
+
+ // When formatter is called in entry.log(), a Buffer may be set to entry
+ Buffer *bytes.Buffer
+
+ // Contains the context set by the user. Useful for hook processing etc.
+ Context context.Context
+
+ // err may contain a field formatting error
+ err string
+}
+
+func NewEntry(logger *Logger) *Entry {
+ return &Entry{
+ Logger: logger,
+ // Default is three fields, plus one optional. Give a little extra room.
+ Data: make(Fields, 6),
+ }
+}
+
+// Returns the string representation from the reader and ultimately the
+// formatter.
+func (entry *Entry) String() (string, error) {
+ serialized, err := entry.Logger.Formatter.Format(entry)
+ if err != nil {
+ return "", err
+ }
+ str := string(serialized)
+ return str, nil
+}
+
+// Add an error as single field (using the key defined in ErrorKey) to the Entry.
+func (entry *Entry) WithError(err error) *Entry {
+ return entry.WithField(ErrorKey, err)
+}
+
+// Add a context to the Entry.
+func (entry *Entry) WithContext(ctx context.Context) *Entry {
+ return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
+}
+
+// Add a single field to the Entry.
+func (entry *Entry) WithField(key string, value interface{}) *Entry {
+ return entry.WithFields(Fields{key: value})
+}
+
+// Add a map of fields to the Entry.
+func (entry *Entry) WithFields(fields Fields) *Entry {
+ data := make(Fields, len(entry.Data)+len(fields))
+ for k, v := range entry.Data {
+ data[k] = v
+ }
+ fieldErr := entry.err
+ for k, v := range fields {
+ isErrField := false
+ if t := reflect.TypeOf(v); t != nil {
+ switch t.Kind() {
+ case reflect.Func:
+ isErrField = true
+ case reflect.Ptr:
+ isErrField = t.Elem().Kind() == reflect.Func
+ }
+ }
+ if isErrField {
+ tmp := fmt.Sprintf("can not add field %q", k)
+ if fieldErr != "" {
+ fieldErr = entry.err + ", " + tmp
+ } else {
+ fieldErr = tmp
+ }
+ } else {
+ data[k] = v
+ }
+ }
+ return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
+}
+
+// Overrides the time of the Entry.
+func (entry *Entry) WithTime(t time.Time) *Entry {
+ return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
+}
+
+// getPackageName reduces a fully qualified function name to the package name
+// There really ought to be to be a better way...
+func getPackageName(f string) string {
+ for {
+ lastPeriod := strings.LastIndex(f, ".")
+ lastSlash := strings.LastIndex(f, "/")
+ if lastPeriod > lastSlash {
+ f = f[:lastPeriod]
+ } else {
+ break
+ }
+ }
+
+ return f
+}
+
+// getCaller retrieves the name of the first non-logrus calling function
+func getCaller() *runtime.Frame {
+
+ // cache this package's fully-qualified name
+ callerInitOnce.Do(func() {
+ pcs := make([]uintptr, 2)
+ _ = runtime.Callers(0, pcs)
+ logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
+
+ // now that we have the cache, we can skip a minimum count of known-logrus functions
+ // XXX this is dubious, the number of frames may vary
+ minimumCallerDepth = knownLogrusFrames
+ })
+
+ // Restrict the lookback frames to avoid runaway lookups
+ pcs := make([]uintptr, maximumCallerDepth)
+ depth := runtime.Callers(minimumCallerDepth, pcs)
+ frames := runtime.CallersFrames(pcs[:depth])
+
+ for f, again := frames.Next(); again; f, again = frames.Next() {
+ pkg := getPackageName(f.Function)
+
+ // If the caller isn't part of this package, we're done
+ if pkg != logrusPackage {
+ return &f
+ }
+ }
+
+ // if we got here, we failed to find the caller's context
+ return nil
+}
+
+func (entry Entry) HasCaller() (has bool) {
+ return entry.Logger != nil &&
+ entry.Logger.ReportCaller &&
+ entry.Caller != nil
+}
+
+// This function is not declared with a pointer value because otherwise
+// race conditions will occur when using multiple goroutines
+func (entry Entry) log(level Level, msg string) {
+ var buffer *bytes.Buffer
+
+ // Default to now, but allow users to override if they want.
+ //
+ // We don't have to worry about polluting future calls to Entry#log()
+ // with this assignment because this function is declared with a
+ // non-pointer receiver.
+ if entry.Time.IsZero() {
+ entry.Time = time.Now()
+ }
+
+ entry.Level = level
+ entry.Message = msg
+ if entry.Logger.ReportCaller {
+ entry.Caller = getCaller()
+ }
+
+ entry.fireHooks()
+
+ buffer = bufferPool.Get().(*bytes.Buffer)
+ buffer.Reset()
+ defer bufferPool.Put(buffer)
+ entry.Buffer = buffer
+
+ entry.write()
+
+ entry.Buffer = nil
+
+ // To avoid Entry#log() returning a value that only would make sense for
+ // panic() to use in Entry#Panic(), we avoid the allocation by checking
+ // directly here.
+ if level <= PanicLevel {
+ panic(&entry)
+ }
+}
+
+func (entry *Entry) fireHooks() {
+ entry.Logger.mu.Lock()
+ defer entry.Logger.mu.Unlock()
+ err := entry.Logger.Hooks.Fire(entry.Level, entry)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
+ }
+}
+
+func (entry *Entry) write() {
+ entry.Logger.mu.Lock()
+ defer entry.Logger.mu.Unlock()
+ serialized, err := entry.Logger.Formatter.Format(entry)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
+ } else {
+ _, err = entry.Logger.Out.Write(serialized)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
+ }
+ }
+}
+
+func (entry *Entry) Log(level Level, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.log(level, fmt.Sprint(args...))
+ }
+}
+
+func (entry *Entry) Trace(args ...interface{}) {
+ entry.Log(TraceLevel, args...)
+}
+
+func (entry *Entry) Debug(args ...interface{}) {
+ entry.Log(DebugLevel, args...)
+}
+
+func (entry *Entry) Print(args ...interface{}) {
+ entry.Info(args...)
+}
+
+func (entry *Entry) Info(args ...interface{}) {
+ entry.Log(InfoLevel, args...)
+}
+
+func (entry *Entry) Warn(args ...interface{}) {
+ entry.Log(WarnLevel, args...)
+}
+
+func (entry *Entry) Warning(args ...interface{}) {
+ entry.Warn(args...)
+}
+
+func (entry *Entry) Error(args ...interface{}) {
+ entry.Log(ErrorLevel, args...)
+}
+
+func (entry *Entry) Fatal(args ...interface{}) {
+ entry.Log(FatalLevel, args...)
+ entry.Logger.Exit(1)
+}
+
+func (entry *Entry) Panic(args ...interface{}) {
+ entry.Log(PanicLevel, args...)
+ panic(fmt.Sprint(args...))
+}
+
+// Entry Printf family functions
+
+func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.Log(level, fmt.Sprintf(format, args...))
+ }
+}
+
+func (entry *Entry) Tracef(format string, args ...interface{}) {
+ entry.Logf(TraceLevel, format, args...)
+}
+
+func (entry *Entry) Debugf(format string, args ...interface{}) {
+ entry.Logf(DebugLevel, format, args...)
+}
+
+func (entry *Entry) Infof(format string, args ...interface{}) {
+ entry.Logf(InfoLevel, format, args...)
+}
+
+func (entry *Entry) Printf(format string, args ...interface{}) {
+ entry.Infof(format, args...)
+}
+
+func (entry *Entry) Warnf(format string, args ...interface{}) {
+ entry.Logf(WarnLevel, format, args...)
+}
+
+func (entry *Entry) Warningf(format string, args ...interface{}) {
+ entry.Warnf(format, args...)
+}
+
+func (entry *Entry) Errorf(format string, args ...interface{}) {
+ entry.Logf(ErrorLevel, format, args...)
+}
+
+func (entry *Entry) Fatalf(format string, args ...interface{}) {
+ entry.Logf(FatalLevel, format, args...)
+ entry.Logger.Exit(1)
+}
+
+func (entry *Entry) Panicf(format string, args ...interface{}) {
+ entry.Logf(PanicLevel, format, args...)
+}
+
+// Entry Println family functions
+
+func (entry *Entry) Logln(level Level, args ...interface{}) {
+ if entry.Logger.IsLevelEnabled(level) {
+ entry.Log(level, entry.sprintlnn(args...))
+ }
+}
+
+func (entry *Entry) Traceln(args ...interface{}) {
+ entry.Logln(TraceLevel, args...)
+}
+
+func (entry *Entry) Debugln(args ...interface{}) {
+ entry.Logln(DebugLevel, args...)
+}
+
+func (entry *Entry) Infoln(args ...interface{}) {
+ entry.Logln(InfoLevel, args...)
+}
+
+func (entry *Entry) Println(args ...interface{}) {
+ entry.Infoln(args...)
+}
+
+func (entry *Entry) Warnln(args ...interface{}) {
+ entry.Logln(WarnLevel, args...)
+}
+
+func (entry *Entry) Warningln(args ...interface{}) {
+ entry.Warnln(args...)
+}
+
+func (entry *Entry) Errorln(args ...interface{}) {
+ entry.Logln(ErrorLevel, args...)
+}
+
+func (entry *Entry) Fatalln(args ...interface{}) {
+ entry.Logln(FatalLevel, args...)
+ entry.Logger.Exit(1)
+}
+
+func (entry *Entry) Panicln(args ...interface{}) {
+ entry.Logln(PanicLevel, args...)
+}
+
+// Sprintlnn => Sprint no newline. This is to get the behavior of how
+// fmt.Sprintln where spaces are always added between operands, regardless of
+// their type. Instead of vendoring the Sprintln implementation to spare a
+// string allocation, we do the simplest thing.
+func (entry *Entry) sprintlnn(args ...interface{}) string {
+ msg := fmt.Sprintln(args...)
+ return msg[:len(msg)-1]
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/exported.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/exported.go
new file mode 100644
index 0000000000..62fc2f2193
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/exported.go
@@ -0,0 +1,225 @@
+package logrus
+
+import (
+ "context"
+ "io"
+ "time"
+)
+
+var (
+ // std is the name of the standard logger in stdlib `log`
+ std = New()
+)
+
+func StandardLogger() *Logger {
+ return std
+}
+
+// SetOutput sets the standard logger output.
+func SetOutput(out io.Writer) {
+ std.SetOutput(out)
+}
+
+// SetFormatter sets the standard logger formatter.
+func SetFormatter(formatter Formatter) {
+ std.SetFormatter(formatter)
+}
+
+// SetReportCaller sets whether the standard logger will include the calling
+// method as a field.
+func SetReportCaller(include bool) {
+ std.SetReportCaller(include)
+}
+
+// SetLevel sets the standard logger level.
+func SetLevel(level Level) {
+ std.SetLevel(level)
+}
+
+// GetLevel returns the standard logger level.
+func GetLevel() Level {
+ return std.GetLevel()
+}
+
+// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
+func IsLevelEnabled(level Level) bool {
+ return std.IsLevelEnabled(level)
+}
+
+// AddHook adds a hook to the standard logger hooks.
+func AddHook(hook Hook) {
+ std.AddHook(hook)
+}
+
+// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
+func WithError(err error) *Entry {
+ return std.WithField(ErrorKey, err)
+}
+
+// WithContext creates an entry from the standard logger and adds a context to it.
+func WithContext(ctx context.Context) *Entry {
+ return std.WithContext(ctx)
+}
+
+// WithField creates an entry from the standard logger and adds a field to
+// it. If you want multiple fields, use `WithFields`.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithField(key string, value interface{}) *Entry {
+ return std.WithField(key, value)
+}
+
+// WithFields creates an entry from the standard logger and adds multiple
+// fields to it. This is simply a helper for `WithField`, invoking it
+// once for each field.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithFields(fields Fields) *Entry {
+ return std.WithFields(fields)
+}
+
+// WithTime creats an entry from the standard logger and overrides the time of
+// logs generated with it.
+//
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
+// or Panic on the Entry it returns.
+func WithTime(t time.Time) *Entry {
+ return std.WithTime(t)
+}
+
+// Trace logs a message at level Trace on the standard logger.
+func Trace(args ...interface{}) {
+ std.Trace(args...)
+}
+
+// Debug logs a message at level Debug on the standard logger.
+func Debug(args ...interface{}) {
+ std.Debug(args...)
+}
+
+// Print logs a message at level Info on the standard logger.
+func Print(args ...interface{}) {
+ std.Print(args...)
+}
+
+// Info logs a message at level Info on the standard logger.
+func Info(args ...interface{}) {
+ std.Info(args...)
+}
+
+// Warn logs a message at level Warn on the standard logger.
+func Warn(args ...interface{}) {
+ std.Warn(args...)
+}
+
+// Warning logs a message at level Warn on the standard logger.
+func Warning(args ...interface{}) {
+ std.Warning(args...)
+}
+
+// Error logs a message at level Error on the standard logger.
+func Error(args ...interface{}) {
+ std.Error(args...)
+}
+
+// Panic logs a message at level Panic on the standard logger.
+func Panic(args ...interface{}) {
+ std.Panic(args...)
+}
+
+// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatal(args ...interface{}) {
+ std.Fatal(args...)
+}
+
+// Tracef logs a message at level Trace on the standard logger.
+func Tracef(format string, args ...interface{}) {
+ std.Tracef(format, args...)
+}
+
+// Debugf logs a message at level Debug on the standard logger.
+func Debugf(format string, args ...interface{}) {
+ std.Debugf(format, args...)
+}
+
+// Printf logs a message at level Info on the standard logger.
+func Printf(format string, args ...interface{}) {
+ std.Printf(format, args...)
+}
+
+// Infof logs a message at level Info on the standard logger.
+func Infof(format string, args ...interface{}) {
+ std.Infof(format, args...)
+}
+
+// Warnf logs a message at level Warn on the standard logger.
+func Warnf(format string, args ...interface{}) {
+ std.Warnf(format, args...)
+}
+
+// Warningf logs a message at level Warn on the standard logger.
+func Warningf(format string, args ...interface{}) {
+ std.Warningf(format, args...)
+}
+
+// Errorf logs a message at level Error on the standard logger.
+func Errorf(format string, args ...interface{}) {
+ std.Errorf(format, args...)
+}
+
+// Panicf logs a message at level Panic on the standard logger.
+func Panicf(format string, args ...interface{}) {
+ std.Panicf(format, args...)
+}
+
+// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatalf(format string, args ...interface{}) {
+ std.Fatalf(format, args...)
+}
+
+// Traceln logs a message at level Trace on the standard logger.
+func Traceln(args ...interface{}) {
+ std.Traceln(args...)
+}
+
+// Debugln logs a message at level Debug on the standard logger.
+func Debugln(args ...interface{}) {
+ std.Debugln(args...)
+}
+
+// Println logs a message at level Info on the standard logger.
+func Println(args ...interface{}) {
+ std.Println(args...)
+}
+
+// Infoln logs a message at level Info on the standard logger.
+func Infoln(args ...interface{}) {
+ std.Infoln(args...)
+}
+
+// Warnln logs a message at level Warn on the standard logger.
+func Warnln(args ...interface{}) {
+ std.Warnln(args...)
+}
+
+// Warningln logs a message at level Warn on the standard logger.
+func Warningln(args ...interface{}) {
+ std.Warningln(args...)
+}
+
+// Errorln logs a message at level Error on the standard logger.
+func Errorln(args ...interface{}) {
+ std.Errorln(args...)
+}
+
+// Panicln logs a message at level Panic on the standard logger.
+func Panicln(args ...interface{}) {
+ std.Panicln(args...)
+}
+
+// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
+func Fatalln(args ...interface{}) {
+ std.Fatalln(args...)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/formatter.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/formatter.go
new file mode 100644
index 0000000000..408883773e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/formatter.go
@@ -0,0 +1,78 @@
+package logrus
+
+import "time"
+
+// Default key names for the default fields
+const (
+ defaultTimestampFormat = time.RFC3339
+ FieldKeyMsg = "msg"
+ FieldKeyLevel = "level"
+ FieldKeyTime = "time"
+ FieldKeyLogrusError = "logrus_error"
+ FieldKeyFunc = "func"
+ FieldKeyFile = "file"
+)
+
+// The Formatter interface is used to implement a custom Formatter. It takes an
+// `Entry`. It exposes all the fields, including the default ones:
+//
+// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
+// * `entry.Data["time"]`. The timestamp.
+// * `entry.Data["level"]. The level the entry was logged at.
+//
+// Any additional fields added with `WithField` or `WithFields` are also in
+// `entry.Data`. Format is expected to return an array of bytes which are then
+// logged to `logger.Out`.
+type Formatter interface {
+ Format(*Entry) ([]byte, error)
+}
+
+// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+// logrus.WithField("level", 1).Info("hello")
+//
+// Would just silently drop the user provided level. Instead with this code
+// it'll logged as:
+//
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+//
+// It's not exported because it's still using Data in an opinionated way. It's to
+// avoid code duplication between the two default formatters.
+func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) {
+ timeKey := fieldMap.resolve(FieldKeyTime)
+ if t, ok := data[timeKey]; ok {
+ data["fields."+timeKey] = t
+ delete(data, timeKey)
+ }
+
+ msgKey := fieldMap.resolve(FieldKeyMsg)
+ if m, ok := data[msgKey]; ok {
+ data["fields."+msgKey] = m
+ delete(data, msgKey)
+ }
+
+ levelKey := fieldMap.resolve(FieldKeyLevel)
+ if l, ok := data[levelKey]; ok {
+ data["fields."+levelKey] = l
+ delete(data, levelKey)
+ }
+
+ logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
+ if l, ok := data[logrusErrKey]; ok {
+ data["fields."+logrusErrKey] = l
+ delete(data, logrusErrKey)
+ }
+
+ // If reportCaller is not set, 'func' will not conflict.
+ if reportCaller {
+ funcKey := fieldMap.resolve(FieldKeyFunc)
+ if l, ok := data[funcKey]; ok {
+ data["fields."+funcKey] = l
+ }
+ fileKey := fieldMap.resolve(FieldKeyFile)
+ if l, ok := data[fileKey]; ok {
+ data["fields."+fileKey] = l
+ }
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.mod
new file mode 100644
index 0000000000..12fdf98984
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.mod
@@ -0,0 +1,10 @@
+module github.com/sirupsen/logrus
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.1
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/stretchr/objx v0.1.1 // indirect
+ github.com/stretchr/testify v1.2.2
+ golang.org/x/sys v0.0.0-20190422165155-953cdadca894
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.sum b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.sum
new file mode 100644
index 0000000000..596c318b9f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/go.sum
@@ -0,0 +1,16 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
+github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/hooks.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/hooks.go
new file mode 100644
index 0000000000..3f151cdc39
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/hooks.go
@@ -0,0 +1,34 @@
+package logrus
+
+// A hook to be fired when logging on the logging levels returned from
+// `Levels()` on your implementation of the interface. Note that this is not
+// fired in a goroutine or a channel with workers, you should handle such
+// functionality yourself if your call is non-blocking and you don't wish for
+// the logging calls for levels returned from `Levels()` to block.
+type Hook interface {
+ Levels() []Level
+ Fire(*Entry) error
+}
+
+// Internal type for storing the hooks on a logger instance.
+type LevelHooks map[Level][]Hook
+
+// Add a hook to an instance of logger. This is called with
+// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
+func (hooks LevelHooks) Add(hook Hook) {
+ for _, level := range hook.Levels() {
+ hooks[level] = append(hooks[level], hook)
+ }
+}
+
+// Fire all the hooks for the passed level. Used by `entry.log` to fire
+// appropriate hooks for a log entry.
+func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
+ for _, hook := range hooks[level] {
+ if err := hook.Fire(entry); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/json_formatter.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/json_formatter.go
new file mode 100644
index 0000000000..098a21a067
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/json_formatter.go
@@ -0,0 +1,121 @@
+package logrus
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "runtime"
+)
+
+type fieldKey string
+
+// FieldMap allows customization of the key names for default fields.
+type FieldMap map[fieldKey]string
+
+func (f FieldMap) resolve(key fieldKey) string {
+ if k, ok := f[key]; ok {
+ return k
+ }
+
+ return string(key)
+}
+
+// JSONFormatter formats logs into parsable json
+type JSONFormatter struct {
+ // TimestampFormat sets the format used for marshaling timestamps.
+ TimestampFormat string
+
+ // DisableTimestamp allows disabling automatic timestamps in output
+ DisableTimestamp bool
+
+ // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
+ DataKey string
+
+ // FieldMap allows users to customize the names of keys for default fields.
+ // As an example:
+ // formatter := &JSONFormatter{
+ // FieldMap: FieldMap{
+ // FieldKeyTime: "@timestamp",
+ // FieldKeyLevel: "@level",
+ // FieldKeyMsg: "@message",
+ // FieldKeyFunc: "@caller",
+ // },
+ // }
+ FieldMap FieldMap
+
+ // CallerPrettyfier can be set by the user to modify the content
+ // of the function and file keys in the json data when ReportCaller is
+ // activated. If any of the returned value is the empty string the
+ // corresponding key will be removed from json fields.
+ CallerPrettyfier func(*runtime.Frame) (function string, file string)
+
+ // PrettyPrint will indent all json logs
+ PrettyPrint bool
+}
+
+// Format renders a single log entry
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
+ data := make(Fields, len(entry.Data)+4)
+ for k, v := range entry.Data {
+ switch v := v.(type) {
+ case error:
+ // Otherwise errors are ignored by `encoding/json`
+ // https://github.com/sirupsen/logrus/issues/137
+ data[k] = v.Error()
+ default:
+ data[k] = v
+ }
+ }
+
+ if f.DataKey != "" {
+ newData := make(Fields, 4)
+ newData[f.DataKey] = data
+ data = newData
+ }
+
+ prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = defaultTimestampFormat
+ }
+
+ if entry.err != "" {
+ data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
+ }
+ if !f.DisableTimestamp {
+ data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
+ }
+ data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
+ data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
+ if entry.HasCaller() {
+ funcVal := entry.Caller.Function
+ fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ }
+ if funcVal != "" {
+ data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
+ }
+ if fileVal != "" {
+ data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
+ }
+ }
+
+ var b *bytes.Buffer
+ if entry.Buffer != nil {
+ b = entry.Buffer
+ } else {
+ b = &bytes.Buffer{}
+ }
+
+ encoder := json.NewEncoder(b)
+ if f.PrettyPrint {
+ encoder.SetIndent("", " ")
+ }
+ if err := encoder.Encode(data); err != nil {
+ return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
+ }
+
+ return b.Bytes(), nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logger.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logger.go
new file mode 100644
index 0000000000..c0c0b1e559
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logger.go
@@ -0,0 +1,351 @@
+package logrus
+
+import (
+ "context"
+ "io"
+ "os"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+type Logger struct {
+ // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
+ // file, or leave it default which is `os.Stderr`. You can also set this to
+ // something more adventurous, such as logging to Kafka.
+ Out io.Writer
+ // Hooks for the logger instance. These allow firing events based on logging
+ // levels and log entries. For example, to send errors to an error tracking
+ // service, log to StatsD or dump the core on fatal errors.
+ Hooks LevelHooks
+ // All log entries pass through the formatter before logged to Out. The
+ // included formatters are `TextFormatter` and `JSONFormatter` for which
+ // TextFormatter is the default. In development (when a TTY is attached) it
+ // logs with colors, but to a file it wouldn't. You can easily implement your
+ // own that implements the `Formatter` interface, see the `README` or included
+ // formatters for examples.
+ Formatter Formatter
+
+ // Flag for whether to log caller info (off by default)
+ ReportCaller bool
+
+ // The logging level the logger should log at. This is typically (and defaults
+ // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
+ // logged.
+ Level Level
+ // Used to sync writing to the log. Locking is enabled by Default
+ mu MutexWrap
+ // Reusable empty entry
+ entryPool sync.Pool
+ // Function to exit the application, defaults to `os.Exit()`
+ ExitFunc exitFunc
+}
+
+type exitFunc func(int)
+
+type MutexWrap struct {
+ lock sync.Mutex
+ disabled bool
+}
+
+func (mw *MutexWrap) Lock() {
+ if !mw.disabled {
+ mw.lock.Lock()
+ }
+}
+
+func (mw *MutexWrap) Unlock() {
+ if !mw.disabled {
+ mw.lock.Unlock()
+ }
+}
+
+func (mw *MutexWrap) Disable() {
+ mw.disabled = true
+}
+
+// Creates a new logger. Configuration should be set by changing `Formatter`,
+// `Out` and `Hooks` directly on the default logger instance. You can also just
+// instantiate your own:
+//
+// var log = &Logger{
+// Out: os.Stderr,
+// Formatter: new(JSONFormatter),
+// Hooks: make(LevelHooks),
+// Level: logrus.DebugLevel,
+// }
+//
+// It's recommended to make this a global instance called `log`.
+func New() *Logger {
+ return &Logger{
+ Out: os.Stderr,
+ Formatter: new(TextFormatter),
+ Hooks: make(LevelHooks),
+ Level: InfoLevel,
+ ExitFunc: os.Exit,
+ ReportCaller: false,
+ }
+}
+
+func (logger *Logger) newEntry() *Entry {
+ entry, ok := logger.entryPool.Get().(*Entry)
+ if ok {
+ return entry
+ }
+ return NewEntry(logger)
+}
+
+func (logger *Logger) releaseEntry(entry *Entry) {
+ entry.Data = map[string]interface{}{}
+ logger.entryPool.Put(entry)
+}
+
+// Adds a field to the log entry, note that it doesn't log until you call
+// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
+// If you want multiple fields, use `WithFields`.
+func (logger *Logger) WithField(key string, value interface{}) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithField(key, value)
+}
+
+// Adds a struct of fields to the log entry. All it does is call `WithField` for
+// each `Field`.
+func (logger *Logger) WithFields(fields Fields) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithFields(fields)
+}
+
+// Add an error as single field to the log entry. All it does is call
+// `WithError` for the given `error`.
+func (logger *Logger) WithError(err error) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithError(err)
+}
+
+// Add a context to the log entry.
+func (logger *Logger) WithContext(ctx context.Context) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithContext(ctx)
+}
+
+// Overrides the time of the log entry.
+func (logger *Logger) WithTime(t time.Time) *Entry {
+ entry := logger.newEntry()
+ defer logger.releaseEntry(entry)
+ return entry.WithTime(t)
+}
+
+func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
+ entry := logger.newEntry()
+ entry.Logf(level, format, args...)
+ logger.releaseEntry(entry)
+ }
+}
+
+func (logger *Logger) Tracef(format string, args ...interface{}) {
+ logger.Logf(TraceLevel, format, args...)
+}
+
+func (logger *Logger) Debugf(format string, args ...interface{}) {
+ logger.Logf(DebugLevel, format, args...)
+}
+
+func (logger *Logger) Infof(format string, args ...interface{}) {
+ logger.Logf(InfoLevel, format, args...)
+}
+
+func (logger *Logger) Printf(format string, args ...interface{}) {
+ entry := logger.newEntry()
+ entry.Printf(format, args...)
+ logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warnf(format string, args ...interface{}) {
+ logger.Logf(WarnLevel, format, args...)
+}
+
+func (logger *Logger) Warningf(format string, args ...interface{}) {
+ logger.Warnf(format, args...)
+}
+
+func (logger *Logger) Errorf(format string, args ...interface{}) {
+ logger.Logf(ErrorLevel, format, args...)
+}
+
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
+ logger.Logf(FatalLevel, format, args...)
+ logger.Exit(1)
+}
+
+func (logger *Logger) Panicf(format string, args ...interface{}) {
+ logger.Logf(PanicLevel, format, args...)
+}
+
+func (logger *Logger) Log(level Level, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
+ entry := logger.newEntry()
+ entry.Log(level, args...)
+ logger.releaseEntry(entry)
+ }
+}
+
+func (logger *Logger) Trace(args ...interface{}) {
+ logger.Log(TraceLevel, args...)
+}
+
+func (logger *Logger) Debug(args ...interface{}) {
+ logger.Log(DebugLevel, args...)
+}
+
+func (logger *Logger) Info(args ...interface{}) {
+ logger.Log(InfoLevel, args...)
+}
+
+func (logger *Logger) Print(args ...interface{}) {
+ entry := logger.newEntry()
+ entry.Print(args...)
+ logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warn(args ...interface{}) {
+ logger.Log(WarnLevel, args...)
+}
+
+func (logger *Logger) Warning(args ...interface{}) {
+ logger.Warn(args...)
+}
+
+func (logger *Logger) Error(args ...interface{}) {
+ logger.Log(ErrorLevel, args...)
+}
+
+func (logger *Logger) Fatal(args ...interface{}) {
+ logger.Log(FatalLevel, args...)
+ logger.Exit(1)
+}
+
+func (logger *Logger) Panic(args ...interface{}) {
+ logger.Log(PanicLevel, args...)
+}
+
+func (logger *Logger) Logln(level Level, args ...interface{}) {
+ if logger.IsLevelEnabled(level) {
+ entry := logger.newEntry()
+ entry.Logln(level, args...)
+ logger.releaseEntry(entry)
+ }
+}
+
+func (logger *Logger) Traceln(args ...interface{}) {
+ logger.Logln(TraceLevel, args...)
+}
+
+func (logger *Logger) Debugln(args ...interface{}) {
+ logger.Logln(DebugLevel, args...)
+}
+
+func (logger *Logger) Infoln(args ...interface{}) {
+ logger.Logln(InfoLevel, args...)
+}
+
+func (logger *Logger) Println(args ...interface{}) {
+ entry := logger.newEntry()
+ entry.Println(args...)
+ logger.releaseEntry(entry)
+}
+
+func (logger *Logger) Warnln(args ...interface{}) {
+ logger.Logln(WarnLevel, args...)
+}
+
+func (logger *Logger) Warningln(args ...interface{}) {
+ logger.Warnln(args...)
+}
+
+func (logger *Logger) Errorln(args ...interface{}) {
+ logger.Logln(ErrorLevel, args...)
+}
+
+func (logger *Logger) Fatalln(args ...interface{}) {
+ logger.Logln(FatalLevel, args...)
+ logger.Exit(1)
+}
+
+func (logger *Logger) Panicln(args ...interface{}) {
+ logger.Logln(PanicLevel, args...)
+}
+
+func (logger *Logger) Exit(code int) {
+ runHandlers()
+ if logger.ExitFunc == nil {
+ logger.ExitFunc = os.Exit
+ }
+ logger.ExitFunc(code)
+}
+
+//When file is opened with appending mode, it's safe to
+//write concurrently to a file (within 4k message on Linux).
+//In these cases user can choose to disable the lock.
+func (logger *Logger) SetNoLock() {
+ logger.mu.Disable()
+}
+
+func (logger *Logger) level() Level {
+ return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
+}
+
+// SetLevel sets the logger level.
+func (logger *Logger) SetLevel(level Level) {
+ atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
+}
+
+// GetLevel returns the logger level.
+func (logger *Logger) GetLevel() Level {
+ return logger.level()
+}
+
+// AddHook adds a hook to the logger hooks.
+func (logger *Logger) AddHook(hook Hook) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Hooks.Add(hook)
+}
+
+// IsLevelEnabled checks if the log level of the logger is greater than the level param
+func (logger *Logger) IsLevelEnabled(level Level) bool {
+ return logger.level() >= level
+}
+
+// SetFormatter sets the logger formatter.
+func (logger *Logger) SetFormatter(formatter Formatter) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Formatter = formatter
+}
+
+// SetOutput sets the logger output.
+func (logger *Logger) SetOutput(output io.Writer) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Out = output
+}
+
+func (logger *Logger) SetReportCaller(reportCaller bool) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.ReportCaller = reportCaller
+}
+
+// ReplaceHooks replaces the logger hooks and returns the old ones
+func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
+ logger.mu.Lock()
+ oldHooks := logger.Hooks
+ logger.Hooks = hooks
+ logger.mu.Unlock()
+ return oldHooks
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logrus.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logrus.go
new file mode 100644
index 0000000000..8644761f73
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/logrus.go
@@ -0,0 +1,186 @@
+package logrus
+
+import (
+ "fmt"
+ "log"
+ "strings"
+)
+
+// Fields type, used to pass to `WithFields`.
+type Fields map[string]interface{}
+
+// Level type
+type Level uint32
+
+// Convert the Level to a string. E.g. PanicLevel becomes "panic".
+func (level Level) String() string {
+ if b, err := level.MarshalText(); err == nil {
+ return string(b)
+ } else {
+ return "unknown"
+ }
+}
+
+// ParseLevel takes a string level and returns the Logrus log level constant.
+func ParseLevel(lvl string) (Level, error) {
+ switch strings.ToLower(lvl) {
+ case "panic":
+ return PanicLevel, nil
+ case "fatal":
+ return FatalLevel, nil
+ case "error":
+ return ErrorLevel, nil
+ case "warn", "warning":
+ return WarnLevel, nil
+ case "info":
+ return InfoLevel, nil
+ case "debug":
+ return DebugLevel, nil
+ case "trace":
+ return TraceLevel, nil
+ }
+
+ var l Level
+ return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (level *Level) UnmarshalText(text []byte) error {
+ l, err := ParseLevel(string(text))
+ if err != nil {
+ return err
+ }
+
+ *level = Level(l)
+
+ return nil
+}
+
+func (level Level) MarshalText() ([]byte, error) {
+ switch level {
+ case TraceLevel:
+ return []byte("trace"), nil
+ case DebugLevel:
+ return []byte("debug"), nil
+ case InfoLevel:
+ return []byte("info"), nil
+ case WarnLevel:
+ return []byte("warning"), nil
+ case ErrorLevel:
+ return []byte("error"), nil
+ case FatalLevel:
+ return []byte("fatal"), nil
+ case PanicLevel:
+ return []byte("panic"), nil
+ }
+
+ return nil, fmt.Errorf("not a valid logrus level %d", level)
+}
+
+// A constant exposing all logging levels
+var AllLevels = []Level{
+ PanicLevel,
+ FatalLevel,
+ ErrorLevel,
+ WarnLevel,
+ InfoLevel,
+ DebugLevel,
+ TraceLevel,
+}
+
+// These are the different logging levels. You can set the logging level to log
+// on your instance of logger, obtained with `logrus.New()`.
+const (
+ // PanicLevel level, highest level of severity. Logs and then calls panic with the
+ // message passed to Debug, Info, ...
+ PanicLevel Level = iota
+ // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
+ // logging level is set to Panic.
+ FatalLevel
+ // ErrorLevel level. Logs. Used for errors that should definitely be noted.
+ // Commonly used for hooks to send errors to an error tracking service.
+ ErrorLevel
+ // WarnLevel level. Non-critical entries that deserve eyes.
+ WarnLevel
+ // InfoLevel level. General operational entries about what's going on inside the
+ // application.
+ InfoLevel
+ // DebugLevel level. Usually only enabled when debugging. Very verbose logging.
+ DebugLevel
+ // TraceLevel level. Designates finer-grained informational events than the Debug.
+ TraceLevel
+)
+
+// Won't compile if StdLogger can't be realized by a log.Logger
+var (
+ _ StdLogger = &log.Logger{}
+ _ StdLogger = &Entry{}
+ _ StdLogger = &Logger{}
+)
+
+// StdLogger is what your logrus-enabled library should take, that way
+// it'll accept a stdlib logger and a logrus logger. There's no standard
+// interface, this is the closest we get, unfortunately.
+type StdLogger interface {
+ Print(...interface{})
+ Printf(string, ...interface{})
+ Println(...interface{})
+
+ Fatal(...interface{})
+ Fatalf(string, ...interface{})
+ Fatalln(...interface{})
+
+ Panic(...interface{})
+ Panicf(string, ...interface{})
+ Panicln(...interface{})
+}
+
+// The FieldLogger interface generalizes the Entry and Logger types
+type FieldLogger interface {
+ WithField(key string, value interface{}) *Entry
+ WithFields(fields Fields) *Entry
+ WithError(err error) *Entry
+
+ Debugf(format string, args ...interface{})
+ Infof(format string, args ...interface{})
+ Printf(format string, args ...interface{})
+ Warnf(format string, args ...interface{})
+ Warningf(format string, args ...interface{})
+ Errorf(format string, args ...interface{})
+ Fatalf(format string, args ...interface{})
+ Panicf(format string, args ...interface{})
+
+ Debug(args ...interface{})
+ Info(args ...interface{})
+ Print(args ...interface{})
+ Warn(args ...interface{})
+ Warning(args ...interface{})
+ Error(args ...interface{})
+ Fatal(args ...interface{})
+ Panic(args ...interface{})
+
+ Debugln(args ...interface{})
+ Infoln(args ...interface{})
+ Println(args ...interface{})
+ Warnln(args ...interface{})
+ Warningln(args ...interface{})
+ Errorln(args ...interface{})
+ Fatalln(args ...interface{})
+ Panicln(args ...interface{})
+
+ // IsDebugEnabled() bool
+ // IsInfoEnabled() bool
+ // IsWarnEnabled() bool
+ // IsErrorEnabled() bool
+ // IsFatalEnabled() bool
+ // IsPanicEnabled() bool
+}
+
+// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
+// here for consistancy. Do not use. Use Logger or Entry instead.
+type Ext1FieldLogger interface {
+ FieldLogger
+ Tracef(format string, args ...interface{})
+ Trace(args ...interface{})
+ Traceln(args ...interface{})
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
new file mode 100644
index 0000000000..2403de9819
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
@@ -0,0 +1,11 @@
+// +build appengine
+
+package logrus
+
+import (
+ "io"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+ return true
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
new file mode 100644
index 0000000000..3c4f43f91c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
@@ -0,0 +1,13 @@
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
new file mode 100644
index 0000000000..97af92c68e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
@@ -0,0 +1,11 @@
+// +build js nacl plan9
+
+package logrus
+
+import (
+ "io"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+ return false
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
new file mode 100644
index 0000000000..3293fb3caa
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
@@ -0,0 +1,17 @@
+// +build !appengine,!js,!windows,!nacl,!plan9
+
+package logrus
+
+import (
+ "io"
+ "os"
+)
+
+func checkIfTerminal(w io.Writer) bool {
+ switch v := w.(type) {
+ case *os.File:
+ return isTerminal(int(v.Fd()))
+ default:
+ return false
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
new file mode 100644
index 0000000000..f6710b3bd0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
@@ -0,0 +1,11 @@
+package logrus
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+ return err == nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
new file mode 100644
index 0000000000..355dc966f0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_unix.go
@@ -0,0 +1,13 @@
+// +build linux aix
+
+package logrus
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_windows.go
new file mode 100644
index 0000000000..572889db21
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/terminal_check_windows.go
@@ -0,0 +1,34 @@
+// +build !appengine,!js,windows
+
+package logrus
+
+import (
+ "io"
+ "os"
+ "syscall"
+
+ sequences "github.com/konsorten/go-windows-terminal-sequences"
+)
+
+func initTerminal(w io.Writer) {
+ switch v := w.(type) {
+ case *os.File:
+ sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
+ }
+}
+
+func checkIfTerminal(w io.Writer) bool {
+ var ret bool
+ switch v := w.(type) {
+ case *os.File:
+ var mode uint32
+ err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode)
+ ret = (err == nil)
+ default:
+ ret = false
+ }
+ if ret {
+ initTerminal(w)
+ }
+ return ret
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/text_formatter.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/text_formatter.go
new file mode 100644
index 0000000000..e01587c437
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/text_formatter.go
@@ -0,0 +1,295 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ red = 31
+ yellow = 33
+ blue = 36
+ gray = 37
+)
+
+var baseTimestamp time.Time
+
+func init() {
+ baseTimestamp = time.Now()
+}
+
+// TextFormatter formats logs into text
+type TextFormatter struct {
+ // Set to true to bypass checking for a TTY before outputting colors.
+ ForceColors bool
+
+ // Force disabling colors.
+ DisableColors bool
+
+ // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
+ EnvironmentOverrideColors bool
+
+ // Disable timestamp logging. useful when output is redirected to logging
+ // system that already adds timestamps.
+ DisableTimestamp bool
+
+ // Enable logging the full timestamp when a TTY is attached instead of just
+ // the time passed since beginning of execution.
+ FullTimestamp bool
+
+ // TimestampFormat to use for display when a full timestamp is printed
+ TimestampFormat string
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
+
+ // The keys sorting function, when uninitialized it uses sort.Strings.
+ SortingFunc func([]string)
+
+ // Disables the truncation of the level text to 4 characters.
+ DisableLevelTruncation bool
+
+ // QuoteEmptyFields will wrap empty fields in quotes if true
+ QuoteEmptyFields bool
+
+ // Whether the logger's out is to a terminal
+ isTerminal bool
+
+ // FieldMap allows users to customize the names of keys for default fields.
+ // As an example:
+ // formatter := &TextFormatter{
+ // FieldMap: FieldMap{
+ // FieldKeyTime: "@timestamp",
+ // FieldKeyLevel: "@level",
+ // FieldKeyMsg: "@message"}}
+ FieldMap FieldMap
+
+ // CallerPrettyfier can be set by the user to modify the content
+ // of the function and file keys in the data when ReportCaller is
+ // activated. If any of the returned value is the empty string the
+ // corresponding key will be removed from fields.
+ CallerPrettyfier func(*runtime.Frame) (function string, file string)
+
+ terminalInitOnce sync.Once
+}
+
+func (f *TextFormatter) init(entry *Entry) {
+ if entry.Logger != nil {
+ f.isTerminal = checkIfTerminal(entry.Logger.Out)
+ }
+}
+
+func (f *TextFormatter) isColored() bool {
+ isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
+
+ if f.EnvironmentOverrideColors {
+ if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
+ isColored = true
+ } else if ok && force == "0" {
+ isColored = false
+ } else if os.Getenv("CLICOLOR") == "0" {
+ isColored = false
+ }
+ }
+
+ return isColored && !f.DisableColors
+}
+
+// Format renders a single log entry
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
+ data := make(Fields)
+ for k, v := range entry.Data {
+ data[k] = v
+ }
+ prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
+ keys := make([]string, 0, len(data))
+ for k := range data {
+ keys = append(keys, k)
+ }
+
+ var funcVal, fileVal string
+
+ fixedKeys := make([]string, 0, 4+len(data))
+ if !f.DisableTimestamp {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
+ }
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
+ if entry.Message != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
+ }
+ if entry.err != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
+ }
+ if entry.HasCaller() {
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ } else {
+ funcVal = entry.Caller.Function
+ fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+ }
+
+ if funcVal != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
+ }
+ if fileVal != "" {
+ fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
+ }
+ }
+
+ if !f.DisableSorting {
+ if f.SortingFunc == nil {
+ sort.Strings(keys)
+ fixedKeys = append(fixedKeys, keys...)
+ } else {
+ if !f.isColored() {
+ fixedKeys = append(fixedKeys, keys...)
+ f.SortingFunc(fixedKeys)
+ } else {
+ f.SortingFunc(keys)
+ }
+ }
+ } else {
+ fixedKeys = append(fixedKeys, keys...)
+ }
+
+ var b *bytes.Buffer
+ if entry.Buffer != nil {
+ b = entry.Buffer
+ } else {
+ b = &bytes.Buffer{}
+ }
+
+ f.terminalInitOnce.Do(func() { f.init(entry) })
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = defaultTimestampFormat
+ }
+ if f.isColored() {
+ f.printColored(b, entry, keys, data, timestampFormat)
+ } else {
+
+ for _, key := range fixedKeys {
+ var value interface{}
+ switch {
+ case key == f.FieldMap.resolve(FieldKeyTime):
+ value = entry.Time.Format(timestampFormat)
+ case key == f.FieldMap.resolve(FieldKeyLevel):
+ value = entry.Level.String()
+ case key == f.FieldMap.resolve(FieldKeyMsg):
+ value = entry.Message
+ case key == f.FieldMap.resolve(FieldKeyLogrusError):
+ value = entry.err
+ case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
+ value = funcVal
+ case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
+ value = fileVal
+ default:
+ value = data[key]
+ }
+ f.appendKeyValue(b, key, value)
+ }
+ }
+
+ b.WriteByte('\n')
+ return b.Bytes(), nil
+}
+
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
+ var levelColor int
+ switch entry.Level {
+ case DebugLevel, TraceLevel:
+ levelColor = gray
+ case WarnLevel:
+ levelColor = yellow
+ case ErrorLevel, FatalLevel, PanicLevel:
+ levelColor = red
+ default:
+ levelColor = blue
+ }
+
+ levelText := strings.ToUpper(entry.Level.String())
+ if !f.DisableLevelTruncation {
+ levelText = levelText[0:4]
+ }
+
+ // Remove a single newline if it already exists in the message to keep
+ // the behavior of logrus text_formatter the same as the stdlib log package
+ entry.Message = strings.TrimSuffix(entry.Message, "\n")
+
+ caller := ""
+ if entry.HasCaller() {
+ funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
+ fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
+
+ if f.CallerPrettyfier != nil {
+ funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
+ }
+
+ if fileVal == "" {
+ caller = funcVal
+ } else if funcVal == "" {
+ caller = fileVal
+ } else {
+ caller = fileVal + " " + funcVal
+ }
+ }
+
+ if f.DisableTimestamp {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
+ } else if !f.FullTimestamp {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
+ } else {
+ fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
+ }
+ for _, k := range keys {
+ v := data[k]
+ fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
+ f.appendValue(b, v)
+ }
+}
+
+func (f *TextFormatter) needsQuoting(text string) bool {
+ if f.QuoteEmptyFields && len(text) == 0 {
+ return true
+ }
+ for _, ch := range text {
+ if !((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
+ return true
+ }
+ }
+ return false
+}
+
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
+ if b.Len() > 0 {
+ b.WriteByte(' ')
+ }
+ b.WriteString(key)
+ b.WriteByte('=')
+ f.appendValue(b, value)
+}
+
+func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
+ stringVal, ok := value.(string)
+ if !ok {
+ stringVal = fmt.Sprint(value)
+ }
+
+ if !f.needsQuoting(stringVal) {
+ b.WriteString(stringVal)
+ } else {
+ b.WriteString(fmt.Sprintf("%q", stringVal))
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/writer.go b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/writer.go
new file mode 100644
index 0000000000..9e1f751359
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/sirupsen/logrus/writer.go
@@ -0,0 +1,64 @@
+package logrus
+
+import (
+ "bufio"
+ "io"
+ "runtime"
+)
+
+func (logger *Logger) Writer() *io.PipeWriter {
+ return logger.WriterLevel(InfoLevel)
+}
+
+func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
+ return NewEntry(logger).WriterLevel(level)
+}
+
+func (entry *Entry) Writer() *io.PipeWriter {
+ return entry.WriterLevel(InfoLevel)
+}
+
+func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
+ reader, writer := io.Pipe()
+
+ var printFunc func(args ...interface{})
+
+ switch level {
+ case TraceLevel:
+ printFunc = entry.Trace
+ case DebugLevel:
+ printFunc = entry.Debug
+ case InfoLevel:
+ printFunc = entry.Info
+ case WarnLevel:
+ printFunc = entry.Warn
+ case ErrorLevel:
+ printFunc = entry.Error
+ case FatalLevel:
+ printFunc = entry.Fatal
+ case PanicLevel:
+ printFunc = entry.Panic
+ default:
+ printFunc = entry.Print
+ }
+
+ go entry.writerScanner(reader, printFunc)
+ runtime.SetFinalizer(writer, writerFinalizer)
+
+ return writer
+}
+
+func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
+ scanner := bufio.NewScanner(reader)
+ for scanner.Scan() {
+ printFunc(scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ entry.Errorf("Error while reading from Writer: %s", err)
+ }
+ reader.Close()
+}
+
+func writerFinalizer(writer *io.PipeWriter) {
+ writer.Close()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/.gitignore
new file mode 100644
index 0000000000..daf913b1b3
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/LICENSE
new file mode 100644
index 0000000000..f20dac8ac9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Tevin Zhang
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/README.md
new file mode 100644
index 0000000000..042d1cadd9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/README.md
@@ -0,0 +1,49 @@
+# ABool :bulb:
+[](https://goreportcard.com/report/github.com/tevino/abool)
+[](https://godoc.org/github.com/tevino/abool)
+
+Atomic Boolean library for Go, optimized for performance yet simple to use.
+
+Use this for cleaner code.
+
+## Usage
+
+```go
+import "github.com/tevino/abool"
+
+cond := abool.New() // default to false
+
+cond.Set() // Set to true
+cond.IsSet() // Returns true
+cond.UnSet() // Set to false
+cond.SetTo(true) // Set to whatever you want
+cond.SetToIf(false, true) // Set to true if it is false, returns false(not set)
+
+
+// embedding
+type Foo struct {
+ cond *abool.AtomicBool // always use pointer to avoid copy
+}
+```
+
+## Benchmark:
+
+- Go 1.6.2
+- OS X 10.11.4
+
+```shell
+# Read
+BenchmarkMutexRead-4 100000000 21.0 ns/op
+BenchmarkAtomicValueRead-4 200000000 6.30 ns/op
+BenchmarkAtomicBoolRead-4 300000000 4.21 ns/op # <--- This package
+
+# Write
+BenchmarkMutexWrite-4 100000000 21.6 ns/op
+BenchmarkAtomicValueWrite-4 30000000 43.4 ns/op
+BenchmarkAtomicBoolWrite-4 200000000 9.87 ns/op # <--- This package
+
+# CAS
+BenchmarkMutexCAS-4 30000000 44.9 ns/op
+BenchmarkAtomicBoolCAS-4 100000000 11.7 ns/op # <--- This package
+```
+
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/bool.go b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/bool.go
new file mode 100644
index 0000000000..fdda210365
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/tevino/abool/bool.go
@@ -0,0 +1,63 @@
+// Package abool provides atomic Boolean type for cleaner code and
+// better performance.
+package abool
+
+import "sync/atomic"
+
+// New creates an AtomicBool with default to false
+func New() *AtomicBool {
+ return new(AtomicBool)
+}
+
+// NewBool creates an AtomicBool with given default value
+func NewBool(ok bool) *AtomicBool {
+ ab := New()
+ if ok {
+ ab.Set()
+ }
+ return ab
+}
+
+// AtomicBool is an atomic Boolean
+// Its methods are all atomic, thus safe to be called by
+// multiple goroutines simultaneously
+// Note: When embedding into a struct, one should always use
+// *AtomicBool to avoid copy
+type AtomicBool int32
+
+// Set sets the Boolean to true
+func (ab *AtomicBool) Set() {
+ atomic.StoreInt32((*int32)(ab), 1)
+}
+
+// UnSet sets the Boolean to false
+func (ab *AtomicBool) UnSet() {
+ atomic.StoreInt32((*int32)(ab), 0)
+}
+
+// IsSet returns whether the Boolean is true
+func (ab *AtomicBool) IsSet() bool {
+ return atomic.LoadInt32((*int32)(ab)) == 1
+}
+
+// SetTo sets the boolean with given Boolean
+func (ab *AtomicBool) SetTo(yes bool) {
+ if yes {
+ atomic.StoreInt32((*int32)(ab), 1)
+ } else {
+ atomic.StoreInt32((*int32)(ab), 0)
+ }
+}
+
+// SetToIf sets the Boolean to new only if the Boolean matches the old
+// Returns whether the set was done
+func (ab *AtomicBool) SetToIf(old, new bool) (set bool) {
+ var o, n int32
+ if old {
+ o = 1
+ }
+ if new {
+ n = 1
+ }
+ return atomic.CompareAndSwapInt32((*int32)(ab), o, n)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.gitignore b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.gitignore
new file mode 100644
index 0000000000..450aeaa906
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.gitignore
@@ -0,0 +1,34 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+# IDEA files
+.idea
+*.iml
+
+# OS X
+.DS_Store
+
+# Unit testing
+*.coverprofile
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.travis.yml b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.travis.yml
new file mode 100644
index 0000000000..0c80b19ab0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+
+go:
+ - 1.5
+ - 1.6
+ - 1.7
+
+install:
+ - make deps
+
+script:
+ - make test
+
+sudo: false
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/LICENSE
new file mode 100644
index 0000000000..b41cb23802
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Denis Parchenko
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/Makefile b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/Makefile
new file mode 100644
index 0000000000..70c028cc6c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/Makefile
@@ -0,0 +1,19 @@
+NAME=logrus-prefixed-formatter
+PACKAGES=$(shell go list ./...)
+
+deps:
+ @echo "--> Installing dependencies"
+ @go get -d -v -t ./...
+
+test-deps:
+ @which ginkgo 2>/dev/null ; if [ $$? -eq 1 ]; then \
+ go get -u -v github.com/onsi/ginkgo/ginkgo; \
+ fi
+
+test: test-deps
+ @echo "--> Running tests"
+ @ginkgo -r --randomizeAllSpecs --randomizeSuites --failOnPending --cover --trace --race
+
+format:
+ @echo "--> Running go fmt"
+ @go fmt $(PACKAGES)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/README.md
new file mode 100644
index 0000000000..cf10f270d6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/README.md
@@ -0,0 +1,116 @@
+# Logrus Prefixed Log Formatter
+[](https://travis-ci.org/x-cray/logrus-prefixed-formatter)
+
+[Logrus](https://github.com/sirupsen/logrus) formatter mainly based on original `logrus.TextFormatter` but with slightly
+modified colored output and support for log entry prefixes, e.g. message source followed by a colon. In addition, custom
+color themes are supported.
+
+
+
+Just like with the original `logrus.TextFormatter` when a TTY is not attached, the output is compatible with the
+[logfmt](http://godoc.org/github.com/kr/logfmt) format:
+
+```text
+time="Oct 27 00:44:26" level=debug msg="Started observing beach" animal=walrus number=8
+time="Oct 27 00:44:26" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+time="Oct 27 00:44:26" level=warning msg="The group's number increased tremendously!" number=122 omg=true
+time="Oct 27 00:44:26" level=debug msg="Temperature changes" temperature=-4
+time="Oct 27 00:44:26" level=panic msg="It's over 9000!" animal=orca size=9009
+time="Oct 27 00:44:26" level=fatal msg="The ice breaks!" number=100 omg=true
+exit status 1
+```
+
+## Installation
+To install formatter, use `go get`:
+
+```sh
+$ go get github.com/x-cray/logrus-prefixed-formatter
+```
+
+## Usage
+Here is how it should be used:
+
+```go
+package main
+
+import (
+ "github.com/sirupsen/logrus"
+ prefixed "github.com/x-cray/logrus-prefixed-formatter"
+)
+
+var log = logrus.New()
+
+func init() {
+ log.Formatter = new(prefixed.TextFormatter)
+ log.Level = logrus.DebugLevel
+}
+
+func main() {
+ log.WithFields(logrus.Fields{
+ "prefix": "main",
+ "animal": "walrus",
+ "number": 8,
+ }).Debug("Started observing beach")
+
+ log.WithFields(logrus.Fields{
+ "prefix": "sensor",
+ "temperature": -4,
+ }).Info("Temperature changes")
+}
+```
+
+## API
+`prefixed.TextFormatter` exposes the following fields and methods.
+
+### Fields
+
+* `ForceColors bool` — set to true to bypass checking for a TTY before outputting colors.
+* `DisableColors bool` — force disabling colors. For a TTY colors are enabled by default.
+* `DisableUppercase bool` — set to true to turn off the conversion of the log level names to uppercase.
+* `ForceFormatting bool` — force formatted layout, even for non-TTY output.
+* `DisableTimestamp bool` — disable timestamp logging. Useful when output is redirected to logging system that already adds timestamps.
+* `FullTimestamp bool` — enable logging the full timestamp when a TTY is attached instead of just the time passed since beginning of execution.
+* `TimestampFormat string` — timestamp format to use for display when a full timestamp is printed.
+* `DisableSorting bool` — the fields are sorted by default for a consistent output. For applications that log extremely frequently and don't use the JSON formatter this may not be desired.
+* `QuoteEmptyFields bool` — wrap empty fields in quotes if true.
+* `QuoteCharacter string` — can be set to the override the default quoting character `"` with something else. For example: `'`, or `` ` ``.
+* `SpacePadding int` — pad msg field with spaces on the right for display. The value for this parameter will be the size of padding. Its default value is zero, which means no padding will be applied.
+
+### Methods
+
+#### `SetColorScheme(colorScheme *prefixed.ColorScheme)`
+
+Sets an alternative color scheme for colored output. `prefixed.ColorScheme` struct supports the following fields:
+* `InfoLevelStyle string` — info level style.
+* `WarnLevelStyle string` — warn level style.
+* `ErrorLevelStyle string` — error style.
+* `FatalLevelStyle string` — fatal level style.
+* `PanicLevelStyle string` — panic level style.
+* `DebugLevelStyle string` — debug level style.
+* `PrefixStyle string` — prefix style.
+* `TimestampStyle string` — timestamp style.
+
+Color styles should be specified using [mgutz/ansi](https://github.com/mgutz/ansi#style-format) style syntax. For example, here is the default theme:
+
+```go
+InfoLevelStyle: "green",
+WarnLevelStyle: "yellow",
+ErrorLevelStyle: "red",
+FatalLevelStyle: "red",
+PanicLevelStyle: "red",
+DebugLevelStyle: "blue",
+PrefixStyle: "cyan",
+TimestampStyle: "black+h"
+```
+
+It's not necessary to specify all colors when changing color scheme if you want to change just specific ones:
+
+```go
+formatter.SetColorScheme(&prefixed.ColorScheme{
+ PrefixStyle: "blue+b",
+ TimestampStyle: "white+h",
+})
+```
+
+# License
+MIT
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/formatter.go b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/formatter.go
new file mode 100644
index 0000000000..1235bcc39c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/x-cray/logrus-prefixed-formatter/formatter.go
@@ -0,0 +1,366 @@
+package prefixed
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "github.com/mgutz/ansi"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+const defaultTimestampFormat = time.RFC3339
+
+var (
+ baseTimestamp time.Time = time.Now()
+ defaultColorScheme *ColorScheme = &ColorScheme{
+ InfoLevelStyle: "green",
+ WarnLevelStyle: "yellow",
+ ErrorLevelStyle: "red",
+ FatalLevelStyle: "red",
+ PanicLevelStyle: "red",
+ DebugLevelStyle: "blue",
+ PrefixStyle: "cyan",
+ TimestampStyle: "black+h",
+ }
+ noColorsColorScheme *compiledColorScheme = &compiledColorScheme{
+ InfoLevelColor: ansi.ColorFunc(""),
+ WarnLevelColor: ansi.ColorFunc(""),
+ ErrorLevelColor: ansi.ColorFunc(""),
+ FatalLevelColor: ansi.ColorFunc(""),
+ PanicLevelColor: ansi.ColorFunc(""),
+ DebugLevelColor: ansi.ColorFunc(""),
+ PrefixColor: ansi.ColorFunc(""),
+ TimestampColor: ansi.ColorFunc(""),
+ }
+ defaultCompiledColorScheme *compiledColorScheme = compileColorScheme(defaultColorScheme)
+)
+
+func miniTS() int {
+ return int(time.Since(baseTimestamp) / time.Second)
+}
+
+type ColorScheme struct {
+ InfoLevelStyle string
+ WarnLevelStyle string
+ ErrorLevelStyle string
+ FatalLevelStyle string
+ PanicLevelStyle string
+ DebugLevelStyle string
+ PrefixStyle string
+ TimestampStyle string
+}
+
+type compiledColorScheme struct {
+ InfoLevelColor func(string) string
+ WarnLevelColor func(string) string
+ ErrorLevelColor func(string) string
+ FatalLevelColor func(string) string
+ PanicLevelColor func(string) string
+ DebugLevelColor func(string) string
+ PrefixColor func(string) string
+ TimestampColor func(string) string
+}
+
+type TextFormatter struct {
+ // Set to true to bypass checking for a TTY before outputting colors.
+ ForceColors bool
+
+ // Force disabling colors. For a TTY colors are enabled by default.
+ DisableColors bool
+
+ // Force formatted layout, even for non-TTY output.
+ ForceFormatting bool
+
+ // Disable timestamp logging. useful when output is redirected to logging
+ // system that already adds timestamps.
+ DisableTimestamp bool
+
+ // Disable the conversion of the log levels to uppercase
+ DisableUppercase bool
+
+ // Enable logging the full timestamp when a TTY is attached instead of just
+ // the time passed since beginning of execution.
+ FullTimestamp bool
+
+ // Timestamp format to use for display when a full timestamp is printed.
+ TimestampFormat string
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
+
+ // Wrap empty fields in quotes if true.
+ QuoteEmptyFields bool
+
+ // Can be set to the override the default quoting character "
+ // with something else. For example: ', or `.
+ QuoteCharacter string
+
+ // Pad msg field with spaces on the right for display.
+ // The value for this parameter will be the size of padding.
+ // Its default value is zero, which means no padding will be applied for msg.
+ SpacePadding int
+
+ // Color scheme to use.
+ colorScheme *compiledColorScheme
+
+ // Whether the logger's out is to a terminal.
+ isTerminal bool
+
+ sync.Once
+}
+
+func getCompiledColor(main string, fallback string) func(string) string {
+ var style string
+ if main != "" {
+ style = main
+ } else {
+ style = fallback
+ }
+ return ansi.ColorFunc(style)
+}
+
+func compileColorScheme(s *ColorScheme) *compiledColorScheme {
+ return &compiledColorScheme{
+ InfoLevelColor: getCompiledColor(s.InfoLevelStyle, defaultColorScheme.InfoLevelStyle),
+ WarnLevelColor: getCompiledColor(s.WarnLevelStyle, defaultColorScheme.WarnLevelStyle),
+ ErrorLevelColor: getCompiledColor(s.ErrorLevelStyle, defaultColorScheme.ErrorLevelStyle),
+ FatalLevelColor: getCompiledColor(s.FatalLevelStyle, defaultColorScheme.FatalLevelStyle),
+ PanicLevelColor: getCompiledColor(s.PanicLevelStyle, defaultColorScheme.PanicLevelStyle),
+ DebugLevelColor: getCompiledColor(s.DebugLevelStyle, defaultColorScheme.DebugLevelStyle),
+ PrefixColor: getCompiledColor(s.PrefixStyle, defaultColorScheme.PrefixStyle),
+ TimestampColor: getCompiledColor(s.TimestampStyle, defaultColorScheme.TimestampStyle),
+ }
+}
+
+func (f *TextFormatter) init(entry *logrus.Entry) {
+ if len(f.QuoteCharacter) == 0 {
+ f.QuoteCharacter = "\""
+ }
+ if entry.Logger != nil {
+ f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
+ }
+}
+
+func (f *TextFormatter) checkIfTerminal(w io.Writer) bool {
+ switch v := w.(type) {
+ case *os.File:
+ return terminal.IsTerminal(int(v.Fd()))
+ default:
+ return false
+ }
+}
+
+func (f *TextFormatter) SetColorScheme(colorScheme *ColorScheme) {
+ f.colorScheme = compileColorScheme(colorScheme)
+}
+
+func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ var b *bytes.Buffer
+ var keys []string = make([]string, 0, len(entry.Data))
+ for k := range entry.Data {
+ keys = append(keys, k)
+ }
+ lastKeyIdx := len(keys) - 1
+
+ if !f.DisableSorting {
+ sort.Strings(keys)
+ }
+ if entry.Buffer != nil {
+ b = entry.Buffer
+ } else {
+ b = &bytes.Buffer{}
+ }
+
+ prefixFieldClashes(entry.Data)
+
+ f.Do(func() { f.init(entry) })
+
+ isFormatted := f.ForceFormatting || f.isTerminal
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = defaultTimestampFormat
+ }
+ if isFormatted {
+ isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
+ var colorScheme *compiledColorScheme
+ if isColored {
+ if f.colorScheme == nil {
+ colorScheme = defaultCompiledColorScheme
+ } else {
+ colorScheme = f.colorScheme
+ }
+ } else {
+ colorScheme = noColorsColorScheme
+ }
+ f.printColored(b, entry, keys, timestampFormat, colorScheme)
+ } else {
+ if !f.DisableTimestamp {
+ f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat), true)
+ }
+ f.appendKeyValue(b, "level", entry.Level.String(), true)
+ if entry.Message != "" {
+ f.appendKeyValue(b, "msg", entry.Message, lastKeyIdx >= 0)
+ }
+ for i, key := range keys {
+ f.appendKeyValue(b, key, entry.Data[key], lastKeyIdx != i)
+ }
+ }
+
+ b.WriteByte('\n')
+ return b.Bytes(), nil
+}
+
+func (f *TextFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string, timestampFormat string, colorScheme *compiledColorScheme) {
+ var levelColor func(string) string
+ var levelText string
+ switch entry.Level {
+ case logrus.InfoLevel:
+ levelColor = colorScheme.InfoLevelColor
+ case logrus.WarnLevel:
+ levelColor = colorScheme.WarnLevelColor
+ case logrus.ErrorLevel:
+ levelColor = colorScheme.ErrorLevelColor
+ case logrus.FatalLevel:
+ levelColor = colorScheme.FatalLevelColor
+ case logrus.PanicLevel:
+ levelColor = colorScheme.PanicLevelColor
+ default:
+ levelColor = colorScheme.DebugLevelColor
+ }
+
+ if entry.Level != logrus.WarnLevel {
+ levelText = entry.Level.String()
+ } else {
+ levelText = "warn"
+ }
+
+ if !f.DisableUppercase {
+ levelText = strings.ToUpper(levelText)
+ }
+
+ level := levelColor(fmt.Sprintf("%5s", levelText))
+ prefix := ""
+ message := entry.Message
+
+ if prefixValue, ok := entry.Data["prefix"]; ok {
+ prefix = colorScheme.PrefixColor(" " + prefixValue.(string) + ":")
+ } else {
+ prefixValue, trimmedMsg := extractPrefix(entry.Message)
+ if len(prefixValue) > 0 {
+ prefix = colorScheme.PrefixColor(" " + prefixValue + ":")
+ message = trimmedMsg
+ }
+ }
+
+ messageFormat := "%s"
+ if f.SpacePadding != 0 {
+ messageFormat = fmt.Sprintf("%%-%ds", f.SpacePadding)
+ }
+
+ if f.DisableTimestamp {
+ fmt.Fprintf(b, "%s%s "+messageFormat, level, prefix, message)
+ } else {
+ var timestamp string
+ if !f.FullTimestamp {
+ timestamp = fmt.Sprintf("[%04d]", miniTS())
+ } else {
+ timestamp = fmt.Sprintf("[%s]", entry.Time.Format(timestampFormat))
+ }
+ fmt.Fprintf(b, "%s %s%s "+messageFormat, colorScheme.TimestampColor(timestamp), level, prefix, message)
+ }
+ for _, k := range keys {
+ if k != "prefix" {
+ v := entry.Data[k]
+ fmt.Fprintf(b, " %s=%+v", levelColor(k), v)
+ }
+ }
+}
+
+func (f *TextFormatter) needsQuoting(text string) bool {
+ if f.QuoteEmptyFields && len(text) == 0 {
+ return true
+ }
+ for _, ch := range text {
+ if !((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ ch == '-' || ch == '.') {
+ return true
+ }
+ }
+ return false
+}
+
+func extractPrefix(msg string) (string, string) {
+ prefix := ""
+ regex := regexp.MustCompile("^\\[(.*?)\\]")
+ if regex.MatchString(msg) {
+ match := regex.FindString(msg)
+ prefix, msg = match[1:len(match)-1], strings.TrimSpace(msg[len(match):])
+ }
+ return prefix, msg
+}
+
+func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}, appendSpace bool) {
+ b.WriteString(key)
+ b.WriteByte('=')
+ f.appendValue(b, value)
+
+ if appendSpace {
+ b.WriteByte(' ')
+ }
+}
+
+func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
+ switch value := value.(type) {
+ case string:
+ if !f.needsQuoting(value) {
+ b.WriteString(value)
+ } else {
+ fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
+ }
+ case error:
+ errmsg := value.Error()
+ if !f.needsQuoting(errmsg) {
+ b.WriteString(errmsg)
+ } else {
+ fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
+ }
+ default:
+ fmt.Fprint(b, value)
+ }
+}
+
+// This is to not silently overwrite `time`, `msg` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+// logrus.WithField("level", 1).Info("hello")
+//
+// would just silently drop the user provided level. Instead with this code
+// it'll be logged as:
+//
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+func prefixFieldClashes(data logrus.Fields) {
+ if t, ok := data["time"]; ok {
+ data["fields.time"] = t
+ }
+
+ if m, ok := data["msg"]; ok {
+ data["fields.msg"] = m
+ }
+
+ if l, ok := data["level"]; ok {
+ data["fields.level"] = l
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/LICENSE
new file mode 100644
index 0000000000..d8121e81b2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 caoyaping
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/README.md
new file mode 100644
index 0000000000..6071973c83
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/README.md
@@ -0,0 +1,89 @@
+### USAGE
+
+1. 如何解析sps/pps/vps
+ 以解析sps为例
+ ```golang
+
+ //sps原始数据,不带start code(0x00000001)
+ var rawsps []byte = []byte{0x67,....}
+
+ //step1 创建BitStream
+ bs := codec.NewBitStream(rawsps)
+
+ //step2 解析sps
+ sps := &SPS{}
+ sps.Decode(bs)
+
+ ```
+2. 获取视频宽高
+ 以h264为例子
+ ```golang
+ //sps原始数据,以startcode开头(0x00000001)
+ var rawsps []byte = []byte{0x00,0x00,0x00,0x01,0x67,.....}
+ w, h := codec.GetH264Resolution(rawsps)
+ ```
+
+3. 生成Extradata
+ 以h264为例子
+ ```golang
+
+ //多个sps原始数据,以startcode开头(0x00000001)
+ var spss [][]byte = [][]byte{
+ []byte{0x00,0x00,0x00,0x01,0x67,...},
+ []byte{0x00,0x00,0x00,0x01,0x67,...},
+ ....
+ }
+
+ //多个pps原始数据,以startcode开头(0x00000001)
+ var ppss [][]byte = [][]byte{
+ []byte{0x00,0x00,0x00,0x01,0x68,...},
+ []byte{0x00,0x00,0x00,0x01,0x68,...},
+ ....
+ }
+ extranData := codec.CreateH264AVCCExtradata(spss,ppss)
+ ```
+
+4. Extradata转为Annex-B格式的sps,pps
+ 以h264为例子
+ ```golang
+
+ //一般从flv/mp4格式中获取 extraData
+ //解析出来多个sps,pps, 且sps pps 都以startcode开头
+ spss,ppss := codec.CovertExtradata(extraData)
+ ```
+
+5. 生成H265 extrandata
+
+ ```golang
+ // H265的extra data 生成过程稍微复杂一些
+ //创建一个 HEVCRecordConfiguration 对象
+
+ hvcc := codec.NewHEVCRecordConfiguration()
+
+ //对每一个 sps/pps/vps,调用相应的UpdateSPS,UpdatePPS,UpdateVPS接口
+ hvcc.UpdateSPS(sps)
+ hvcc.UpdatePPS(pps)
+ hvcc.UpdateVPS(vps)
+
+ //调用Encode接口生成
+ extran := hvcc.Encode()
+ ```
+6. 获取对应的sps id/vps id/pps id
+
+ ```golang
+ //以h264为例子,有四个接口
+ //sps 以startcode 开头
+ codec.GetSPSIdWithStartCode(sps)
+
+ //sps2 不以startcode 开头
+ codec.GetSPSId(sps2)
+
+ //pps 以startcode 开头
+ codec.GetPPSIdWithStartCode(pps)
+
+ //pps2 不以startcode 开头
+ codec.GetPPSId(pps2)
+
+
+
+ ```
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/aac.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/aac.go
new file mode 100644
index 0000000000..c9383a1d44
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/aac.go
@@ -0,0 +1,255 @@
+package codec
+
+import "errors"
+
+// Table 31 – Profiles
+// index profile
+// 0 Main profile
+// 1 Low Complexity profile (LC)
+// 2 Scalable Sampling Rate profile (SSR)
+// 3 (reserved)
+
+type AAC_PROFILE int
+
+const (
+ MAIN AAC_PROFILE = iota
+ LC
+ SSR
+)
+
+type AAC_SAMPLING_FREQUENCY int
+
+const (
+ AAC_SAMPLE_96000 AAC_SAMPLING_FREQUENCY = iota
+ AAC_SAMPLE_88200
+ AAC_SAMPLE_64000
+ AAC_SAMPLE_48000
+ AAC_SAMPLE_44100
+ AAC_SAMPLE_32000
+ AAC_SAMPLE_24000
+ AAC_SAMPLE_22050
+ AAC_SAMPLE_16000
+ AAC_SAMPLE_11025
+ AAC_SAMPLE_8000
+)
+
+var AAC_Sampling_Idx [11]int = [11]int{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}
+
+// Table 4 – Syntax of adts_sequence()
+// adts_sequence() {
+// while (nextbits() == syncword) {
+// adts_frame();
+// }
+// }
+// Table 5 – Syntax of adts_frame()
+// adts_frame() {
+// adts_fixed_header();
+// adts_variable_header();
+// if (number_of_raw_data_blocks_in_frame == 0) {
+// adts_error_check();
+// raw_data_block();
+// }
+// else {
+// adts_header_error_check();
+// for (i = 0; i <= number_of_raw_data_blocks_in_frame;i++ {
+// raw_data_block();
+// adts_raw_data_block_error_check();
+// }
+// }
+// }
+
+// adts_fixed_header()
+// {
+// syncword; 12 bslbf
+// ID; 1 bslbf
+// layer; 2 uimsbf
+// protection_absent; 1 bslbf
+// profile; 2 uimsbf
+// sampling_frequency_index; 4 uimsbf
+// private_bit; 1 bslbf
+// channel_configuration; 3 uimsbf
+// original/copy; 1 bslbf
+// home; 1 bslbf
+// }
+
+type ADTS_Fix_Header struct {
+ ID uint8
+ Layer uint8
+ Protection_absent uint8
+ Profile uint8
+ Sampling_frequency_index uint8
+ Private_bit uint8
+ Channel_configuration uint8
+ Originalorcopy uint8
+ Home uint8
+}
+
+// adts_variable_header() {
+// copyright_identification_bit; 1 bslbf
+// copyright_identification_start; 1 bslbf
+// frame_length; 13 bslbf
+// adts_buffer_fullness; 11 bslbf
+// number_of_raw_data_blocks_in_frame; 2 uimsfb
+// }
+
+type ADTS_Variable_Header struct {
+ Copyright_identification_bit uint8
+ copyright_identification_start uint8
+ Frame_length uint16
+ Adts_buffer_fullness uint16
+ Number_of_raw_data_blocks_in_frame uint8
+}
+
+type ADTS_Frame_Header struct {
+ Fix_Header ADTS_Fix_Header
+ Variable_Header ADTS_Variable_Header
+}
+
+func NewAdtsFrameHeader() *ADTS_Frame_Header {
+ return &ADTS_Frame_Header{
+ Fix_Header: ADTS_Fix_Header{
+ ID: 0,
+ Layer: 0,
+ Protection_absent: 1,
+ Profile: uint8(MAIN),
+ Sampling_frequency_index: uint8(AAC_SAMPLE_44100),
+ Private_bit: 0,
+ Channel_configuration: 0,
+ Originalorcopy: 0,
+ Home: 0,
+ },
+
+ Variable_Header: ADTS_Variable_Header{
+ copyright_identification_start: 0,
+ Copyright_identification_bit: 0,
+ Frame_length: 0,
+ Adts_buffer_fullness: 0,
+ Number_of_raw_data_blocks_in_frame: 0,
+ },
+ }
+}
+
+func (frame *ADTS_Frame_Header) Decode(aac []byte) error {
+ _ = aac[6]
+ frame.Fix_Header.ID = aac[1] >> 3
+ frame.Fix_Header.Layer = aac[1] >> 1 & 0x03
+ frame.Fix_Header.Protection_absent = aac[1] & 0x01
+ frame.Fix_Header.Profile = aac[2] >> 6 & 0x03
+ frame.Fix_Header.Sampling_frequency_index = aac[2] >> 2 & 0x0F
+ frame.Fix_Header.Private_bit = aac[2] >> 1 & 0x01
+ frame.Fix_Header.Channel_configuration = (aac[2] & 0x01 << 2) | (aac[3] >> 6)
+ frame.Fix_Header.Originalorcopy = aac[3] >> 5 & 0x01
+ frame.Fix_Header.Home = aac[3] >> 4 & 0x01
+ frame.Variable_Header.Copyright_identification_bit = aac[3] >> 3 & 0x01
+ frame.Variable_Header.copyright_identification_start = aac[3] >> 2 & 0x01
+ frame.Variable_Header.Frame_length = (uint16(aac[3]&0x03) << 11) | (uint16(aac[4]) << 3) | (uint16(aac[5]>>5) & 0x07)
+ frame.Variable_Header.Adts_buffer_fullness = (uint16(aac[5]&0x1F) << 6) | uint16(aac[6]>>2)
+ frame.Variable_Header.Number_of_raw_data_blocks_in_frame = aac[6] & 0x03
+ return nil
+}
+
+func (frame *ADTS_Frame_Header) Encode() []byte {
+ var hdr []byte
+ if frame.Fix_Header.Protection_absent == 1 {
+ hdr = make([]byte, 7)
+ } else {
+ hdr = make([]byte, 9)
+ }
+ hdr[0] = 0xFF
+ hdr[1] = 0xF0
+ hdr[1] = hdr[1] | (frame.Fix_Header.ID << 3) | (frame.Fix_Header.Layer << 1) | frame.Fix_Header.Protection_absent
+ hdr[2] = frame.Fix_Header.Profile<<6 | frame.Fix_Header.Sampling_frequency_index<<2 | frame.Fix_Header.Private_bit<<1 | frame.Fix_Header.Channel_configuration>>2
+ hdr[3] = frame.Fix_Header.Channel_configuration<<6 | frame.Fix_Header.Originalorcopy<<5 | frame.Fix_Header.Home<<4
+ hdr[3] = hdr[3] | frame.Variable_Header.copyright_identification_start<<3 | frame.Variable_Header.Copyright_identification_bit<<2 | byte(frame.Variable_Header.Frame_length<<11)
+ hdr[4] = byte(frame.Variable_Header.Frame_length >> 3)
+ hdr[5] = byte((frame.Variable_Header.Frame_length&0x07)<<5) | byte(frame.Variable_Header.Adts_buffer_fullness>>3)
+ hdr[6] = byte(frame.Variable_Header.Adts_buffer_fullness&0x3F<<2) | frame.Variable_Header.Number_of_raw_data_blocks_in_frame
+ return hdr
+}
+
+func SampleToAACSampleIndex(sampling int) int {
+ for i, v := range AAC_Sampling_Idx {
+ if v == sampling {
+ return i
+ }
+ }
+ panic("not Found AAC Sample Index")
+}
+
+func AACSampleIdxToSample(idx int) int {
+ return AAC_Sampling_Idx[idx]
+}
+
+// +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+// | audio object type(5 bits) | sampling frequency index(4 bits) | channel configuration(4 bits) | GA framelength flag(1 bits) | GA Depends on core coder(1 bits) | GA Extension Flag(1 bits) |
+// +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+type AudioSpecificConfiguration struct {
+ Audio_object_type uint8
+ Sample_freq_index uint8
+ Channel_configuration uint8
+ GA_framelength_flag uint8
+ GA_depends_on_core_coder uint8
+ GA_extension_flag uint8
+}
+
+func NewAudioSpecificConfiguration() *AudioSpecificConfiguration {
+ return &AudioSpecificConfiguration{
+ Audio_object_type: 0,
+ Sample_freq_index: 0,
+ Channel_configuration: 0,
+ GA_framelength_flag: 0,
+ GA_depends_on_core_coder: 0,
+ GA_extension_flag: 0,
+ }
+}
+
+func (asc *AudioSpecificConfiguration) Encode() []byte {
+ buf := make([]byte, 2)
+ buf[0] = (asc.Audio_object_type & 0x1f << 3) | (asc.Sample_freq_index & 0x0F >> 1)
+ buf[1] = (asc.Sample_freq_index & 0x0F << 7) | (asc.Channel_configuration & 0x0F << 3) | (asc.GA_framelength_flag & 0x01 << 2) | (asc.GA_depends_on_core_coder & 0x01 << 1) | (asc.GA_extension_flag & 0x01)
+ return buf
+}
+
+func (asc *AudioSpecificConfiguration) Decode(buf []byte) error {
+
+ if len(buf) < 2 {
+ return errors.New("len of buf < 2 ")
+ }
+
+ asc.Audio_object_type = buf[0] >> 3
+ asc.Sample_freq_index = (buf[0] & 0x07 << 1) | (buf[1] >> 7)
+ asc.Channel_configuration = buf[1] >> 3 & 0x0F
+ asc.GA_framelength_flag = buf[1] >> 2 & 0x01
+ asc.GA_depends_on_core_coder = buf[1] >> 1 & 0x01
+ asc.GA_extension_flag = buf[1] & 0x01
+ return nil
+}
+
+func ConvertADTSToASC(frame []byte) ([]byte, error) {
+
+ if len(frame) < 7 {
+ return nil, errors.New("len of frame < 7")
+ }
+
+ adts := NewAdtsFrameHeader()
+ adts.Decode(frame)
+ asc := NewAudioSpecificConfiguration()
+ asc.Audio_object_type = adts.Fix_Header.Profile + 1
+ asc.Channel_configuration = adts.Fix_Header.Channel_configuration
+ asc.Sample_freq_index = adts.Fix_Header.Sampling_frequency_index
+ return asc.Encode(), nil
+}
+
+func ConvertASCToADTS(asc []byte, aacbytes int) []byte {
+ aac_asc := NewAudioSpecificConfiguration()
+ aac_asc.Decode(asc)
+ aac_adts := NewAdtsFrameHeader()
+ aac_adts.Fix_Header.Profile = aac_asc.Audio_object_type - 1
+ aac_adts.Fix_Header.Channel_configuration = aac_asc.Channel_configuration
+ aac_adts.Fix_Header.Sampling_frequency_index = aac_asc.Sample_freq_index
+ aac_adts.Fix_Header.Protection_absent = 1
+ aac_adts.Variable_Header.Adts_buffer_fullness = 0x3F
+ aac_adts.Variable_Header.Frame_length = uint16(aacbytes)
+ return aac_adts.Encode()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/bitstream.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/bitstream.go
new file mode 100644
index 0000000000..6d719b01ad
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/bitstream.go
@@ -0,0 +1,358 @@
+package codec
+
+import (
+ "encoding/binary"
+)
+
+var BitMask [8]byte = [8]byte{0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}
+
+type BitStream struct {
+ bits []byte
+ bytesOffset int
+ bitsOffset int
+ bitsmark int
+ bytemark int
+}
+
+func NewBitStream(buf []byte) *BitStream {
+ return &BitStream{
+ bits: buf,
+ bytesOffset: 0,
+ bitsOffset: 0,
+ bitsmark: 0,
+ bytemark: 0,
+ }
+}
+
+func (bs *BitStream) Uint8(n int) uint8 {
+ return uint8(bs.GetBits(n))
+}
+
+func (bs *BitStream) Uint16(n int) uint16 {
+ return uint16(bs.GetBits(n))
+}
+
+func (bs *BitStream) Uint32(n int) uint32 {
+ return uint32(bs.GetBits(n))
+}
+
+func (bs *BitStream) GetBytes(n int) []byte {
+ if bs.bytesOffset+n > len(bs.bits) {
+ panic("OUT OF RANGE")
+ }
+ if bs.bitsOffset != 0 {
+ panic("invaild operation")
+ }
+ data := make([]byte, n)
+ copy(data, bs.bits[bs.bytesOffset:bs.bytesOffset+n])
+ bs.bytesOffset += n
+ return data
+}
+
+//n <= 64
+func (bs *BitStream) GetBits(n int) uint64 {
+ if bs.bytesOffset >= len(bs.bits) {
+ panic("OUT OF RANGE")
+ }
+ var ret uint64 = 0
+ if 8-bs.bitsOffset >= n {
+ ret = uint64((bs.bits[bs.bytesOffset] >> (8 - bs.bitsOffset - n)) & BitMask[n-1])
+ bs.bitsOffset += n
+ if bs.bitsOffset == 8 {
+ bs.bytesOffset++
+ bs.bitsOffset = 0
+ }
+ } else {
+ ret = uint64(bs.bits[bs.bytesOffset] & BitMask[8-bs.bitsOffset-1])
+ bs.bytesOffset++
+ n -= 8 - bs.bitsOffset
+ bs.bitsOffset = 0
+ for n > 0 {
+ if bs.bytesOffset >= len(bs.bits) {
+ panic("OUT OF RANGE")
+ }
+ if n >= 8 {
+ ret = ret<<8 | uint64(bs.bits[bs.bytesOffset])
+ bs.bytesOffset++
+ n -= 8
+ } else {
+ ret = (ret << n) | uint64((bs.bits[bs.bytesOffset]>>(8-n))&BitMask[n-1])
+ bs.bitsOffset = n
+ break
+ }
+ }
+ }
+ return ret
+}
+
+func (bs *BitStream) GetBit() uint8 {
+ if bs.bytesOffset >= len(bs.bits) {
+ panic("OUT OF RANGE")
+ }
+ ret := bs.bits[bs.bytesOffset] >> (7 - bs.bitsOffset) & 0x01
+ bs.bitsOffset++
+ if bs.bitsOffset >= 8 {
+ bs.bytesOffset++
+ bs.bitsOffset = 0
+ }
+ return ret
+}
+
+func (bs *BitStream) SkipBits(n int) {
+ bytecount := n / 8
+ bitscount := n % 8
+ bs.bytesOffset += bytecount
+ if bs.bitsOffset+bitscount < 8 {
+ bs.bitsOffset += bitscount
+ } else {
+ bs.bytesOffset += 1
+ bs.bitsOffset += bitscount - 8
+ }
+}
+
+func (bs *BitStream) Markdot() {
+ bs.bitsmark = bs.bitsOffset
+ bs.bytemark = bs.bytesOffset
+}
+
+func (bs *BitStream) DistanceFromMarkDot() int {
+ bytecount := bs.bytesOffset - bs.bytemark - 1
+ bitscount := bs.bitsOffset + (8 - bs.bitsmark)
+ return bytecount*8 + bitscount
+}
+
+func (bs *BitStream) RemainBytes() int {
+ if bs.bitsOffset > 0 {
+ return len(bs.bits) - bs.bytesOffset - 1
+ } else {
+ return len(bs.bits) - bs.bytesOffset
+ }
+}
+
+func (bs *BitStream) RemainBits() int {
+ if bs.bitsOffset > 0 {
+ return bs.RemainBytes()*8 + 8 - bs.bitsOffset
+ } else {
+ return bs.RemainBytes() * 8
+ }
+
+}
+
+func (bs *BitStream) Bits() []byte {
+ return bs.bits
+}
+
+func (bs *BitStream) RemainData() []byte {
+ return bs.bits[bs.bytesOffset:]
+}
+
+//无符号哥伦布熵编码
+func (bs *BitStream) ReadUE() uint64 {
+ leadingZeroBits := 0
+ for bs.GetBit() == 0 {
+ leadingZeroBits++
+ }
+ if leadingZeroBits == 0 {
+ return 0
+ }
+ info := bs.GetBits(leadingZeroBits)
+ return uint64(1)<= 8 {
+ bs.bytesOffset--
+ least -= 8
+ }
+ if least > 0 {
+ bs.bytesOffset--
+ bs.bitsOffset = 8 - least
+ }
+ }
+}
+
+func (bs *BitStream) NextBits(n int) uint64 {
+ r := bs.GetBits(n)
+ bs.UnRead(n)
+ return r
+}
+
+func (bs *BitStream) EOS() bool {
+ return bs.bytesOffset == len(bs.bits) && bs.bitsOffset == 0
+}
+
+type BitStreamWriter struct {
+ bits []byte
+ byteoffset int
+ bitsoffset int
+ bitsmark int
+ bytemark int
+}
+
+func NewBitStreamWriter(n int) *BitStreamWriter {
+ return &BitStreamWriter{
+ bits: make([]byte, n),
+ byteoffset: 0,
+ bitsoffset: 0,
+ bitsmark: 0,
+ bytemark: 0,
+ }
+}
+
+func (bsw *BitStreamWriter) expandSpace(n int) {
+ if (len(bsw.bits)-bsw.byteoffset-1)*8+8-bsw.bitsoffset < n {
+ newlen := 0
+ if len(bsw.bits)*8 < n {
+ newlen = len(bsw.bits) + n/8 + 1
+ } else {
+ newlen = len(bsw.bits) * 2
+ }
+ tmp := make([]byte, newlen)
+ copy(tmp, bsw.bits)
+ bsw.bits = tmp
+ }
+}
+
+func (bsw *BitStreamWriter) ByteOffset() int {
+ return bsw.byteoffset
+}
+
+func (bsw *BitStreamWriter) BitOffset() int {
+ return bsw.bitsoffset
+}
+
+func (bsw *BitStreamWriter) Markdot() {
+ bsw.bitsmark = bsw.bitsoffset
+ bsw.bytemark = bsw.byteoffset
+}
+
+func (bsw *BitStreamWriter) DistanceFromMarkDot() int {
+ bytecount := bsw.byteoffset - bsw.bytemark - 1
+ bitscount := bsw.bitsoffset + (8 - bsw.bitsmark)
+ return bytecount*8 + bitscount
+}
+
+func (bsw *BitStreamWriter) PutByte(v byte) {
+ bsw.expandSpace(8)
+ if bsw.bitsoffset == 0 {
+ bsw.bits[bsw.byteoffset] = v
+ bsw.byteoffset++
+ } else {
+ bsw.bits[bsw.byteoffset] |= v >> byte(bsw.bitsoffset)
+ bsw.byteoffset++
+ bsw.bits[bsw.byteoffset] = v & BitMask[bsw.bitsoffset-1]
+ }
+}
+
+func (bsw *BitStreamWriter) PutBytes(v []byte) {
+ if bsw.bitsoffset != 0 {
+ panic("bsw.bitsoffset > 0")
+ }
+ bsw.expandSpace(8 * len(v))
+ copy(bsw.bits[bsw.byteoffset:], v)
+ bsw.byteoffset += len(v)
+}
+
+func (bsw *BitStreamWriter) PutRepetValue(v byte, n int) {
+ if bsw.bitsoffset != 0 {
+ panic("bsw.bitsoffset > 0")
+ }
+ bsw.expandSpace(8 * n)
+ for i := 0; i < n; i++ {
+ bsw.bits[bsw.byteoffset] = v
+ bsw.byteoffset++
+ }
+}
+
+func (bsw *BitStreamWriter) PutUint8(v uint8, n int) {
+ bsw.PutUint64(uint64(v), n)
+}
+
+func (bsw *BitStreamWriter) PutUint16(v uint16, n int) {
+ bsw.PutUint64(uint64(v), n)
+}
+
+func (bsw *BitStreamWriter) PutUint32(v uint32, n int) {
+ bsw.PutUint64(uint64(v), n)
+}
+
+func (bsw *BitStreamWriter) PutUint64(v uint64, n int) {
+ bsw.expandSpace(n)
+ if 8-bsw.bitsoffset >= n {
+ bsw.bits[bsw.byteoffset] |= uint8(v) & BitMask[n-1] << (8 - bsw.bitsoffset - n)
+ bsw.bitsoffset += n
+ if bsw.bitsoffset == 8 {
+ bsw.bitsoffset = 0
+ bsw.byteoffset++
+ }
+ } else {
+ bsw.bits[bsw.byteoffset] |= uint8(v>>(n-int(8-bsw.bitsoffset))) & BitMask[8-bsw.bitsoffset-1]
+ bsw.byteoffset++
+ n -= 8 - bsw.bitsoffset
+ for n-8 >= 0 {
+ bsw.bits[bsw.byteoffset] = uint8(v>>(n-8)) & 0xFF
+ bsw.byteoffset++
+ n -= 8
+ }
+ bsw.bitsoffset = n
+ if n > 0 {
+ bsw.bits[bsw.byteoffset] |= (uint8(v) & BitMask[n-1]) << (8 - n)
+ }
+ }
+}
+
+func (bsw *BitStreamWriter) SetByte(v byte, where int) {
+ bsw.bits[where] = v
+}
+
+func (bsw *BitStreamWriter) SetUint16(v uint16, where int) {
+ binary.BigEndian.PutUint16(bsw.bits[where:where+2], v)
+}
+
+func (bsw *BitStreamWriter) Bits() []byte {
+ if bsw.byteoffset == len(bsw.bits) {
+ return bsw.bits
+ }
+ if bsw.bitsoffset > 0 {
+ return bsw.bits[0 : bsw.byteoffset+1]
+ } else {
+ return bsw.bits[0:bsw.byteoffset]
+ }
+}
+
+//用v 填充剩余字节
+func (bsw *BitStreamWriter) FillRemainData(v byte) {
+ for i := bsw.byteoffset; i < len(bsw.bits); i++ {
+ bsw.bits[i] = v
+ }
+ bsw.byteoffset = len(bsw.bits)
+ bsw.bitsoffset = 0
+}
+
+func (bsw *BitStreamWriter) Reset() {
+ for i := 0; i < len(bsw.bits); i++ {
+ bsw.bits[i] = 0
+ }
+ bsw.bitsmark = 0
+ bsw.bytemark = 0
+ bsw.bitsoffset = 0
+ bsw.byteoffset = 0
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/codec.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/codec.go
new file mode 100644
index 0000000000..8f577c8860
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/codec.go
@@ -0,0 +1,64 @@
+package codec
+
+type CodecID int
+
+const (
+ CODECID_VIDEO_H264 CodecID = iota
+ CODECID_VIDEO_H265
+ CODECID_VIDEO_VP8
+
+ CODECID_AUDIO_AAC CodecID = iota + 98
+ CODECID_AUDIO_G711A
+ CODECID_AUDIO_G711U
+ CODECID_AUDIO_OPUS
+
+ CODECID_UNRECOGNIZED = 999
+)
+
+type H264_NAL_TYPE int
+
+const (
+ H264_NAL_RESERVED H264_NAL_TYPE = iota
+ H264_NAL_P_SLICE
+ H264_NAL_SLICE_A
+ H264_NAL_SLICE_B
+ H264_NAL_SLICE_C
+ H264_NAL_I_SLICE
+ H264_NAL_SEI
+ H264_NAL_SPS
+ H264_NAL_PPS
+ H264_NAL_AUD
+)
+
+type H265_NAL_TYPE int
+
+const (
+ H265_NAL_Slice_TRAIL_N H265_NAL_TYPE = iota
+ H265_NAL_LICE_TRAIL_R
+ H265_NAL_SLICE_TSA_N
+ H265_NAL_SLICE_TSA_R
+ H265_NAL_SLICE_STSA_N
+ H265_NAL_SLICE_STSA_R
+ H265_NAL_SLICE_RADL_N
+ H265_NAL_SLICE_RADL_R
+ H265_NAL_SLICE_RASL_N
+ H265_NAL_SLICE_RASL_R
+
+ //IDR
+ H265_NAL_SLICE_BLA_W_LP H265_NAL_TYPE = iota + 6
+ H265_NAL_SLICE_BLA_W_RADL
+ H265_NAL_SLICE_BLA_N_LP
+ H265_NAL_SLICE_IDR_W_RADL
+ H265_NAL_SLICE_IDR_N_LP
+ H265_NAL_SLICE_CRA
+
+ //vps pps sps
+ H265_NAL_VPS H265_NAL_TYPE = iota + 16
+ H265_NAL_SPS
+ H265_NAL_PPS
+ H265_NAL_AUD
+
+ //SEI
+ H265_NAL_SEI H265_NAL_TYPE = iota + 19
+ H265_NAL_SEI_SUFFIX
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/go.mod
new file mode 100644
index 0000000000..15686938f0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/go.mod
@@ -0,0 +1,3 @@
+module github.com/yapingcat/gomedia/codec
+
+go 1.16
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h264.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h264.go
new file mode 100644
index 0000000000..36fd31a0be
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h264.go
@@ -0,0 +1,396 @@
+package codec
+
+import "encoding/binary"
+
+// nal_unit( NumBytesInNALunit ) {
+// forbidden_zero_bit All f(1)
+// nal_ref_idc All u(2)
+// nal_unit_type u(5)
+// }
+
+type H264NaluHdr struct {
+ Forbidden_zero_bit uint8
+ Nal_ref_idc uint8
+ Nal_unit_type uint8
+}
+
+func (hdr *H264NaluHdr) Decode(bs *BitStream) {
+ hdr.Forbidden_zero_bit = bs.GetBit()
+ hdr.Nal_ref_idc = bs.Uint8(2)
+ hdr.Nal_unit_type = bs.Uint8(5)
+}
+
+type SliceHeader struct {
+ First_mb_in_slice uint64
+ Slice_type uint64
+ Pic_parameter_set_id uint64
+ Frame_num uint64
+}
+
+//调用方根据sps中的log2_max_frame_num_minus4的值来解析Frame_num
+func (sh *SliceHeader) Decode(bs *BitStream) {
+ sh.First_mb_in_slice = bs.ReadUE()
+ sh.Slice_type = bs.ReadUE()
+ sh.Pic_parameter_set_id = bs.ReadUE()
+}
+
+type SPS struct {
+ Profile_idc uint8
+ Constraint_set0_flag uint8
+ Constraint_set1_flag uint8
+ Constraint_set2_flag uint8
+ Constraint_set3_flag uint8
+ Constraint_set4_flag uint8
+ Constraint_set5_flag uint8
+ Reserved_zero_2bits uint8
+ Level_idc uint8
+ Seq_parameter_set_id uint64
+ Chroma_format_idc uint64
+ Separate_colour_plane_flag uint8
+ Bit_depth_luma_minus8 uint64
+ Bit_depth_chroma_minus8 uint64
+ Log2_max_frame_num_minus4 uint64
+ Pic_order_cnt_type uint64
+ Max_num_ref_frames uint64
+ Gaps_in_frame_num_value_allowed_flag uint8
+ Pic_width_in_mbs_minus1 uint64
+ Pic_height_in_map_units_minus1 uint64
+ Frame_mbs_only_flag uint8
+ Direct_8x8_inference_flag uint8
+ Frame_cropping_flag uint8
+ Frame_crop_left_offset uint64
+ Frame_crop_right_offset uint64
+ Frame_crop_top_offset uint64
+ Frame_crop_bottom_offset uint64
+ Vui_parameters_present_flag uint8
+}
+
+func (sps *SPS) Decode(bs *BitStream) {
+ sps.Profile_idc = bs.Uint8(8)
+ sps.Constraint_set0_flag = bs.GetBit()
+ sps.Constraint_set1_flag = bs.GetBit()
+ sps.Constraint_set2_flag = bs.GetBit()
+ sps.Constraint_set3_flag = bs.GetBit()
+ sps.Constraint_set4_flag = bs.GetBit()
+ sps.Constraint_set5_flag = bs.GetBit()
+ sps.Reserved_zero_2bits = bs.Uint8(2)
+ sps.Level_idc = bs.Uint8(8)
+ sps.Seq_parameter_set_id = bs.ReadUE()
+ if sps.Profile_idc == 100 || sps.Profile_idc == 110 ||
+ sps.Profile_idc == 122 || sps.Profile_idc == 244 ||
+ sps.Profile_idc == 44 || sps.Profile_idc == 83 ||
+ sps.Profile_idc == 86 || sps.Profile_idc == 118 || sps.Profile_idc == 128 {
+ sps.Chroma_format_idc = bs.ReadUE()
+ if sps.Chroma_format_idc == 3 {
+ sps.Separate_colour_plane_flag = bs.Uint8(1) //separate_colour_plane_flag
+ }
+ sps.Bit_depth_luma_minus8 = bs.ReadUE() //bit_depth_luma_minus8
+ sps.Bit_depth_chroma_minus8 = bs.ReadUE() //bit_depth_chroma_minus8
+ bs.SkipBits(1) //qpprime_y_zero_transform_bypass_flag
+ seq_scaling_matrix_present_flag := bs.GetBit()
+ if seq_scaling_matrix_present_flag == 1 {
+ //seq_scaling_list_present_flag[i]
+ if sps.Chroma_format_idc == 3 {
+ bs.SkipBits(12)
+ } else {
+ bs.SkipBits(8)
+ }
+ }
+ }
+ sps.Log2_max_frame_num_minus4 = bs.ReadUE()
+ sps.Pic_order_cnt_type = bs.ReadUE()
+ if sps.Pic_order_cnt_type == 0 {
+ bs.ReadUE() // log2_max_pic_order_cnt_lsb_minus4
+ } else if sps.Pic_order_cnt_type == 1 {
+ bs.SkipBits(1) //delta_pic_order_always_zero_flag
+ bs.ReadSE() //offset_for_non_ref_pic
+ bs.ReadSE() //offset_for_top_to_bottom_field
+ num_ref_frames_in_pic_order_cnt_cycle := bs.ReadUE()
+ for i := 0; i < int(num_ref_frames_in_pic_order_cnt_cycle); i++ {
+ bs.ReadSE() //offset_for_ref_frame
+ }
+ }
+ sps.Max_num_ref_frames = bs.ReadUE()
+ sps.Gaps_in_frame_num_value_allowed_flag = bs.GetBit()
+ sps.Pic_width_in_mbs_minus1 = bs.ReadUE()
+ sps.Pic_height_in_map_units_minus1 = bs.ReadUE()
+ sps.Frame_mbs_only_flag = bs.GetBit()
+ if sps.Frame_mbs_only_flag == 0 {
+ bs.SkipBits(1) // mb_adaptive_frame_field_flag
+ }
+ sps.Direct_8x8_inference_flag = bs.GetBit()
+ sps.Frame_cropping_flag = bs.GetBit()
+ if sps.Frame_cropping_flag == 1 {
+ sps.Frame_crop_left_offset = bs.ReadUE() //frame_crop_left_offset
+ sps.Frame_crop_right_offset = bs.ReadUE() //frame_crop_right_offset
+ sps.Frame_crop_top_offset = bs.ReadUE() //frame_crop_top_offset
+ sps.Frame_crop_bottom_offset = bs.ReadUE() //frame_crop_bottom_offset
+ }
+ sps.Vui_parameters_present_flag = bs.GetBit()
+}
+
+type PPS struct {
+ Pic_parameter_set_id uint64
+ Seq_parameter_set_id uint64
+ Entropy_coding_mode_flag uint8
+ Bottom_field_pic_order_in_frame_present_flag uint8
+ Num_slice_groups_minus1 uint64
+}
+
+func (pps *PPS) Decode(bs *BitStream) {
+ pps.Pic_parameter_set_id = bs.ReadUE()
+ pps.Seq_parameter_set_id = bs.ReadUE()
+ pps.Entropy_coding_mode_flag = bs.GetBit()
+ pps.Bottom_field_pic_order_in_frame_present_flag = bs.GetBit()
+ pps.Num_slice_groups_minus1 = bs.ReadUE()
+}
+
+type SEIReaderWriter interface {
+ Read(size uint16, bs *BitStream)
+ Write(bsw *BitStreamWriter)
+}
+
+type UserDataUnregistered struct {
+ UUID []byte
+ UserData []byte
+}
+
+func (udu *UserDataUnregistered) Read(size uint16, bs *BitStream) {
+ udu.UUID = bs.GetBytes(16)
+ udu.UserData = bs.GetBytes(int(size - 16))
+}
+
+func (udu *UserDataUnregistered) Write(bsw *BitStreamWriter) {
+ bsw.PutBytes(udu.UUID)
+ bsw.PutBytes(udu.UserData)
+}
+
+type SEI struct {
+ PayloadType uint16
+ PayloadSize uint16
+ Sei_payload SEIReaderWriter
+}
+
+func (sei *SEI) Decode(bs *BitStream) {
+ for bs.NextBits(8) == 0xFF {
+ sei.PayloadType += 255
+ }
+ sei.PayloadType += uint16(bs.Uint8(8))
+ for bs.NextBits(8) == 0xFF {
+ sei.PayloadSize += 255
+ }
+ sei.PayloadSize += uint16(bs.Uint8(8))
+ if sei.PayloadType == 5 {
+ sei.Sei_payload = new(UserDataUnregistered)
+ sei.Sei_payload.Read(sei.PayloadSize, bs)
+ }
+}
+
+func (sei *SEI) Encode(bsw *BitStreamWriter) []byte {
+ payloadType := sei.PayloadType
+ payloadSize := sei.PayloadSize
+ for payloadType >= 0xFF {
+ bsw.PutByte(0xFF)
+ payloadType -= 255
+ }
+ bsw.PutByte(uint8(payloadType))
+ for payloadSize >= 0xFF {
+ bsw.PutByte(0xFF)
+ payloadSize -= 255
+ }
+ bsw.PutByte(uint8(payloadSize))
+ sei.Sei_payload.Write(bsw)
+ return bsw.Bits()
+}
+
+func GetSPSIdWithStartCode(sps []byte) uint64 {
+ start, sc := FindStartCode(sps, 0)
+ return GetSPSId(sps[start+int(sc):])
+}
+
+func GetSPSId(sps []byte) uint64 {
+ sps = sps[1:]
+ bs := NewBitStream(sps)
+ bs.SkipBits(24)
+ return bs.ReadUE()
+}
+
+func GetPPSIdWithStartCode(pps []byte) uint64 {
+ start, sc := FindStartCode(pps, 0)
+ return GetPPSId(pps[start+int(sc):])
+}
+
+func GetPPSId(pps []byte) uint64 {
+ pps = pps[1:]
+ bs := NewBitStream(pps)
+ return bs.ReadUE()
+}
+
+//https://stackoverflow.com/questions/12018535/get-the-width-height-of-the-video-from-h-264-nalu
+//int Width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_right_offset *2 - frame_crop_left_offset *2;
+//int Height = ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_bottom_offset* 2) - (frame_crop_top_offset* 2);
+func GetH264Resolution(sps []byte) (width uint32, height uint32) {
+ start, sc := FindStartCode(sps, 0)
+ bs := NewBitStream(sps[start+int(sc)+1:])
+ var s SPS
+ s.Decode(bs)
+
+ widthInSample := (uint32(s.Pic_width_in_mbs_minus1) + 1) * 16
+ widthCrop := uint32(s.Frame_crop_left_offset)*2 - uint32(s.Frame_crop_right_offset)*2
+ width = widthInSample - widthCrop
+
+ heightInSample := ((2 - uint32(s.Frame_mbs_only_flag)) * (uint32(s.Pic_height_in_map_units_minus1) + 1) * 16)
+ heightCrop := uint32(s.Frame_crop_bottom_offset)*2 - uint32(s.Frame_crop_top_offset)*2
+ height = heightInSample - heightCrop
+
+ return
+}
+
+// aligned(8) class AVCDecoderConfigurationRecord {
+// unsigned int(8) configurationVersion = 1;
+// unsigned int(8) AVCProfileIndication;
+// unsigned int(8) profile_compatibility;
+// unsigned int(8) AVCLevelIndication;
+// bit(6) reserved = ‘111111’b;
+// unsigned int(2) lengthSizeMinusOne;
+// bit(3) reserved = ‘111’b;
+// unsigned int(5) numOfSequenceParameterSets;
+// for (i=0; i< numOfSequenceParameterSets; i++) {
+// unsigned int(16) sequenceParameterSetLength ;
+// bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
+// }
+// unsigned int(8) numOfPictureParameterSets;
+// for (i=0; i< numOfPictureParameterSets; i++) {
+// unsigned int(16) pictureParameterSetLength;
+// bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
+// }
+// if( profile_idc == 100 || profile_idc == 110 ||
+// profile_idc == 122 || profile_idc == 144 )
+// {
+// bit(6) reserved = ‘111111’b;
+// unsigned int(2) chroma_format;
+// bit(5) reserved = ‘11111’b;
+// unsigned int(3) bit_depth_luma_minus8;
+// bit(5) reserved = ‘11111’b;
+// unsigned int(3) bit_depth_chroma_minus8;
+// unsigned int(8) numOfSequenceParameterSetExt;
+// for (i=0; i< numOfSequenceParameterSetExt; i++) {
+// unsigned int(16) sequenceParameterSetExtLength;
+// bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit;
+// }
+// }
+
+// }
+// }
+// bits
+// 8 version ( always 0x01 )
+// 8 avc profile ( sps[0][1] )
+// 8 avc compatibility ( sps[0][2] )
+// 8 avc level ( sps[0][3] )
+// 6 reserved ( all bits on )
+// 2 NALULengthSizeMinusOne
+// 3 reserved ( all bits on )
+// 5 number of SPS NALUs (usually 1)
+
+// repeated once per SPS:
+// 16 SPS size
+// variable SPS NALU data
+
+// 8 number of PPS NALUs (usually 1)
+// repeated once per PPS:
+// 16 PPS size
+// variable PPS NALU data
+
+func CreateH264AVCCExtradata(spss [][]byte, ppss [][]byte) []byte {
+ extradata := make([]byte, 6, 256)
+ for i, sps := range spss {
+ start, sc := FindStartCode(sps, 0)
+ spss[i] = sps[start+int(sc):]
+ }
+
+ for i, pps := range ppss {
+ start, sc := FindStartCode(pps, 0)
+ ppss[i] = pps[start+int(sc):]
+ }
+
+ extradata[0] = 0x01
+ extradata[1] = spss[0][1]
+ extradata[2] = spss[0][2]
+ extradata[3] = spss[0][3]
+ extradata[4] = 0xFF
+ extradata[5] = 0xE0 | uint8(len(spss))
+ for _, sps := range spss {
+ spssize := make([]byte, 2)
+ binary.BigEndian.PutUint16(spssize, uint16(len(sps)))
+ extradata = append(extradata, spssize...)
+ extradata = append(extradata, sps...)
+ }
+ extradata = append(extradata, uint8(len(ppss)))
+ for _, pps := range ppss {
+ ppssize := make([]byte, 2)
+ binary.BigEndian.PutUint16(ppssize, uint16(len(pps)))
+ extradata = append(extradata, ppssize...)
+ extradata = append(extradata, pps...)
+ }
+ var h264sps SPS
+ h264sps.Decode(NewBitStream(spss[0][1:]))
+ if h264sps.Profile_idc == 100 ||
+ h264sps.Profile_idc == 110 ||
+ h264sps.Profile_idc == 122 ||
+ h264sps.Profile_idc == 144 {
+ tmp := make([]byte, 4)
+ tmp[0] = 0xFC | uint8(h264sps.Chroma_format_idc&0x03)
+ tmp[1] = 0xF8 | uint8(h264sps.Bit_depth_luma_minus8&0x07)
+ tmp[2] = 0xF8 | uint8(h264sps.Bit_depth_chroma_minus8&0x07)
+ tmp[3] = 0
+ extradata = append(extradata, tmp...)
+ }
+
+ return extradata
+}
+
+func CovertExtradata(extraData []byte) ([][]byte, [][]byte) {
+ spsnum := extraData[5] & 0x1F
+ spss := make([][]byte, spsnum)
+ offset := 6
+ for i := 0; i < int(spsnum); i++ {
+ spssize := binary.BigEndian.Uint16(extraData[offset:])
+ sps := make([]byte, spssize+4)
+ copy(sps, []byte{0x00, 0x00, 0x00, 0x01})
+ copy(sps[4:], extraData[offset+2:offset+2+int(spssize)])
+ offset += 2 + int(spssize)
+ spss[i] = sps
+ }
+ ppsnum := extraData[offset]
+ ppss := make([][]byte, ppsnum)
+ offset++
+ for i := 0; i < int(ppsnum); i++ {
+ ppssize := binary.BigEndian.Uint16(extraData[offset:])
+ pps := make([]byte, ppssize+4)
+ copy(pps, []byte{0x00, 0x00, 0x00, 0x01})
+ copy(pps[4:], extraData[offset+2:offset+2+int(ppssize)])
+ offset += 2 + int(ppssize)
+ ppss[i] = pps
+ }
+ return spss, ppss
+}
+
+func ConvertAnnexBToAVCC(annexb []byte) []byte {
+ start, sc := FindStartCode(annexb, 0)
+ if sc == START_CODE_4 {
+ binary.BigEndian.PutUint32(annexb[start:], uint32(len(annexb)-4))
+ return annexb
+ } else {
+ avcc := make([]byte, 1+len(annexb))
+ binary.BigEndian.PutUint32(avcc, uint32(len(annexb)-3))
+ copy(avcc[4:], annexb[start+3:])
+ return avcc
+ }
+}
+
+func CovertAVCCToAnnexB(avcc []byte) {
+ avcc[0] = 0x00
+ avcc[1] = 0x00
+ avcc[2] = 0x00
+ avcc[3] = 0x01
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h265.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h265.go
new file mode 100644
index 0000000000..1277be6c1f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/h265.go
@@ -0,0 +1,998 @@
+package codec
+
+import (
+ "bytes"
+)
+
+// nal_unit_header() {
+// forbidden_zero_bit f(1)
+// nal_unit_type u(6)
+// nuh_layer_id u(6)
+// nuh_temporal_id_plus1 u(3)
+// }
+
+type H265NaluHdr struct {
+ Forbidden_zero_bit uint8
+ Nal_unit_type uint8
+ Nuh_layer_id uint8
+ Nuh_temporal_id_plus1 uint8
+}
+
+func (hdr *H265NaluHdr) Decode(bs *BitStream) {
+ hdr.Forbidden_zero_bit = bs.GetBit()
+ hdr.Nal_unit_type = bs.Uint8(6)
+ hdr.Nuh_layer_id = bs.Uint8(6)
+ hdr.Nuh_temporal_id_plus1 = bs.Uint8(3)
+}
+
+type VPS struct {
+ Vps_video_parameter_set_id uint8
+ Vps_base_layer_internal_flag uint8
+ Vps_base_layer_available_flag uint8
+ Vps_max_layers_minus1 uint8
+ Vps_max_sub_layers_minus1 uint8
+ Vps_temporal_id_nesting_flag uint8
+ Vps_reserved_0xffff_16bits uint16
+ Ptl ProfileTierLevel
+ Vps_sub_layer_ordering_info_present_flag uint8
+ Vps_max_dec_pic_buffering_minus1 [8]uint64
+ Vps_max_num_reorder_pics [8]uint64
+ Vps_max_latency_increase_plus1 [8]uint64
+ Vps_max_layer_id uint8
+ Vps_num_layer_sets_minus1 uint64
+ Layer_id_included_flag [][]uint8
+ Vps_timing_info_present_flag uint8
+ TimeInfo VPSTimeInfo
+ // Vps_extension_flag uint8
+}
+
+type VPSTimeInfo struct {
+ Vps_num_units_in_tick uint32
+ Vps_time_scale uint32
+ Vps_poc_proportional_to_timing_flag uint8
+ Vps_num_ticks_poc_diff_one_minus1 uint64
+ Vps_num_hrd_parameters uint64
+ Hrd_layer_set_idx []uint64
+ Cprms_present_flag []uint8
+}
+
+type ProfileTierLevel struct {
+ General_profile_space uint8
+ General_tier_flag uint8
+ General_profile_idc uint8
+ General_profile_compatibility_flag uint32
+ General_constraint_indicator_flag uint64
+ General_level_idc uint8
+ Sub_layer_profile_present_flag [8]uint8
+ Sub_layer_level_present_flag [8]uint8
+}
+
+//nalu without startcode
+func (vps *VPS) Decode(nalu []byte) {
+ sodb := CovertRbspToSodb(nalu)
+ bs := NewBitStream(sodb)
+ hdr := H265NaluHdr{}
+ hdr.Decode(bs)
+ vps.Vps_video_parameter_set_id = bs.Uint8(4)
+ vps.Vps_base_layer_internal_flag = bs.Uint8(1)
+ vps.Vps_base_layer_available_flag = bs.Uint8(1)
+ vps.Vps_max_layers_minus1 = bs.Uint8(6)
+ vps.Vps_max_sub_layers_minus1 = bs.Uint8(3)
+ vps.Vps_temporal_id_nesting_flag = bs.Uint8(1)
+ vps.Vps_reserved_0xffff_16bits = bs.Uint16(16)
+ vps.Ptl = Profile_tier_level(1, vps.Vps_max_sub_layers_minus1, bs)
+ vps.Vps_sub_layer_ordering_info_present_flag = bs.Uint8(1)
+ var i int
+ if vps.Vps_sub_layer_ordering_info_present_flag > 0 {
+ i = 0
+ } else {
+ i = int(vps.Vps_max_sub_layers_minus1)
+ }
+ for ; i <= int(vps.Vps_max_sub_layers_minus1); i++ {
+ vps.Vps_max_dec_pic_buffering_minus1[i] = bs.ReadUE()
+ vps.Vps_max_num_reorder_pics[i] = bs.ReadUE()
+ vps.Vps_max_latency_increase_plus1[i] = bs.ReadUE()
+ }
+ vps.Vps_max_layer_id = bs.Uint8(6)
+ vps.Vps_num_layer_sets_minus1 = bs.ReadUE()
+ vps.Layer_id_included_flag = make([][]uint8, vps.Vps_num_layer_sets_minus1)
+ for i := 1; i <= int(vps.Vps_num_layer_sets_minus1); i++ {
+ vps.Layer_id_included_flag[i] = make([]uint8, vps.Vps_max_layer_id)
+ for j := 0; j <= int(vps.Vps_max_layer_id); j++ {
+ vps.Layer_id_included_flag[i][j] = bs.Uint8(1)
+ }
+ }
+ vps.Vps_timing_info_present_flag = bs.Uint8(1)
+ if vps.Vps_timing_info_present_flag == 1 {
+ vps.TimeInfo = ParserVPSTimeinfo(bs)
+ }
+}
+
+//ffmpeg hevc.c
+//static void hvcc_parse_ptl(GetBitContext *gb,HEVCDecoderConfigurationRecord *hvcc,unsigned int max_sub_layers_minus1)
+func Profile_tier_level(profilePresentFlag uint8, maxNumSubLayersMinus1 uint8, bs *BitStream) ProfileTierLevel {
+ var ptl ProfileTierLevel
+ ptl.General_profile_space = bs.Uint8(2)
+ ptl.General_tier_flag = bs.Uint8(1)
+ ptl.General_profile_idc = bs.Uint8(5)
+ ptl.General_profile_compatibility_flag = bs.Uint32(32)
+ ptl.General_constraint_indicator_flag = bs.GetBits(48)
+ ptl.General_level_idc = bs.Uint8(8)
+ for i := 0; i < int(maxNumSubLayersMinus1); i++ {
+ ptl.Sub_layer_profile_present_flag[i] = bs.GetBit()
+ ptl.Sub_layer_level_present_flag[i] = bs.GetBit()
+ }
+ if maxNumSubLayersMinus1 > 0 {
+ for i := maxNumSubLayersMinus1; i < 8; i++ {
+ bs.SkipBits(2)
+ }
+ }
+
+ for i := 0; i < int(maxNumSubLayersMinus1); i++ {
+ if ptl.Sub_layer_profile_present_flag[i] == 1 {
+ /*
+ * sub_layer_profile_space[i] u(2)
+ * sub_layer_tier_flag[i] u(1)
+ * sub_layer_profile_idc[i] u(5)
+ * sub_layer_profile_compatibility_flag[i][0..31] u(32)
+ * sub_layer_progressive_source_flag[i] u(1)
+ * sub_layer_interlaced_source_flag[i] u(1)
+ * sub_layer_non_packed_constraint_flag[i] u(1)
+ * sub_layer_frame_only_constraint_flag[i] u(1)
+ * sub_layer_reserved_zero_44bits[i] u(44)
+ */
+ bs.SkipBits(88)
+ }
+ if ptl.Sub_layer_level_present_flag[i] == 1 {
+ bs.SkipBits(8)
+ }
+ }
+ return ptl
+}
+
+func ParserVPSTimeinfo(bs *BitStream) VPSTimeInfo {
+ var ti VPSTimeInfo
+ ti.Vps_num_units_in_tick = bs.Uint32(32)
+ ti.Vps_time_scale = bs.Uint32(32)
+ ti.Vps_poc_proportional_to_timing_flag = bs.Uint8(1)
+ if ti.Vps_poc_proportional_to_timing_flag == 1 {
+ ti.Vps_num_ticks_poc_diff_one_minus1 = bs.ReadUE()
+ }
+ ti.Vps_num_hrd_parameters = bs.ReadUE()
+ // for i := 0; i < int(ti.Vps_num_hrd_parameters); i++ {
+ // ti.Hrd_layer_set_idx[i] = bs.ReadUE()
+ // if i > 0 {
+ // ti.Cprms_present_flag[i] = bs.Uint8(1)
+ // }
+ // //Hrd_parameters(ti.Cprms_present_flag[i])
+ // }
+ return ti
+}
+
+type H265RawSPS struct {
+ Sps_video_parameter_set_id uint8
+ Sps_max_sub_layers_minus1 uint8
+ Sps_temporal_id_nesting_flag uint8
+ Ptl ProfileTierLevel
+ Sps_seq_parameter_set_id uint64
+ Chroma_format_idc uint64
+ Pic_width_in_luma_samples uint64
+ Pic_height_in_luma_samples uint64
+ Conformance_window_flag uint8
+ Conf_win_left_offset uint64
+ Conf_win_right_offset uint64
+ Conf_win_top_offset uint64
+ Conf_win_bottom_offset uint64
+ Bit_depth_luma_minus8 uint64
+ Bit_depth_chroma_minus8 uint64
+ Log2_max_pic_order_cnt_lsb_minus4 uint64
+ Sps_sub_layer_ordering_info_present_flag uint8
+ Vui_parameters_present_flag uint8
+ Vui VUI_Parameters
+}
+
+//nalu without startcode
+func (sps *H265RawSPS) Decode(nalu []byte) {
+ sodb := CovertRbspToSodb(nalu)
+ bs := NewBitStream(sodb)
+ hdr := H265NaluHdr{}
+ hdr.Decode(bs)
+ sps.Sps_video_parameter_set_id = bs.Uint8(4)
+ sps.Sps_max_sub_layers_minus1 = bs.Uint8(3)
+ sps.Sps_temporal_id_nesting_flag = bs.Uint8(1)
+ sps.Ptl = Profile_tier_level(1, sps.Sps_max_sub_layers_minus1, bs)
+ sps.Sps_seq_parameter_set_id = bs.ReadUE()
+ sps.Chroma_format_idc = bs.ReadUE()
+ if sps.Chroma_format_idc == 3 {
+ bs.SkipBits(1)
+ }
+ sps.Pic_width_in_luma_samples = bs.ReadUE()
+ sps.Pic_height_in_luma_samples = bs.ReadUE()
+ sps.Conformance_window_flag = bs.Uint8(1)
+ if sps.Conformance_window_flag == 1 {
+ sps.Conf_win_left_offset = bs.ReadUE()
+ sps.Conf_win_right_offset = bs.ReadUE()
+ sps.Conf_win_top_offset = bs.ReadUE()
+ sps.Conf_win_bottom_offset = bs.ReadUE()
+ }
+ sps.Bit_depth_luma_minus8 = bs.ReadUE()
+ sps.Bit_depth_chroma_minus8 = bs.ReadUE()
+ sps.Log2_max_pic_order_cnt_lsb_minus4 = bs.ReadUE()
+ sps.Sps_sub_layer_ordering_info_present_flag = bs.Uint8(1)
+ i := 0
+ if sps.Sps_sub_layer_ordering_info_present_flag == 0 {
+ i = int(sps.Sps_max_sub_layers_minus1)
+ }
+ for ; i <= int(sps.Sps_max_sub_layers_minus1); i++ {
+ bs.ReadUE()
+ bs.ReadUE()
+ bs.ReadUE()
+ }
+
+ bs.ReadUE() // log2_min_luma_coding_block_size_minus3
+ bs.ReadUE() // log2_diff_max_min_luma_coding_block_size
+ bs.ReadUE() // log2_min_transform_block_size_minus2
+ bs.ReadUE() // log2_diff_max_min_transform_block_size
+ bs.ReadUE() // max_transform_hierarchy_depth_inter
+ bs.ReadUE() // max_transform_hierarchy_depth_intra
+ scaling_list_enabled_flag := bs.GetBit()
+ if scaling_list_enabled_flag > 0 {
+ sps_scaling_list_data_present_flag := bs.GetBit()
+ if sps_scaling_list_data_present_flag > 0 {
+ scaling_list_data(bs)
+ }
+ }
+
+ bs.SkipBits(1)
+ bs.SkipBits(1)
+ if bs.GetBit() == 1 {
+ bs.GetBits(4)
+ bs.GetBits(4)
+ bs.ReadUE()
+ bs.ReadUE()
+ bs.GetBit()
+ }
+ num_short_term_ref_pic_sets := bs.ReadUE()
+ if num_short_term_ref_pic_sets > 64 {
+ panic("beyond HEVC_MAX_SHORT_TERM_REF_PIC_SETS")
+ }
+ var num_delta_pocs [64]uint32
+ for i := 0; i < int(num_short_term_ref_pic_sets); i++ {
+ parse_rps(i, num_short_term_ref_pic_sets, num_delta_pocs, bs)
+ }
+ if bs.GetBit() == 1 {
+ num_long_term_ref_pics_sps := bs.ReadUE()
+ for i := 0; i < int(num_long_term_ref_pics_sps); i++ {
+ length := Min(int(sps.Log2_max_pic_order_cnt_lsb_minus4+4), 16)
+ bs.SkipBits(length)
+ bs.SkipBits(1)
+ }
+ }
+ bs.SkipBits(1)
+ bs.SkipBits(1)
+ sps.Vui_parameters_present_flag = bs.GetBit()
+ if sps.Vui_parameters_present_flag == 1 {
+ sps.Vui.Decode(bs, sps.Sps_max_sub_layers_minus1)
+ }
+}
+
+type VUI_Parameters struct {
+ Aspect_ratio_info_present_flag uint8
+ Overscan_info_present_flag uint8
+ Chroma_loc_info_present_flag uint8
+ Neutral_chroma_indication_flag uint8
+ Field_seq_flag uint8
+ Frame_field_info_present_flag uint8
+ Default_display_window_flag uint8
+ Vui_timing_info_present_flag uint8
+ Vui_num_units_in_tick uint32
+ Vui_time_scale uint32
+ Vui_poc_proportional_to_timing_flag uint8
+ Vui_hrd_parameters_present_flag uint8
+ Bitstream_restriction_flag uint8
+ Tiles_fixed_structure_flag uint8
+ Motion_vectors_over_pic_boundaries_flag uint8
+ Restricted_ref_pic_lists_flag uint8
+ Min_spatial_segmentation_idc uint64
+ Max_bytes_per_pic_denom uint64
+ Max_bits_per_min_cu_denom uint64
+ Log2_max_mv_length_horizontal uint64
+ Log2_max_mv_length_vertical uint64
+}
+
+func (vui *VUI_Parameters) Decode(bs *BitStream, max_sub_layers_minus1 uint8) {
+ vui.Aspect_ratio_info_present_flag = bs.Uint8(1)
+ if vui.Aspect_ratio_info_present_flag == 1 {
+ if bs.Uint8(8) == 255 {
+ bs.SkipBits(32)
+ }
+ }
+ vui.Overscan_info_present_flag = bs.Uint8(1)
+ if vui.Overscan_info_present_flag == 1 {
+ bs.SkipBits(1)
+ }
+ if bs.GetBit() == 1 {
+ bs.SkipBits(4)
+ if bs.GetBit() == 1 {
+ bs.SkipBits(24)
+ }
+ }
+ vui.Chroma_loc_info_present_flag = bs.GetBit()
+ if vui.Chroma_loc_info_present_flag == 1 {
+ bs.ReadUE()
+ bs.ReadUE()
+ }
+ vui.Neutral_chroma_indication_flag = bs.GetBit()
+ vui.Field_seq_flag = bs.GetBit()
+ vui.Frame_field_info_present_flag = bs.GetBit()
+ vui.Default_display_window_flag = bs.GetBit()
+ if vui.Default_display_window_flag == 1 {
+ bs.ReadUE()
+ bs.ReadUE()
+ bs.ReadUE()
+ bs.ReadUE()
+ }
+ vui.Vui_timing_info_present_flag = bs.GetBit()
+ if vui.Vui_timing_info_present_flag == 1 {
+ vui.Vui_num_units_in_tick = bs.Uint32(32)
+ vui.Vui_time_scale = bs.Uint32(32)
+ vui.Vui_poc_proportional_to_timing_flag = bs.GetBit()
+ if vui.Vui_poc_proportional_to_timing_flag == 1 {
+ bs.ReadUE()
+ }
+ vui.Vui_hrd_parameters_present_flag = bs.GetBit()
+ if vui.Vui_hrd_parameters_present_flag == 1 {
+ skip_hrd_parameters(1, uint32(max_sub_layers_minus1), bs)
+ }
+ }
+ vui.Bitstream_restriction_flag = bs.GetBit()
+ if vui.Bitstream_restriction_flag == 1 {
+ vui.Tiles_fixed_structure_flag = bs.GetBit()
+ vui.Motion_vectors_over_pic_boundaries_flag = bs.GetBit()
+ vui.Restricted_ref_pic_lists_flag = bs.GetBit()
+ vui.Min_spatial_segmentation_idc = bs.ReadUE()
+ vui.Max_bytes_per_pic_denom = bs.ReadUE()
+ vui.Max_bits_per_min_cu_denom = bs.ReadUE()
+ vui.Log2_max_mv_length_horizontal = bs.ReadUE()
+ vui.Log2_max_mv_length_vertical = bs.ReadUE()
+ }
+}
+
+func skip_hrd_parameters(cprms_present_flag uint8, max_sub_layers_minus1 uint32, bs *BitStream) {
+ nal_hrd_parameters_present_flag := uint8(0)
+ vcl_hrd_parameters_present_flag := uint8(0)
+ sub_pic_hrd_params_present_flag := uint8(0)
+ if cprms_present_flag == 1 {
+ nal_hrd_parameters_present_flag = bs.GetBit()
+ vcl_hrd_parameters_present_flag = bs.GetBit()
+
+ if nal_hrd_parameters_present_flag == 1 || vcl_hrd_parameters_present_flag == 1 {
+ sub_pic_hrd_params_present_flag = bs.GetBit()
+
+ if sub_pic_hrd_params_present_flag == 1 {
+ /*
+ * tick_divisor_minus2 u(8)
+ * du_cpb_removal_delay_increment_length_minus1 u(5)
+ * sub_pic_cpb_params_in_pic_timing_sei_flag u(1)
+ * dpb_output_delay_du_length_minus1 u(5)
+ */
+ bs.SkipBits(19)
+ }
+
+ bs.SkipBits(8)
+
+ if sub_pic_hrd_params_present_flag == 1 {
+ // cpb_size_du_scale
+ bs.SkipBits(4)
+ }
+
+ /*
+ * initial_cpb_removal_delay_length_minus1 u(5)
+ * au_cpb_removal_delay_length_minus1 u(5)
+ * dpb_output_delay_length_minus1 u(5)
+ */
+ bs.SkipBits(15)
+ }
+ }
+ for i := 0; i <= int(max_sub_layers_minus1); i++ {
+ fixed_pic_rate_general_flag := bs.GetBit()
+ fixed_pic_rate_within_cvs_flag := uint8(0)
+ low_delay_hrd_flag := uint8(0)
+ cpb_cnt_minus1 := uint32(0)
+ if fixed_pic_rate_general_flag == 0 {
+ fixed_pic_rate_within_cvs_flag = bs.GetBit()
+ }
+ if fixed_pic_rate_within_cvs_flag == 1 {
+ bs.ReadUE()
+ } else {
+ low_delay_hrd_flag = bs.GetBit()
+ }
+ if low_delay_hrd_flag == 0 {
+ cpb_cnt_minus1 = uint32(bs.ReadUE())
+ if cpb_cnt_minus1 > 31 {
+ panic("cpb_cnt_minus1 > 31")
+ }
+ }
+ skip_sub_layer_hrd_parameters := func() {
+ for i := 0; i < int(cpb_cnt_minus1); i++ {
+ bs.ReadUE()
+ bs.ReadUE()
+ if sub_pic_hrd_params_present_flag == 1 {
+ bs.ReadUE()
+ bs.ReadUE()
+ }
+ bs.SkipBits(1)
+ }
+ }
+ if nal_hrd_parameters_present_flag == 1 {
+ skip_sub_layer_hrd_parameters()
+ }
+ if vcl_hrd_parameters_present_flag == 1 {
+ skip_sub_layer_hrd_parameters()
+ }
+ }
+}
+
+func scaling_list_data(bs *BitStream) {
+ for i := 0; i < 4; i++ {
+ maxj := 6
+ if i == 3 {
+ maxj = 2
+ }
+ for j := 0; j < maxj; j++ {
+ if bs.GetBit() == 0 {
+ bs.ReadUE()
+ } else {
+ num_coeffs := Min(64, 1<<(4+(i<<1)))
+ if i > 1 {
+ bs.ReadSE()
+ }
+ for k := 0; k < num_coeffs; k++ {
+ bs.ReadSE()
+ }
+ }
+ }
+ }
+}
+
+func parse_rps(rps_idx int, nums_rps uint64, num_delta_pocs [64]uint32, bs *BitStream) {
+ if rps_idx > 0 && bs.GetBit() > 0 {
+ if rps_idx > int(nums_rps) {
+ panic("rps_idx > int(nums_rps)")
+ }
+ bs.SkipBits(1)
+ bs.ReadUE()
+ num_delta_pocs[rps_idx] = 0
+ for i := uint32(0); i <= num_delta_pocs[rps_idx-1]; i++ {
+ var use_delta_flag uint8
+ var used_by_curr_pic_flag uint8 = bs.GetBit()
+ if used_by_curr_pic_flag == 0 {
+ use_delta_flag = bs.GetBit()
+ }
+ if use_delta_flag > 0 || used_by_curr_pic_flag > 0 {
+ num_delta_pocs[rps_idx]++
+ }
+ }
+ } else {
+ num_negative_pics := bs.ReadUE()
+ num_positive_pics := bs.ReadUE()
+ if (num_negative_pics+num_positive_pics)*2 > uint64(bs.RemainBits()) {
+ panic("(num_negative_pics + num_positive_pics) * 2> uint64(bs.RemainBits())")
+ }
+ for i := 0; i < int(num_negative_pics); i++ {
+ bs.ReadUE()
+ bs.SkipBits(1)
+ }
+ for i := 0; i < int(num_positive_pics); i++ {
+ bs.ReadUE()
+ bs.SkipBits(1)
+ }
+ }
+}
+
+type H265RawPPS struct {
+ Pps_pic_parameter_set_id uint64
+ Pps_seq_parameter_set_id uint64
+ Dependent_slice_segments_enabled_flag uint8
+ Output_flag_present_flag uint8
+ Num_extra_slice_header_bits uint8
+ Sign_data_hiding_enabled_flag uint8
+ Cabac_init_present_flag uint8
+ Num_ref_idx_l0_default_active_minus1 uint64
+ Num_ref_idx_l1_default_active_minus1 uint64
+ Init_qp_minus26 int64
+ Constrained_intra_pred_flag uint8
+ Transform_skip_enabled_flag uint8
+ Cu_qp_delta_enabled_flag uint8
+ Diff_cu_qp_delta_depth uint64
+ Pps_cb_qp_offset int64
+ Pps_cr_qp_offset int64
+ Pps_slice_chroma_qp_offsets_present_flag uint8
+ Weighted_pred_flag uint8
+ Weighted_bipred_flag uint8
+ Transquant_bypass_enabled_flag uint8
+ Tiles_enabled_flag uint8
+ Entropy_coding_sync_enabled_flag uint8
+}
+
+//nalu without startcode
+func (pps *H265RawPPS) Decode(nalu []byte) {
+ sodb := CovertRbspToSodb(nalu)
+ bs := NewBitStream(sodb)
+ hdr := H265NaluHdr{}
+ hdr.Decode(bs)
+ pps.Pps_pic_parameter_set_id = bs.ReadUE()
+ pps.Pps_seq_parameter_set_id = bs.ReadUE()
+ pps.Dependent_slice_segments_enabled_flag = bs.GetBit()
+ pps.Output_flag_present_flag = bs.GetBit()
+ pps.Num_extra_slice_header_bits = bs.Uint8(3)
+ pps.Sign_data_hiding_enabled_flag = bs.GetBit()
+ pps.Cabac_init_present_flag = bs.GetBit()
+ pps.Num_ref_idx_l0_default_active_minus1 = bs.ReadUE()
+ pps.Num_ref_idx_l1_default_active_minus1 = bs.ReadUE()
+ pps.Init_qp_minus26 = bs.ReadSE()
+ pps.Constrained_intra_pred_flag = bs.GetBit()
+ pps.Transform_skip_enabled_flag = bs.GetBit()
+ pps.Cu_qp_delta_enabled_flag = bs.GetBit()
+ if pps.Cu_qp_delta_enabled_flag == 1 {
+ pps.Diff_cu_qp_delta_depth = bs.ReadUE()
+ }
+ pps.Pps_cb_qp_offset = bs.ReadSE()
+ pps.Pps_cr_qp_offset = bs.ReadSE()
+ pps.Pps_slice_chroma_qp_offsets_present_flag = bs.GetBit()
+ pps.Weighted_pred_flag = bs.GetBit()
+ pps.Weighted_bipred_flag = bs.GetBit()
+ pps.Transquant_bypass_enabled_flag = bs.GetBit()
+ pps.Tiles_enabled_flag = bs.GetBit()
+ pps.Entropy_coding_sync_enabled_flag = bs.GetBit()
+}
+
+func GetH265Resolution(sps []byte) (width uint32, height uint32) {
+ start, sc := FindStartCode(sps, 0)
+ h265sps := H265RawSPS{}
+ h265sps.Decode(sps[start+int(sc):])
+ width = uint32(h265sps.Pic_width_in_luma_samples)
+ height = uint32(h265sps.Pic_height_in_luma_samples)
+ return
+}
+
+func GetVPSIdWithStartCode(vps []byte) uint8 {
+ start, sc := FindStartCode(vps, 0)
+ return GetVPSId(vps[start+int(sc):])
+}
+
+func GetVPSId(vps []byte) uint8 {
+ var rawvps VPS
+ rawvps.Decode(vps)
+ return rawvps.Vps_video_parameter_set_id
+}
+
+func GetH265SPSIdWithStartCode(sps []byte) uint64 {
+ start, sc := FindStartCode(sps, 0)
+ return GetH265SPSId(sps[start+int(sc):])
+}
+
+func GetH265SPSId(sps []byte) uint64 {
+ var rawsps H265RawSPS
+ rawsps.Decode(sps)
+ return rawsps.Sps_seq_parameter_set_id
+}
+
+func GetH65PPSIdWithStartCode(pps []byte) uint64 {
+ start, sc := FindStartCode(pps, 0)
+ return GetH265SPSId(pps[start+int(sc):])
+}
+
+func GetH265PPSId(pps []byte) uint64 {
+ var rawpps H265RawPPS
+ rawpps.Decode(pps)
+ return rawpps.Pps_pic_parameter_set_id
+}
+
+/*
+ISO/IEC 14496-15:2017(E) 8.3.3.1.2 Syntax (p71)
+
+aligned(8) class HEVCDecoderConfigurationRecord {
+ unsigned int(8) configurationVersion = 1;
+ unsigned int(2) general_profile_space;
+ unsigned int(1) general_tier_flag;
+ unsigned int(5) general_profile_idc;
+ unsigned int(32) general_profile_compatibility_flags;
+ unsigned int(48) general_constraint_indicator_flags;
+ unsigned int(8) general_level_idc;
+ bit(4) reserved = '1111'b;
+ unsigned int(12) min_spatial_segmentation_idc;
+ bit(6) reserved = '111111'b;
+ unsigned int(2) parallelismType;
+ bit(6) reserved = '111111'b;
+ unsigned int(2) chromaFormat;
+ bit(5) reserved = '11111'b;
+ unsigned int(3) bitDepthLumaMinus8;
+ bit(5) reserved = '11111'b;
+ unsigned int(3) bitDepthChromaMinus8;
+ bit(16) avgFrameRate;
+ bit(2) constantFrameRate;
+ bit(3) numTemporalLayers;
+ bit(1) temporalIdNested;
+ unsigned int(2) lengthSizeMinusOne;
+ unsigned int(8) numOfArrays;
+ for (j=0; j < numOfArrays; j++) {
+ bit(1) array_completeness;
+ unsigned int(1) reserved = 0;
+ unsigned int(6) NAL_unit_type;
+ unsigned int(16) numNalus;
+ for (i=0; i< numNalus; i++) {
+ unsigned int(16) nalUnitLength;
+ bit(8*nalUnitLength) nalUnit;
+ }
+ }
+}
+*/
+
+type NalUnit struct {
+ NalUnitLength uint16
+ Nalu []byte
+}
+
+type HVCCNALUnitArray struct {
+ Array_completeness uint8
+ NAL_unit_type uint8
+ NumNalus uint16
+ NalUnits []*NalUnit
+}
+
+type HEVCRecordConfiguration struct {
+ ConfigurationVersion uint8
+ General_profile_space uint8
+ General_tier_flag uint8
+ General_profile_idc uint8
+ General_profile_compatibility_flags uint32
+ General_constraint_indicator_flags uint64
+ General_level_idc uint8
+ Min_spatial_segmentation_idc uint16
+ ParallelismType uint8
+ ChromaFormat uint8
+ BitDepthLumaMinus8 uint8
+ BitDepthChromaMinus8 uint8
+ AvgFrameRate uint16
+ ConstantFrameRate uint8
+ NumTemporalLayers uint8
+ TemporalIdNested uint8
+ LengthSizeMinusOne uint8
+ NumOfArrays uint8
+ Arrays []*HVCCNALUnitArray
+}
+
+func NewHEVCRecordConfiguration() *HEVCRecordConfiguration {
+ return &HEVCRecordConfiguration{
+ ConfigurationVersion: 1,
+ General_profile_compatibility_flags: 0xffffffff,
+ General_constraint_indicator_flags: 0xffffffffffffffff,
+ Min_spatial_segmentation_idc: 4097,
+ LengthSizeMinusOne: 3,
+ }
+}
+
+func (hvcc *HEVCRecordConfiguration) Encode() []byte {
+ bsw := NewBitStreamWriter(512)
+ bsw.PutByte(hvcc.ConfigurationVersion)
+ bsw.PutUint8(hvcc.General_profile_space, 2)
+ bsw.PutUint8(hvcc.General_tier_flag, 1)
+ bsw.PutUint8(hvcc.General_profile_idc, 5)
+ bsw.PutUint32(hvcc.General_profile_compatibility_flags, 32)
+ bsw.PutUint64(hvcc.General_constraint_indicator_flags, 48)
+ bsw.PutByte(hvcc.General_level_idc)
+ bsw.PutUint8(0x0F, 4)
+ bsw.PutUint16(hvcc.Min_spatial_segmentation_idc, 12)
+ bsw.PutUint8(0x3F, 6)
+ //ffmpeg hvcc_write(AVIOContext *pb, HEVCDecoderConfigurationRecord *hvcc)
+ /*
+ * parallelismType indicates the type of parallelism that is used to meet
+ * the restrictions imposed by min_spatial_segmentation_idc when the value
+ * of min_spatial_segmentation_idc is greater than 0.
+ */
+ if hvcc.Min_spatial_segmentation_idc == 0 {
+ hvcc.ParallelismType = 0
+ }
+ bsw.PutUint8(hvcc.ParallelismType, 2)
+ bsw.PutUint8(0x3F, 6)
+ bsw.PutUint8(hvcc.ChromaFormat, 2)
+ bsw.PutUint8(0x1F, 5)
+ bsw.PutUint8(hvcc.BitDepthLumaMinus8, 3)
+ bsw.PutUint8(0x1F, 5)
+ bsw.PutUint8(hvcc.BitDepthChromaMinus8, 3)
+ bsw.PutUint16(hvcc.AvgFrameRate, 16)
+ bsw.PutUint8(hvcc.ConstantFrameRate, 2)
+ bsw.PutUint8(hvcc.NumTemporalLayers, 3)
+ bsw.PutUint8(hvcc.TemporalIdNested, 1)
+ bsw.PutUint8(hvcc.LengthSizeMinusOne, 2)
+ bsw.PutByte(uint8(len(hvcc.Arrays)))
+ for _, arrays := range hvcc.Arrays {
+ bsw.PutUint8(arrays.Array_completeness, 1)
+ bsw.PutUint8(0, 1)
+ bsw.PutUint8(arrays.NAL_unit_type, 6)
+ bsw.PutUint16(arrays.NumNalus, 16)
+ for _, nalu := range arrays.NalUnits {
+ bsw.PutUint16(nalu.NalUnitLength, 16)
+ bsw.PutBytes(nalu.Nalu)
+ }
+ }
+ return bsw.Bits()
+}
+
+func (hvcc *HEVCRecordConfiguration) Decode(hevc []byte) {
+ bs := NewBitStream(hevc)
+ hvcc.ConfigurationVersion = bs.Uint8(8)
+ hvcc.General_profile_space = bs.Uint8(2)
+ hvcc.General_tier_flag = bs.Uint8(1)
+ hvcc.General_profile_idc = bs.Uint8(5)
+ hvcc.General_profile_compatibility_flags = bs.Uint32(32)
+ hvcc.General_constraint_indicator_flags = bs.GetBits(48)
+ hvcc.General_level_idc = bs.Uint8(8)
+ bs.SkipBits(4)
+ hvcc.Min_spatial_segmentation_idc = bs.Uint16(12)
+ bs.SkipBits(6)
+ hvcc.ParallelismType = bs.Uint8(2)
+ bs.SkipBits(6)
+ hvcc.ChromaFormat = bs.Uint8(2)
+ bs.SkipBits(5)
+ hvcc.BitDepthLumaMinus8 = bs.Uint8(3)
+ bs.SkipBits(5)
+ hvcc.BitDepthChromaMinus8 = bs.Uint8(3)
+ hvcc.AvgFrameRate = bs.Uint16(16)
+ hvcc.ConstantFrameRate = bs.Uint8(2)
+ hvcc.NumTemporalLayers = bs.Uint8(3)
+ hvcc.TemporalIdNested = bs.Uint8(1)
+ hvcc.LengthSizeMinusOne = bs.Uint8(2)
+ hvcc.NumOfArrays = bs.Uint8(8)
+ hvcc.Arrays = make([]*HVCCNALUnitArray, hvcc.NumOfArrays)
+ for i := 0; i < int(hvcc.NumOfArrays); i++ {
+ hvcc.Arrays[i] = new(HVCCNALUnitArray)
+ hvcc.Arrays[i].Array_completeness = bs.GetBit()
+ bs.SkipBits(1)
+ hvcc.Arrays[i].NAL_unit_type = bs.Uint8(6)
+ hvcc.Arrays[i].NumNalus = bs.Uint16(16)
+ hvcc.Arrays[i].NalUnits = make([]*NalUnit, hvcc.Arrays[i].NumNalus)
+ for j := 0; j < int(hvcc.Arrays[i].NumNalus); j++ {
+ hvcc.Arrays[i].NalUnits[j] = new(NalUnit)
+ hvcc.Arrays[i].NalUnits[j].NalUnitLength = bs.Uint16(16)
+ hvcc.Arrays[i].NalUnits[j].Nalu = bs.GetBytes(int(hvcc.Arrays[i].NalUnits[j].NalUnitLength))
+ }
+ }
+}
+
+func (hvcc *HEVCRecordConfiguration) UpdateSPS(sps []byte) {
+ start, sc := FindStartCode(sps, 0)
+ sps = sps[start+int(sc):]
+ var rawsps H265RawSPS
+ rawsps.Decode(sps)
+ spsid := rawsps.Sps_seq_parameter_set_id
+ var needUpdate bool = false
+ i := 0
+ for ; i < len(hvcc.Arrays); i++ {
+ arrays := hvcc.Arrays[i]
+ found := false
+ if arrays.NAL_unit_type == uint8(H265_NAL_SPS) {
+ j := 0
+ for ; j < len(arrays.NalUnits); j++ {
+ if spsid != GetH265SPSId(arrays.NalUnits[j].Nalu) {
+ found = true
+ continue
+ }
+ //find the same sps nalu
+ if arrays.NalUnits[j].NalUnitLength == uint16(len(sps)) && bytes.Equal(arrays.NalUnits[j].Nalu, sps) {
+ return
+ }
+ tmpsps := make([]byte, len(sps))
+ copy(tmpsps, sps)
+ arrays.NalUnits[j].Nalu = tmpsps
+ arrays.NalUnits[j].NalUnitLength = uint16(len(tmpsps))
+ needUpdate = true
+ break
+ }
+ if j == len(arrays.NalUnits) {
+ nalu := &NalUnit{
+ Nalu: make([]byte, len(sps)),
+ NalUnitLength: uint16(len(sps)),
+ }
+ copy(nalu.Nalu, sps)
+ arrays.NalUnits = append(arrays.NalUnits, nalu)
+ needUpdate = true
+ }
+ }
+ if found {
+ break
+ }
+ }
+ if i == len(hvcc.Arrays) {
+ nua := &HVCCNALUnitArray{
+ Array_completeness: 1,
+ NAL_unit_type: 33,
+ NumNalus: 1,
+ NalUnits: make([]*NalUnit, 1),
+ }
+ nu := &NalUnit{
+ NalUnitLength: uint16(len(sps)),
+ Nalu: make([]byte, len(sps)),
+ }
+ copy(nu.Nalu, sps)
+ nua.NalUnits[0] = nu
+ hvcc.Arrays = append(hvcc.Arrays, nua)
+ needUpdate = true
+ }
+ if needUpdate {
+ hvcc.NumTemporalLayers = uint8(Max(int(hvcc.NumTemporalLayers), int(rawsps.Sps_max_sub_layers_minus1+1)))
+ hvcc.TemporalIdNested = rawsps.Sps_temporal_id_nesting_flag
+ hvcc.ChromaFormat = uint8(rawsps.Chroma_format_idc)
+ hvcc.BitDepthChromaMinus8 = uint8(rawsps.Bit_depth_chroma_minus8)
+ hvcc.BitDepthLumaMinus8 = uint8(rawsps.Bit_depth_luma_minus8)
+ hvcc.updatePtl(rawsps.Ptl)
+ hvcc.updateVui(rawsps.Vui)
+ }
+}
+
+func (hvcc *HEVCRecordConfiguration) UpdatePPS(pps []byte) {
+ start, sc := FindStartCode(pps, 0)
+ pps = pps[start+int(sc):]
+ var rawpps H265RawPPS
+ rawpps.Decode(pps)
+ ppsid := rawpps.Pps_pic_parameter_set_id
+ var needUpdate bool = false
+ i := 0
+ for ; i < len(hvcc.Arrays); i++ {
+ arrays := hvcc.Arrays[i]
+ found := false
+ if arrays.NAL_unit_type == uint8(H265_NAL_PPS) {
+ j := 0
+ for ; j < len(arrays.NalUnits); j++ {
+ if ppsid != GetH265PPSId(arrays.NalUnits[j].Nalu) {
+ found = true
+ continue
+ }
+ //find the same sps nalu
+ if arrays.NalUnits[j].NalUnitLength == uint16(len(pps)) && bytes.Equal(arrays.NalUnits[j].Nalu, pps) {
+ return
+ }
+ tmppps := make([]byte, len(pps))
+ copy(tmppps, pps)
+ arrays.NalUnits[j].Nalu = tmppps
+ arrays.NalUnits[j].NalUnitLength = uint16(len(tmppps))
+ needUpdate = true
+ break
+ }
+ if j == len(arrays.NalUnits) {
+ nalu := &NalUnit{
+ Nalu: make([]byte, len(pps)),
+ NalUnitLength: uint16(len(pps)),
+ }
+ copy(nalu.Nalu, pps)
+ arrays.NalUnits = append(arrays.NalUnits, nalu)
+ needUpdate = true
+ }
+ }
+ if found {
+ break
+ }
+ }
+ if i == len(hvcc.Arrays) {
+ nua := &HVCCNALUnitArray{
+ Array_completeness: 1,
+ NAL_unit_type: 34,
+ NumNalus: 1,
+ NalUnits: make([]*NalUnit, 1),
+ }
+ nu := &NalUnit{
+ NalUnitLength: uint16(len(pps)),
+ Nalu: make([]byte, len(pps)),
+ }
+ copy(nu.Nalu, pps)
+ nua.NalUnits[0] = nu
+ hvcc.Arrays = append(hvcc.Arrays, nua)
+ needUpdate = true
+ }
+ if needUpdate {
+ if rawpps.Entropy_coding_sync_enabled_flag == 1 && rawpps.Tiles_enabled_flag == 1 {
+ hvcc.ParallelismType = 0
+ } else if rawpps.Entropy_coding_sync_enabled_flag == 1 {
+ hvcc.ParallelismType = 3
+ } else if rawpps.Tiles_enabled_flag == 1 {
+ hvcc.ParallelismType = 2
+ } else {
+ hvcc.ParallelismType = 1
+ }
+ }
+}
+
+func (hvcc *HEVCRecordConfiguration) UpdateVPS(vps []byte) {
+ start, sc := FindStartCode(vps, 0)
+ vps = vps[start+int(sc):]
+ var rawvps VPS
+ rawvps.Decode(vps)
+ vpsid := rawvps.Vps_video_parameter_set_id
+ var needUpdate bool = false
+ i := 0
+ for ; i < len(hvcc.Arrays); i++ {
+ arrays := hvcc.Arrays[i]
+ found := false
+ if arrays.NAL_unit_type == uint8(H265_NAL_VPS) {
+ found = true
+ j := 0
+ for ; j < len(arrays.NalUnits); j++ {
+ if vpsid != GetVPSId(arrays.NalUnits[j].Nalu) {
+ found = true
+ continue
+ }
+ //find the same sps nalu
+ if arrays.NalUnits[j].NalUnitLength == uint16(len(vps)) && bytes.Equal(arrays.NalUnits[j].Nalu, vps) {
+ return
+ }
+ tmpvps := make([]byte, len(vps))
+ copy(tmpvps, vps)
+ arrays.NalUnits[j].Nalu = tmpvps
+ arrays.NalUnits[j].NalUnitLength = uint16(len(tmpvps))
+ needUpdate = true
+ break
+ }
+ if j == len(arrays.NalUnits) {
+ nalu := &NalUnit{
+ Nalu: make([]byte, len(vps)),
+ NalUnitLength: uint16(len(vps)),
+ }
+ copy(nalu.Nalu, vps)
+ arrays.NalUnits = append(arrays.NalUnits, nalu)
+ needUpdate = true
+ }
+ }
+ if found {
+ break
+ }
+ }
+ if i == len(hvcc.Arrays) {
+ nua := &HVCCNALUnitArray{
+ Array_completeness: 1,
+ NAL_unit_type: 32,
+ NumNalus: 1,
+ NalUnits: make([]*NalUnit, 1),
+ }
+ nu := &NalUnit{
+ NalUnitLength: uint16(len(vps)),
+ Nalu: make([]byte, len(vps)),
+ }
+ copy(nu.Nalu, vps)
+ nua.NalUnits[0] = nu
+ hvcc.Arrays = append(hvcc.Arrays, nua)
+ needUpdate = true
+ }
+ if needUpdate {
+ hvcc.NumTemporalLayers = uint8(Max(int(hvcc.NumTemporalLayers), int(rawvps.Vps_max_layers_minus1+1)))
+ hvcc.updatePtl(rawvps.Ptl)
+ }
+}
+
+func (hvcc *HEVCRecordConfiguration) ToNalus() (nalus []byte) {
+ startcode := []byte{0x00, 0x00, 0x00, 0x01}
+ for _, arrays := range hvcc.Arrays {
+ for _, unit := range arrays.NalUnits {
+ nalus = append(nalus, startcode...)
+ nalus = append(nalus, unit.Nalu[:unit.NalUnitLength]...)
+ }
+ }
+ return
+}
+
+func (hvcc *HEVCRecordConfiguration) updatePtl(ptl ProfileTierLevel) {
+ hvcc.General_profile_space = ptl.General_profile_space
+ if hvcc.General_tier_flag < ptl.General_tier_flag {
+ hvcc.General_level_idc = ptl.General_level_idc
+ } else {
+ hvcc.General_level_idc = uint8(Max(int(hvcc.General_level_idc), int(ptl.General_level_idc)))
+ }
+ hvcc.General_tier_flag = uint8(Max(int(hvcc.General_tier_flag), int(ptl.General_tier_flag)))
+ hvcc.General_profile_idc = uint8(Max(int(hvcc.General_profile_idc), int(ptl.General_profile_idc)))
+ hvcc.General_profile_compatibility_flags &= ptl.General_profile_compatibility_flag
+ hvcc.General_constraint_indicator_flags &= ptl.General_constraint_indicator_flag
+}
+
+func (hvcc *HEVCRecordConfiguration) updateVui(vui VUI_Parameters) {
+ hvcc.Min_spatial_segmentation_idc = uint16(Min(int(hvcc.Min_spatial_segmentation_idc), int(vui.Min_spatial_segmentation_idc)))
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/opus.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/opus.go
new file mode 100644
index 0000000000..170bc52abd
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/opus.go
@@ -0,0 +1,430 @@
+package codec
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+// rfc6716 https://datatracker.ietf.org/doc/html/rfc6716
+//
+// TOC byte
+// 0
+// 0 1 2 3 4 5 6 7
+// +-+-+-+-+-+-+-+-+
+// | config |s| c |
+// +-+-+-+-+-+-+-+-+
+
+// +-----------------------+-----------+-----------+-------------------+
+// | Configuration | Mode | Bandwidth | Frame Sizes |
+// | Number(s) | | | |
+// +-----------------------+-----------+-----------+-------------------+
+// | 0...3 | SILK-only | NB | 10, 20, 40, 60 ms |
+// | | | | |
+// | 4...7 | SILK-only | MB | 10, 20, 40, 60 ms |
+// | | | | |
+// | 8...11 | SILK-only | WB | 10, 20, 40, 60 ms |
+// | | | | |
+// | 12...13 | Hybrid | SWB | 10, 20 ms |
+// | | | | |
+// | 14...15 | Hybrid | FB | 10, 20 ms |
+// | | | | |
+// | 16...19 | CELT-only | NB | 2.5, 5, 10, 20 ms |
+// | | | | |
+// | 20...23 | CELT-only | WB | 2.5, 5, 10, 20 ms |
+// | | | | |
+// | 24...27 | CELT-only | SWB | 2.5, 5, 10, 20 ms |
+// | | | | |
+// | 28...31 | CELT-only | FB | 2.5, 5, 10, 20 ms |
+// +-----------------------+-----------+-----------+-------------------+
+
+// s: with 0 indicating mono and 1 indicating stereo.
+//
+// c : codes 0 to 3
+// 0: 1 frame in the packet
+// 1: 2 frames in the packet, each with equal compressed size
+// 2: 2 frames in the packet, with different compressed sizes
+// 3: an arbitrary number of frames in the packet
+
+// Frame Length Coding
+// 0: No frame (Discontinuous Transmission (DTX) or lost packet)
+// 1...251: Length of the frame in bytes
+// 252...255: A second byte is needed. The total length is (second_byte*4)+first_byte
+
+// Code 0:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | config |s|0|0| |
+// +-+-+-+-+-+-+-+-+ |
+// | Compressed frame 1 (N-1 bytes)... :
+// : |
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// Code 1:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | config |s|0|1| |
+// +-+-+-+-+-+-+-+-+ :
+// | Compressed frame 1 ((N-1)/2 bytes)... |
+// : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ :
+// | Compressed frame 2 ((N-1)/2 bytes)... |
+// : +-+-+-+-+-+-+-+-+
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// Code 2:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | config |s|1|0| N1 (1-2 bytes): |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ :
+// | Compressed frame 1 (N1 bytes)... |
+// : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | Compressed frame 2... :
+// : |
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+//Code 3:
+// Frame Count Byte
+// 0
+// 0 1 2 3 4 5 6 7
+// +-+-+-+-+-+-+-+-+
+// |v|p| M |
+// +-+-+-+-+-+-+-+-+
+// v: 0 - CBR 1 - VBR
+// p: 0 - no padding 1 - padding after frame
+// M: frame count
+
+// A CBR Code 3 Packet
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | config |s|1|1|0|p| M | Padding length (Optional) :
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame 1 (R/M bytes)... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame 2 (R/M bytes)... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : ... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame M (R/M bytes)... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : Opus Padding (Optional)... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+
+//
+// A VBR Code 3 Packet
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | config |s|1|1|1|p| M | Padding length (Optional) :
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : N1 (1-2 bytes): N2 (1-2 bytes): ... : N[M-1] |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame 1 (N1 bytes)... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame 2 (N2 bytes)... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : ... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | |
+// : Compressed frame M... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : Opus Padding (Optional)... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+var (
+ /// 10ms,20ms,40ms,60ms, samplerate 48000
+ // sample num per millisecond
+ // 48000 / 1000ms * 10 = 480 ...
+ SLKOpusSampleSize [4]int = [4]int{480, 960, 1920, 2880}
+ HybridOpusSampleSize [4]int = [4]int{480, 960}
+ CELTOpusSampleSize [4]int = [4]int{120, 210, 480, 960}
+)
+
+func OpusPacketDuration(packet []byte) uint64 {
+ config := int(packet[0] >> 3)
+ code := packet[0] & 0x03
+ frameCount := 0
+ var duration uint64
+ if code == 0 {
+ frameCount = 1
+ } else if code == 1 || code == 2 {
+ frameCount = 2
+ } else if code == 3 {
+ frameCount = int(packet[1] & 0x1F)
+ } else {
+ panic("code must <= 3")
+ }
+
+ switch {
+ case config >= 0 && config < 12:
+ duration = uint64(frameCount * SLKOpusSampleSize[config%4])
+ case config >= 12 && config < 16:
+ duration = uint64(frameCount * HybridOpusSampleSize[config%2])
+ case config >= 16 && config < 32:
+ duration = uint64(frameCount * CELTOpusSampleSize[config%4])
+ default:
+ panic("unkown opus config")
+ }
+
+ return duration
+}
+
+//ffmpeg opus.h OpusPacket
+type OpusPacket struct {
+ Code int
+ Config int
+ Stereo int
+ Vbr int
+ FrameCount int
+ FrameLen []uint16
+ Frame []byte
+ Duration uint64
+}
+
+func DecodeOpusPacket(packet []byte) *OpusPacket {
+ pkt := &OpusPacket{}
+ pkt.Code = int(packet[0] & 0x03)
+ pkt.Stereo = int((packet[0] >> 2) & 0x01)
+ pkt.Config = int(packet[0] >> 3)
+
+ switch pkt.Code {
+ case 0:
+ pkt.FrameCount = 1
+ pkt.FrameLen = make([]uint16, 1)
+ pkt.FrameLen[0] = uint16(len(packet) - 1)
+ pkt.Frame = packet[1:]
+ case 1:
+ pkt.FrameCount = 2
+ pkt.FrameLen = make([]uint16, 1)
+ pkt.FrameLen[0] = uint16(len(packet)-1) / 2
+ pkt.Frame = packet[1:]
+ case 2:
+ pkt.FrameCount = 2
+ hdr := 1
+ N1 := int(packet[1])
+ if N1 >= 252 {
+ N1 = N1 + int(packet[2]*4)
+ hdr = 2
+ }
+ pkt.FrameLen = make([]uint16, 2)
+ pkt.FrameLen[0] = uint16(N1)
+ pkt.FrameLen[1] = uint16(len(packet)-hdr) - uint16(N1)
+ case 3:
+ hdr := 2
+ pkt.Vbr = int(packet[1] >> 7)
+ padding := packet[1] >> 6
+ pkt.FrameCount = int(packet[1] & 0x1F)
+ paddingLen := 0
+ if padding == 1 {
+ for packet[hdr] == 255 {
+ paddingLen += 254
+ hdr++
+ }
+ paddingLen += int(packet[hdr])
+ }
+
+ if pkt.Vbr == 0 {
+ pkt.FrameLen = make([]uint16, 1)
+ pkt.FrameLen[0] = uint16(len(packet)-hdr-paddingLen) / uint16(pkt.FrameCount)
+ pkt.Frame = packet[hdr : hdr+int(pkt.FrameLen[0]*uint16(pkt.FrameCount))]
+ } else {
+ n := 0
+ for i := 0; i < int(pkt.FrameCount)-1; i++ {
+ N1 := int(packet[hdr])
+ hdr += 1
+ if N1 >= 252 {
+ N1 = N1 + int(packet[hdr]*4)
+ hdr += 1
+ }
+ n += N1
+ pkt.FrameLen = append(pkt.FrameLen, uint16(N1))
+ }
+ lastFrameLen := len(packet) - hdr - paddingLen - n
+ pkt.FrameLen = append(pkt.FrameLen, uint16(lastFrameLen))
+ pkt.Frame = packet[hdr : hdr+n+lastFrameLen]
+ }
+ default:
+ panic("Error C must <= 3")
+ }
+ OpusPacketDuration(packet)
+ return pkt
+}
+
+const (
+ LEFT_CHANNEL = 0
+ RIGHT_CHANNEL = 1
+)
+
+var (
+ vorbisChanLayoutOffset [8][8]byte = [8][8]byte{
+ {0},
+ {0, 1},
+ {0, 2, 1},
+ {0, 1, 2, 3},
+ {0, 2, 1, 3, 4},
+ {0, 2, 1, 5, 3, 4},
+ {0, 2, 1, 6, 5, 3, 4},
+ {0, 2, 1, 7, 5, 6, 3, 4},
+ }
+)
+
+type ChannelOrder func(channels int, idx int) int
+
+func defalutOrder(channels int, idx int) int {
+ return idx
+}
+
+func vorbisOrder(channels int, idx int) int {
+ return int(vorbisChanLayoutOffset[channels-1][idx])
+}
+
+type ChannelMap struct {
+ StreamIdx int
+ ChannelIdx int
+ Silence bool
+ Copy bool
+ CopyFrom int
+}
+
+type OpusContext struct {
+ Preskip int
+ SampleRate int
+ ChannelCount int
+ StreamCount int
+ StereoStreamCount int
+ OutputGain uint16
+ MapType uint8
+ ChannelMaps []ChannelMap
+}
+
+// opus ID Head
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | 'O' | 'p' | 'u' | 's' |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | 'H' | 'e' | 'a' | 'd' |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Version = 1 | Channel Count | Pre-skip |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Input Sample Rate (Hz) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Output Gain (Q7.8 in dB) | Mapping Family| |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ :
+// | |
+// : Optional Channel Mapping Table... :
+// | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+
+// | Stream Count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Coupled Count | Channel Mapping... :
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+func (ctx *OpusContext) ParseExtranData(extraData []byte) error {
+ if string(extraData[0:8]) != "OpusHead" {
+ return errors.New("magic signature must equal OpusHead")
+ }
+
+ _ = extraData[8] // version
+ ctx.ChannelCount = int(extraData[9])
+ ctx.Preskip = int(binary.LittleEndian.Uint16(extraData[10:]))
+ ctx.SampleRate = int(binary.LittleEndian.Uint32(extraData[12:]))
+ ctx.OutputGain = binary.LittleEndian.Uint16(extraData[16:])
+ ctx.MapType = extraData[18]
+ var channel []byte
+ var order ChannelOrder
+ if ctx.MapType == 0 {
+ ctx.StreamCount = 1
+ ctx.StereoStreamCount = ctx.ChannelCount - 1
+ channel = []byte{0, 1}
+ order = defalutOrder
+ } else if ctx.MapType == 1 || ctx.MapType == 2 || ctx.MapType == 255 {
+ ctx.StreamCount = int(extraData[19])
+ ctx.StereoStreamCount = int(extraData[20])
+ if ctx.MapType == 1 {
+ channel = extraData[21 : 21+ctx.ChannelCount]
+ order = vorbisOrder
+ }
+ } else {
+ return errors.New("unsupport map type 255")
+ }
+
+ for i := 0; i < ctx.ChannelCount; i++ {
+ cm := ChannelMap{}
+ index := channel[order(ctx.ChannelCount, i)]
+ if index == 255 {
+ cm.Silence = true
+ continue
+ } else if index > byte(ctx.StereoStreamCount)+byte(ctx.StreamCount) {
+ return errors.New("index must < (streamcount + stereo streamcount)")
+ }
+
+ for j := 0; j < i; j++ {
+ if channel[order(ctx.ChannelCount, i)] == index {
+ cm.Copy = true
+ cm.CopyFrom = j
+ break
+ }
+ }
+
+ if int(index) < 2*ctx.StereoStreamCount {
+ cm.StreamIdx = int(index) / 2
+ if index&1 == 0 {
+ cm.ChannelIdx = LEFT_CHANNEL
+ } else {
+ cm.ChannelIdx = RIGHT_CHANNEL
+ }
+ } else {
+ cm.StreamIdx = int(index) - ctx.StereoStreamCount
+ cm.ChannelIdx = 0
+ }
+ ctx.ChannelMaps = append(ctx.ChannelMaps, cm)
+ }
+
+ return nil
+}
+
+func (ctx *OpusContext) WriteOpusExtraData() []byte {
+ extraData := make([]byte, 19)
+ copy(extraData, string("OpusHead"))
+ extraData[8] = 0x01
+ extraData[9] = byte(ctx.ChannelCount)
+ binary.LittleEndian.PutUint16(extraData[10:], uint16(ctx.Preskip))
+ binary.LittleEndian.PutUint32(extraData[12:], uint32(ctx.SampleRate))
+ return extraData
+}
+
+func WriteDefaultOpusExtraData() []byte {
+ return []byte{
+ 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/util.go
new file mode 100644
index 0000000000..3d6b69a1c4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/util.go
@@ -0,0 +1,257 @@
+package codec
+
+import "fmt"
+
+type START_CODE_TYPE int
+
+const (
+ START_CODE_3 START_CODE_TYPE = 3
+ START_CODE_4 = 4
+)
+
+func FindStartCode(nalu []byte, offset int) (int, START_CODE_TYPE) {
+ for i := offset; i < len(nalu)-4; i++ {
+ if nalu[i] == 0x00 && nalu[i+1] == 0x00 {
+ if nalu[i+2] == 0x01 {
+ return i, START_CODE_3
+ } else if nalu[i+2] == 0x00 && nalu[i+3] == 0x01 {
+ return i, START_CODE_4
+ }
+ }
+ }
+ return -1, START_CODE_3
+}
+
+func FindSyncword(aac []byte, offset int) int {
+ for i := offset; i < len(aac); i++ {
+ if aac[i] == 0xFF && aac[i+1]&0xF0 == 0xF0 {
+ return i
+ }
+ }
+ return -1
+}
+
+func SplitFrame(frames []byte, onFrame func(nalu []byte) bool) {
+ beg, sc := FindStartCode(frames, 0)
+ for beg >= 0 {
+ end, sc2 := FindStartCode(frames, beg+int(sc))
+ if end == -1 {
+ if onFrame != nil {
+ onFrame(frames[beg+int(sc):])
+ }
+ break
+ }
+ if onFrame != nil && onFrame(frames[beg+int(sc):end]) == false {
+ break
+ }
+ beg = end
+ sc = sc2
+ }
+}
+
+func SplitFrameWithStartCode(frames []byte, onFrame func(nalu []byte) bool) {
+ beg, sc := FindStartCode(frames, 0)
+ for beg >= 0 {
+ end, sc2 := FindStartCode(frames, beg+int(sc))
+ if end == -1 {
+ if onFrame != nil {
+ onFrame(frames[beg:])
+ }
+ break
+ }
+ if onFrame != nil && onFrame(frames[beg:end]) == false {
+ break
+ }
+ beg = end
+ sc = sc2
+ }
+}
+
+func SplitAACFrame(frames []byte, onFrame func(aac []byte)) {
+ var adts ADTS_Frame_Header
+ start := FindSyncword(frames, 0)
+ for start >= 0 {
+ adts.Decode(frames[start:])
+ onFrame(frames[start : start+int(adts.Variable_Header.Frame_length)])
+ start = FindSyncword(frames, start+int(adts.Variable_Header.Frame_length))
+ }
+}
+
+func H264NaluType(h264 []byte) H264_NAL_TYPE {
+ loc, sc := FindStartCode(h264, 0)
+ return H264_NAL_TYPE(h264[loc+int(sc)] & 0x1F)
+}
+
+func H264NaluTypeWithoutStartCode(h264 []byte) H264_NAL_TYPE {
+ return H264_NAL_TYPE(h264[0] & 0x1F)
+}
+
+func H265NaluType(h265 []byte) H265_NAL_TYPE {
+ loc, sc := FindStartCode(h265, 0)
+ return H265_NAL_TYPE((h265[loc+int(sc)] >> 1) & 0x3F)
+}
+
+func H265NaluTypeWithoutStartCode(h265 []byte) H265_NAL_TYPE {
+ return H265_NAL_TYPE((h265[0] >> 1) & 0x3F)
+}
+
+func GetH264FirstMbInSlice(nalu []byte) uint64 {
+ start, sc := FindStartCode(nalu, 0)
+ bs := NewBitStream(nalu[start+int(sc)+1:])
+ sliceHdr := &SliceHeader{}
+ sliceHdr.Decode(bs)
+ return sliceHdr.First_mb_in_slice
+}
+
+func GetH265FirstMbInSlice(nalu []byte) uint64 {
+ start, sc := FindStartCode(nalu, 0)
+ bs := NewBitStream(nalu[start+int(sc)+2:])
+ sliceHdr := &SliceHeader{}
+ sliceHdr.Decode(bs)
+ return sliceHdr.First_mb_in_slice
+}
+
+func IsH264IDRFrame(h264 []byte) bool {
+
+ ret := false
+ onnalu := func(nalu []byte) bool {
+ nal_type := H264NaluTypeWithoutStartCode(nalu)
+ if nal_type < 5 {
+ return false
+ } else if nal_type == 5 {
+ ret = true
+ return false
+ } else {
+ return true
+ }
+ }
+ SplitFrame(h264, onnalu)
+ return ret
+}
+
+func IsH264VCLNaluType(nal_type H264_NAL_TYPE) bool {
+ if nal_type <= H264_NAL_I_SLICE && nal_type > H264_NAL_RESERVED {
+ return true
+ }
+ return false
+}
+
+func IsH265VCLNaluType(nal_type H265_NAL_TYPE) bool {
+ if (nal_type <= H265_NAL_SLICE_CRA && nal_type >= H265_NAL_SLICE_BLA_W_LP) ||
+ (nal_type <= H265_NAL_SLICE_RASL_R && nal_type >= H265_NAL_Slice_TRAIL_N) {
+ return true
+ }
+ return false
+}
+
+func IsH265IDRFrame(h265 []byte) bool {
+ ret := false
+ onnalu := func(nalu []byte) bool {
+ nal_type := H264NaluTypeWithoutStartCode(nalu)
+ if nal_type <= 9 && nal_type >= 0 {
+ return false
+ } else if nal_type >= 16 && nal_type <= 21 {
+ ret = true
+ return false
+ } else {
+ return true
+ }
+ }
+ SplitFrame(h265, onnalu)
+ return ret
+}
+
+func Max(x, y int) int {
+ if x > y {
+ return x
+ } else {
+ return y
+ }
+}
+
+func Min(x, y int) int {
+ if x > y {
+ return y
+ } else {
+ return x
+ }
+}
+
+func ShowPacketHexdump(data []byte) {
+ for k := 0; k < len(data); k++ {
+ if k%8 == 0 && k != 0 {
+ fmt.Printf("\n")
+ }
+ fmt.Printf("%02x ", data[k])
+ }
+ fmt.Printf("\n")
+}
+
+var crc32table [256]uint32 = [256]uint32{
+ 0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517,
+ 0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B,
+ 0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048,
+ 0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652,
+ 0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D,
+ 0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095,
+ 0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA,
+ 0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0,
+ 0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3,
+ 0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF,
+ 0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730,
+ 0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A,
+ 0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05,
+ 0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475,
+ 0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A,
+ 0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840,
+ 0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB,
+ 0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87,
+ 0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4,
+ 0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE,
+ 0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1,
+ 0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64,
+ 0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B,
+ 0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351,
+ 0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832,
+ 0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E,
+ 0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5,
+ 0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF,
+ 0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0,
+ 0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0,
+ 0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F,
+ 0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185,
+ 0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A,
+ 0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176,
+ 0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15,
+ 0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F,
+ 0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620,
+ 0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8,
+ 0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7,
+ 0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD,
+ 0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E,
+ 0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2,
+ 0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1,
+}
+
+func CalcCrc32(crc uint32, buffer []byte) uint32 {
+ var i int = 0
+ for i = 0; i < len(buffer); i++ {
+ crc = crc32table[(crc^uint32(buffer[i]))&0xff] ^ (crc >> 8)
+ }
+ return crc
+}
+
+func CovertRbspToSodb(rbsp []byte) []byte {
+ bs := NewBitStream(rbsp)
+ bsw := NewBitStreamWriter(len(rbsp))
+ for !bs.EOS() {
+ if bs.RemainBytes() > 3 && bs.NextBits(24) == 0x000003 {
+ bsw.PutByte(bs.Uint8(8))
+ bsw.PutByte(bs.Uint8(8))
+ bs.SkipBits(8)
+ } else {
+ bsw.PutByte(bs.Uint8(8))
+ }
+ }
+ return bsw.Bits()
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/vp8.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/vp8.go
new file mode 100644
index 0000000000..211e0765f1
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/codec/vp8.go
@@ -0,0 +1,72 @@
+package codec
+
+import "errors"
+
+type VP8FrameTag struct {
+ FrameType uint32 //0: I frame , 1: P frame
+ Version uint32
+ Display uint32
+ FirstPartSize uint32
+}
+
+type VP8KeyFrameHead struct {
+ Width int
+ Height int
+ HorizScale int
+ VertScale int
+}
+
+func DecodeFrameTag(frame []byte) (*VP8FrameTag, error) {
+ if len(frame) < 3 {
+ return nil, errors.New("frame bytes < 3")
+ }
+ var tmp uint32 = (uint32(frame[2]) << 16) | (uint32(frame[1]) << 8) | uint32(frame[0])
+ tag := &VP8FrameTag{}
+ tag.FrameType = tmp & 0x01
+ tag.Version = (tmp >> 1) & 0x07
+ tag.Display = (tmp >> 4) & 0x01
+ tag.FirstPartSize = (tmp >> 5) & 0x7FFFF
+ return tag, nil
+}
+
+func DecodeKeyFrameHead(frame []byte) (*VP8KeyFrameHead, error) {
+ if len(frame) < 7 {
+ return nil, errors.New("frame bytes < 3")
+ }
+
+ if frame[0] != 0x9d || frame[1] != 0x01 || frame[2] != 0x2a {
+ return nil, errors.New("not find Start code")
+ }
+
+ head := &VP8KeyFrameHead{}
+ head.Width = int(uint16(frame[4]&0x3f)<<8 | uint16(frame[3]))
+ head.HorizScale = int(frame[4] >> 6)
+ head.Height = int(uint16(frame[6]&0x3f)<<8 | uint16(frame[5]))
+ head.VertScale = int(frame[6] >> 6)
+ return head, nil
+}
+
+func IsKeyFrame(frame []byte) bool {
+ tag, err := DecodeFrameTag(frame)
+ if err != nil {
+ return false
+ }
+
+ if tag.FrameType == 0 {
+ return true
+ } else {
+ return false
+ }
+}
+
+func GetResloution(frame []byte) (width int, height int, err error) {
+ if !IsKeyFrame(frame) {
+ return 0, 0, errors.New("the frame is not Key frame")
+ }
+
+ head, err := DecodeKeyFrameHead(frame[3:])
+ if err != nil {
+ return 0, 0, err
+ }
+ return head.Width, head.Height, nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/LICENSE b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/LICENSE
new file mode 100644
index 0000000000..d8121e81b2
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 caoyaping
+
+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.
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.mod b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.mod
new file mode 100644
index 0000000000..7e519bfbbf
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.mod
@@ -0,0 +1,5 @@
+module github.com/yapingcat/gomedia/mpeg2
+
+go 1.16
+
+require github.com/yapingcat/gomedia/codec v0.0.0-20220609081842-9e0c0e8a19a0
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.sum b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.sum
new file mode 100644
index 0000000000..aaa662d16b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/go.sum
@@ -0,0 +1,2 @@
+github.com/yapingcat/gomedia/codec v0.0.0-20220609081842-9e0c0e8a19a0 h1:gAmdgY8sSyyw081bp3gdwNwpXWeYddcCF0ZfzRG1Au8=
+github.com/yapingcat/gomedia/codec v0.0.0-20220609081842-9e0c0e8a19a0/go.mod h1:obSECV6X3NPUsLL0olA7DurvQHKMq7J3iBTNQ4bL/vQ=
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/pes-proto.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/pes-proto.go
new file mode 100644
index 0000000000..ac8bc50376
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/pes-proto.go
@@ -0,0 +1,349 @@
+package mpeg2
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/yapingcat/gomedia/codec"
+)
+
+var H264_AUD_NALU []byte = []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xF0} //ffmpeg mpegtsenc.c mpegts_write_packet_internal
+var H265_AUD_NALU []byte = []byte{0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x50}
+
+type PES_STREMA_ID int
+
+const (
+ PES_STREAM_END PES_STREMA_ID = 0xB9
+ PES_STREAM_START PES_STREMA_ID = 0xBA
+ PES_STREAM_SYSTEM_HEAD PES_STREMA_ID = 0xBB
+ PES_STREAM_MAP PES_STREMA_ID = 0xBC
+ PES_STREAM_PRIVATE PES_STREMA_ID = 0xBD
+ PES_STREAM_AUDIO PES_STREMA_ID = 0xC0
+ PES_STREAM_VIDEO PES_STREMA_ID = 0xE0
+)
+
+func findPESIDByStreamType(cid TS_STREAM_TYPE) PES_STREMA_ID {
+ if cid == TS_STREAM_AAC {
+ return PES_STREAM_AUDIO
+ } else if cid == TS_STREAM_H264 || cid == TS_STREAM_H265 {
+ return PES_STREAM_VIDEO
+ } else {
+ return PES_STREAM_PRIVATE
+ }
+}
+
+type PesPacket struct {
+ Stream_id uint8
+ PES_packet_length uint16
+ PES_scrambling_control uint8
+ PES_priority uint8
+ Data_alignment_indicator uint8
+ Copyright uint8
+ Original_or_copy uint8
+ PTS_DTS_flags uint8
+ ESCR_flag uint8
+ ES_rate_flag uint8
+ DSM_trick_mode_flag uint8
+ Additional_copy_info_flag uint8
+ PES_CRC_flag uint8
+ PES_extension_flag uint8
+ PES_header_data_length uint8
+ Pts uint64
+ Dts uint64
+ ESCR_base uint64
+ ESCR_extension uint16
+ ES_rate uint32
+ Trick_mode_control uint8
+ Trick_value uint8
+ Additional_copy_info uint8
+ Previous_PES_packet_CRC uint16
+ Pes_payload []byte
+ //TODO
+ //if ( PES_extension_flag == '1')
+ // PES_private_data_flag uint8
+ // pack_header_field_flag uint8
+ // program_packet_sequence_counter_flag uint8
+ // P_STD_buffer_flag uint8
+ // PES_extension_flag_2 uint8
+ // PES_private_data [16]byte
+}
+
+func NewPesPacket() *PesPacket {
+ return new(PesPacket)
+}
+
+func (pkg *PesPacket) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("stream_id:%d\n", pkg.Stream_id))
+ file.WriteString(fmt.Sprintf("PES_packet_length:%d\n", pkg.PES_packet_length))
+ file.WriteString(fmt.Sprintf("PES_scrambling_control:%d\n", pkg.PES_scrambling_control))
+ file.WriteString(fmt.Sprintf("PES_priority:%d\n", pkg.PES_priority))
+ file.WriteString(fmt.Sprintf("data_alignment_indicator:%d\n", pkg.Data_alignment_indicator))
+ file.WriteString(fmt.Sprintf("copyright:%d\n", pkg.Copyright))
+ file.WriteString(fmt.Sprintf("original_or_copy:%d\n", pkg.Original_or_copy))
+ file.WriteString(fmt.Sprintf("PTS_DTS_flags:%d\n", pkg.PTS_DTS_flags))
+ file.WriteString(fmt.Sprintf("ESCR_flag:%d\n", pkg.ESCR_flag))
+ file.WriteString(fmt.Sprintf("ES_rate_flag:%d\n", pkg.ES_rate_flag))
+ file.WriteString(fmt.Sprintf("DSM_trick_mode_flag:%d\n", pkg.DSM_trick_mode_flag))
+ file.WriteString(fmt.Sprintf("additional_copy_info_flag:%d\n", pkg.Additional_copy_info_flag))
+ file.WriteString(fmt.Sprintf("PES_CRC_flag:%d\n", pkg.PES_CRC_flag))
+ file.WriteString(fmt.Sprintf("PES_extension_flag:%d\n", pkg.PES_extension_flag))
+ file.WriteString(fmt.Sprintf("PES_header_data_length:%d\n", pkg.PES_header_data_length))
+ if pkg.PTS_DTS_flags&0x02 == 0x02 {
+ file.WriteString(fmt.Sprintf("PTS:%d\n", pkg.Pts))
+ }
+ if pkg.PTS_DTS_flags&0x03 == 0x03 {
+ file.WriteString(fmt.Sprintf("DTS:%d\n", pkg.Dts))
+ }
+
+ if pkg.ESCR_flag == 1 {
+ file.WriteString(fmt.Sprintf("ESCR_base:%d\n", pkg.ESCR_base))
+ file.WriteString(fmt.Sprintf("ESCR_extension:%d\n", pkg.ESCR_extension))
+ }
+
+ if pkg.ES_rate_flag == 1 {
+ file.WriteString(fmt.Sprintf("ES_rate:%d\n", pkg.ES_rate))
+ }
+
+ if pkg.DSM_trick_mode_flag == 1 {
+ file.WriteString(fmt.Sprintf("trick_mode_control:%d\n", pkg.Trick_mode_control))
+ }
+
+ if pkg.Additional_copy_info_flag == 1 {
+ file.WriteString(fmt.Sprintf("additional_copy_info:%d\n", pkg.Additional_copy_info))
+ }
+
+ if pkg.PES_CRC_flag == 1 {
+ file.WriteString(fmt.Sprintf("previous_PES_packet_CRC:%d\n", pkg.Previous_PES_packet_CRC))
+ }
+ file.WriteString("PES_packet_data_byte:\n")
+ file.WriteString(fmt.Sprintf(" Size: %d\n", len(pkg.Pes_payload)))
+ file.WriteString(" data:")
+ for i := 0; i < 12 && i < len(pkg.Pes_payload); i++ {
+ if i%4 == 0 {
+ file.WriteString("\n")
+ file.WriteString(" ")
+ }
+ file.WriteString(fmt.Sprintf("0x%02x ", pkg.Pes_payload[i]))
+ }
+ file.WriteString("\n")
+}
+
+func (pkg *PesPacket) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 9 {
+ return errNeedMore
+ }
+ bs.SkipBits(24) //packet_start_code_prefix
+ pkg.Stream_id = bs.Uint8(8) //stream_id
+ pkg.PES_packet_length = bs.Uint16(16)
+ bs.SkipBits(2) //'10'
+ pkg.PES_scrambling_control = bs.Uint8(2)
+ pkg.PES_priority = bs.Uint8(1)
+ pkg.Data_alignment_indicator = bs.Uint8(1)
+ pkg.Copyright = bs.Uint8(1)
+ pkg.Original_or_copy = bs.Uint8(1)
+ pkg.PTS_DTS_flags = bs.Uint8(2)
+ pkg.ESCR_flag = bs.Uint8(1)
+ pkg.ES_rate_flag = bs.Uint8(1)
+ pkg.DSM_trick_mode_flag = bs.Uint8(1)
+ pkg.Additional_copy_info_flag = bs.Uint8(1)
+ pkg.PES_CRC_flag = bs.Uint8(1)
+ pkg.PES_extension_flag = bs.Uint8(1)
+ pkg.PES_header_data_length = bs.Uint8(8)
+ if bs.RemainBytes() < int(pkg.PES_header_data_length) {
+ bs.UnRead(9 * 8)
+ return errNeedMore
+ }
+ bs.Markdot()
+ if pkg.PTS_DTS_flags&0x02 == 0x02 {
+ bs.SkipBits(4)
+ pkg.Pts = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.Pts = (pkg.Pts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Pts = (pkg.Pts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ }
+ if pkg.PTS_DTS_flags&0x03 == 0x03 {
+ bs.SkipBits(4)
+ pkg.Dts = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.Dts = (pkg.Dts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Dts = (pkg.Dts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ } else {
+ pkg.Dts = pkg.Pts
+ }
+
+ if pkg.ESCR_flag == 1 {
+ bs.SkipBits(2)
+ pkg.ESCR_base = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.ESCR_base = (pkg.Pts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.ESCR_base = (pkg.Pts << 15) | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.ESCR_extension = bs.Uint16(9)
+ bs.SkipBits(1)
+ }
+
+ if pkg.ES_rate_flag == 1 {
+ bs.SkipBits(1)
+ pkg.ES_rate = bs.Uint32(22)
+ bs.SkipBits(1)
+ }
+
+ if pkg.DSM_trick_mode_flag == 1 {
+ pkg.Trick_mode_control = bs.Uint8(3)
+ pkg.Trick_value = bs.Uint8(5)
+ }
+
+ if pkg.Additional_copy_info_flag == 1 {
+ pkg.Additional_copy_info = bs.Uint8(7)
+ }
+
+ if pkg.PES_CRC_flag == 1 {
+ pkg.Previous_PES_packet_CRC = bs.Uint16(16)
+ }
+
+ loc := bs.DistanceFromMarkDot()
+ bs.SkipBits(int(pkg.PES_header_data_length)*8 - loc) // skip remaining header
+
+ // the -3 bytes are the combined lengths
+ // of all fields between PES_packet_length and PES_header_data_length (2 bytes)
+ // and the PES_header_data_length itself (1 byte)
+ dataLen := int(pkg.PES_packet_length - 3 - uint16(pkg.PES_header_data_length))
+
+ if bs.RemainBytes() < dataLen {
+ pkg.Pes_payload = bs.RemainData()
+ bs.UnRead((9 + int(pkg.PES_header_data_length)) * 8)
+ return errNeedMore
+ }
+
+ if pkg.PES_packet_length == 0 || bs.RemainBytes() <= dataLen {
+ pkg.Pes_payload = bs.RemainData()
+ bs.SkipBits(bs.RemainBits())
+ } else {
+ pkg.Pes_payload = bs.RemainData()[:dataLen]
+ bs.SkipBits(dataLen * 8)
+ }
+
+ return nil
+}
+
+func (pkg *PesPacket) DecodeMpeg1(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 6 {
+ return errNeedMore
+ }
+ bs.SkipBits(24) //packet_start_code_prefix
+ pkg.Stream_id = bs.Uint8(8) //stream_id
+ pkg.PES_packet_length = bs.Uint16(16)
+ if pkg.PES_packet_length != 0 && bs.RemainBytes() < int(pkg.PES_packet_length) {
+ bs.UnRead(6 * 8)
+ return errNeedMore
+ }
+ bs.Markdot()
+ for bs.NextBits(8) == 0xFF {
+ bs.SkipBits(8)
+ }
+ if bs.NextBits(2) == 0x01 {
+ bs.SkipBits(16)
+ }
+ if bs.NextBits(4) == 0x02 {
+ bs.SkipBits(4)
+ pkg.Pts = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.Pts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Pts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ } else if bs.NextBits(4) == 0x03 {
+ bs.SkipBits(4)
+ pkg.Pts = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.Pts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Pts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Dts = bs.GetBits(3)
+ bs.SkipBits(1)
+ pkg.Dts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ pkg.Dts = pkg.Pts<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ } else if bs.NextBits(8) == 0x0F {
+ bs.SkipBits(8)
+ } else {
+ return errParser
+ }
+ loc := bs.DistanceFromMarkDot() / 8
+ if pkg.PES_packet_length < uint16(loc) {
+ return errParser
+ }
+ if pkg.PES_packet_length == 0 ||
+ bs.RemainBits() <= int(pkg.PES_packet_length-uint16(loc))*8 {
+ pkg.Pes_payload = bs.RemainData()
+ bs.SkipBits(bs.RemainBits())
+ } else {
+ pkg.Pes_payload = bs.RemainData()[:pkg.PES_packet_length-uint16(loc)]
+ bs.SkipBits(int(pkg.PES_packet_length-uint16(loc)) * 8)
+ }
+ return nil
+}
+
+func (pkg *PesPacket) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutBytes([]byte{0x00, 0x00, 0x01})
+ bsw.PutByte(pkg.Stream_id)
+ bsw.PutUint16(pkg.PES_packet_length, 16)
+ bsw.PutUint8(0x02, 2)
+ bsw.PutUint8(pkg.PES_scrambling_control, 2)
+ bsw.PutUint8(pkg.PES_priority, 1)
+ bsw.PutUint8(pkg.Data_alignment_indicator, 1)
+ bsw.PutUint8(pkg.Copyright, 1)
+ bsw.PutUint8(pkg.Original_or_copy, 1)
+ bsw.PutUint8(pkg.PTS_DTS_flags, 2)
+ bsw.PutUint8(pkg.ESCR_flag, 1)
+ bsw.PutUint8(pkg.ES_rate_flag, 1)
+ bsw.PutUint8(pkg.DSM_trick_mode_flag, 1)
+ bsw.PutUint8(pkg.Additional_copy_info_flag, 1)
+ bsw.PutUint8(pkg.PES_CRC_flag, 1)
+ bsw.PutUint8(pkg.PES_extension_flag, 1)
+ bsw.PutByte(pkg.PES_header_data_length)
+ if pkg.PTS_DTS_flags == 0x02 {
+ bsw.PutUint8(0x02, 4)
+ bsw.PutUint64(pkg.Pts>>30, 3)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Pts>>15, 15)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Pts, 15)
+ bsw.PutUint8(0x01, 1)
+ }
+
+ if pkg.PTS_DTS_flags == 0x03 {
+ bsw.PutUint8(0x03, 4)
+ bsw.PutUint64(pkg.Pts>>30, 3)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Pts>>15, 15)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Pts, 15)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint8(0x01, 4)
+ bsw.PutUint64(pkg.Dts>>30, 3)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Dts>>15, 15)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.Dts, 15)
+ bsw.PutUint8(0x01, 1)
+ }
+
+ if pkg.ESCR_flag == 1 {
+ bsw.PutUint8(0x03, 2)
+ bsw.PutUint64(pkg.ESCR_base>>30, 3)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.ESCR_base>>15, 15)
+ bsw.PutUint8(0x01, 1)
+ bsw.PutUint64(pkg.ESCR_base, 15)
+ bsw.PutUint8(0x01, 1)
+ }
+ bsw.PutBytes(pkg.Pes_payload)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-demuxer.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-demuxer.go
new file mode 100644
index 0000000000..460fda8a65
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-demuxer.go
@@ -0,0 +1,280 @@
+package mpeg2
+
+import (
+ "github.com/yapingcat/gomedia/codec"
+)
+
+type psstream struct {
+ sid uint8
+ cid PS_STREAM_TYPE
+ pts uint64
+ dts uint64
+ streamBuf []byte
+}
+
+func newpsstream(sid uint8, cid PS_STREAM_TYPE) *psstream {
+ return &psstream{
+ sid: sid,
+ cid: cid,
+ streamBuf: make([]byte, 0, 4096),
+ }
+}
+
+type PSDemuxer struct {
+ streamMap map[uint8]*psstream
+ pkg *PSPacket
+ mpeg1 bool
+ cache []byte
+ OnFrame func(frame []byte, cid PS_STREAM_TYPE, pts uint64, dts uint64)
+ //解ps包过程中,解码回调psm,system header,pes包等
+ //decodeResult 解码ps包时的产生的错误
+ //这个回调主要用于debug,查看是否ps包存在问题
+ OnPacket func(pkg Display, decodeResult error)
+}
+
+func NewPSDemuxer() *PSDemuxer {
+ return &PSDemuxer{
+ streamMap: make(map[uint8]*psstream),
+ pkg: new(PSPacket),
+ cache: make([]byte, 0, 256),
+ OnFrame: nil,
+ OnPacket: nil,
+ }
+}
+
+func (psdemuxer *PSDemuxer) Input(data []byte) error {
+ var bs *codec.BitStream
+ if len(psdemuxer.cache) > 0 {
+ psdemuxer.cache = append(psdemuxer.cache, data...)
+ bs = codec.NewBitStream(psdemuxer.cache)
+ } else {
+ bs = codec.NewBitStream(data)
+ }
+
+ saveReseved := func() {
+ tmpcache := make([]byte, bs.RemainBytes())
+ copy(tmpcache, bs.RemainData())
+ psdemuxer.cache = tmpcache
+ }
+
+ var ret error = nil
+ for !bs.EOS() {
+ if mpegerr, ok := ret.(Error); ok {
+ if mpegerr.NeedMore() {
+ saveReseved()
+ }
+ break
+ }
+ if bs.RemainBits() < 32 {
+ ret = errNeedMore
+ saveReseved()
+ break
+ }
+ prefix_code := bs.NextBits(32)
+ switch prefix_code {
+ case 0x000001BA: //pack header
+ if psdemuxer.pkg.Header == nil {
+ psdemuxer.pkg.Header = new(PSPackHeader)
+ }
+ ret = psdemuxer.pkg.Header.Decode(bs)
+ psdemuxer.mpeg1 = psdemuxer.pkg.Header.IsMpeg1
+ if psdemuxer.OnPacket != nil {
+ psdemuxer.OnPacket(psdemuxer.pkg.Header, ret)
+ }
+ case 0x000001BB: //system header
+ if psdemuxer.pkg.Header == nil {
+ panic("psdemuxer.pkg.Header must not be nil")
+ }
+ if psdemuxer.pkg.System == nil {
+ psdemuxer.pkg.System = new(System_header)
+ }
+ ret = psdemuxer.pkg.System.Decode(bs)
+ if psdemuxer.OnPacket != nil {
+ psdemuxer.OnPacket(psdemuxer.pkg.System, ret)
+ }
+ case 0x000001BC: //program stream map
+ if psdemuxer.pkg.Psm == nil {
+ psdemuxer.pkg.Psm = new(Program_stream_map)
+ }
+ if ret = psdemuxer.pkg.Psm.Decode(bs); ret == nil {
+ for _, streaminfo := range psdemuxer.pkg.Psm.Stream_map {
+ if _, found := psdemuxer.streamMap[streaminfo.Elementary_stream_id]; !found {
+ stream := newpsstream(streaminfo.Elementary_stream_id, PS_STREAM_TYPE(streaminfo.Stream_type))
+ psdemuxer.streamMap[stream.sid] = stream
+ }
+ }
+ }
+ if psdemuxer.OnPacket != nil {
+ psdemuxer.OnPacket(psdemuxer.pkg.Psm, ret)
+ }
+ case 0x000001BD, 0x000001BE, 0x000001BF, 0x000001F0, 0x000001F1,
+ 0x000001F2, 0x000001F3, 0x000001F4, 0x000001F5, 0x000001F6,
+ 0x000001F7, 0x000001F8, 0x000001F9, 0x000001FA, 0x000001FB:
+ if psdemuxer.pkg.CommPes == nil {
+ psdemuxer.pkg.CommPes = new(CommonPesPacket)
+ }
+ ret = psdemuxer.pkg.CommPes.Decode(bs)
+ case 0x000001FF: //program stream directory
+ if psdemuxer.pkg.Psd == nil {
+ psdemuxer.pkg.Psd = new(Program_stream_directory)
+ }
+ ret = psdemuxer.pkg.Psd.Decode(bs)
+ case 0x000001B9: //MPEG_program_end_code
+ continue
+ default:
+ if prefix_code&0xFFFFFFE0 == 0x000001C0 || prefix_code&0xFFFFFFE0 == 0x000001E0 {
+ if psdemuxer.pkg.Pes == nil {
+ psdemuxer.pkg.Pes = NewPesPacket()
+ }
+ if psdemuxer.mpeg1 {
+ ret = psdemuxer.pkg.Pes.DecodeMpeg1(bs)
+ } else {
+ ret = psdemuxer.pkg.Pes.Decode(bs)
+ }
+ if psdemuxer.OnPacket != nil {
+ psdemuxer.OnPacket(psdemuxer.pkg.Pes, ret)
+ }
+ if ret == nil {
+ if stream, found := psdemuxer.streamMap[psdemuxer.pkg.Pes.Stream_id]; found {
+ if psdemuxer.mpeg1 && stream.cid == PS_STREAM_UNKNOW {
+ psdemuxer.guessCodecid(stream)
+ }
+ psdemuxer.demuxPespacket(stream, psdemuxer.pkg.Pes)
+ } else {
+ if psdemuxer.mpeg1 {
+ stream := newpsstream(psdemuxer.pkg.Pes.Stream_id, PS_STREAM_UNKNOW)
+ psdemuxer.streamMap[stream.sid] = stream
+ stream.streamBuf = append(stream.streamBuf, psdemuxer.pkg.Pes.Pes_payload...)
+ stream.pts = psdemuxer.pkg.Pes.Pts
+ stream.dts = psdemuxer.pkg.Pes.Dts
+ }
+ }
+ }
+ } else {
+ bs.SkipBits(8)
+ }
+ }
+ }
+
+ if ret == nil && len(psdemuxer.cache) > 0 {
+ psdemuxer.cache = nil
+ }
+
+ return ret
+}
+
+func (psdemuxer *PSDemuxer) Flush() {
+ for _, stream := range psdemuxer.streamMap {
+ if len(stream.streamBuf) == 0 {
+ continue
+ }
+ if psdemuxer.OnFrame != nil {
+ psdemuxer.OnFrame(stream.streamBuf, stream.cid, stream.pts/90, stream.dts/90)
+ }
+ }
+}
+
+func (psdemuxer *PSDemuxer) guessCodecid(stream *psstream) {
+ if stream.sid&0xE0 == uint8(PES_STREAM_AUDIO) {
+ stream.cid = PS_STREAM_AAC
+ } else if stream.sid&0xE0 == uint8(PES_STREAM_VIDEO) {
+ h264score := 0
+ h265score := 0
+ codec.SplitFrame(stream.streamBuf, func(nalu []byte) bool {
+ h264nalutype := codec.H264NaluTypeWithoutStartCode(nalu)
+ h265nalutype := codec.H265NaluTypeWithoutStartCode(nalu)
+ if h264nalutype == codec.H264_NAL_PPS ||
+ h264nalutype == codec.H264_NAL_SPS ||
+ h264nalutype == codec.H264_NAL_I_SLICE {
+ h264score += 2
+ } else if h264nalutype < 5 {
+ h264score += 1
+ } else if h264nalutype > 20 {
+ h264score -= 1
+ }
+
+ if h265nalutype == codec.H265_NAL_PPS ||
+ h265nalutype == codec.H265_NAL_SPS ||
+ h265nalutype == codec.H265_NAL_VPS ||
+ (h265nalutype >= codec.H265_NAL_SLICE_BLA_W_LP && h265nalutype <= codec.H265_NAL_SLICE_CRA) {
+ h265score += 2
+ } else if h265nalutype >= codec.H265_NAL_Slice_TRAIL_N && h265nalutype <= codec.H265_NAL_SLICE_RASL_R {
+ h265score += 1
+ } else if h265nalutype > 40 {
+ h265score -= 1
+ }
+ if h264score > h265score && h264score >= 4 {
+ stream.cid = PS_STREAM_H264
+ } else if h264score < h265score && h265score >= 4 {
+ stream.cid = PS_STREAM_H265
+ }
+ return true
+ })
+ }
+}
+
+func (psdemuxer *PSDemuxer) demuxPespacket(stream *psstream, pes *PesPacket) error {
+ switch stream.cid {
+ case PS_STREAM_AAC, PS_STREAM_G711A, PS_STREAM_G711U:
+ return psdemuxer.demuxAudio(stream, pes)
+ case PS_STREAM_H264, PS_STREAM_H265:
+ return psdemuxer.demuxH26x(stream, pes)
+ case PS_STREAM_UNKNOW:
+ if stream.pts != pes.Pts {
+ stream.streamBuf = nil
+ }
+ stream.streamBuf = append(stream.streamBuf, pes.Pes_payload...)
+ stream.pts = pes.Pts
+ stream.dts = pes.Dts
+ }
+ return nil
+}
+
+func (psdemuxer *PSDemuxer) demuxAudio(stream *psstream, pes *PesPacket) error {
+ if stream.pts != pes.Pts && len(stream.streamBuf) > 0 {
+ if psdemuxer.OnFrame != nil {
+ psdemuxer.OnFrame(stream.streamBuf, stream.cid, stream.pts/90, stream.dts/90)
+ }
+ stream.streamBuf = stream.streamBuf[:0]
+ }
+ stream.streamBuf = append(stream.streamBuf, pes.Pes_payload...)
+ stream.pts = pes.Pts
+ stream.dts = pes.Dts
+ return nil
+}
+
+func (psdemuxer *PSDemuxer) demuxH26x(stream *psstream, pes *PesPacket) error {
+ if len(stream.streamBuf) == 0 {
+ stream.pts = pes.Pts
+ stream.dts = pes.Dts
+ }
+ stream.streamBuf = append(stream.streamBuf, pes.Pes_payload...)
+ start, sc := codec.FindStartCode(stream.streamBuf, 0)
+ for start >= 0 {
+ end, sc2 := codec.FindStartCode(stream.streamBuf, start+int(sc))
+ if end < 0 {
+ break
+ }
+ if stream.cid == PS_STREAM_H264 {
+ naluType := codec.H264NaluType(stream.streamBuf[start:])
+ if naluType != codec.H264_NAL_AUD {
+ if psdemuxer.OnFrame != nil {
+ psdemuxer.OnFrame(stream.streamBuf[start:end], stream.cid, stream.pts/90, stream.dts/90)
+ }
+ }
+ } else if stream.cid == PS_STREAM_H265 {
+ naluType := codec.H265NaluType(stream.streamBuf[start:])
+ if naluType != codec.H265_NAL_AUD {
+ if psdemuxer.OnFrame != nil {
+ psdemuxer.OnFrame(stream.streamBuf[start:end], stream.cid, stream.pts/90, stream.dts/90)
+ }
+ }
+ }
+ start = end
+ sc = sc2
+ }
+ stream.streamBuf = stream.streamBuf[start:]
+ stream.pts = pes.Pts
+ stream.dts = pes.Dts
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-muxer.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-muxer.go
new file mode 100644
index 0000000000..a8834e0dcb
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-muxer.go
@@ -0,0 +1,148 @@
+package mpeg2
+
+import "github.com/yapingcat/gomedia/codec"
+
+type PSMuxer struct {
+ system *System_header
+ psm *Program_stream_map
+ OnPacket func(pkg []byte)
+ firstframe bool
+}
+
+func NewPsMuxer() *PSMuxer {
+ muxer := new(PSMuxer)
+ muxer.firstframe = true
+ muxer.system = new(System_header)
+ muxer.system.Rate_bound = 26234
+ muxer.psm = new(Program_stream_map)
+ muxer.psm.Current_next_indicator = 1
+ muxer.psm.Program_stream_map_version = 1
+ muxer.OnPacket = nil
+ return muxer
+}
+
+func (muxer *PSMuxer) AddStream(cid PS_STREAM_TYPE) uint8 {
+ if cid == PS_STREAM_H265 || cid == PS_STREAM_H264 {
+ es := NewElementary_Stream(uint8(PES_STREAM_VIDEO) + muxer.system.Video_bound)
+ es.P_STD_buffer_bound_scale = 1
+ es.P_STD_buffer_size_bound = 400
+ muxer.system.Streams = append(muxer.system.Streams, es)
+ muxer.system.Video_bound++
+ muxer.psm.Stream_map = append(muxer.psm.Stream_map, NewElementary_stream_elem(uint8(cid), es.Stream_id))
+ muxer.psm.Program_stream_map_version++
+ return es.Stream_id
+ } else {
+ es := NewElementary_Stream(uint8(PES_STREAM_AUDIO) + muxer.system.Audio_bound)
+ es.P_STD_buffer_bound_scale = 0
+ es.P_STD_buffer_size_bound = 32
+ muxer.system.Streams = append(muxer.system.Streams, es)
+ muxer.system.Audio_bound++
+ muxer.psm.Stream_map = append(muxer.psm.Stream_map, NewElementary_stream_elem(uint8(cid), es.Stream_id))
+ muxer.psm.Program_stream_map_version++
+ return es.Stream_id
+ }
+}
+
+func (muxer *PSMuxer) Write(sid uint8, frame []byte, pts uint64, dts uint64) error {
+ var stream *Elementary_stream_elem = nil
+ for _, es := range muxer.psm.Stream_map {
+ if es.Elementary_stream_id == sid {
+ stream = es
+ break
+ }
+ }
+ if stream == nil {
+ return errNotFound
+ }
+ var withaud bool = false
+ var idr_flag bool = false
+ var first bool = true
+ var vcl bool = false
+ if stream.Stream_type == uint8(PS_STREAM_H264) || stream.Stream_type == uint8(PS_STREAM_H265) {
+ codec.SplitFrame(frame, func(nalu []byte) bool {
+ if stream.Stream_type == uint8(PS_STREAM_H264) {
+ nalu_type := codec.H264NaluTypeWithoutStartCode(nalu)
+ if nalu_type == codec.H264_NAL_AUD {
+ withaud = true
+ return false
+ } else if codec.IsH264VCLNaluType(nalu_type) {
+ if nalu_type == codec.H264_NAL_I_SLICE {
+ idr_flag = true
+ }
+ vcl = true
+ return false
+ }
+ return true
+ } else {
+ nalu_type := codec.H265NaluTypeWithoutStartCode(nalu)
+ if nalu_type == codec.H265_NAL_AUD {
+ withaud = true
+ return false
+ } else if codec.IsH265VCLNaluType(nalu_type) {
+ if nalu_type >= codec.H265_NAL_SLICE_BLA_W_LP && nalu_type <= codec.H265_NAL_SLICE_CRA {
+ idr_flag = true
+ }
+ vcl = true
+ return false
+ }
+ return true
+ }
+ })
+ }
+
+ dts = dts * 90
+ pts = pts * 90
+ bsw := codec.NewBitStreamWriter(1024)
+ var pack PSPackHeader
+ pack.System_clock_reference_base = dts - 3600
+ pack.System_clock_reference_extension = 0
+ pack.Program_mux_rate = 6106
+ pack.Encode(bsw)
+ if muxer.firstframe || idr_flag {
+ muxer.system.Encode(bsw)
+ muxer.psm.Encode(bsw)
+ muxer.firstframe = false
+ }
+ if muxer.OnPacket != nil {
+ muxer.OnPacket(bsw.Bits())
+ }
+ bsw.Reset()
+ pespkg := NewPesPacket()
+ for len(frame) > 0 {
+ peshdrlen := 13
+ pespkg.Stream_id = sid
+ pespkg.PTS_DTS_flags = 0x03
+ pespkg.PES_header_data_length = 10
+ pespkg.Pts = pts
+ pespkg.Dts = dts
+ if idr_flag {
+ pespkg.Data_alignment_indicator = 1
+ }
+ if first && !withaud && vcl {
+ if stream.Stream_type == uint8(PS_STREAM_H264) {
+ pespkg.Pes_payload = append(pespkg.Pes_payload, H264_AUD_NALU...)
+ peshdrlen += 6
+ } else if stream.Stream_type == uint8(PS_STREAM_H265) {
+ pespkg.Pes_payload = append(pespkg.Pes_payload, H265_AUD_NALU...)
+ peshdrlen += 7
+ }
+ }
+ if peshdrlen+len(frame) >= 0xFFFF {
+ pespkg.PES_packet_length = 0xFFFF
+ pespkg.Pes_payload = append(pespkg.Pes_payload, frame[0:0xFFFF-peshdrlen]...)
+ frame = frame[0xFFFF-peshdrlen:]
+ } else {
+ pespkg.PES_packet_length = uint16(peshdrlen + len(frame))
+ pespkg.Pes_payload = append(pespkg.Pes_payload, frame[0:]...)
+ frame = frame[:0]
+ }
+ pespkg.Encode(bsw)
+ pespkg.Pes_payload = pespkg.Pes_payload[:0]
+ if muxer.OnPacket != nil {
+ muxer.OnPacket(bsw.Bits())
+ }
+ bsw.Reset()
+ first = false
+ }
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-proto.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-proto.go
new file mode 100644
index 0000000000..baf52a395e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ps-proto.go
@@ -0,0 +1,521 @@
+package mpeg2
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+
+ "github.com/yapingcat/gomedia/codec"
+)
+
+type Error interface {
+ NeedMore() bool
+ ParserError() bool
+ StreamIdNotFound() bool
+}
+
+var errNeedMore error = &needmoreError{}
+
+type needmoreError struct{}
+
+func (e *needmoreError) Error() string { return "need more bytes" }
+func (e *needmoreError) NeedMore() bool { return true }
+func (e *needmoreError) ParserError() bool { return false }
+func (e *needmoreError) StreamIdNotFound() bool { return false }
+
+var errParser error = &parserError{}
+
+type parserError struct{}
+
+func (e *parserError) Error() string { return "parser packet error" }
+func (e *parserError) NeedMore() bool { return false }
+func (e *parserError) ParserError() bool { return true }
+func (e *parserError) StreamIdNotFound() bool { return false }
+
+var errNotFound error = &sidNotFoundError{}
+
+type sidNotFoundError struct{}
+
+func (e *sidNotFoundError) Error() string { return "stream id not found" }
+func (e *sidNotFoundError) NeedMore() bool { return false }
+func (e *sidNotFoundError) ParserError() bool { return false }
+func (e *sidNotFoundError) StreamIdNotFound() bool { return true }
+
+type PS_STREAM_TYPE int
+
+const (
+ PS_STREAM_UNKNOW PS_STREAM_TYPE = 0xFF
+ PS_STREAM_AAC PS_STREAM_TYPE = 0x0F
+ PS_STREAM_H264 PS_STREAM_TYPE = 0x1B
+ PS_STREAM_H265 PS_STREAM_TYPE = 0x24
+ PS_STREAM_G711A PS_STREAM_TYPE = 0x90
+ PS_STREAM_G711U PS_STREAM_TYPE = 0x91
+)
+
+// Table 2-33 – Program Stream pack header
+// pack_header() {
+// pack_start_code 32 bslbf
+// '01' 2 bslbf
+// system_clock_reference_base [32..30] 3 bslbf
+// marker_bit 1 bslbf
+// system_clock_reference_base [29..15] 15 bslbf
+// marker_bit 1 bslbf
+// system_clock_reference_base [14..0] 15 bslbf
+// marker_bit 1 bslbf
+// system_clock_reference_extension 9 uimsbf
+// marker_bit 1 bslbf
+// program_mux_rate 22 uimsbf
+// marker_bit 1 bslbf
+// marker_bit 1 bslbf
+// reserved 5 bslbf
+// pack_stuffing_length 3 uimsbf
+// for (i = 0; i < pack_stuffing_length; i++) {
+// stuffing_byte 8 bslbf
+// }
+// if (nextbits() == system_header_start_code) {
+// system_header ()
+// }
+// }
+
+type PSPackHeader struct {
+ IsMpeg1 bool
+ System_clock_reference_base uint64 //33 bits
+ System_clock_reference_extension uint16 //9 bits
+ Program_mux_rate uint32 //22 bits
+ Pack_stuffing_length uint8 //3 bitss
+}
+
+func (ps_pkg_hdr *PSPackHeader) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("IsMpeg1:%t\n", ps_pkg_hdr.IsMpeg1))
+ file.WriteString(fmt.Sprintf("System_clock_reference_base:%d\n", ps_pkg_hdr.System_clock_reference_base))
+ file.WriteString(fmt.Sprintf("System_clock_reference_extension:%d\n", ps_pkg_hdr.System_clock_reference_extension))
+ file.WriteString(fmt.Sprintf("Program_mux_rate:%d\n", ps_pkg_hdr.Program_mux_rate))
+ file.WriteString(fmt.Sprintf("Pack_stuffing_length:%d\n", ps_pkg_hdr.Pack_stuffing_length))
+}
+
+func (ps_pkg_hdr *PSPackHeader) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 5 {
+ return errNeedMore
+ }
+ if bs.Uint32(32) != 0x000001BA {
+ panic("ps header must start with 000001BA")
+ }
+
+ if bs.NextBits(2) == 0x01 { //mpeg2
+ if bs.RemainBytes() < 10 {
+ return errNeedMore
+ }
+ return ps_pkg_hdr.decodeMpeg2(bs)
+ } else if bs.NextBits(4) == 0x02 { //mpeg1
+ if bs.RemainBytes() < 8 {
+ return errNeedMore
+ }
+ ps_pkg_hdr.IsMpeg1 = true
+ return ps_pkg_hdr.decodeMpeg1(bs)
+ } else {
+ return errParser
+ }
+}
+
+func (ps_pkg_hdr *PSPackHeader) decodeMpeg2(bs *codec.BitStream) error {
+ bs.SkipBits(2)
+ ps_pkg_hdr.System_clock_reference_base = bs.GetBits(3)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_base = ps_pkg_hdr.System_clock_reference_base<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_base = ps_pkg_hdr.System_clock_reference_base<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_extension = bs.Uint16(9)
+ bs.SkipBits(1)
+ ps_pkg_hdr.Program_mux_rate = bs.Uint32(22)
+ bs.SkipBits(1)
+ bs.SkipBits(1)
+ bs.SkipBits(5)
+ ps_pkg_hdr.Pack_stuffing_length = bs.Uint8(3)
+ if bs.RemainBytes() < int(ps_pkg_hdr.Pack_stuffing_length) {
+ bs.UnRead(10 * 8)
+ return errNeedMore
+ }
+ bs.SkipBits(int(ps_pkg_hdr.Pack_stuffing_length) * 8)
+ return nil
+}
+
+func (ps_pkg_hdr *PSPackHeader) decodeMpeg1(bs *codec.BitStream) error {
+ bs.SkipBits(4)
+ ps_pkg_hdr.System_clock_reference_base = bs.GetBits(3)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_base = ps_pkg_hdr.System_clock_reference_base<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_base = ps_pkg_hdr.System_clock_reference_base<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ ps_pkg_hdr.System_clock_reference_extension = 1
+ ps_pkg_hdr.Program_mux_rate = bs.Uint32(7)
+ bs.SkipBits(1)
+ ps_pkg_hdr.Program_mux_rate = ps_pkg_hdr.Program_mux_rate<<15 | bs.Uint32(15)
+ bs.SkipBits(1)
+ return nil
+}
+
+func (ps_pkg_hdr *PSPackHeader) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutBytes([]byte{0x00, 0x00, 0x01, 0xBA})
+ bsw.PutUint8(1, 2)
+ bsw.PutUint64(ps_pkg_hdr.System_clock_reference_base>>30, 3)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint64(ps_pkg_hdr.System_clock_reference_base>>15, 15)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint64(ps_pkg_hdr.System_clock_reference_base, 15)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint16(ps_pkg_hdr.System_clock_reference_extension, 9)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint32(ps_pkg_hdr.Program_mux_rate, 22)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint8(0x1F, 5)
+ bsw.PutUint8(ps_pkg_hdr.Pack_stuffing_length, 3)
+ bsw.PutRepetValue(0xFF, int(ps_pkg_hdr.Pack_stuffing_length))
+}
+
+type Elementary_Stream struct {
+ Stream_id uint8
+ P_STD_buffer_bound_scale uint8
+ P_STD_buffer_size_bound uint16
+}
+
+func NewElementary_Stream(sid uint8) *Elementary_Stream {
+ return &Elementary_Stream{
+ Stream_id: sid,
+ }
+}
+
+// system_header () {
+// system_header_start_code 32 bslbf
+// header_length 16 uimsbf
+// marker_bit 1 bslbf
+// rate_bound 22 uimsbf
+// marker_bit 1 bslbf
+// audio_bound 6 uimsbf
+// fixed_flag 1 bslbf
+// CSPS_flag 1 bslbf
+// system_audio_lock_flag 1 bslbf
+// system_video_lock_flag 1 bslbf
+// marker_bit 1 bslbf
+// video_bound 5 uimsbf
+// packet_rate_restriction_flag 1 bslbf
+// reserved_bits 7 bslbf
+// while (nextbits () == '1') {
+// stream_id 8 uimsbf
+// '11' 2 bslbf
+// P-STD_buffer_bound_scale 1 bslbf
+// P-STD_buffer_size_bound 13 uimsbf
+// }
+// }
+
+type System_header struct {
+ Header_length uint16
+ Rate_bound uint32
+ Audio_bound uint8
+ Fixed_flag uint8
+ CSPS_flag uint8
+ System_audio_lock_flag uint8
+ System_video_lock_flag uint8
+ Video_bound uint8
+ Packet_rate_restriction_flag uint8
+ Streams []*Elementary_Stream
+}
+
+func (sh *System_header) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("Header_length:%d\n", sh.Header_length))
+ file.WriteString(fmt.Sprintf("Rate_bound:%d\n", sh.Rate_bound))
+ file.WriteString(fmt.Sprintf("Audio_bound:%d\n", sh.Audio_bound))
+ file.WriteString(fmt.Sprintf("Fixed_flag:%d\n", sh.Fixed_flag))
+ file.WriteString(fmt.Sprintf("CSPS_flag:%d\n", sh.CSPS_flag))
+ file.WriteString(fmt.Sprintf("System_audio_lock_flag:%d\n", sh.System_audio_lock_flag))
+ file.WriteString(fmt.Sprintf("System_video_lock_flag:%d\n", sh.System_video_lock_flag))
+ file.WriteString(fmt.Sprintf("Video_bound:%d\n", sh.Video_bound))
+ file.WriteString(fmt.Sprintf("Packet_rate_restriction_flag:%d\n", sh.Packet_rate_restriction_flag))
+ for i, es := range sh.Streams {
+ file.WriteString(fmt.Sprintf("----streams %d\n", i))
+ file.WriteString(fmt.Sprintf(" Stream_id:%d\n", es.Stream_id))
+ file.WriteString(fmt.Sprintf(" P_STD_buffer_bound_scale:%d\n", es.P_STD_buffer_bound_scale))
+ file.WriteString(fmt.Sprintf(" P_STD_buffer_size_bound:%d\n", es.P_STD_buffer_size_bound))
+ }
+}
+
+func (sh *System_header) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutBytes([]byte{0x00, 0x00, 0x01, 0xBB})
+ loc := bsw.ByteOffset()
+ bsw.PutUint16(0, 16)
+ bsw.Markdot()
+ bsw.PutUint8(1, 1)
+ bsw.PutUint32(sh.Rate_bound, 22)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint8(sh.Audio_bound, 6)
+ bsw.PutUint8(sh.Fixed_flag, 1)
+ bsw.PutUint8(sh.CSPS_flag, 1)
+ bsw.PutUint8(sh.System_audio_lock_flag, 1)
+ bsw.PutUint8(sh.System_video_lock_flag, 1)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint8(sh.Video_bound, 5)
+ bsw.PutUint8(sh.Packet_rate_restriction_flag, 1)
+ bsw.PutUint8(0x7F, 7)
+ for _, stream := range sh.Streams {
+ bsw.PutUint8(stream.Stream_id, 8)
+ bsw.PutUint8(3, 2)
+ bsw.PutUint8(stream.P_STD_buffer_bound_scale, 1)
+ bsw.PutUint16(stream.P_STD_buffer_size_bound, 13)
+ }
+ length := bsw.DistanceFromMarkDot() / 8
+ bsw.SetUint16(uint16(length), loc)
+}
+
+func (sh *System_header) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 12 {
+ return errNeedMore
+ }
+ if bs.Uint32(32) != 0x000001BB {
+ panic("system header must start with 000001BB")
+ }
+ sh.Header_length = bs.Uint16(16)
+ if bs.RemainBytes() < int(sh.Header_length) {
+ bs.UnRead(6 * 8)
+ return errNeedMore
+ }
+ if sh.Header_length < 6 || (sh.Header_length-6)%3 != 0 {
+ return errParser
+ }
+ bs.SkipBits(1)
+ sh.Rate_bound = bs.Uint32(22)
+ bs.SkipBits(1)
+ sh.Audio_bound = bs.Uint8(6)
+ sh.Fixed_flag = bs.Uint8(1)
+ sh.CSPS_flag = bs.Uint8(1)
+ sh.System_audio_lock_flag = bs.Uint8(1)
+ sh.System_video_lock_flag = bs.Uint8(1)
+ bs.SkipBits(1)
+ sh.Video_bound = bs.Uint8(5)
+ sh.Packet_rate_restriction_flag = bs.Uint8(1)
+ bs.SkipBits(7)
+ sh.Streams = sh.Streams[:0]
+ least := sh.Header_length - 6
+ for least > 0 && bs.NextBits(1) == 0x01 {
+ es := new(Elementary_Stream)
+ es.Stream_id = bs.Uint8(8)
+ bs.SkipBits(2)
+ es.P_STD_buffer_bound_scale = bs.GetBit()
+ es.P_STD_buffer_size_bound = bs.Uint16(13)
+ sh.Streams = append(sh.Streams, es)
+ least -= 3
+ }
+ if least > 0 {
+ return errParser
+ }
+ return nil
+}
+
+type Elementary_stream_elem struct {
+ Stream_type uint8
+ Elementary_stream_id uint8
+ Elementary_stream_info_length uint16
+}
+
+func NewElementary_stream_elem(stype uint8, esid uint8) *Elementary_stream_elem {
+ return &Elementary_stream_elem{
+ Stream_type: stype,
+ Elementary_stream_id: esid,
+ }
+}
+
+// program_stream_map() {
+// packet_start_code_prefix 24 bslbf
+// map_stream_id 8 uimsbf
+// program_stream_map_length 16 uimsbf
+// current_next_indicator 1 bslbf
+// reserved 2 bslbf
+// program_stream_map_version 5 uimsbf
+// reserved 7 bslbf
+// marker_bit 1 bslbf
+// program_stream_info_length 16 uimsbf
+// for (i = 0; i < N; i++) {
+// descriptor()
+// }
+// elementary_stream_map_length 16 uimsbf
+// for (i = 0; i < N1; i++) {
+// stream_type 8 uimsbf
+// elementary_stream_id 8 uimsbf
+// elementary_stream_info_length 16 uimsbf
+// for (i = 0; i < N2; i++) {
+// descriptor()
+// }
+// }
+// CRC_32 32 rpchof
+// }
+
+type Program_stream_map struct {
+ Map_stream_id uint8
+ Program_stream_map_length uint16
+ Current_next_indicator uint8
+ Program_stream_map_version uint8
+ Program_stream_info_length uint16
+ Elementary_stream_map_length uint16
+ Stream_map []*Elementary_stream_elem
+}
+
+func (psm *Program_stream_map) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("map_stream_id:%d\n", psm.Map_stream_id))
+ file.WriteString(fmt.Sprintf("program_stream_map_length:%d\n", psm.Program_stream_map_length))
+ file.WriteString(fmt.Sprintf("current_next_indicator:%d\n", psm.Current_next_indicator))
+ file.WriteString(fmt.Sprintf("program_stream_map_version:%d\n", psm.Program_stream_map_version))
+ file.WriteString(fmt.Sprintf("program_stream_info_length:%d\n", psm.Program_stream_info_length))
+ file.WriteString(fmt.Sprintf("elementary_stream_map_length:%d\n", psm.Elementary_stream_map_length))
+ for i, es := range psm.Stream_map {
+ file.WriteString(fmt.Sprintf("----ES stream %d\n", i))
+ if es.Stream_type == uint8(PS_STREAM_AAC) {
+ file.WriteString(" stream_type:AAC\n")
+ } else if es.Stream_type == uint8(PS_STREAM_G711A) {
+ file.WriteString(" stream_type:G711A\n")
+ } else if es.Stream_type == uint8(PS_STREAM_G711U) {
+ file.WriteString(" stream_type:G711U\n")
+ } else if es.Stream_type == uint8(PS_STREAM_H264) {
+ file.WriteString(" stream_type:H264\n")
+ } else if es.Stream_type == uint8(PS_STREAM_H265) {
+ file.WriteString(" stream_type:H265\n")
+ }
+ file.WriteString(fmt.Sprintf(" elementary_stream_id:%d\n", es.Elementary_stream_id))
+ file.WriteString(fmt.Sprintf(" elementary_stream_info_length:%d\n", es.Elementary_stream_info_length))
+ }
+}
+
+func (psm *Program_stream_map) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutBytes([]byte{0x00, 0x00, 0x01, 0xBC})
+ loc := bsw.ByteOffset()
+ bsw.PutUint16(psm.Program_stream_map_length, 16)
+ bsw.Markdot()
+ bsw.PutUint8(psm.Current_next_indicator, 1)
+ bsw.PutUint8(3, 2)
+ bsw.PutUint8(psm.Program_stream_map_version, 5)
+ bsw.PutUint8(0x7F, 7)
+ bsw.PutUint8(1, 1)
+ bsw.PutUint16(0, 16)
+ psm.Elementary_stream_map_length = uint16(len(psm.Stream_map) * 4)
+ bsw.PutUint16(psm.Elementary_stream_map_length, 16)
+ for _, streaminfo := range psm.Stream_map {
+ bsw.PutUint8(streaminfo.Stream_type, 8)
+ bsw.PutUint8(streaminfo.Elementary_stream_id, 8)
+ bsw.PutUint16(0, 16)
+ }
+ length := bsw.DistanceFromMarkDot()/8 + 4
+ bsw.SetUint16(uint16(length), loc)
+ crc := codec.CalcCrc32(0xffffffff, bsw.Bits()[bsw.ByteOffset()-int(length-4)-4:bsw.ByteOffset()])
+ tmpcrc := make([]byte, 4)
+ binary.LittleEndian.PutUint32(tmpcrc, crc)
+ bsw.PutBytes(tmpcrc)
+}
+
+func (psm *Program_stream_map) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 16 {
+ return errNeedMore
+ }
+ if bs.Uint32(24) != 0x000001 {
+ panic("program stream map must startwith 0x000001")
+ }
+ psm.Map_stream_id = bs.Uint8(8)
+ if psm.Map_stream_id != 0xBC {
+ panic("map stream id must be 0xBC")
+ }
+ psm.Program_stream_map_length = bs.Uint16(16)
+ if bs.RemainBytes() < int(psm.Program_stream_map_length) {
+ bs.UnRead(6 * 8)
+ return errNeedMore
+ }
+ psm.Current_next_indicator = bs.Uint8(1)
+ bs.SkipBits(2)
+ psm.Program_stream_map_version = bs.Uint8(5)
+ bs.SkipBits(8)
+ psm.Program_stream_info_length = bs.Uint16(16)
+ if bs.RemainBytes() < int(psm.Program_stream_info_length)+2 {
+ bs.UnRead(10 * 8)
+ return errNeedMore
+ }
+ bs.SkipBits(int(psm.Program_stream_info_length) * 8)
+ psm.Elementary_stream_map_length = bs.Uint16(16)
+ if psm.Program_stream_map_length != 6+psm.Program_stream_info_length+psm.Elementary_stream_map_length+4 {
+ return errParser
+ }
+ if bs.RemainBytes() < int(psm.Elementary_stream_map_length)+4 {
+ bs.UnRead(12*8 + int(psm.Program_stream_info_length)*8)
+ return errNeedMore
+ }
+
+ i := 0
+ psm.Stream_map = psm.Stream_map[:0]
+ for i < int(psm.Elementary_stream_map_length) {
+ elem := new(Elementary_stream_elem)
+ elem.Stream_type = bs.Uint8(8)
+ elem.Elementary_stream_id = bs.Uint8(8)
+ elem.Elementary_stream_info_length = bs.Uint16(16)
+ //TODO Parser descriptor
+ if bs.RemainBytes() < int(elem.Elementary_stream_info_length) {
+ return errParser
+ }
+ bs.SkipBits(int(elem.Elementary_stream_info_length) * 8)
+ i += int(4 + elem.Elementary_stream_info_length)
+ psm.Stream_map = append(psm.Stream_map, elem)
+ }
+
+ if i != int(psm.Elementary_stream_map_length) {
+ return errParser
+ }
+
+ bs.SkipBits(32)
+ return nil
+}
+
+type Program_stream_directory struct {
+ PES_packet_length uint16
+}
+
+func (psd *Program_stream_directory) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 6 {
+ return errNeedMore
+ }
+ if bs.Uint32(32) != 0x000001FF {
+ panic("program stream directory 000001FF")
+ }
+ psd.PES_packet_length = bs.Uint16(16)
+ if bs.RemainBytes() < int(psd.PES_packet_length) {
+ bs.UnRead(6 * 8)
+ return errNeedMore
+ }
+ //TODO Program Stream directory
+ bs.SkipBits(int(psd.PES_packet_length) * 8)
+ return nil
+}
+
+type CommonPesPacket struct {
+ Stream_id uint8
+ PES_packet_length uint16
+}
+
+func (compes *CommonPesPacket) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 6 {
+ return errNeedMore
+ }
+ bs.SkipBits(24)
+ compes.Stream_id = bs.Uint8(8)
+ compes.PES_packet_length = bs.Uint16(16)
+ if bs.RemainBytes() < int(compes.PES_packet_length) {
+ bs.UnRead(6 * 8)
+ return errNeedMore
+ }
+ bs.SkipBits(int(compes.PES_packet_length) * 8)
+ return nil
+}
+
+type PSPacket struct {
+ Header *PSPackHeader
+ System *System_header
+ Psm *Program_stream_map
+ Psd *Program_stream_directory
+ CommPes *CommonPesPacket
+ Pes *PesPacket
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-demuxer.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-demuxer.go
new file mode 100644
index 0000000000..4bfb4244a6
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-demuxer.go
@@ -0,0 +1,218 @@
+package mpeg2
+
+import (
+ "errors"
+ "io"
+
+ "github.com/yapingcat/gomedia/codec"
+)
+
+type pakcet_t struct {
+ payload []byte
+ pts uint64
+ dts uint64
+}
+
+func newPacket_t(size uint32) *pakcet_t {
+ return &pakcet_t{
+ payload: make([]byte, 0, size),
+ pts: 0,
+ dts: 0,
+ }
+}
+
+type tsstream struct {
+ cid TS_STREAM_TYPE
+ pes_sid PES_STREMA_ID
+ pes_pkg *PesPacket
+ pkg *pakcet_t
+}
+
+type tsprogram struct {
+ pn uint16
+ streams map[uint16]*tsstream
+}
+
+type TSDemuxer struct {
+ programs map[uint16]*tsprogram
+ OnFrame func(cid TS_STREAM_TYPE, frame []byte, pts uint64, dts uint64)
+ OnTSPacket func(pkg *TSPacket)
+}
+
+func NewTSDemuxer() *TSDemuxer {
+ return &TSDemuxer{
+ programs: make(map[uint16]*tsprogram),
+ OnFrame: nil,
+ OnTSPacket: nil,
+ }
+}
+
+func (demuxer *TSDemuxer) Input(r io.Reader) error {
+ buf := make([]byte, TS_PAKCET_SIZE)
+ _, err := io.ReadFull(r, buf)
+ if err != nil {
+ return errNeedMore
+ }
+ for {
+ bs := codec.NewBitStream(buf)
+ var pkg TSPacket
+ if err := pkg.DecodeHeader(bs); err != nil {
+ return err
+ }
+ if pkg.PID == uint16(TS_PID_PAT) {
+ if pkg.Payload_unit_start_indicator == 1 {
+ bs.SkipBits(8)
+ }
+ pat := NewPat()
+ if err := pat.Decode(bs); err != nil {
+ return err
+ }
+ pkg.Payload = pat
+ if pat.Table_id != uint8(TS_TID_PAS) {
+ return errors.New("pat table id is wrong")
+ }
+ for _, pmt := range pat.Pmts {
+ if pmt.Program_number != 0x0000 {
+ if _, found := demuxer.programs[pmt.PID]; !found {
+ demuxer.programs[pmt.PID] = &tsprogram{pn: 0, streams: make(map[uint16]*tsstream)}
+ }
+ }
+ }
+ } else {
+ for p, s := range demuxer.programs {
+ if p == pkg.PID { // pmt table
+ if pkg.Payload_unit_start_indicator == 1 {
+ bs.SkipBits(8) //pointer filed
+ }
+ pmt := NewPmt()
+ if err := pmt.Decode(bs); err != nil {
+ return err
+ }
+ pkg.Payload = pmt
+ s.pn = pmt.Program_number
+ for _, ps := range pmt.Streams {
+ if _, found := s.streams[ps.Elementary_PID]; !found {
+ s.streams[ps.Elementary_PID] = &tsstream{
+ cid: TS_STREAM_TYPE(ps.StreamType),
+ pes_sid: findPESIDByStreamType(TS_STREAM_TYPE(ps.StreamType)),
+ pes_pkg: NewPesPacket(),
+ }
+ }
+ }
+ } else {
+ for sid, stream := range s.streams {
+ if sid != pkg.PID {
+ continue
+ }
+ if pkg.Payload_unit_start_indicator == 1 {
+ err := stream.pes_pkg.Decode(bs)
+ // ignore error if it was a short payload read, next ts packet should append missing data
+ if err != nil && !(errors.Is(err, errNeedMore) && stream.pes_pkg.Pes_payload != nil) {
+ return err
+ }
+ pkg.Payload = stream.pes_pkg
+ } else {
+ stream.pes_pkg.Pes_payload = bs.RemainData()
+ pkg.Payload = bs.RemainData()
+ }
+ stype := findPESIDByStreamType(stream.cid)
+ if stype == PES_STREAM_AUDIO {
+ demuxer.doAudioPesPacket(stream, pkg.Payload_unit_start_indicator)
+ } else if stype == PES_STREAM_VIDEO {
+ demuxer.doVideoPesPacket(stream, pkg.Payload_unit_start_indicator)
+ }
+ }
+ }
+ }
+ }
+ if demuxer.OnTSPacket != nil {
+ demuxer.OnTSPacket(&pkg)
+ }
+ _, err := io.ReadFull(r, buf)
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ } else {
+ return errNeedMore
+ }
+ }
+ }
+ demuxer.flush()
+ return nil
+}
+
+func (demuxer *TSDemuxer) flush() {
+ for _, pm := range demuxer.programs {
+ for _, stream := range pm.streams {
+ if stream.pkg == nil || len(stream.pkg.payload) == 0 {
+ continue
+ }
+ if demuxer.OnFrame != nil {
+ demuxer.OnFrame(stream.cid, stream.pkg.payload, stream.pkg.pts/90, stream.pkg.dts/90)
+ }
+ }
+ }
+}
+
+func (demuxer *TSDemuxer) doVideoPesPacket(stream *tsstream, start uint8) {
+ if stream.cid != TS_STREAM_H264 && stream.cid != TS_STREAM_H265 {
+ return
+ }
+ if stream.pkg == nil {
+ stream.pkg = newPacket_t(1024)
+ stream.pkg.pts = stream.pes_pkg.Pts
+ stream.pkg.dts = stream.pes_pkg.Dts
+ }
+ stream.pkg.payload = append(stream.pkg.payload, stream.pes_pkg.Pes_payload...)
+ demuxer.splitH26XFrame(stream)
+ stream.pkg.pts = stream.pes_pkg.Pts
+ stream.pkg.dts = stream.pes_pkg.Dts
+}
+
+func (demuxer *TSDemuxer) doAudioPesPacket(stream *tsstream, start uint8) {
+ if stream.cid != TS_STREAM_AAC {
+ return
+ }
+
+ if stream.pkg == nil {
+ stream.pkg = newPacket_t(1024)
+ stream.pkg.pts = stream.pes_pkg.Pts
+ stream.pkg.dts = stream.pes_pkg.Dts
+ }
+
+ if len(stream.pkg.payload) > 0 && (start == 1 || stream.pes_pkg.Pts != stream.pkg.pts) {
+ if demuxer.OnFrame != nil {
+ demuxer.OnFrame(stream.cid, stream.pkg.payload, stream.pkg.pts/90, stream.pkg.dts/90)
+ }
+ stream.pkg.payload = stream.pkg.payload[:0]
+ }
+ stream.pkg.payload = append(stream.pkg.payload, stream.pes_pkg.Pes_payload...)
+ stream.pkg.pts = stream.pes_pkg.Pts
+ stream.pkg.dts = stream.pes_pkg.Dts
+}
+
+func (demuxer *TSDemuxer) splitH26XFrame(stream *tsstream) {
+ data := stream.pkg.payload
+ start, _ := codec.FindStartCode(data, 0)
+ datalen := len(data)
+ for start < datalen {
+ end, _ := codec.FindStartCode(data, start+3)
+ if end < 0 {
+ break
+ }
+ if (stream.cid == TS_STREAM_H264 && codec.H264NaluTypeWithoutStartCode(data[start:end]) == codec.H264_NAL_AUD) ||
+ (stream.cid == TS_STREAM_H265 && codec.H265NaluTypeWithoutStartCode(data[start:end]) == codec.H265_NAL_AUD) {
+ start = end
+ continue
+ }
+ if demuxer.OnFrame != nil {
+ demuxer.OnFrame(stream.cid, data[start:end], stream.pkg.pts/90, stream.pkg.dts/90)
+ }
+ start = end
+ }
+ if start == 0 {
+ return
+ }
+ copy(stream.pkg.payload, data[start:datalen])
+ stream.pkg.payload = stream.pkg.payload[0 : datalen-start]
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-muxer.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-muxer.go
new file mode 100644
index 0000000000..804b853094
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-muxer.go
@@ -0,0 +1,333 @@
+package mpeg2
+
+import (
+ "errors"
+
+ "github.com/yapingcat/gomedia/codec"
+)
+
+type pes_stream struct {
+ pid uint16
+ cc uint8
+ streamtype TS_STREAM_TYPE
+}
+
+func NewPESStream(pid uint16, cid TS_STREAM_TYPE) *pes_stream {
+ return &pes_stream{
+ pid: pid,
+ cc: 0,
+ streamtype: cid,
+ }
+}
+
+type table_pmt struct {
+ pid uint16
+ cc uint8
+ pcr_pid uint16
+ version_number uint8
+ pm uint16
+ streams []*pes_stream
+}
+
+func NewTablePmt() *table_pmt {
+ return &table_pmt{
+ pid: 0,
+ cc: 0,
+ pcr_pid: 0,
+ version_number: 0,
+ pm: 0,
+ streams: make([]*pes_stream, 0, 2),
+ }
+}
+
+type table_pat struct {
+ cc uint8
+ version_number uint8
+ pmts []*table_pmt
+}
+
+func NewTablePat() *table_pat {
+ return &table_pat{
+ cc: 0,
+ version_number: 0,
+ pmts: make([]*table_pmt, 0, 8),
+ }
+}
+
+type TSMuxer struct {
+ pat *table_pat
+ stream_pid uint16
+ pmt_pid uint16
+ pat_period uint64
+ OnPacket func(pkg []byte)
+}
+
+func NewTSMuxer() *TSMuxer {
+ return &TSMuxer{
+ pat: NewTablePat(),
+ stream_pid: 0x100,
+ pmt_pid: 0x200,
+ pat_period: 0,
+ OnPacket: nil,
+ }
+}
+
+func (mux *TSMuxer) AddStream(cid TS_STREAM_TYPE) uint16 {
+ if mux.pat == nil {
+ mux.pat = NewTablePat()
+ }
+ if len(mux.pat.pmts) == 0 {
+ tmppmt := NewTablePmt()
+ tmppmt.pid = mux.pmt_pid
+ tmppmt.pm = 1
+ mux.pmt_pid++
+ mux.pat.pmts = append(mux.pat.pmts, tmppmt)
+ }
+ sid := mux.stream_pid
+ tmpstream := NewPESStream(sid, cid)
+ mux.stream_pid++
+ mux.pat.pmts[0].streams = append(mux.pat.pmts[0].streams, tmpstream)
+ return sid
+}
+
+/// Muxer audio/video stream data
+/// pid: stream id by AddStream
+/// pts: audio/video stream timestamp in ms
+/// dts: audio/video stream timestamp in ms
+func (mux *TSMuxer) Write(pid uint16, data []byte, pts uint64, dts uint64) error {
+ var whichpmt *table_pmt = nil
+ var whichstream *pes_stream = nil
+ for _, pmt := range mux.pat.pmts {
+ for _, stream := range pmt.streams {
+ if stream.pid == pid {
+ whichpmt = pmt
+ whichstream = stream
+ break
+ }
+ }
+ }
+ if whichpmt == nil || whichstream == nil {
+ return errors.New("not Found pid stream")
+ }
+ if whichpmt.pcr_pid == 0 || (findPESIDByStreamType(whichstream.streamtype) == PES_STREAM_VIDEO && whichpmt.pcr_pid != pid) {
+ whichpmt.pcr_pid = pid
+ }
+
+ var withaud bool = false
+
+ if whichstream.streamtype == TS_STREAM_H264 || whichstream.streamtype == TS_STREAM_H265 {
+ codec.SplitFrame(data, func(nalu []byte) bool {
+ if whichstream.streamtype == TS_STREAM_H264 {
+ nalu_type := codec.H264NaluTypeWithoutStartCode(nalu)
+ if nalu_type == codec.H264_NAL_AUD {
+ withaud = true
+ return false
+ } else if codec.IsH264VCLNaluType(nalu_type) {
+ return false
+ }
+ return true
+ } else {
+ nalu_type := codec.H265NaluTypeWithoutStartCode(nalu)
+ if nalu_type == codec.H265_NAL_AUD {
+ withaud = true
+ return false
+ } else if codec.IsH265VCLNaluType(nalu_type) {
+ return false
+ }
+ return true
+ }
+ })
+ }
+
+ if mux.pat_period == 0 || mux.pat_period+400 < dts {
+ mux.pat_period = dts
+ if mux.pat_period == 0 {
+ mux.pat_period = 1 //avoid write pat twice
+ }
+ tmppat := NewPat()
+ tmppat.Version_number = mux.pat.version_number
+ for _, pmt := range mux.pat.pmts {
+ tmppm := PmtPair{
+ Program_number: pmt.pm,
+ PID: pmt.pid,
+ }
+ tmppat.Pmts = append(tmppat.Pmts, tmppm)
+ }
+ mux.writePat(tmppat)
+
+ for _, pmt := range mux.pat.pmts {
+ tmppmt := NewPmt()
+ tmppmt.Program_number = pmt.pm
+ tmppmt.Version_number = pmt.version_number
+ tmppmt.PCR_PID = pmt.pcr_pid
+ for _, stream := range pmt.streams {
+ var sp StreamPair
+ sp.StreamType = uint8(stream.streamtype)
+ sp.Elementary_PID = stream.pid
+ sp.ES_Info_Length = 0
+ tmppmt.Streams = append(tmppmt.Streams, sp)
+ }
+ mux.writePmt(tmppmt, pmt)
+ }
+ }
+
+ flag := false
+ switch whichstream.streamtype {
+ case TS_STREAM_H264:
+ flag = codec.IsH264IDRFrame(data)
+ case TS_STREAM_H265:
+ flag = codec.IsH265IDRFrame(data)
+ }
+
+ mux.writePES(whichstream, whichpmt, data, pts*90, dts*90, flag, withaud)
+ return nil
+}
+
+func (mux *TSMuxer) writePat(pat *Pat) {
+ var tshdr TSPacket
+ tshdr.Payload_unit_start_indicator = 1
+ tshdr.PID = 0
+ tshdr.Adaptation_field_control = 0x01
+ tshdr.Continuity_counter = mux.pat.cc
+ mux.pat.cc++
+ mux.pat.cc = (mux.pat.cc + 1) % 16
+ bsw := codec.NewBitStreamWriter(TS_PAKCET_SIZE)
+ tshdr.EncodeHeader(bsw)
+ bsw.PutByte(0x00) //pointer
+ pat.Encode(bsw)
+ bsw.FillRemainData(0xff)
+ if mux.OnPacket != nil {
+ mux.OnPacket(bsw.Bits())
+ }
+}
+
+func (mux *TSMuxer) writePmt(pmt *Pmt, t_pmt *table_pmt) {
+ var tshdr TSPacket
+ tshdr.Payload_unit_start_indicator = 1
+ tshdr.PID = t_pmt.pid
+ tshdr.Adaptation_field_control = 0x01
+ tshdr.Continuity_counter = t_pmt.cc
+ t_pmt.cc = (t_pmt.cc + 1) % 16
+ bsw := codec.NewBitStreamWriter(TS_PAKCET_SIZE)
+ tshdr.EncodeHeader(bsw)
+ bsw.PutByte(0x00) //pointer
+ pmt.Encode(bsw)
+ bsw.FillRemainData(0xff)
+ if mux.OnPacket != nil {
+ mux.OnPacket(bsw.Bits())
+ }
+}
+
+func (mux *TSMuxer) writePES(pes *pes_stream, pmt *table_pmt, data []byte, pts uint64, dts uint64, idr_flag bool, withaud bool) {
+ var firstPesPacket bool = true
+ bsw := codec.NewBitStreamWriter(TS_PAKCET_SIZE)
+ for {
+ bsw.Reset()
+ var tshdr TSPacket
+ if firstPesPacket {
+ tshdr.Payload_unit_start_indicator = 1
+ }
+ tshdr.PID = pes.pid
+ tshdr.Adaptation_field_control = 0x01
+ tshdr.Continuity_counter = pes.cc
+ headlen := 4
+ pes.cc = (pes.cc + 1) % 16
+ var adaptation *Adaptation_field = nil
+ if firstPesPacket && idr_flag {
+ adaptation = new(Adaptation_field)
+ tshdr.Adaptation_field_control = tshdr.Adaptation_field_control | 0x20
+ adaptation.Random_access_indicator = 1
+ headlen += 2
+ }
+
+ if firstPesPacket && pes.pid == pmt.pcr_pid {
+ if adaptation == nil {
+ adaptation = new(Adaptation_field)
+ headlen += 2
+ }
+ tshdr.Adaptation_field_control = tshdr.Adaptation_field_control | 0x20
+ adaptation.PCR_flag = 1
+ var pcr_base uint64 = 0
+ var pcr_ext uint16 = 0
+ if dts == 0 {
+ pcr_base = pts * 300 / 300
+ pcr_ext = uint16(pts * 300 % 300)
+ } else {
+ pcr_base = dts * 300 / 300
+ pcr_ext = uint16(dts * 300 % 300)
+ }
+ adaptation.Program_clock_reference_base = pcr_base
+ adaptation.Program_clock_reference_extension = pcr_ext
+ headlen += 6
+ }
+
+ var payload []byte
+ var pespkg *PesPacket = nil
+ if firstPesPacket {
+ oldheadlen := headlen
+ headlen += 19
+ if !withaud && pes.streamtype == TS_STREAM_H264 {
+ headlen += 6
+ payload = append(payload, H264_AUD_NALU...)
+ } else if !withaud && pes.streamtype == TS_STREAM_H265 {
+ payload = append(payload, H265_AUD_NALU...)
+ headlen += 7
+ }
+ pespkg = NewPesPacket()
+ pespkg.PTS_DTS_flags = 0x03
+ pespkg.PES_header_data_length = 10
+ pespkg.Pts = pts
+ pespkg.Dts = dts
+ pespkg.Stream_id = uint8(findPESIDByStreamType(pes.streamtype))
+ if idr_flag {
+ pespkg.Data_alignment_indicator = 1
+ }
+ if headlen-oldheadlen-6+len(data) > 0xFFFF {
+ pespkg.PES_packet_length = 0
+ } else {
+ pespkg.PES_packet_length = uint16(len(data) + headlen - oldheadlen - 6)
+ }
+
+ }
+
+ if len(data)+headlen < TS_PAKCET_SIZE {
+ if adaptation == nil {
+ adaptation = new(Adaptation_field)
+ headlen += 1
+ if TS_PAKCET_SIZE-len(data)-headlen >= 1 {
+ headlen += 1
+ } else {
+ adaptation.SingleStuffingByte = true
+ }
+ }
+ adaptation.Stuffing_byte = uint8(TS_PAKCET_SIZE - len(data) - headlen)
+ payload = append(payload, data...)
+ data = data[:0]
+ } else {
+ payload = append(payload, data[0:TS_PAKCET_SIZE-headlen]...)
+ data = data[TS_PAKCET_SIZE-headlen:]
+ }
+
+ if adaptation != nil {
+ tshdr.Field = adaptation
+ tshdr.Adaptation_field_control |= 0x02
+ }
+ tshdr.EncodeHeader(bsw)
+ if pespkg != nil {
+ pespkg.Pes_payload = payload
+ pespkg.Encode(bsw)
+ } else {
+ bsw.PutBytes(payload)
+ }
+ firstPesPacket = false
+ if mux.OnPacket != nil {
+ if len(bsw.Bits()) != TS_PAKCET_SIZE {
+ panic("packet ts packet failed")
+ }
+ mux.OnPacket(bsw.Bits())
+ }
+ if len(data) == 0 {
+ break
+ }
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-proto.go b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-proto.go
new file mode 100644
index 0000000000..d0dc3d1eb7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/github.com/yapingcat/gomedia/mpeg2/ts-proto.go
@@ -0,0 +1,602 @@
+package mpeg2
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/yapingcat/gomedia/codec"
+)
+
+//PID
+type TS_PID int
+
+const (
+ TS_PID_PAT TS_PID = 0x0000
+ TS_PID_CAT
+ TS_PID_TSDT
+ TS_PID_IPMP
+ TS_PID_Nil = 0x1FFFF
+)
+
+//Table id
+type PAT_TID int
+
+const (
+ TS_TID_PAS PAT_TID = 0x00 // program_association_section
+ TS_TID_CAS = 0x01 // conditional_access_section(CA_section)
+ TS_TID_PMS = 0x02 // TS_program_map_section
+ TS_TID_SDS = 0x03 //TS_description_section
+ TS_TID_FORBIDDEN PAT_TID = 0xFF
+)
+
+type TS_STREAM_TYPE int
+
+const (
+ TS_STREAM_AAC TS_STREAM_TYPE = 0x0F
+ TS_STREAM_H264 TS_STREAM_TYPE = 0x1B
+ TS_STREAM_H265 TS_STREAM_TYPE = 0x24
+)
+
+const (
+ TS_PAKCET_SIZE = 188
+)
+
+type Display interface {
+ PrettyPrint(file *os.File)
+}
+
+// transport_packet(){
+// sync_byte 8 bslbf
+// transport_error_indicator 1 bslbf
+// payload_unit_start_indicator 1 bslbf
+// transport_priority 1 bslbf
+// PID 13 uimsbf
+// transport_scrambling_control 2 bslbf
+// adaptation_field_control 2 bslbf
+// continuity_counter 4 uimsbf
+// if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
+// adaptation_field()
+// }
+// if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
+// for (i = 0; i < N; i++){
+// data_byte 8 bslbf
+// }
+// }
+// }
+
+type TSPacket struct {
+ Transport_error_indicator uint8
+ Payload_unit_start_indicator uint8
+ Transport_priority uint8
+ PID uint16
+ Transport_scrambling_control uint8
+ Adaptation_field_control uint8
+ Continuity_counter uint8
+ Field *Adaptation_field
+ Payload interface{}
+}
+
+func (pkg *TSPacket) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("Transport_error_indicator:%d\n", pkg.Transport_error_indicator))
+ file.WriteString(fmt.Sprintf("Payload_unit_start_indicator:%d\n", pkg.Payload_unit_start_indicator))
+ file.WriteString(fmt.Sprintf("Transport_priority:%d\n", pkg.Transport_priority))
+ file.WriteString(fmt.Sprintf("PID:%d\n", pkg.PID))
+ file.WriteString(fmt.Sprintf("Transport_scrambling_control:%d\n", pkg.Transport_scrambling_control))
+ file.WriteString(fmt.Sprintf("Adaptation_field_control:%d\n", pkg.Adaptation_field_control))
+ file.WriteString(fmt.Sprintf("Continuity_counter:%d\n", pkg.Continuity_counter))
+}
+
+func (pkg *TSPacket) EncodeHeader(bsw *codec.BitStreamWriter) {
+ bsw.PutByte(0x47)
+ bsw.PutUint8(pkg.Transport_error_indicator, 1)
+ bsw.PutUint8(pkg.Payload_unit_start_indicator, 1)
+ bsw.PutUint8(pkg.Transport_priority, 1)
+ bsw.PutUint16(pkg.PID, 13)
+ bsw.PutUint8(pkg.Transport_scrambling_control, 2)
+ bsw.PutUint8(pkg.Adaptation_field_control, 2)
+ bsw.PutUint8(pkg.Continuity_counter, 4)
+ if pkg.Field != nil && (pkg.Adaptation_field_control&0x02) != 0 {
+ pkg.Field.Encode(bsw)
+ }
+}
+
+func (pkg *TSPacket) DecodeHeader(bs *codec.BitStream) error {
+ sync_byte := bs.Uint8(8)
+ if sync_byte != 0x47 {
+ return errors.New("ts packet must start with 0x47")
+ }
+ pkg.Transport_error_indicator = bs.GetBit()
+ pkg.Payload_unit_start_indicator = bs.GetBit()
+ pkg.Transport_priority = bs.GetBit()
+ pkg.PID = bs.Uint16(13)
+ pkg.Transport_scrambling_control = bs.Uint8(2)
+ pkg.Adaptation_field_control = bs.Uint8(2)
+ pkg.Continuity_counter = bs.Uint8(4)
+ if pkg.Adaptation_field_control == 0x02 || pkg.Adaptation_field_control == 0x03 {
+ if pkg.Field == nil {
+ pkg.Field = new(Adaptation_field)
+ }
+ err := pkg.Field.Decode(bs)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+//
+// adaptation_field() {
+// adaptation_field_length
+// if (adaptation_field_length > 0) {
+// discontinuity_indicator
+// random_access_indicator
+// elementary_stream_priority_indicator
+// PCR_flag
+// OPCR_flag
+// splicing_point_flag
+// transport_private_data_flag
+// adaptation_field_extension_flag
+// if (PCR_flag == '1') {
+// program_clock_reference_base
+// reserved
+// program_clock_reference_extension
+// }
+// if (OPCR_flag == '1') {
+// original_program_clock_reference_base
+// reserved
+// original_program_clock_reference_extension
+// }
+// if (splicing_point_flag == '1') {
+// splice_countdown
+// }
+// if (transport_private_data_flag == '1') {
+// transport_private_data_length
+// for (i = 0; i < transport_private_data_length; i++) {
+// private_data_byte
+// }
+// }
+// if (adaptation_field_extension_flag == '1') {
+// adaptation_field_extension_length
+// ltw_flag piecewise_rate_flag
+// seamless_splice_flag
+// reserved
+// if (ltw_flag == '1') {
+// ltw_valid_flag
+// ltw_offset
+// }
+// if (piecewise_rate_flag == '1') {
+// reserved
+// piecewise_rate
+// }
+// if (seamless_splice_flag == '1') {
+// splice_type
+// DTS_next_AU[32..30]
+// marker_bit
+// DTS_next_AU[29..15]
+// marker_bit
+// DTS_next_AU[14..0]
+// marker_bit 1
+// }
+// for (i = 0; i < N; i++) {
+// reserved 8
+// }
+// }
+// for (i = 0; i < N; i++) {
+// stuffing_byte 8
+// }
+// }
+
+type Adaptation_field struct {
+ SingleStuffingByte bool // The value 0 is for inserting a single stuffing byte in a Transport Stream packet
+ Adaptation_field_length uint8 //8 uimsbf
+ Discontinuity_indicator uint8 //1 bslbf
+ Random_access_indicator uint8 //1 bslbf
+ Elementary_stream_priority_indicator uint8 //1 bslbf
+ PCR_flag uint8 //1 bslbf
+ OPCR_flag uint8 //1 bslbf
+ Splicing_point_flag uint8 //1 bslbf
+ Transport_private_data_flag uint8 //1 bslbf
+ Adaptation_field_extension_flag uint8 //1 bslbf
+ Program_clock_reference_base uint64 //33 uimsbf
+ Program_clock_reference_extension uint16 //9 uimsbf
+ Original_program_clock_reference_base uint64 //33 uimsbf
+ Original_program_clock_reference_extension uint16 //9 uimsbf
+ Splice_countdown uint8 //8 uimsbf
+ Transport_private_data_length uint8 //8 uimsbf
+ Adaptation_field_extension_length uint8 //8 uimsbf
+ Ltw_flag uint8 //1 bslbf
+ Piecewise_rate_flag uint8 //1 bslbf
+ Seamless_splice_flag uint8 //1 bslbf
+ Ltw_valid_flag uint8 //1 bslbf
+ Ltw_offset uint16 //15 uimsbf
+ Piecewise_rate uint32 //22 uimsbf
+ Splice_type uint8 //4 uimsbf
+ DTS_next_AU uint64
+ Stuffing_byte uint8
+}
+
+func (adaptation *Adaptation_field) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("Adaptation_field_length:%d\n", adaptation.Adaptation_field_length))
+ file.WriteString(fmt.Sprintf("Discontinuity_indicator:%d\n", adaptation.Discontinuity_indicator))
+ file.WriteString(fmt.Sprintf("Random_access_indicator:%d\n", adaptation.Random_access_indicator))
+ file.WriteString(fmt.Sprintf("Elementary_stream_priority_indicator:%d\n", adaptation.Elementary_stream_priority_indicator))
+ file.WriteString(fmt.Sprintf("PCR_flag:%d\n", adaptation.PCR_flag))
+ file.WriteString(fmt.Sprintf("OPCR_flag:%d\n", adaptation.OPCR_flag))
+ file.WriteString(fmt.Sprintf("Splicing_point_flag:%d\n", adaptation.Splicing_point_flag))
+ file.WriteString(fmt.Sprintf("Transport_private_data_flag:%d\n", adaptation.Transport_private_data_flag))
+ file.WriteString(fmt.Sprintf("Adaptation_field_extension_flag:%d\n", adaptation.Adaptation_field_extension_flag))
+ if adaptation.PCR_flag == 1 {
+ file.WriteString(fmt.Sprintf("Program_clock_reference_base:%d\n", adaptation.Program_clock_reference_base))
+ file.WriteString(fmt.Sprintf("Program_clock_reference_extension:%d\n", adaptation.Program_clock_reference_extension))
+ }
+ if adaptation.OPCR_flag == 1 {
+ file.WriteString(fmt.Sprintf("Original_program_clock_reference_base:%d\n", adaptation.Original_program_clock_reference_base))
+ file.WriteString(fmt.Sprintf("Original_program_clock_reference_extension:%d\n", adaptation.Original_program_clock_reference_extension))
+ }
+ if adaptation.Splicing_point_flag == 1 {
+ file.WriteString(fmt.Sprintf("Splice_countdown:%d\n", adaptation.Splice_countdown))
+ }
+ if adaptation.Transport_private_data_flag == 1 {
+ file.WriteString(fmt.Sprintf("Transport_private_data_length:%d\n", adaptation.Transport_private_data_length))
+ }
+ if adaptation.Adaptation_field_extension_flag == 1 {
+ file.WriteString(fmt.Sprintf("Adaptation_field_extension_length:%d\n", adaptation.Adaptation_field_extension_length))
+ file.WriteString(fmt.Sprintf("Ltw_flag:%d\n", adaptation.Ltw_flag))
+ file.WriteString(fmt.Sprintf("Piecewise_rate_flag:%d\n", adaptation.Piecewise_rate_flag))
+ file.WriteString(fmt.Sprintf("Seamless_splice_flag:%d\n", adaptation.Seamless_splice_flag))
+ if adaptation.Ltw_flag == 1 {
+ file.WriteString(fmt.Sprintf("Ltw_valid_flag:%d\n", adaptation.Ltw_valid_flag))
+ file.WriteString(fmt.Sprintf("Ltw_offset:%d\n", adaptation.Ltw_offset))
+ }
+ if adaptation.Piecewise_rate_flag == 1 {
+ file.WriteString(fmt.Sprintf("Piecewise_rate:%d\n", adaptation.Piecewise_rate))
+ }
+ if adaptation.Seamless_splice_flag == 1 {
+ file.WriteString(fmt.Sprintf("Splice_type:%d\n", adaptation.Splice_type))
+ file.WriteString(fmt.Sprintf("DTS_next_AU:%d\n", adaptation.DTS_next_AU))
+ }
+ }
+}
+
+func (adaptation *Adaptation_field) Encode(bsw *codec.BitStreamWriter) {
+ loc := bsw.ByteOffset()
+ bsw.PutUint8(adaptation.Adaptation_field_length, 8)
+ if adaptation.SingleStuffingByte {
+ return
+ }
+ bsw.Markdot()
+ bsw.PutUint8(adaptation.Discontinuity_indicator, 1)
+ bsw.PutUint8(adaptation.Random_access_indicator, 1)
+ bsw.PutUint8(adaptation.Elementary_stream_priority_indicator, 1)
+ bsw.PutUint8(adaptation.PCR_flag, 1)
+ bsw.PutUint8(adaptation.OPCR_flag, 1)
+ bsw.PutUint8(adaptation.Splicing_point_flag, 1)
+ bsw.PutUint8(0 /*adaptation.Transport_private_data_flag*/, 1)
+ bsw.PutUint8(0 /*adaptation.Adaptation_field_extension_flag*/, 1)
+ if adaptation.PCR_flag == 1 {
+ bsw.PutUint64(adaptation.Program_clock_reference_base, 33)
+ bsw.PutUint8(0, 6)
+ bsw.PutUint16(adaptation.Program_clock_reference_extension, 9)
+ }
+ if adaptation.OPCR_flag == 1 {
+ bsw.PutUint64(adaptation.Original_program_clock_reference_base, 33)
+ bsw.PutUint8(0, 6)
+ bsw.PutUint16(adaptation.Original_program_clock_reference_extension, 9)
+ }
+ if adaptation.Splicing_point_flag == 1 {
+ bsw.PutUint8(adaptation.Splice_countdown, 8)
+ }
+ //TODO
+ // if adaptation.Transport_private_data_flag == 0 {
+ // }
+ // if adaptation.Adaptation_field_extension_flag == 0 {
+ // }
+ adaptation.Adaptation_field_length = uint8(bsw.DistanceFromMarkDot() / 8)
+ bsw.PutRepetValue(0xff, int(adaptation.Stuffing_byte))
+ adaptation.Adaptation_field_length += adaptation.Stuffing_byte
+ bsw.SetByte(adaptation.Adaptation_field_length, loc)
+}
+
+func (adaptation *Adaptation_field) Decode(bs *codec.BitStream) error {
+ if bs.RemainBytes() < 1 {
+ return errors.New("len of data < 1 byte")
+ }
+ adaptation.Adaptation_field_length = bs.Uint8(8)
+ startoffset := bs.ByteOffset()
+ //fmt.Printf("Adaptation_field_length=%d\n", adaptation.Adaptation_field_length)
+ if bs.RemainBytes() < int(adaptation.Adaptation_field_length) {
+ return errors.New("len of data < Adaptation_field_length")
+ }
+ if adaptation.Adaptation_field_length == 0 {
+ return nil
+ }
+ adaptation.Discontinuity_indicator = bs.GetBit()
+ adaptation.Random_access_indicator = bs.GetBit()
+ adaptation.Elementary_stream_priority_indicator = bs.GetBit()
+ adaptation.PCR_flag = bs.GetBit()
+ adaptation.OPCR_flag = bs.GetBit()
+ adaptation.Splicing_point_flag = bs.GetBit()
+ adaptation.Transport_private_data_flag = bs.GetBit()
+ adaptation.Adaptation_field_extension_flag = bs.GetBit()
+ if adaptation.PCR_flag == 1 {
+ adaptation.Program_clock_reference_base = bs.GetBits(33)
+ bs.SkipBits(6)
+ adaptation.Program_clock_reference_extension = uint16(bs.GetBits(9))
+ }
+ if adaptation.OPCR_flag == 1 {
+ adaptation.Original_program_clock_reference_base = bs.GetBits(33)
+ bs.SkipBits(6)
+ adaptation.Original_program_clock_reference_extension = uint16(bs.GetBits(9))
+ }
+ if adaptation.Splicing_point_flag == 1 {
+ adaptation.Splice_countdown = bs.Uint8(8)
+ }
+ if adaptation.Transport_private_data_flag == 1 {
+ adaptation.Transport_private_data_length = bs.Uint8(8)
+ bs.SkipBits(8 * int(adaptation.Transport_private_data_length))
+ }
+ if adaptation.Adaptation_field_extension_flag == 1 {
+ adaptation.Adaptation_field_extension_length = bs.Uint8(8)
+ bs.Markdot()
+ adaptation.Ltw_flag = bs.GetBit()
+ adaptation.Piecewise_rate_flag = bs.GetBit()
+ adaptation.Seamless_splice_flag = bs.GetBit()
+ bs.SkipBits(5)
+ if adaptation.Ltw_flag == 1 {
+ adaptation.Ltw_valid_flag = bs.GetBit()
+ adaptation.Ltw_offset = uint16(bs.GetBits(15))
+ }
+ if adaptation.Piecewise_rate_flag == 1 {
+ bs.SkipBits(2)
+ adaptation.Piecewise_rate = uint32(bs.GetBits(22))
+ }
+ if adaptation.Seamless_splice_flag == 1 {
+ adaptation.Splice_type = uint8(bs.GetBits(4))
+ adaptation.DTS_next_AU = bs.GetBits(3)
+ bs.SkipBits(1)
+ adaptation.DTS_next_AU = adaptation.DTS_next_AU<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ adaptation.DTS_next_AU = adaptation.DTS_next_AU<<15 | bs.GetBits(15)
+ bs.SkipBits(1)
+ }
+ bitscount := bs.DistanceFromMarkDot()
+ if bitscount%8 > 0 {
+ panic("maybe parser ts file failed")
+ }
+ bs.SkipBits(int(adaptation.Adaptation_field_extension_length*8 - uint8(bitscount)))
+ }
+ endoffset := bs.ByteOffset()
+ bs.SkipBits((int(adaptation.Adaptation_field_length) - (endoffset - startoffset)) * 8)
+ return nil
+}
+
+type PmtPair struct {
+ Program_number uint16
+ PID uint16
+}
+
+type Pat struct {
+ Table_id uint8 //8 uimsbf
+ Section_syntax_indicator uint8 //1 bslbf
+ Section_length uint16 //12 uimsbf
+ Transport_stream_id uint16 //16 uimsbf
+ Version_number uint8 //5 uimsbf
+ Current_next_indicator uint8 //1 bslbf
+ Section_number uint8 //8 uimsbf
+ Last_section_number uint8 //8 uimsbf
+ Pmts []PmtPair
+}
+
+func NewPat() *Pat {
+ return &Pat{
+ Table_id: uint8(TS_TID_PAS),
+ Pmts: make([]PmtPair, 0, 8),
+ }
+}
+
+func (pat *Pat) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("Table id:%d\n", pat.Table_id))
+ file.WriteString(fmt.Sprintf("Section_syntax_indicator:%d\n", pat.Section_syntax_indicator))
+ file.WriteString(fmt.Sprintf("Section_length:%d\n", pat.Section_length))
+ file.WriteString(fmt.Sprintf("Transport_stream_id:%d\n", pat.Transport_stream_id))
+ file.WriteString(fmt.Sprintf("Version_number:%d\n", pat.Version_number))
+ file.WriteString(fmt.Sprintf("Current_next_indicator:%d\n", pat.Current_next_indicator))
+ file.WriteString(fmt.Sprintf("Section_number:%d\n", pat.Section_number))
+ file.WriteString(fmt.Sprintf("Last_section_number:%d\n", pat.Last_section_number))
+ for i, pmt := range pat.Pmts {
+ file.WriteString(fmt.Sprintf("----pmt %d\n", i))
+ file.WriteString(fmt.Sprintf(" program_number:%d\n", pmt.Program_number))
+ if pmt.Program_number == 0x0000 {
+ file.WriteString(fmt.Sprintf(" network_PID:%d\n", pmt.PID))
+ } else {
+ file.WriteString(fmt.Sprintf(" program_map_PID:%d\n", pmt.PID))
+ }
+ }
+}
+
+func (pat *Pat) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutUint8(0x00, 8)
+ loc := bsw.ByteOffset()
+ bsw.PutUint8(pat.Section_syntax_indicator, 1)
+ bsw.PutUint8(0x00, 1)
+ bsw.PutUint8(0x03, 2)
+ bsw.PutUint16(0, 12)
+ bsw.Markdot()
+ bsw.PutUint16(pat.Transport_stream_id, 16)
+ bsw.PutUint8(0x03, 2)
+ bsw.PutUint8(pat.Version_number, 5)
+ bsw.PutUint8(pat.Current_next_indicator, 1)
+ bsw.PutUint8(pat.Section_number, 8)
+ bsw.PutUint8(pat.Last_section_number, 8)
+ for _, pms := range pat.Pmts {
+ bsw.PutUint16(pms.Program_number, 16)
+ bsw.PutUint8(0x07, 3)
+ bsw.PutUint16(pms.PID, 13)
+ }
+ length := bsw.DistanceFromMarkDot()
+ //|Section_syntax_indicator|'0'|reserved|Section_length|
+ pat.Section_length = uint16(length)/8 + 4
+ bsw.SetUint16(pat.Section_length&0x0FFF|(uint16(pat.Section_syntax_indicator)<<15)|0x3000, loc)
+ crc := codec.CalcCrc32(0xffffffff, bsw.Bits()[bsw.ByteOffset()-int(pat.Section_length-4)-3:bsw.ByteOffset()])
+ tmpcrc := make([]byte, 4)
+ binary.LittleEndian.PutUint32(tmpcrc, crc)
+ bsw.PutBytes(tmpcrc)
+}
+
+func (pat *Pat) Decode(bs *codec.BitStream) error {
+ pat.Table_id = bs.Uint8(8)
+
+ if pat.Table_id != uint8(TS_TID_PAS) {
+ return errors.New("table id is Not TS_TID_PAS")
+ }
+ pat.Section_syntax_indicator = bs.Uint8(1)
+ bs.SkipBits(3)
+ pat.Section_length = bs.Uint16(12)
+ pat.Transport_stream_id = bs.Uint16(16)
+ bs.SkipBits(2)
+ pat.Version_number = bs.Uint8(5)
+ pat.Current_next_indicator = bs.Uint8(1)
+ pat.Section_number = bs.Uint8(8)
+ pat.Last_section_number = bs.Uint8(8)
+ for i := 0; i+4 <= int(pat.Section_length)-5-4; i = i + 4 {
+ tmp := PmtPair{
+ Program_number: 0,
+ PID: 0,
+ }
+ tmp.Program_number = bs.Uint16(16)
+ bs.SkipBits(3)
+ tmp.PID = bs.Uint16(13)
+ pat.Pmts = append(pat.Pmts, tmp)
+ }
+ return nil
+}
+
+type StreamPair struct {
+ StreamType uint8 //8 uimsbf
+ Elementary_PID uint16 //13 uimsbf
+ ES_Info_Length uint16 //12 uimsbf
+}
+
+type Pmt struct {
+ Table_id uint8 //8 uimsbf
+ Section_syntax_indicator uint8 //1 bslbf
+ Section_length uint16 //12 uimsbf
+ Program_number uint16 //16 uimsbf
+ Version_number uint8 //5 uimsbf
+ Current_next_indicator uint8 //1 bslbf
+ Section_number uint8 //8 uimsbf
+ Last_section_number uint8 //8 uimsbf
+ PCR_PID uint16 //13 uimsbf
+ Program_info_length uint16 //12 uimsbf
+ Streams []StreamPair
+}
+
+func NewPmt() *Pmt {
+ return &Pmt{
+ Table_id: uint8(TS_TID_PMS),
+ Streams: make([]StreamPair, 0, 8),
+ }
+}
+
+func (pmt *Pmt) PrettyPrint(file *os.File) {
+ file.WriteString(fmt.Sprintf("Table id:%d\n", pmt.Table_id))
+ file.WriteString(fmt.Sprintf("Section_syntax_indicator:%d\n", pmt.Section_syntax_indicator))
+ file.WriteString(fmt.Sprintf("Section_length:%d\n", pmt.Section_length))
+ file.WriteString(fmt.Sprintf("Program_number:%d\n", pmt.Program_number))
+ file.WriteString(fmt.Sprintf("Version_number:%d\n", pmt.Version_number))
+ file.WriteString(fmt.Sprintf("Current_next_indicator:%d\n", pmt.Current_next_indicator))
+ file.WriteString(fmt.Sprintf("Section_number:%d\n", pmt.Section_number))
+ file.WriteString(fmt.Sprintf("Last_section_number:%d\n", pmt.Last_section_number))
+ file.WriteString(fmt.Sprintf("PCR_PID:%d\n", pmt.PCR_PID))
+ file.WriteString(fmt.Sprintf("program_info_length:%d\n", pmt.Program_info_length))
+ for i, stream := range pmt.Streams {
+ file.WriteString(fmt.Sprintf("----stream %d\n", i))
+ if stream.StreamType == uint8(TS_STREAM_AAC) {
+ file.WriteString(" stream_type:AAC\n")
+ } else if stream.StreamType == uint8(TS_STREAM_H264) {
+ file.WriteString(" stream_type:H264\n")
+ } else if stream.StreamType == uint8(TS_STREAM_H265) {
+ file.WriteString(" stream_type:H265\n")
+ }
+ file.WriteString(fmt.Sprintf(" elementary_PID:%d\n", stream.Elementary_PID))
+ file.WriteString(fmt.Sprintf(" ES_info_length:%d\n", stream.ES_Info_Length))
+ }
+}
+
+func (pmt *Pmt) Encode(bsw *codec.BitStreamWriter) {
+ bsw.PutUint8(pmt.Table_id, 8)
+ loc := bsw.ByteOffset()
+ bsw.PutUint8(pmt.Section_syntax_indicator, 1)
+ bsw.PutUint8(0x00, 1)
+ bsw.PutUint8(0x03, 2)
+ bsw.PutUint16(pmt.Section_length, 12)
+ bsw.Markdot()
+ bsw.PutUint16(pmt.Program_number, 16)
+ bsw.PutUint8(0x03, 2)
+ bsw.PutUint8(pmt.Version_number, 5)
+ bsw.PutUint8(pmt.Current_next_indicator, 1)
+ bsw.PutUint8(pmt.Section_number, 8)
+ bsw.PutUint8(pmt.Last_section_number, 8)
+ bsw.PutUint8(0x07, 3)
+ bsw.PutUint16(pmt.PCR_PID, 13)
+ bsw.PutUint8(0x0f, 4)
+ //TODO Program info length
+ bsw.PutUint16(0x0000 /*pmt.Program_info_length*/, 12)
+ for _, stream := range pmt.Streams {
+ bsw.PutUint8(stream.StreamType, 8)
+ bsw.PutUint8(0x00, 3)
+ bsw.PutUint16(stream.Elementary_PID, 13)
+ bsw.PutUint8(0x00, 4)
+ //TODO ES_info
+ bsw.PutUint8(0 /*ES_info_length*/, 12)
+ }
+ length := bsw.DistanceFromMarkDot()
+ pmt.Section_length = uint16(length)/8 + 4
+ bsw.SetUint16(pmt.Section_length&0x0FFF|(uint16(pmt.Section_syntax_indicator)<<15)|0x3000, loc)
+ crc := codec.CalcCrc32(0xffffffff, bsw.Bits()[bsw.ByteOffset()-int(pmt.Section_length-4)-3:bsw.ByteOffset()])
+ tmpcrc := make([]byte, 4)
+ binary.LittleEndian.PutUint32(tmpcrc, crc)
+ bsw.PutBytes(tmpcrc)
+}
+
+func (pmt *Pmt) Decode(bs *codec.BitStream) error {
+ pmt.Table_id = bs.Uint8(8)
+ if pmt.Table_id != uint8(TS_TID_PMS) {
+ return errors.New("table id is Not TS_TID_PAS")
+ }
+ pmt.Section_syntax_indicator = bs.Uint8(1)
+ bs.SkipBits(3)
+ pmt.Section_length = bs.Uint16(12)
+ pmt.Program_number = bs.Uint16(16)
+ bs.SkipBits(2)
+ pmt.Version_number = bs.Uint8(5)
+ pmt.Current_next_indicator = bs.Uint8(1)
+ pmt.Section_number = bs.Uint8(8)
+ pmt.Last_section_number = bs.Uint8(8)
+ bs.SkipBits(3)
+ pmt.PCR_PID = bs.Uint16(13)
+ bs.SkipBits(4)
+ pmt.Program_info_length = bs.Uint16(12)
+ //TODO N loop descriptors
+ bs.SkipBits(int(pmt.Program_info_length) * 8)
+ //fmt.Printf("section length %d pmt.Pogram_info_length=%d\n", pmt.Section_length, pmt.Pogram_info_length)
+ for i := 0; i < int(pmt.Section_length)-9-int(pmt.Program_info_length)-4; {
+ tmp := StreamPair{
+ StreamType: 0,
+ Elementary_PID: 0,
+ ES_Info_Length: 0,
+ }
+ tmp.StreamType = bs.Uint8(8)
+ bs.SkipBits(3)
+ tmp.Elementary_PID = bs.Uint16(13)
+ bs.SkipBits(4)
+ tmp.ES_Info_Length = bs.Uint16(12)
+ //TODO N loop descriptors
+ bs.SkipBits(int(tmp.ES_Info_Length) * 8)
+ pmt.Streams = append(pmt.Streams, tmp)
+ i += 5 + int(tmp.ES_Info_Length)
+ }
+ return nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/ssh/terminal/terminal.go
new file mode 100644
index 0000000000..a4d1919a9e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/ssh/terminal/terminal.go
@@ -0,0 +1,76 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Deprecated: this package moved to golang.org/x/term.
+package terminal
+
+import (
+ "io"
+
+ "golang.org/x/term"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes = term.EscapeCodes
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal = term.Terminal
+
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+ return term.NewTerminal(c, prompt)
+}
+
+// ErrPasteIndicator may be returned from ReadLine as the error, in addition
+// to valid line data. It indicates that bracketed paste mode is enabled and
+// that the returned line consists only of pasted data. Programs may wish to
+// interpret pasted data more literally than typed data.
+var ErrPasteIndicator = term.ErrPasteIndicator
+
+// State contains the state of a terminal.
+type State = term.State
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ return term.IsTerminal(fd)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ return term.ReadPassword(fd)
+}
+
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ return term.MakeRaw(fd)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+ return term.Restore(fd, oldState)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ return term.GetState(fd)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ return term.GetSize(fd)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm.s
new file mode 100644
index 0000000000..06449ebfa9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm.s
@@ -0,0 +1,8 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·use(SB),NOSPLIT,$0
+ RET
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_386.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_386.s
new file mode 100644
index 0000000000..bc5cab1f34
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_386.s
@@ -0,0 +1,30 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+//
+// System call support for 386, Plan 9
+//
+
+// Just jump to package syscall's implementation for all these functions.
+// The runtime may know about them.
+
+TEXT ·Syscall(SB),NOSPLIT,$0-32
+ JMP syscall·Syscall(SB)
+
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
+ JMP syscall·Syscall6(SB)
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
+ JMP syscall·RawSyscall(SB)
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
+ JMP syscall·RawSyscall6(SB)
+
+TEXT ·seek(SB),NOSPLIT,$0-36
+ JMP syscall·seek(SB)
+
+TEXT ·exit(SB),NOSPLIT,$4-4
+ JMP syscall·exit(SB)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
new file mode 100644
index 0000000000..d3448e6750
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
@@ -0,0 +1,30 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+//
+// System call support for amd64, Plan 9
+//
+
+// Just jump to package syscall's implementation for all these functions.
+// The runtime may know about them.
+
+TEXT ·Syscall(SB),NOSPLIT,$0-64
+ JMP syscall·Syscall(SB)
+
+TEXT ·Syscall6(SB),NOSPLIT,$0-88
+ JMP syscall·Syscall6(SB)
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0-56
+ JMP syscall·RawSyscall(SB)
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
+ JMP syscall·RawSyscall6(SB)
+
+TEXT ·seek(SB),NOSPLIT,$0-56
+ JMP syscall·seek(SB)
+
+TEXT ·exit(SB),NOSPLIT,$8-8
+ JMP syscall·exit(SB)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_arm.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_arm.s
new file mode 100644
index 0000000000..afb7c0a9b9
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/asm_plan9_arm.s
@@ -0,0 +1,25 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// System call support for plan9 on arm
+
+// Just jump to package syscall's implementation for all these functions.
+// The runtime may know about them.
+
+TEXT ·Syscall(SB),NOSPLIT,$0-32
+ JMP syscall·Syscall(SB)
+
+TEXT ·Syscall6(SB),NOSPLIT,$0-44
+ JMP syscall·Syscall6(SB)
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
+ JMP syscall·RawSyscall(SB)
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
+ JMP syscall·RawSyscall6(SB)
+
+TEXT ·seek(SB),NOSPLIT,$0-36
+ JMP syscall·exit(SB)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/const_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/const_plan9.go
new file mode 100644
index 0000000000..b4e85a3a9d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/const_plan9.go
@@ -0,0 +1,70 @@
+package plan9
+
+// Plan 9 Constants
+
+// Open modes
+const (
+ O_RDONLY = 0
+ O_WRONLY = 1
+ O_RDWR = 2
+ O_TRUNC = 16
+ O_CLOEXEC = 32
+ O_EXCL = 0x1000
+)
+
+// Rfork flags
+const (
+ RFNAMEG = 1 << 0
+ RFENVG = 1 << 1
+ RFFDG = 1 << 2
+ RFNOTEG = 1 << 3
+ RFPROC = 1 << 4
+ RFMEM = 1 << 5
+ RFNOWAIT = 1 << 6
+ RFCNAMEG = 1 << 10
+ RFCENVG = 1 << 11
+ RFCFDG = 1 << 12
+ RFREND = 1 << 13
+ RFNOMNT = 1 << 14
+)
+
+// Qid.Type bits
+const (
+ QTDIR = 0x80
+ QTAPPEND = 0x40
+ QTEXCL = 0x20
+ QTMOUNT = 0x10
+ QTAUTH = 0x08
+ QTTMP = 0x04
+ QTFILE = 0x00
+)
+
+// Dir.Mode bits
+const (
+ DMDIR = 0x80000000
+ DMAPPEND = 0x40000000
+ DMEXCL = 0x20000000
+ DMMOUNT = 0x10000000
+ DMAUTH = 0x08000000
+ DMTMP = 0x04000000
+ DMREAD = 0x4
+ DMWRITE = 0x2
+ DMEXEC = 0x1
+)
+
+const (
+ STATMAX = 65535
+ ERRMAX = 128
+ STATFIXLEN = 49
+)
+
+// Mount and bind flags
+const (
+ MREPL = 0x0000
+ MBEFORE = 0x0001
+ MAFTER = 0x0002
+ MORDER = 0x0003
+ MCREATE = 0x0004
+ MCACHE = 0x0010
+ MMASK = 0x0017
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/dir_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/dir_plan9.go
new file mode 100644
index 0000000000..0955e0c53e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/dir_plan9.go
@@ -0,0 +1,212 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Plan 9 directory marshalling. See intro(5).
+
+package plan9
+
+import "errors"
+
+var (
+ ErrShortStat = errors.New("stat buffer too short")
+ ErrBadStat = errors.New("malformed stat buffer")
+ ErrBadName = errors.New("bad character in file name")
+)
+
+// A Qid represents a 9P server's unique identification for a file.
+type Qid struct {
+ Path uint64 // the file server's unique identification for the file
+ Vers uint32 // version number for given Path
+ Type uint8 // the type of the file (plan9.QTDIR for example)
+}
+
+// A Dir contains the metadata for a file.
+type Dir struct {
+ // system-modified data
+ Type uint16 // server type
+ Dev uint32 // server subtype
+
+ // file data
+ Qid Qid // unique id from server
+ Mode uint32 // permissions
+ Atime uint32 // last read time
+ Mtime uint32 // last write time
+ Length int64 // file length
+ Name string // last element of path
+ Uid string // owner name
+ Gid string // group name
+ Muid string // last modifier name
+}
+
+var nullDir = Dir{
+ Type: ^uint16(0),
+ Dev: ^uint32(0),
+ Qid: Qid{
+ Path: ^uint64(0),
+ Vers: ^uint32(0),
+ Type: ^uint8(0),
+ },
+ Mode: ^uint32(0),
+ Atime: ^uint32(0),
+ Mtime: ^uint32(0),
+ Length: ^int64(0),
+}
+
+// Null assigns special "don't touch" values to members of d to
+// avoid modifying them during plan9.Wstat.
+func (d *Dir) Null() { *d = nullDir }
+
+// Marshal encodes a 9P stat message corresponding to d into b
+//
+// If there isn't enough space in b for a stat message, ErrShortStat is returned.
+func (d *Dir) Marshal(b []byte) (n int, err error) {
+ n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
+ if n > len(b) {
+ return n, ErrShortStat
+ }
+
+ for _, c := range d.Name {
+ if c == '/' {
+ return n, ErrBadName
+ }
+ }
+
+ b = pbit16(b, uint16(n)-2)
+ b = pbit16(b, d.Type)
+ b = pbit32(b, d.Dev)
+ b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
+ b = pbit32(b, d.Mode)
+ b = pbit32(b, d.Atime)
+ b = pbit32(b, d.Mtime)
+ b = pbit64(b, uint64(d.Length))
+ b = pstring(b, d.Name)
+ b = pstring(b, d.Uid)
+ b = pstring(b, d.Gid)
+ b = pstring(b, d.Muid)
+
+ return n, nil
+}
+
+// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
+//
+// If b is too small to hold a valid stat message, ErrShortStat is returned.
+//
+// If the stat message itself is invalid, ErrBadStat is returned.
+func UnmarshalDir(b []byte) (*Dir, error) {
+ if len(b) < STATFIXLEN {
+ return nil, ErrShortStat
+ }
+ size, buf := gbit16(b)
+ if len(b) != int(size)+2 {
+ return nil, ErrBadStat
+ }
+ b = buf
+
+ var d Dir
+ d.Type, b = gbit16(b)
+ d.Dev, b = gbit32(b)
+ d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
+ d.Mode, b = gbit32(b)
+ d.Atime, b = gbit32(b)
+ d.Mtime, b = gbit32(b)
+
+ n, b := gbit64(b)
+ d.Length = int64(n)
+
+ var ok bool
+ if d.Name, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Uid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Gid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Muid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+
+ return &d, nil
+}
+
+// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
+func pbit8(b []byte, v uint8) []byte {
+ b[0] = byte(v)
+ return b[1:]
+}
+
+// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit16(b []byte, v uint16) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ return b[2:]
+}
+
+// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit32(b []byte, v uint32) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ b[2] = byte(v >> 16)
+ b[3] = byte(v >> 24)
+ return b[4:]
+}
+
+// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit64(b []byte, v uint64) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ b[2] = byte(v >> 16)
+ b[3] = byte(v >> 24)
+ b[4] = byte(v >> 32)
+ b[5] = byte(v >> 40)
+ b[6] = byte(v >> 48)
+ b[7] = byte(v >> 56)
+ return b[8:]
+}
+
+// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
+// returning the remaining slice of b..
+func pstring(b []byte, s string) []byte {
+ b = pbit16(b, uint16(len(s)))
+ n := copy(b, s)
+ return b[n:]
+}
+
+// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
+func gbit8(b []byte) (uint8, []byte) {
+ return uint8(b[0]), b[1:]
+}
+
+// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit16(b []byte) (uint16, []byte) {
+ return uint16(b[0]) | uint16(b[1])<<8, b[2:]
+}
+
+// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit32(b []byte) (uint32, []byte) {
+ return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
+}
+
+// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit64(b []byte) (uint64, []byte) {
+ lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+ hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
+ return uint64(lo) | uint64(hi)<<32, b[8:]
+}
+
+// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
+// It returns the string with the remaining slice of b and a boolean. If the length is
+// greater than the number of bytes in b, the boolean will be false.
+func gstring(b []byte) (string, []byte, bool) {
+ n, b := gbit16(b)
+ if int(n) > len(b) {
+ return "", b, false
+ }
+ return string(b[:n]), b[n:], true
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/env_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/env_plan9.go
new file mode 100644
index 0000000000..8f1918004f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/env_plan9.go
@@ -0,0 +1,31 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Plan 9 environment variables.
+
+package plan9
+
+import (
+ "syscall"
+)
+
+func Getenv(key string) (value string, found bool) {
+ return syscall.Getenv(key)
+}
+
+func Setenv(key, value string) error {
+ return syscall.Setenv(key, value)
+}
+
+func Clearenv() {
+ syscall.Clearenv()
+}
+
+func Environ() []string {
+ return syscall.Environ()
+}
+
+func Unsetenv(key string) error {
+ return syscall.Unsetenv(key)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/errors_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/errors_plan9.go
new file mode 100644
index 0000000000..65fe74d3ef
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/errors_plan9.go
@@ -0,0 +1,50 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package plan9
+
+import "syscall"
+
+// Constants
+const (
+ // Invented values to support what package os expects.
+ O_CREAT = 0x02000
+ O_APPEND = 0x00400
+ O_NOCTTY = 0x00000
+ O_NONBLOCK = 0x00000
+ O_SYNC = 0x00000
+ O_ASYNC = 0x00000
+
+ S_IFMT = 0x1f000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+)
+
+// Errors
+var (
+ EINVAL = syscall.NewError("bad arg in system call")
+ ENOTDIR = syscall.NewError("not a directory")
+ EISDIR = syscall.NewError("file is a directory")
+ ENOENT = syscall.NewError("file does not exist")
+ EEXIST = syscall.NewError("file already exists")
+ EMFILE = syscall.NewError("no free file descriptors")
+ EIO = syscall.NewError("i/o error")
+ ENAMETOOLONG = syscall.NewError("file name too long")
+ EINTR = syscall.NewError("interrupted")
+ EPERM = syscall.NewError("permission denied")
+ EBUSY = syscall.NewError("no free devices")
+ ETIMEDOUT = syscall.NewError("connection timed out")
+ EPLAN9 = syscall.NewError("not supported by plan 9")
+
+ // The following errors do not correspond to any
+ // Plan 9 system messages. Invented to support
+ // what package os and others expect.
+ EACCES = syscall.NewError("access permission denied")
+ EAFNOSUPPORT = syscall.NewError("address family not supported by protocol")
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkall.sh b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkall.sh
new file mode 100644
index 0000000000..1650fbcc74
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkall.sh
@@ -0,0 +1,150 @@
+#!/usr/bin/env bash
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# The plan9 package provides access to the raw system call
+# interface of the underlying operating system. Porting Go to
+# a new architecture/operating system combination requires
+# some manual effort, though there are tools that automate
+# much of the process. The auto-generated files have names
+# beginning with z.
+#
+# This script runs or (given -n) prints suggested commands to generate z files
+# for the current system. Running those commands is not automatic.
+# This script is documentation more than anything else.
+#
+# * asm_${GOOS}_${GOARCH}.s
+#
+# This hand-written assembly file implements system call dispatch.
+# There are three entry points:
+#
+# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
+# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
+# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
+#
+# The first and second are the standard ones; they differ only in
+# how many arguments can be passed to the kernel.
+# The third is for low-level use by the ForkExec wrapper;
+# unlike the first two, it does not call into the scheduler to
+# let it know that a system call is running.
+#
+# * syscall_${GOOS}.go
+#
+# This hand-written Go file implements system calls that need
+# special handling and lists "//sys" comments giving prototypes
+# for ones that can be auto-generated. Mksyscall reads those
+# comments to generate the stubs.
+#
+# * syscall_${GOOS}_${GOARCH}.go
+#
+# Same as syscall_${GOOS}.go except that it contains code specific
+# to ${GOOS} on one particular architecture.
+#
+# * types_${GOOS}.c
+#
+# This hand-written C file includes standard C headers and then
+# creates typedef or enum names beginning with a dollar sign
+# (use of $ in variable names is a gcc extension). The hardest
+# part about preparing this file is figuring out which headers to
+# include and which symbols need to be #defined to get the
+# actual data structures that pass through to the kernel system calls.
+# Some C libraries present alternate versions for binary compatibility
+# and translate them on the way in and out of system calls, but
+# there is almost always a #define that can get the real ones.
+# See types_darwin.c and types_linux.c for examples.
+#
+# * zerror_${GOOS}_${GOARCH}.go
+#
+# This machine-generated file defines the system's error numbers,
+# error strings, and signal numbers. The generator is "mkerrors.sh".
+# Usually no arguments are needed, but mkerrors.sh will pass its
+# arguments on to godefs.
+#
+# * zsyscall_${GOOS}_${GOARCH}.go
+#
+# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
+#
+# * zsysnum_${GOOS}_${GOARCH}.go
+#
+# Generated by mksysnum_${GOOS}.
+#
+# * ztypes_${GOOS}_${GOARCH}.go
+#
+# Generated by godefs; see types_${GOOS}.c above.
+
+GOOSARCH="${GOOS}_${GOARCH}"
+
+# defaults
+mksyscall="go run mksyscall.go"
+mkerrors="./mkerrors.sh"
+zerrors="zerrors_$GOOSARCH.go"
+mksysctl=""
+zsysctl="zsysctl_$GOOSARCH.go"
+mksysnum=
+mktypes=
+run="sh"
+
+case "$1" in
+-syscalls)
+ for i in zsyscall*go
+ do
+ sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
+ rm _$i
+ done
+ exit 0
+ ;;
+-n)
+ run="cat"
+ shift
+esac
+
+case "$#" in
+0)
+ ;;
+*)
+ echo 'usage: mkall.sh [-n]' 1>&2
+ exit 2
+esac
+
+case "$GOOSARCH" in
+_* | *_ | _)
+ echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
+ exit 1
+ ;;
+plan9_386)
+ mkerrors=
+ mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,386"
+ mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
+ mktypes="XXX"
+ ;;
+plan9_amd64)
+ mkerrors=
+ mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,amd64"
+ mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
+ mktypes="XXX"
+ ;;
+plan9_arm)
+ mkerrors=
+ mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,arm"
+ mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
+ mktypes="XXX"
+ ;;
+*)
+ echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
+ exit 1
+ ;;
+esac
+
+(
+ if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
+ case "$GOOS" in
+ plan9)
+ syscall_goos="syscall_$GOOS.go"
+ if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos |gofmt >zsyscall_$GOOSARCH.go"; fi
+ ;;
+ esac
+ if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
+ if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
+ if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi
+) | $run
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkerrors.sh b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkerrors.sh
new file mode 100644
index 0000000000..85309c4a5b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mkerrors.sh
@@ -0,0 +1,246 @@
+#!/usr/bin/env bash
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Generate Go code listing errors and other #defined constant
+# values (ENAMETOOLONG etc.), by asking the preprocessor
+# about the definitions.
+
+unset LANG
+export LC_ALL=C
+export LC_CTYPE=C
+
+CC=${CC:-gcc}
+
+uname=$(uname)
+
+includes='
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+'
+
+ccflags="$@"
+
+# Write go tool cgo -godefs input.
+(
+ echo package plan9
+ echo
+ echo '/*'
+ indirect="includes_$(uname)"
+ echo "${!indirect} $includes"
+ echo '*/'
+ echo 'import "C"'
+ echo
+ echo 'const ('
+
+ # The gcc command line prints all the #defines
+ # it encounters while processing the input
+ echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
+ awk '
+ $1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
+
+ $2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
+ $2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
+ $2 ~ /^(SCM_SRCRT)$/ {next}
+ $2 ~ /^(MAP_FAILED)$/ {next}
+
+ $2 !~ /^ETH_/ &&
+ $2 !~ /^EPROC_/ &&
+ $2 !~ /^EQUIV_/ &&
+ $2 !~ /^EXPR_/ &&
+ $2 ~ /^E[A-Z0-9_]+$/ ||
+ $2 ~ /^B[0-9_]+$/ ||
+ $2 ~ /^V[A-Z0-9]+$/ ||
+ $2 ~ /^CS[A-Z0-9]/ ||
+ $2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
+ $2 ~ /^IGN/ ||
+ $2 ~ /^IX(ON|ANY|OFF)$/ ||
+ $2 ~ /^IN(LCR|PCK)$/ ||
+ $2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
+ $2 ~ /^C(LOCAL|READ)$/ ||
+ $2 == "BRKINT" ||
+ $2 == "HUPCL" ||
+ $2 == "PENDIN" ||
+ $2 == "TOSTOP" ||
+ $2 ~ /^PAR/ ||
+ $2 ~ /^SIG[^_]/ ||
+ $2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
+ $2 ~ /^IN_/ ||
+ $2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
+ $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
+ $2 == "ICMPV6_FILTER" ||
+ $2 == "SOMAXCONN" ||
+ $2 == "NAME_MAX" ||
+ $2 == "IFNAMSIZ" ||
+ $2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
+ $2 ~ /^SYSCTL_VERS/ ||
+ $2 ~ /^(MS|MNT)_/ ||
+ $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
+ $2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
+ $2 ~ /^LINUX_REBOOT_CMD_/ ||
+ $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
+ $2 !~ "NLA_TYPE_MASK" &&
+ $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
+ $2 ~ /^SIOC/ ||
+ $2 ~ /^TIOC/ ||
+ $2 !~ "RTF_BITS" &&
+ $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
+ $2 ~ /^BIOC/ ||
+ $2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
+ $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
+ $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
+ $2 ~ /^CLONE_[A-Z_]+/ ||
+ $2 !~ /^(BPF_TIMEVAL)$/ &&
+ $2 ~ /^(BPF|DLT)_/ ||
+ $2 !~ "WMESGLEN" &&
+ $2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)}
+ $2 ~ /^__WCOREFLAG$/ {next}
+ $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
+
+ {next}
+ ' | sort
+
+ echo ')'
+) >_const.go
+
+# Pull out the error names for later.
+errors=$(
+ echo '#include ' | $CC -x c - -E -dM $ccflags |
+ awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
+ sort
+)
+
+# Pull out the signal names for later.
+signals=$(
+ echo '#include ' | $CC -x c - -E -dM $ccflags |
+ awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
+ egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
+ sort
+)
+
+# Again, writing regexps to a file.
+echo '#include ' | $CC -x c - -E -dM $ccflags |
+ awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
+ sort >_error.grep
+echo '#include ' | $CC -x c - -E -dM $ccflags |
+ awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
+ egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
+ sort >_signal.grep
+
+echo '// mkerrors.sh' "$@"
+echo '// Code generated by the command above; DO NOT EDIT.'
+echo
+go tool cgo -godefs -- "$@" _const.go >_error.out
+cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
+echo
+echo '// Errors'
+echo 'const ('
+cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/'
+echo ')'
+
+echo
+echo '// Signals'
+echo 'const ('
+cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/'
+echo ')'
+
+# Run C program to print error and syscall strings.
+(
+ echo -E "
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
+
+int errors[] = {
+"
+ for i in $errors
+ do
+ echo -E ' '$i,
+ done
+
+ echo -E "
+};
+
+int signals[] = {
+"
+ for i in $signals
+ do
+ echo -E ' '$i,
+ done
+
+ # Use -E because on some systems bash builtin interprets \n itself.
+ echo -E '
+};
+
+static int
+intcmp(const void *a, const void *b)
+{
+ return *(int*)a - *(int*)b;
+}
+
+int
+main(void)
+{
+ int i, j, e;
+ char buf[1024], *p;
+
+ printf("\n\n// Error table\n");
+ printf("var errors = [...]string {\n");
+ qsort(errors, nelem(errors), sizeof errors[0], intcmp);
+ for(i=0; i 0 && errors[i-1] == e)
+ continue;
+ strcpy(buf, strerror(e));
+ // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
+ if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
+ buf[0] += a - A;
+ printf("\t%d: \"%s\",\n", e, buf);
+ }
+ printf("}\n\n");
+
+ printf("\n\n// Signal table\n");
+ printf("var signals = [...]string {\n");
+ qsort(signals, nelem(signals), sizeof signals[0], intcmp);
+ for(i=0; i 0 && signals[i-1] == e)
+ continue;
+ strcpy(buf, strsignal(e));
+ // lowercase first letter: Bad -> bad, but STREAM -> STREAM.
+ if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
+ buf[0] += a - A;
+ // cut trailing : number.
+ p = strrchr(buf, ":"[0]);
+ if(p)
+ *p = '\0';
+ printf("\t%d: \"%s\",\n", e, buf);
+ }
+ printf("}\n\n");
+
+ return 0;
+}
+
+'
+) >_errors.c
+
+$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh
new file mode 100644
index 0000000000..3c3ab05810
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+COMMAND="mksysnum_plan9.sh $@"
+
+cat <= 10 {
+ buf[i] = byte(val%10 + '0')
+ i--
+ val /= 10
+ }
+ buf[i] = byte(val + '0')
+ return string(buf[i:])
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall.go
new file mode 100644
index 0000000000..e7363a2f54
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall.go
@@ -0,0 +1,116 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+// Package plan9 contains an interface to the low-level operating system
+// primitives. OS details vary depending on the underlying system, and
+// by default, godoc will display the OS-specific documentation for the current
+// system. If you want godoc to display documentation for another
+// system, set $GOOS and $GOARCH to the desired system. For example, if
+// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
+// to freebsd and $GOARCH to arm.
+//
+// The primary use of this package is inside other packages that provide a more
+// portable interface to the system, such as "os", "time" and "net". Use
+// those packages rather than this one if you can.
+//
+// For details of the functions and data types in this package consult
+// the manuals for the appropriate operating system.
+//
+// These calls return err == nil to indicate success; otherwise
+// err represents an operating system error describing the failure and
+// holds a value of type syscall.ErrorString.
+package plan9 // import "golang.org/x/sys/plan9"
+
+import (
+ "bytes"
+ "strings"
+ "unsafe"
+
+ "golang.org/x/sys/internal/unsafeheader"
+)
+
+// ByteSliceFromString returns a NUL-terminated slice of bytes
+// containing the text of s. If s contains a NUL byte at any
+// location, it returns (nil, EINVAL).
+func ByteSliceFromString(s string) ([]byte, error) {
+ if strings.IndexByte(s, 0) != -1 {
+ return nil, EINVAL
+ }
+ a := make([]byte, len(s)+1)
+ copy(a, s)
+ return a, nil
+}
+
+// BytePtrFromString returns a pointer to a NUL-terminated array of
+// bytes containing the text of s. If s contains a NUL byte at any
+// location, it returns (nil, EINVAL).
+func BytePtrFromString(s string) (*byte, error) {
+ a, err := ByteSliceFromString(s)
+ if err != nil {
+ return nil, err
+ }
+ return &a[0], nil
+}
+
+// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
+// bytes after the NUL removed.
+func ByteSliceToString(s []byte) string {
+ if i := bytes.IndexByte(s, 0); i != -1 {
+ s = s[:i]
+ }
+ return string(s)
+}
+
+// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
+// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
+// at a zero byte; if the zero byte is not present, the program may crash.
+func BytePtrToString(p *byte) string {
+ if p == nil {
+ return ""
+ }
+ if *p == 0 {
+ return ""
+ }
+
+ // Find NUL terminator.
+ n := 0
+ for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
+ ptr = unsafe.Pointer(uintptr(ptr) + 1)
+ }
+
+ var s []byte
+ h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
+ h.Data = unsafe.Pointer(p)
+ h.Len = n
+ h.Cap = n
+
+ return string(s)
+}
+
+// Single-word zero for use when we need a valid pointer to 0 bytes.
+// See mksyscall.pl.
+var _zero uintptr
+
+func (ts *Timespec) Unix() (sec int64, nsec int64) {
+ return int64(ts.Sec), int64(ts.Nsec)
+}
+
+func (tv *Timeval) Unix() (sec int64, nsec int64) {
+ return int64(tv.Sec), int64(tv.Usec) * 1000
+}
+
+func (ts *Timespec) Nano() int64 {
+ return int64(ts.Sec)*1e9 + int64(ts.Nsec)
+}
+
+func (tv *Timeval) Nano() int64 {
+ return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
+}
+
+// use is a no-op, but the compiler cannot see that it is.
+// Calling use(p) ensures that p is kept live until that point.
+//go:noescape
+func use(p unsafe.Pointer)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall_plan9.go
new file mode 100644
index 0000000000..84e1471481
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/syscall_plan9.go
@@ -0,0 +1,349 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Plan 9 system calls.
+// This file is compiled as ordinary Go code,
+// but it is also input to mksyscall,
+// which parses the //sys lines and generates system call stubs.
+// Note that sometimes we use a lowercase //sys name and
+// wrap it in our own nicer implementation.
+
+package plan9
+
+import (
+ "bytes"
+ "syscall"
+ "unsafe"
+)
+
+// A Note is a string describing a process note.
+// It implements the os.Signal interface.
+type Note string
+
+func (n Note) Signal() {}
+
+func (n Note) String() string {
+ return string(n)
+}
+
+var (
+ Stdin = 0
+ Stdout = 1
+ Stderr = 2
+)
+
+// For testing: clients can set this flag to force
+// creation of IPv6 sockets to return EAFNOSUPPORT.
+var SocketDisableIPv6 bool
+
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
+func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
+func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+
+func atoi(b []byte) (n uint) {
+ n = 0
+ for i := 0; i < len(b); i++ {
+ n = n*10 + uint(b[i]-'0')
+ }
+ return
+}
+
+func cstring(s []byte) string {
+ i := bytes.IndexByte(s, 0)
+ if i == -1 {
+ i = len(s)
+ }
+ return string(s[:i])
+}
+
+func errstr() string {
+ var buf [ERRMAX]byte
+
+ RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
+
+ buf[len(buf)-1] = 0
+ return cstring(buf[:])
+}
+
+// Implemented in assembly to import from runtime.
+func exit(code int)
+
+func Exit(code int) { exit(code) }
+
+func readnum(path string) (uint, error) {
+ var b [12]byte
+
+ fd, e := Open(path, O_RDONLY)
+ if e != nil {
+ return 0, e
+ }
+ defer Close(fd)
+
+ n, e := Pread(fd, b[:], 0)
+
+ if e != nil {
+ return 0, e
+ }
+
+ m := 0
+ for ; m < n && b[m] == ' '; m++ {
+ }
+
+ return atoi(b[m : n-1]), nil
+}
+
+func Getpid() (pid int) {
+ n, _ := readnum("#c/pid")
+ return int(n)
+}
+
+func Getppid() (ppid int) {
+ n, _ := readnum("#c/ppid")
+ return int(n)
+}
+
+func Read(fd int, p []byte) (n int, err error) {
+ return Pread(fd, p, -1)
+}
+
+func Write(fd int, p []byte) (n int, err error) {
+ return Pwrite(fd, p, -1)
+}
+
+var ioSync int64
+
+//sys fd2path(fd int, buf []byte) (err error)
+func Fd2path(fd int) (path string, err error) {
+ var buf [512]byte
+
+ e := fd2path(fd, buf[:])
+ if e != nil {
+ return "", e
+ }
+ return cstring(buf[:]), nil
+}
+
+//sys pipe(p *[2]int32) (err error)
+func Pipe(p []int) (err error) {
+ if len(p) != 2 {
+ return syscall.ErrorString("bad arg in system call")
+ }
+ var pp [2]int32
+ err = pipe(&pp)
+ p[0] = int(pp[0])
+ p[1] = int(pp[1])
+ return
+}
+
+// Underlying system call writes to newoffset via pointer.
+// Implemented in assembly to avoid allocation.
+func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
+ newoffset, e := seek(0, fd, offset, whence)
+
+ if newoffset == -1 {
+ err = syscall.ErrorString(e)
+ }
+ return
+}
+
+func Mkdir(path string, mode uint32) (err error) {
+ fd, err := Create(path, O_RDONLY, DMDIR|mode)
+
+ if fd != -1 {
+ Close(fd)
+ }
+
+ return
+}
+
+type Waitmsg struct {
+ Pid int
+ Time [3]uint32
+ Msg string
+}
+
+func (w Waitmsg) Exited() bool { return true }
+func (w Waitmsg) Signaled() bool { return false }
+
+func (w Waitmsg) ExitStatus() int {
+ if len(w.Msg) == 0 {
+ // a normal exit returns no message
+ return 0
+ }
+ return 1
+}
+
+//sys await(s []byte) (n int, err error)
+func Await(w *Waitmsg) (err error) {
+ var buf [512]byte
+ var f [5][]byte
+
+ n, err := await(buf[:])
+
+ if err != nil || w == nil {
+ return
+ }
+
+ nf := 0
+ p := 0
+ for i := 0; i < n && nf < len(f)-1; i++ {
+ if buf[i] == ' ' {
+ f[nf] = buf[p:i]
+ p = i + 1
+ nf++
+ }
+ }
+ f[nf] = buf[p:]
+ nf++
+
+ if nf != len(f) {
+ return syscall.ErrorString("invalid wait message")
+ }
+ w.Pid = int(atoi(f[0]))
+ w.Time[0] = uint32(atoi(f[1]))
+ w.Time[1] = uint32(atoi(f[2]))
+ w.Time[2] = uint32(atoi(f[3]))
+ w.Msg = cstring(f[4])
+ if w.Msg == "''" {
+ // await() returns '' for no error
+ w.Msg = ""
+ }
+ return
+}
+
+func Unmount(name, old string) (err error) {
+ fixwd()
+ oldp, err := BytePtrFromString(old)
+ if err != nil {
+ return err
+ }
+ oldptr := uintptr(unsafe.Pointer(oldp))
+
+ var r0 uintptr
+ var e syscall.ErrorString
+
+ // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
+ if name == "" {
+ r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
+ } else {
+ namep, err := BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+ r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
+ }
+
+ if int32(r0) == -1 {
+ err = e
+ }
+ return
+}
+
+func Fchdir(fd int) (err error) {
+ path, err := Fd2path(fd)
+
+ if err != nil {
+ return
+ }
+
+ return Chdir(path)
+}
+
+type Timespec struct {
+ Sec int32
+ Nsec int32
+}
+
+type Timeval struct {
+ Sec int32
+ Usec int32
+}
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ nsec += 999 // round up to microsecond
+ tv.Usec = int32(nsec % 1e9 / 1e3)
+ tv.Sec = int32(nsec / 1e9)
+ return
+}
+
+func nsec() int64 {
+ var scratch int64
+
+ r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
+ // TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
+ if r0 == 0 {
+ return scratch
+ }
+ return int64(r0)
+}
+
+func Gettimeofday(tv *Timeval) error {
+ nsec := nsec()
+ *tv = NsecToTimeval(nsec)
+ return nil
+}
+
+func Getpagesize() int { return 0x1000 }
+
+func Getegid() (egid int) { return -1 }
+func Geteuid() (euid int) { return -1 }
+func Getgid() (gid int) { return -1 }
+func Getuid() (uid int) { return -1 }
+
+func Getgroups() (gids []int, err error) {
+ return make([]int, 0), nil
+}
+
+//sys open(path string, mode int) (fd int, err error)
+func Open(path string, mode int) (fd int, err error) {
+ fixwd()
+ return open(path, mode)
+}
+
+//sys create(path string, mode int, perm uint32) (fd int, err error)
+func Create(path string, mode int, perm uint32) (fd int, err error) {
+ fixwd()
+ return create(path, mode, perm)
+}
+
+//sys remove(path string) (err error)
+func Remove(path string) error {
+ fixwd()
+ return remove(path)
+}
+
+//sys stat(path string, edir []byte) (n int, err error)
+func Stat(path string, edir []byte) (n int, err error) {
+ fixwd()
+ return stat(path, edir)
+}
+
+//sys bind(name string, old string, flag int) (err error)
+func Bind(name string, old string, flag int) (err error) {
+ fixwd()
+ return bind(name, old, flag)
+}
+
+//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
+func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ fixwd()
+ return mount(fd, afd, old, flag, aname)
+}
+
+//sys wstat(path string, edir []byte) (err error)
+func Wstat(path string, edir []byte) (err error) {
+ fixwd()
+ return wstat(path, edir)
+}
+
+//sys chdir(path string) (err error)
+//sys Dup(oldfd int, newfd int) (fd int, err error)
+//sys Pread(fd int, p []byte, offset int64) (n int, err error)
+//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
+//sys Close(fd int) (err error)
+//sys Fstat(fd int, edir []byte) (n int, err error)
+//sys Fwstat(fd int, edir []byte) (err error)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
new file mode 100644
index 0000000000..6819bc2094
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
@@ -0,0 +1,284 @@
+// go run mksyscall.go -l32 -plan9 -tags plan9,386 syscall_plan9.go
+// Code generated by the command above; see README.md. DO NOT EDIT.
+
+// +build plan9,386
+
+package plan9
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]int32) (err error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func open(path string, mode int) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func create(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func remove(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func stat(path string, edir []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(name string, old string, flag int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wstat(path string, edir []byte) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
new file mode 100644
index 0000000000..418abbbfc7
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
@@ -0,0 +1,284 @@
+// go run mksyscall.go -l32 -plan9 -tags plan9,amd64 syscall_plan9.go
+// Code generated by the command above; see README.md. DO NOT EDIT.
+
+// +build plan9,amd64
+
+package plan9
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]int32) (err error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func open(path string, mode int) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func create(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func remove(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func stat(path string, edir []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(name string, old string, flag int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wstat(path string, edir []byte) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go
new file mode 100644
index 0000000000..3e8a1a58ca
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go
@@ -0,0 +1,284 @@
+// go run mksyscall.go -l32 -plan9 -tags plan9,arm syscall_plan9.go
+// Code generated by the command above; see README.md. DO NOT EDIT.
+
+// +build plan9,arm
+
+package plan9
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]int32) (err error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func open(path string, mode int) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func create(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func remove(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func stat(path string, edir []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(name string, old string, flag int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wstat(path string, edir []byte) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
new file mode 100644
index 0000000000..22e8abd43d
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
@@ -0,0 +1,49 @@
+// mksysnum_plan9.sh /opt/plan9/sys/src/libc/9syscall/sys.h
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package plan9
+
+const (
+ SYS_SYSR1 = 0
+ SYS_BIND = 2
+ SYS_CHDIR = 3
+ SYS_CLOSE = 4
+ SYS_DUP = 5
+ SYS_ALARM = 6
+ SYS_EXEC = 7
+ SYS_EXITS = 8
+ SYS_FAUTH = 10
+ SYS_SEGBRK = 12
+ SYS_OPEN = 14
+ SYS_OSEEK = 16
+ SYS_SLEEP = 17
+ SYS_RFORK = 19
+ SYS_PIPE = 21
+ SYS_CREATE = 22
+ SYS_FD2PATH = 23
+ SYS_BRK_ = 24
+ SYS_REMOVE = 25
+ SYS_NOTIFY = 28
+ SYS_NOTED = 29
+ SYS_SEGATTACH = 30
+ SYS_SEGDETACH = 31
+ SYS_SEGFREE = 32
+ SYS_SEGFLUSH = 33
+ SYS_RENDEZVOUS = 34
+ SYS_UNMOUNT = 35
+ SYS_SEMACQUIRE = 37
+ SYS_SEMRELEASE = 38
+ SYS_SEEK = 39
+ SYS_FVERSION = 40
+ SYS_ERRSTR = 41
+ SYS_STAT = 42
+ SYS_FSTAT = 43
+ SYS_WSTAT = 44
+ SYS_FWSTAT = 45
+ SYS_MOUNT = 46
+ SYS_AWAIT = 47
+ SYS_PREAD = 50
+ SYS_PWRITE = 51
+ SYS_TSEMACQUIRE = 52
+ SYS_NSEC = 53
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.1_13.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.1_13.go
index dc0befee37..ee852f1abc 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.1_13.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.1_13.go
@@ -26,7 +26,6 @@ func fdopendir(fd int) (dir uintptr, err error) {
func libc_fdopendir_trampoline()
-//go:linkname libc_fdopendir libc_fdopendir
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go
index 84a9e5277a..28be1306ec 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -641,6 +641,36 @@ func (sa *SockaddrCAN) sockaddr() (unsafe.Pointer, _Socklen, error) {
return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil
}
+// SockaddrCANJ1939 implements the Sockaddr interface for AF_CAN using J1939
+// protocol (https://en.wikipedia.org/wiki/SAE_J1939). For more information
+// on the purposes of the fields, check the official linux kernel documentation
+// available here: https://www.kernel.org/doc/Documentation/networking/j1939.rst
+type SockaddrCANJ1939 struct {
+ Ifindex int
+ Name uint64
+ PGN uint32
+ Addr uint8
+ raw RawSockaddrCAN
+}
+
+func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) {
+ if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff {
+ return nil, 0, EINVAL
+ }
+ sa.raw.Family = AF_CAN
+ sa.raw.Ifindex = int32(sa.Ifindex)
+ n := (*[8]byte)(unsafe.Pointer(&sa.Name))
+ for i := 0; i < 8; i++ {
+ sa.raw.Addr[i] = n[i]
+ }
+ p := (*[4]byte)(unsafe.Pointer(&sa.PGN))
+ for i := 0; i < 4; i++ {
+ sa.raw.Addr[i+8] = p[i]
+ }
+ sa.raw.Addr[12] = sa.Addr
+ return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil
+}
+
// SockaddrALG implements the Sockaddr interface for AF_ALG type sockets.
// SockaddrALG enables userspace access to the Linux kernel's cryptography
// subsystem. The Type and Name fields specify which type of hash or cipher
@@ -952,6 +982,10 @@ func (sa *SockaddrIUCV) sockaddr() (unsafe.Pointer, _Socklen, error) {
return unsafe.Pointer(&sa.raw), SizeofSockaddrIUCV, nil
}
+var socketProtocol = func(fd int) (int, error) {
+ return GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
+}
+
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family {
case AF_NETLINK:
@@ -1002,7 +1036,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
return sa, nil
case AF_INET:
- proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
+ proto, err := socketProtocol(fd)
if err != nil {
return nil, err
}
@@ -1028,7 +1062,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
}
case AF_INET6:
- proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
+ proto, err := socketProtocol(fd)
if err != nil {
return nil, err
}
@@ -1063,7 +1097,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
}
return sa, nil
case AF_BLUETOOTH:
- proto, err := GetsockoptInt(fd, SOL_SOCKET, SO_PROTOCOL)
+ proto, err := socketProtocol(fd)
if err != nil {
return nil, err
}
@@ -1150,20 +1184,43 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
return sa, nil
case AF_CAN:
- pp := (*RawSockaddrCAN)(unsafe.Pointer(rsa))
- sa := &SockaddrCAN{
- Ifindex: int(pp.Ifindex),
- }
- rx := (*[4]byte)(unsafe.Pointer(&sa.RxID))
- for i := 0; i < 4; i++ {
- rx[i] = pp.Addr[i]
- }
- tx := (*[4]byte)(unsafe.Pointer(&sa.TxID))
- for i := 0; i < 4; i++ {
- tx[i] = pp.Addr[i+4]
+ proto, err := socketProtocol(fd)
+ if err != nil {
+ return nil, err
}
- return sa, nil
+ pp := (*RawSockaddrCAN)(unsafe.Pointer(rsa))
+
+ switch proto {
+ case CAN_J1939:
+ sa := &SockaddrCANJ1939{
+ Ifindex: int(pp.Ifindex),
+ }
+ name := (*[8]byte)(unsafe.Pointer(&sa.Name))
+ for i := 0; i < 8; i++ {
+ name[i] = pp.Addr[i]
+ }
+ pgn := (*[4]byte)(unsafe.Pointer(&sa.PGN))
+ for i := 0; i < 4; i++ {
+ pgn[i] = pp.Addr[i+8]
+ }
+ addr := (*[1]byte)(unsafe.Pointer(&sa.Addr))
+ addr[0] = pp.Addr[12]
+ return sa, nil
+ default:
+ sa := &SockaddrCAN{
+ Ifindex: int(pp.Ifindex),
+ }
+ rx := (*[4]byte)(unsafe.Pointer(&sa.RxID))
+ for i := 0; i < 4; i++ {
+ rx[i] = pp.Addr[i]
+ }
+ tx := (*[4]byte)(unsafe.Pointer(&sa.TxID))
+ for i := 0; i < 4; i++ {
+ tx[i] = pp.Addr[i+4]
+ }
+ return sa, nil
+ }
}
return nil, EAFNOSUPPORT
}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/timestruct.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/timestruct.go
index 4a672f5694..103604299e 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/timestruct.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/timestruct.go
@@ -8,12 +8,10 @@ package unix
import "time"
-// TimespecToNsec converts a Timespec value into a number of
-// nanoseconds since the Unix epoch.
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+// TimespecToNSec returns the time stored in ts as nanoseconds.
+func TimespecToNsec(ts Timespec) int64 { return ts.Nano() }
-// NsecToTimespec takes a number of nanoseconds since the Unix epoch
-// and returns the corresponding Timespec value.
+// NsecToTimespec converts a number of nanoseconds into a Timespec.
func NsecToTimespec(nsec int64) Timespec {
sec := nsec / 1e9
nsec = nsec % 1e9
@@ -42,12 +40,10 @@ func TimeToTimespec(t time.Time) (Timespec, error) {
return ts, nil
}
-// TimevalToNsec converts a Timeval value into a number of nanoseconds
-// since the Unix epoch.
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+// TimevalToNsec returns the time stored in tv as nanoseconds.
+func TimevalToNsec(tv Timeval) int64 { return tv.Nano() }
-// NsecToTimeval takes a number of nanoseconds since the Unix epoch
-// and returns the corresponding Timeval value.
+// NsecToTimeval converts a number of nanoseconds into a Timeval.
func NsecToTimeval(nsec int64) Timeval {
nsec += 999 // round up to microsecond
usec := nsec % 1e9 / 1e3
@@ -59,24 +55,22 @@ func NsecToTimeval(nsec int64) Timeval {
return setTimeval(sec, usec)
}
-// Unix returns ts as the number of seconds and nanoseconds elapsed since the
-// Unix epoch.
+// Unix returns the time stored in ts as seconds plus nanoseconds.
func (ts *Timespec) Unix() (sec int64, nsec int64) {
return int64(ts.Sec), int64(ts.Nsec)
}
-// Unix returns tv as the number of seconds and nanoseconds elapsed since the
-// Unix epoch.
+// Unix returns the time stored in tv as seconds plus nanoseconds.
func (tv *Timeval) Unix() (sec int64, nsec int64) {
return int64(tv.Sec), int64(tv.Usec) * 1000
}
-// Nano returns ts as the number of nanoseconds elapsed since the Unix epoch.
+// Nano returns the time stored in ts as nanoseconds.
func (ts *Timespec) Nano() int64 {
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
}
-// Nano returns tv as the number of nanoseconds elapsed since the Unix epoch.
+// Nano returns the time stored in tv as nanoseconds.
func (tv *Timeval) Nano() int64 {
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.go
index e263fbdb8b..c8c142c59a 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.1_13.go
@@ -24,7 +24,6 @@ func closedir(dir uintptr) (err error) {
func libc_closedir_trampoline()
-//go:linkname libc_closedir libc_closedir
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -37,5 +36,4 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
func libc_readdir_r_trampoline()
-//go:linkname libc_readdir_r libc_readdir_r
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go
index 6eb4579832..7f0f117d32 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_386.go
@@ -25,7 +25,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
func libc_getgroups_trampoline()
-//go:linkname libc_getgroups libc_getgroups
//go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -40,7 +39,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) {
func libc_setgroups_trampoline()
-//go:linkname libc_setgroups libc_setgroups
//go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -56,7 +54,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
func libc_wait4_trampoline()
-//go:linkname libc_wait4 libc_wait4
//go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -72,7 +69,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
func libc_accept_trampoline()
-//go:linkname libc_accept libc_accept
//go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -87,7 +83,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_bind_trampoline()
-//go:linkname libc_bind libc_bind
//go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -102,7 +97,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_connect_trampoline()
-//go:linkname libc_connect libc_connect
//go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -118,7 +112,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
func libc_socket_trampoline()
-//go:linkname libc_socket libc_socket
//go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -133,7 +126,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen
func libc_getsockopt_trampoline()
-//go:linkname libc_getsockopt libc_getsockopt
//go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -148,7 +140,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr)
func libc_setsockopt_trampoline()
-//go:linkname libc_setsockopt libc_setsockopt
//go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -163,7 +154,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getpeername_trampoline()
-//go:linkname libc_getpeername libc_getpeername
//go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -178,7 +168,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getsockname_trampoline()
-//go:linkname libc_getsockname libc_getsockname
//go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -193,7 +182,6 @@ func Shutdown(s int, how int) (err error) {
func libc_shutdown_trampoline()
-//go:linkname libc_shutdown libc_shutdown
//go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -208,7 +196,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
func libc_socketpair_trampoline()
-//go:linkname libc_socketpair libc_socketpair
//go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -230,7 +217,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
func libc_recvfrom_trampoline()
-//go:linkname libc_recvfrom libc_recvfrom
//go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -251,7 +237,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
func libc_sendto_trampoline()
-//go:linkname libc_sendto libc_sendto
//go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -267,7 +252,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_recvmsg_trampoline()
-//go:linkname libc_recvmsg libc_recvmsg
//go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -283,7 +267,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_sendmsg_trampoline()
-//go:linkname libc_sendmsg libc_sendmsg
//go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -299,7 +282,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne
func libc_kevent_trampoline()
-//go:linkname libc_kevent libc_kevent
//go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -319,7 +301,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) {
func libc_utimes_trampoline()
-//go:linkname libc_utimes libc_utimes
//go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -334,7 +315,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) {
func libc_futimes_trampoline()
-//go:linkname libc_futimes libc_futimes
//go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -350,7 +330,6 @@ func poll(fds *PollFd, nfds int, timeout int) (n int, err error) {
func libc_poll_trampoline()
-//go:linkname libc_poll libc_poll
//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -371,7 +350,6 @@ func Madvise(b []byte, behav int) (err error) {
func libc_madvise_trampoline()
-//go:linkname libc_madvise libc_madvise
//go:cgo_import_dynamic libc_madvise madvise "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -392,7 +370,6 @@ func Mlock(b []byte) (err error) {
func libc_mlock_trampoline()
-//go:linkname libc_mlock libc_mlock
//go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -407,7 +384,6 @@ func Mlockall(flags int) (err error) {
func libc_mlockall_trampoline()
-//go:linkname libc_mlockall libc_mlockall
//go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -428,7 +404,6 @@ func Mprotect(b []byte, prot int) (err error) {
func libc_mprotect_trampoline()
-//go:linkname libc_mprotect libc_mprotect
//go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -449,7 +424,6 @@ func Msync(b []byte, flags int) (err error) {
func libc_msync_trampoline()
-//go:linkname libc_msync libc_msync
//go:cgo_import_dynamic libc_msync msync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -470,7 +444,6 @@ func Munlock(b []byte) (err error) {
func libc_munlock_trampoline()
-//go:linkname libc_munlock libc_munlock
//go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -485,7 +458,6 @@ func Munlockall() (err error) {
func libc_munlockall_trampoline()
-//go:linkname libc_munlockall libc_munlockall
//go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -502,7 +474,6 @@ func pipe() (r int, w int, err error) {
func libc_pipe_trampoline()
-//go:linkname libc_pipe libc_pipe
//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -528,7 +499,6 @@ func getxattr(path string, attr string, dest *byte, size int, position uint32, o
func libc_getxattr_trampoline()
-//go:linkname libc_getxattr libc_getxattr
//go:cgo_import_dynamic libc_getxattr getxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -549,7 +519,6 @@ func fgetxattr(fd int, attr string, dest *byte, size int, position uint32, optio
func libc_fgetxattr_trampoline()
-//go:linkname libc_fgetxattr libc_fgetxattr
//go:cgo_import_dynamic libc_fgetxattr fgetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -574,7 +543,6 @@ func setxattr(path string, attr string, data *byte, size int, position uint32, o
func libc_setxattr_trampoline()
-//go:linkname libc_setxattr libc_setxattr
//go:cgo_import_dynamic libc_setxattr setxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -594,7 +562,6 @@ func fsetxattr(fd int, attr string, data *byte, size int, position uint32, optio
func libc_fsetxattr_trampoline()
-//go:linkname libc_fsetxattr libc_fsetxattr
//go:cgo_import_dynamic libc_fsetxattr fsetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -619,7 +586,6 @@ func removexattr(path string, attr string, options int) (err error) {
func libc_removexattr_trampoline()
-//go:linkname libc_removexattr libc_removexattr
//go:cgo_import_dynamic libc_removexattr removexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -639,7 +605,6 @@ func fremovexattr(fd int, attr string, options int) (err error) {
func libc_fremovexattr_trampoline()
-//go:linkname libc_fremovexattr libc_fremovexattr
//go:cgo_import_dynamic libc_fremovexattr fremovexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -660,7 +625,6 @@ func listxattr(path string, dest *byte, size int, options int) (sz int, err erro
func libc_listxattr_trampoline()
-//go:linkname libc_listxattr libc_listxattr
//go:cgo_import_dynamic libc_listxattr listxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -676,7 +640,6 @@ func flistxattr(fd int, dest *byte, size int, options int) (sz int, err error) {
func libc_flistxattr_trampoline()
-//go:linkname libc_flistxattr libc_flistxattr
//go:cgo_import_dynamic libc_flistxattr flistxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -691,7 +654,6 @@ func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintp
func libc_setattrlist_trampoline()
-//go:linkname libc_setattrlist libc_setattrlist
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -707,7 +669,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
func libc_fcntl_trampoline()
-//go:linkname libc_fcntl libc_fcntl
//go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -722,7 +683,6 @@ func kill(pid int, signum int, posix int) (err error) {
func libc_kill_trampoline()
-//go:linkname libc_kill libc_kill
//go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -737,7 +697,6 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
func libc_ioctl_trampoline()
-//go:linkname libc_ioctl libc_ioctl
//go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -758,7 +717,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
func libc_sysctl_trampoline()
-//go:linkname libc_sysctl libc_sysctl
//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -773,7 +731,6 @@ func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer
func libc_sendfile_trampoline()
-//go:linkname libc_sendfile libc_sendfile
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -793,7 +750,6 @@ func Access(path string, mode uint32) (err error) {
func libc_access_trampoline()
-//go:linkname libc_access libc_access
//go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -808,7 +764,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
func libc_adjtime_trampoline()
-//go:linkname libc_adjtime libc_adjtime
//go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -828,7 +783,6 @@ func Chdir(path string) (err error) {
func libc_chdir_trampoline()
-//go:linkname libc_chdir libc_chdir
//go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -848,7 +802,6 @@ func Chflags(path string, flags int) (err error) {
func libc_chflags_trampoline()
-//go:linkname libc_chflags libc_chflags
//go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -868,7 +821,6 @@ func Chmod(path string, mode uint32) (err error) {
func libc_chmod_trampoline()
-//go:linkname libc_chmod libc_chmod
//go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -888,7 +840,6 @@ func Chown(path string, uid int, gid int) (err error) {
func libc_chown_trampoline()
-//go:linkname libc_chown libc_chown
//go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -908,7 +859,6 @@ func Chroot(path string) (err error) {
func libc_chroot_trampoline()
-//go:linkname libc_chroot libc_chroot
//go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -923,7 +873,6 @@ func ClockGettime(clockid int32, time *Timespec) (err error) {
func libc_clock_gettime_trampoline()
-//go:linkname libc_clock_gettime libc_clock_gettime
//go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -938,7 +887,6 @@ func Close(fd int) (err error) {
func libc_close_trampoline()
-//go:linkname libc_close libc_close
//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -963,7 +911,6 @@ func Clonefile(src string, dst string, flags int) (err error) {
func libc_clonefile_trampoline()
-//go:linkname libc_clonefile libc_clonefile
//go:cgo_import_dynamic libc_clonefile clonefile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -988,7 +935,6 @@ func Clonefileat(srcDirfd int, src string, dstDirfd int, dst string, flags int)
func libc_clonefileat_trampoline()
-//go:linkname libc_clonefileat libc_clonefileat
//go:cgo_import_dynamic libc_clonefileat clonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1004,7 +950,6 @@ func Dup(fd int) (nfd int, err error) {
func libc_dup_trampoline()
-//go:linkname libc_dup libc_dup
//go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1019,7 +964,6 @@ func Dup2(from int, to int) (err error) {
func libc_dup2_trampoline()
-//go:linkname libc_dup2 libc_dup2
//go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1044,7 +988,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) {
func libc_exchangedata_trampoline()
-//go:linkname libc_exchangedata libc_exchangedata
//go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1056,7 +999,6 @@ func Exit(code int) {
func libc_exit_trampoline()
-//go:linkname libc_exit libc_exit
//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1076,7 +1018,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_faccessat_trampoline()
-//go:linkname libc_faccessat libc_faccessat
//go:cgo_import_dynamic libc_faccessat faccessat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1091,7 +1032,6 @@ func Fchdir(fd int) (err error) {
func libc_fchdir_trampoline()
-//go:linkname libc_fchdir libc_fchdir
//go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1106,7 +1046,6 @@ func Fchflags(fd int, flags int) (err error) {
func libc_fchflags_trampoline()
-//go:linkname libc_fchflags libc_fchflags
//go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1121,7 +1060,6 @@ func Fchmod(fd int, mode uint32) (err error) {
func libc_fchmod_trampoline()
-//go:linkname libc_fchmod libc_fchmod
//go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1141,7 +1079,6 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_fchmodat_trampoline()
-//go:linkname libc_fchmodat libc_fchmodat
//go:cgo_import_dynamic libc_fchmodat fchmodat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1156,7 +1093,6 @@ func Fchown(fd int, uid int, gid int) (err error) {
func libc_fchown_trampoline()
-//go:linkname libc_fchown libc_fchown
//go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1176,7 +1112,6 @@ func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
func libc_fchownat_trampoline()
-//go:linkname libc_fchownat libc_fchownat
//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1196,7 +1131,6 @@ func Fclonefileat(srcDirfd int, dstDirfd int, dst string, flags int) (err error)
func libc_fclonefileat_trampoline()
-//go:linkname libc_fclonefileat libc_fclonefileat
//go:cgo_import_dynamic libc_fclonefileat fclonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1211,7 +1145,6 @@ func Flock(fd int, how int) (err error) {
func libc_flock_trampoline()
-//go:linkname libc_flock libc_flock
//go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1227,7 +1160,6 @@ func Fpathconf(fd int, name int) (val int, err error) {
func libc_fpathconf_trampoline()
-//go:linkname libc_fpathconf libc_fpathconf
//go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1242,7 +1174,6 @@ func Fsync(fd int) (err error) {
func libc_fsync_trampoline()
-//go:linkname libc_fsync libc_fsync
//go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1257,7 +1188,6 @@ func Ftruncate(fd int, length int64) (err error) {
func libc_ftruncate_trampoline()
-//go:linkname libc_ftruncate libc_ftruncate
//go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1279,7 +1209,6 @@ func Getcwd(buf []byte) (n int, err error) {
func libc_getcwd_trampoline()
-//go:linkname libc_getcwd libc_getcwd
//go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1292,7 +1221,6 @@ func Getdtablesize() (size int) {
func libc_getdtablesize_trampoline()
-//go:linkname libc_getdtablesize libc_getdtablesize
//go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1305,7 +1233,6 @@ func Getegid() (egid int) {
func libc_getegid_trampoline()
-//go:linkname libc_getegid libc_getegid
//go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1318,7 +1245,6 @@ func Geteuid() (uid int) {
func libc_geteuid_trampoline()
-//go:linkname libc_geteuid libc_geteuid
//go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1331,7 +1257,6 @@ func Getgid() (gid int) {
func libc_getgid_trampoline()
-//go:linkname libc_getgid libc_getgid
//go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1347,7 +1272,6 @@ func Getpgid(pid int) (pgid int, err error) {
func libc_getpgid_trampoline()
-//go:linkname libc_getpgid libc_getpgid
//go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1360,7 +1284,6 @@ func Getpgrp() (pgrp int) {
func libc_getpgrp_trampoline()
-//go:linkname libc_getpgrp libc_getpgrp
//go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1373,7 +1296,6 @@ func Getpid() (pid int) {
func libc_getpid_trampoline()
-//go:linkname libc_getpid libc_getpid
//go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1386,7 +1308,6 @@ func Getppid() (ppid int) {
func libc_getppid_trampoline()
-//go:linkname libc_getppid libc_getppid
//go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1402,7 +1323,6 @@ func Getpriority(which int, who int) (prio int, err error) {
func libc_getpriority_trampoline()
-//go:linkname libc_getpriority libc_getpriority
//go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1417,7 +1337,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) {
func libc_getrlimit_trampoline()
-//go:linkname libc_getrlimit libc_getrlimit
//go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1432,7 +1351,6 @@ func Getrusage(who int, rusage *Rusage) (err error) {
func libc_getrusage_trampoline()
-//go:linkname libc_getrusage libc_getrusage
//go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1448,7 +1366,6 @@ func Getsid(pid int) (sid int, err error) {
func libc_getsid_trampoline()
-//go:linkname libc_getsid libc_getsid
//go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1463,7 +1380,6 @@ func Gettimeofday(tp *Timeval) (err error) {
func libc_gettimeofday_trampoline()
-//go:linkname libc_gettimeofday libc_gettimeofday
//go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1476,7 +1392,6 @@ func Getuid() (uid int) {
func libc_getuid_trampoline()
-//go:linkname libc_getuid libc_getuid
//go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1489,7 +1404,6 @@ func Issetugid() (tainted bool) {
func libc_issetugid_trampoline()
-//go:linkname libc_issetugid libc_issetugid
//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1505,7 +1419,6 @@ func Kqueue() (fd int, err error) {
func libc_kqueue_trampoline()
-//go:linkname libc_kqueue libc_kqueue
//go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1525,7 +1438,6 @@ func Lchown(path string, uid int, gid int) (err error) {
func libc_lchown_trampoline()
-//go:linkname libc_lchown libc_lchown
//go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1550,7 +1462,6 @@ func Link(path string, link string) (err error) {
func libc_link_trampoline()
-//go:linkname libc_link libc_link
//go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1575,7 +1486,6 @@ func Linkat(pathfd int, path string, linkfd int, link string, flags int) (err er
func libc_linkat_trampoline()
-//go:linkname libc_linkat libc_linkat
//go:cgo_import_dynamic libc_linkat linkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1590,7 +1500,6 @@ func Listen(s int, backlog int) (err error) {
func libc_listen_trampoline()
-//go:linkname libc_listen libc_listen
//go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1610,7 +1519,6 @@ func Mkdir(path string, mode uint32) (err error) {
func libc_mkdir_trampoline()
-//go:linkname libc_mkdir libc_mkdir
//go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1630,7 +1538,6 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
func libc_mkdirat_trampoline()
-//go:linkname libc_mkdirat libc_mkdirat
//go:cgo_import_dynamic libc_mkdirat mkdirat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1650,7 +1557,6 @@ func Mkfifo(path string, mode uint32) (err error) {
func libc_mkfifo_trampoline()
-//go:linkname libc_mkfifo libc_mkfifo
//go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1670,7 +1576,6 @@ func Mknod(path string, mode uint32, dev int) (err error) {
func libc_mknod_trampoline()
-//go:linkname libc_mknod libc_mknod
//go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1691,7 +1596,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
func libc_open_trampoline()
-//go:linkname libc_open libc_open
//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1712,7 +1616,6 @@ func Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
func libc_openat_trampoline()
-//go:linkname libc_openat libc_openat
//go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1733,7 +1636,6 @@ func Pathconf(path string, name int) (val int, err error) {
func libc_pathconf_trampoline()
-//go:linkname libc_pathconf libc_pathconf
//go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1755,7 +1657,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
func libc_pread_trampoline()
-//go:linkname libc_pread libc_pread
//go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1777,7 +1678,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
func libc_pwrite_trampoline()
-//go:linkname libc_pwrite libc_pwrite
//go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1799,7 +1699,6 @@ func read(fd int, p []byte) (n int, err error) {
func libc_read_trampoline()
-//go:linkname libc_read libc_read
//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1826,7 +1725,6 @@ func Readlink(path string, buf []byte) (n int, err error) {
func libc_readlink_trampoline()
-//go:linkname libc_readlink libc_readlink
//go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1853,7 +1751,6 @@ func Readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
func libc_readlinkat_trampoline()
-//go:linkname libc_readlinkat libc_readlinkat
//go:cgo_import_dynamic libc_readlinkat readlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1878,7 +1775,6 @@ func Rename(from string, to string) (err error) {
func libc_rename_trampoline()
-//go:linkname libc_rename libc_rename
//go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1903,7 +1799,6 @@ func Renameat(fromfd int, from string, tofd int, to string) (err error) {
func libc_renameat_trampoline()
-//go:linkname libc_renameat libc_renameat
//go:cgo_import_dynamic libc_renameat renameat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1923,7 +1818,6 @@ func Revoke(path string) (err error) {
func libc_revoke_trampoline()
-//go:linkname libc_revoke libc_revoke
//go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1943,7 +1837,6 @@ func Rmdir(path string) (err error) {
func libc_rmdir_trampoline()
-//go:linkname libc_rmdir libc_rmdir
//go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1959,7 +1852,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
func libc_lseek_trampoline()
-//go:linkname libc_lseek libc_lseek
//go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1975,7 +1867,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
func libc_select_trampoline()
-//go:linkname libc_select libc_select
//go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1990,7 +1881,6 @@ func Setegid(egid int) (err error) {
func libc_setegid_trampoline()
-//go:linkname libc_setegid libc_setegid
//go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2005,7 +1895,6 @@ func Seteuid(euid int) (err error) {
func libc_seteuid_trampoline()
-//go:linkname libc_seteuid libc_seteuid
//go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2020,7 +1909,6 @@ func Setgid(gid int) (err error) {
func libc_setgid_trampoline()
-//go:linkname libc_setgid libc_setgid
//go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2040,7 +1928,6 @@ func Setlogin(name string) (err error) {
func libc_setlogin_trampoline()
-//go:linkname libc_setlogin libc_setlogin
//go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2055,7 +1942,6 @@ func Setpgid(pid int, pgid int) (err error) {
func libc_setpgid_trampoline()
-//go:linkname libc_setpgid libc_setpgid
//go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2070,7 +1956,6 @@ func Setpriority(which int, who int, prio int) (err error) {
func libc_setpriority_trampoline()
-//go:linkname libc_setpriority libc_setpriority
//go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2085,7 +1970,6 @@ func Setprivexec(flag int) (err error) {
func libc_setprivexec_trampoline()
-//go:linkname libc_setprivexec libc_setprivexec
//go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2100,7 +1984,6 @@ func Setregid(rgid int, egid int) (err error) {
func libc_setregid_trampoline()
-//go:linkname libc_setregid libc_setregid
//go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2115,7 +1998,6 @@ func Setreuid(ruid int, euid int) (err error) {
func libc_setreuid_trampoline()
-//go:linkname libc_setreuid libc_setreuid
//go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2130,7 +2012,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) {
func libc_setrlimit_trampoline()
-//go:linkname libc_setrlimit libc_setrlimit
//go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2146,7 +2027,6 @@ func Setsid() (pid int, err error) {
func libc_setsid_trampoline()
-//go:linkname libc_setsid libc_setsid
//go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2161,7 +2041,6 @@ func Settimeofday(tp *Timeval) (err error) {
func libc_settimeofday_trampoline()
-//go:linkname libc_settimeofday libc_settimeofday
//go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2176,7 +2055,6 @@ func Setuid(uid int) (err error) {
func libc_setuid_trampoline()
-//go:linkname libc_setuid libc_setuid
//go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2201,7 +2079,6 @@ func Symlink(path string, link string) (err error) {
func libc_symlink_trampoline()
-//go:linkname libc_symlink libc_symlink
//go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2226,7 +2103,6 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
func libc_symlinkat_trampoline()
-//go:linkname libc_symlinkat libc_symlinkat
//go:cgo_import_dynamic libc_symlinkat symlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2241,7 +2117,6 @@ func Sync() (err error) {
func libc_sync_trampoline()
-//go:linkname libc_sync libc_sync
//go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2261,7 +2136,6 @@ func Truncate(path string, length int64) (err error) {
func libc_truncate_trampoline()
-//go:linkname libc_truncate libc_truncate
//go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2274,7 +2148,6 @@ func Umask(newmask int) (oldmask int) {
func libc_umask_trampoline()
-//go:linkname libc_umask libc_umask
//go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2294,7 +2167,6 @@ func Undelete(path string) (err error) {
func libc_undelete_trampoline()
-//go:linkname libc_undelete libc_undelete
//go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2314,7 +2186,6 @@ func Unlink(path string) (err error) {
func libc_unlink_trampoline()
-//go:linkname libc_unlink libc_unlink
//go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2334,7 +2205,6 @@ func Unlinkat(dirfd int, path string, flags int) (err error) {
func libc_unlinkat_trampoline()
-//go:linkname libc_unlinkat libc_unlinkat
//go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2354,7 +2224,6 @@ func Unmount(path string, flags int) (err error) {
func libc_unmount_trampoline()
-//go:linkname libc_unmount libc_unmount
//go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2376,7 +2245,6 @@ func write(fd int, p []byte) (n int, err error) {
func libc_write_trampoline()
-//go:linkname libc_write libc_write
//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2392,7 +2260,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (
func libc_mmap_trampoline()
-//go:linkname libc_mmap libc_mmap
//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2407,7 +2274,6 @@ func munmap(addr uintptr, length uintptr) (err error) {
func libc_munmap_trampoline()
-//go:linkname libc_munmap libc_munmap
//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2444,7 +2310,6 @@ func Fstat(fd int, stat *Stat_t) (err error) {
func libc_fstat64_trampoline()
-//go:linkname libc_fstat64 libc_fstat64
//go:cgo_import_dynamic libc_fstat64 fstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2464,7 +2329,6 @@ func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
func libc_fstatat64_trampoline()
-//go:linkname libc_fstatat64 libc_fstatat64
//go:cgo_import_dynamic libc_fstatat64 fstatat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2479,7 +2343,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) {
func libc_fstatfs64_trampoline()
-//go:linkname libc_fstatfs64 libc_fstatfs64
//go:cgo_import_dynamic libc_fstatfs64 fstatfs64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2495,7 +2358,6 @@ func getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) {
func libc_getfsstat64_trampoline()
-//go:linkname libc_getfsstat64 libc_getfsstat64
//go:cgo_import_dynamic libc_getfsstat64 getfsstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2515,7 +2377,6 @@ func Lstat(path string, stat *Stat_t) (err error) {
func libc_lstat64_trampoline()
-//go:linkname libc_lstat64 libc_lstat64
//go:cgo_import_dynamic libc_lstat64 lstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2530,7 +2391,6 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
func libc_ptrace_trampoline()
-//go:linkname libc_ptrace libc_ptrace
//go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2550,7 +2410,6 @@ func Stat(path string, stat *Stat_t) (err error) {
func libc_stat64_trampoline()
-//go:linkname libc_stat64 libc_stat64
//go:cgo_import_dynamic libc_stat64 stat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2570,5 +2429,4 @@ func Statfs(path string, stat *Statfs_t) (err error) {
func libc_statfs64_trampoline()
-//go:linkname libc_statfs64 libc_statfs64
//go:cgo_import_dynamic libc_statfs64 statfs64 "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.go
index 314042a9d4..8882623613 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.go
@@ -24,7 +24,6 @@ func closedir(dir uintptr) (err error) {
func libc_closedir_trampoline()
-//go:linkname libc_closedir libc_closedir
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -37,5 +36,4 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
func libc_readdir_r_trampoline()
-//go:linkname libc_readdir_r libc_readdir_r
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
index 889c14059e..2daf0bd628 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
@@ -25,7 +25,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
func libc_getgroups_trampoline()
-//go:linkname libc_getgroups libc_getgroups
//go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -40,7 +39,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) {
func libc_setgroups_trampoline()
-//go:linkname libc_setgroups libc_setgroups
//go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -56,7 +54,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
func libc_wait4_trampoline()
-//go:linkname libc_wait4 libc_wait4
//go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -72,7 +69,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
func libc_accept_trampoline()
-//go:linkname libc_accept libc_accept
//go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -87,7 +83,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_bind_trampoline()
-//go:linkname libc_bind libc_bind
//go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -102,7 +97,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_connect_trampoline()
-//go:linkname libc_connect libc_connect
//go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -118,7 +112,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
func libc_socket_trampoline()
-//go:linkname libc_socket libc_socket
//go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -133,7 +126,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen
func libc_getsockopt_trampoline()
-//go:linkname libc_getsockopt libc_getsockopt
//go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -148,7 +140,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr)
func libc_setsockopt_trampoline()
-//go:linkname libc_setsockopt libc_setsockopt
//go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -163,7 +154,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getpeername_trampoline()
-//go:linkname libc_getpeername libc_getpeername
//go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -178,7 +168,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getsockname_trampoline()
-//go:linkname libc_getsockname libc_getsockname
//go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -193,7 +182,6 @@ func Shutdown(s int, how int) (err error) {
func libc_shutdown_trampoline()
-//go:linkname libc_shutdown libc_shutdown
//go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -208,7 +196,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
func libc_socketpair_trampoline()
-//go:linkname libc_socketpair libc_socketpair
//go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -230,7 +217,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
func libc_recvfrom_trampoline()
-//go:linkname libc_recvfrom libc_recvfrom
//go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -251,7 +237,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
func libc_sendto_trampoline()
-//go:linkname libc_sendto libc_sendto
//go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -267,7 +252,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_recvmsg_trampoline()
-//go:linkname libc_recvmsg libc_recvmsg
//go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -283,7 +267,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_sendmsg_trampoline()
-//go:linkname libc_sendmsg libc_sendmsg
//go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -299,7 +282,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne
func libc_kevent_trampoline()
-//go:linkname libc_kevent libc_kevent
//go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -319,7 +301,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) {
func libc_utimes_trampoline()
-//go:linkname libc_utimes libc_utimes
//go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -334,7 +315,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) {
func libc_futimes_trampoline()
-//go:linkname libc_futimes libc_futimes
//go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -350,7 +330,6 @@ func poll(fds *PollFd, nfds int, timeout int) (n int, err error) {
func libc_poll_trampoline()
-//go:linkname libc_poll libc_poll
//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -371,7 +350,6 @@ func Madvise(b []byte, behav int) (err error) {
func libc_madvise_trampoline()
-//go:linkname libc_madvise libc_madvise
//go:cgo_import_dynamic libc_madvise madvise "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -392,7 +370,6 @@ func Mlock(b []byte) (err error) {
func libc_mlock_trampoline()
-//go:linkname libc_mlock libc_mlock
//go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -407,7 +384,6 @@ func Mlockall(flags int) (err error) {
func libc_mlockall_trampoline()
-//go:linkname libc_mlockall libc_mlockall
//go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -428,7 +404,6 @@ func Mprotect(b []byte, prot int) (err error) {
func libc_mprotect_trampoline()
-//go:linkname libc_mprotect libc_mprotect
//go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -449,7 +424,6 @@ func Msync(b []byte, flags int) (err error) {
func libc_msync_trampoline()
-//go:linkname libc_msync libc_msync
//go:cgo_import_dynamic libc_msync msync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -470,7 +444,6 @@ func Munlock(b []byte) (err error) {
func libc_munlock_trampoline()
-//go:linkname libc_munlock libc_munlock
//go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -485,7 +458,6 @@ func Munlockall() (err error) {
func libc_munlockall_trampoline()
-//go:linkname libc_munlockall libc_munlockall
//go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -502,7 +474,6 @@ func pipe() (r int, w int, err error) {
func libc_pipe_trampoline()
-//go:linkname libc_pipe libc_pipe
//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -528,7 +499,6 @@ func getxattr(path string, attr string, dest *byte, size int, position uint32, o
func libc_getxattr_trampoline()
-//go:linkname libc_getxattr libc_getxattr
//go:cgo_import_dynamic libc_getxattr getxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -549,7 +519,6 @@ func fgetxattr(fd int, attr string, dest *byte, size int, position uint32, optio
func libc_fgetxattr_trampoline()
-//go:linkname libc_fgetxattr libc_fgetxattr
//go:cgo_import_dynamic libc_fgetxattr fgetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -574,7 +543,6 @@ func setxattr(path string, attr string, data *byte, size int, position uint32, o
func libc_setxattr_trampoline()
-//go:linkname libc_setxattr libc_setxattr
//go:cgo_import_dynamic libc_setxattr setxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -594,7 +562,6 @@ func fsetxattr(fd int, attr string, data *byte, size int, position uint32, optio
func libc_fsetxattr_trampoline()
-//go:linkname libc_fsetxattr libc_fsetxattr
//go:cgo_import_dynamic libc_fsetxattr fsetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -619,7 +586,6 @@ func removexattr(path string, attr string, options int) (err error) {
func libc_removexattr_trampoline()
-//go:linkname libc_removexattr libc_removexattr
//go:cgo_import_dynamic libc_removexattr removexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -639,7 +605,6 @@ func fremovexattr(fd int, attr string, options int) (err error) {
func libc_fremovexattr_trampoline()
-//go:linkname libc_fremovexattr libc_fremovexattr
//go:cgo_import_dynamic libc_fremovexattr fremovexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -660,7 +625,6 @@ func listxattr(path string, dest *byte, size int, options int) (sz int, err erro
func libc_listxattr_trampoline()
-//go:linkname libc_listxattr libc_listxattr
//go:cgo_import_dynamic libc_listxattr listxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -676,7 +640,6 @@ func flistxattr(fd int, dest *byte, size int, options int) (sz int, err error) {
func libc_flistxattr_trampoline()
-//go:linkname libc_flistxattr libc_flistxattr
//go:cgo_import_dynamic libc_flistxattr flistxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -691,7 +654,6 @@ func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintp
func libc_setattrlist_trampoline()
-//go:linkname libc_setattrlist libc_setattrlist
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -707,7 +669,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
func libc_fcntl_trampoline()
-//go:linkname libc_fcntl libc_fcntl
//go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -722,7 +683,6 @@ func kill(pid int, signum int, posix int) (err error) {
func libc_kill_trampoline()
-//go:linkname libc_kill libc_kill
//go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -737,7 +697,6 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
func libc_ioctl_trampoline()
-//go:linkname libc_ioctl libc_ioctl
//go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -758,7 +717,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
func libc_sysctl_trampoline()
-//go:linkname libc_sysctl libc_sysctl
//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -773,7 +731,6 @@ func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer
func libc_sendfile_trampoline()
-//go:linkname libc_sendfile libc_sendfile
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -793,7 +750,6 @@ func Access(path string, mode uint32) (err error) {
func libc_access_trampoline()
-//go:linkname libc_access libc_access
//go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -808,7 +764,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
func libc_adjtime_trampoline()
-//go:linkname libc_adjtime libc_adjtime
//go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -828,7 +783,6 @@ func Chdir(path string) (err error) {
func libc_chdir_trampoline()
-//go:linkname libc_chdir libc_chdir
//go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -848,7 +802,6 @@ func Chflags(path string, flags int) (err error) {
func libc_chflags_trampoline()
-//go:linkname libc_chflags libc_chflags
//go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -868,7 +821,6 @@ func Chmod(path string, mode uint32) (err error) {
func libc_chmod_trampoline()
-//go:linkname libc_chmod libc_chmod
//go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -888,7 +840,6 @@ func Chown(path string, uid int, gid int) (err error) {
func libc_chown_trampoline()
-//go:linkname libc_chown libc_chown
//go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -908,7 +859,6 @@ func Chroot(path string) (err error) {
func libc_chroot_trampoline()
-//go:linkname libc_chroot libc_chroot
//go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -923,7 +873,6 @@ func ClockGettime(clockid int32, time *Timespec) (err error) {
func libc_clock_gettime_trampoline()
-//go:linkname libc_clock_gettime libc_clock_gettime
//go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -938,7 +887,6 @@ func Close(fd int) (err error) {
func libc_close_trampoline()
-//go:linkname libc_close libc_close
//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -963,7 +911,6 @@ func Clonefile(src string, dst string, flags int) (err error) {
func libc_clonefile_trampoline()
-//go:linkname libc_clonefile libc_clonefile
//go:cgo_import_dynamic libc_clonefile clonefile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -988,7 +935,6 @@ func Clonefileat(srcDirfd int, src string, dstDirfd int, dst string, flags int)
func libc_clonefileat_trampoline()
-//go:linkname libc_clonefileat libc_clonefileat
//go:cgo_import_dynamic libc_clonefileat clonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1004,7 +950,6 @@ func Dup(fd int) (nfd int, err error) {
func libc_dup_trampoline()
-//go:linkname libc_dup libc_dup
//go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1019,7 +964,6 @@ func Dup2(from int, to int) (err error) {
func libc_dup2_trampoline()
-//go:linkname libc_dup2 libc_dup2
//go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1044,7 +988,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) {
func libc_exchangedata_trampoline()
-//go:linkname libc_exchangedata libc_exchangedata
//go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1056,7 +999,6 @@ func Exit(code int) {
func libc_exit_trampoline()
-//go:linkname libc_exit libc_exit
//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1076,7 +1018,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_faccessat_trampoline()
-//go:linkname libc_faccessat libc_faccessat
//go:cgo_import_dynamic libc_faccessat faccessat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1091,7 +1032,6 @@ func Fchdir(fd int) (err error) {
func libc_fchdir_trampoline()
-//go:linkname libc_fchdir libc_fchdir
//go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1106,7 +1046,6 @@ func Fchflags(fd int, flags int) (err error) {
func libc_fchflags_trampoline()
-//go:linkname libc_fchflags libc_fchflags
//go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1121,7 +1060,6 @@ func Fchmod(fd int, mode uint32) (err error) {
func libc_fchmod_trampoline()
-//go:linkname libc_fchmod libc_fchmod
//go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1141,7 +1079,6 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_fchmodat_trampoline()
-//go:linkname libc_fchmodat libc_fchmodat
//go:cgo_import_dynamic libc_fchmodat fchmodat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1156,7 +1093,6 @@ func Fchown(fd int, uid int, gid int) (err error) {
func libc_fchown_trampoline()
-//go:linkname libc_fchown libc_fchown
//go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1176,7 +1112,6 @@ func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
func libc_fchownat_trampoline()
-//go:linkname libc_fchownat libc_fchownat
//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1196,7 +1131,6 @@ func Fclonefileat(srcDirfd int, dstDirfd int, dst string, flags int) (err error)
func libc_fclonefileat_trampoline()
-//go:linkname libc_fclonefileat libc_fclonefileat
//go:cgo_import_dynamic libc_fclonefileat fclonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1211,7 +1145,6 @@ func Flock(fd int, how int) (err error) {
func libc_flock_trampoline()
-//go:linkname libc_flock libc_flock
//go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1227,7 +1160,6 @@ func Fpathconf(fd int, name int) (val int, err error) {
func libc_fpathconf_trampoline()
-//go:linkname libc_fpathconf libc_fpathconf
//go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1242,7 +1174,6 @@ func Fsync(fd int) (err error) {
func libc_fsync_trampoline()
-//go:linkname libc_fsync libc_fsync
//go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1257,7 +1188,6 @@ func Ftruncate(fd int, length int64) (err error) {
func libc_ftruncate_trampoline()
-//go:linkname libc_ftruncate libc_ftruncate
//go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1279,7 +1209,6 @@ func Getcwd(buf []byte) (n int, err error) {
func libc_getcwd_trampoline()
-//go:linkname libc_getcwd libc_getcwd
//go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1292,7 +1221,6 @@ func Getdtablesize() (size int) {
func libc_getdtablesize_trampoline()
-//go:linkname libc_getdtablesize libc_getdtablesize
//go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1305,7 +1233,6 @@ func Getegid() (egid int) {
func libc_getegid_trampoline()
-//go:linkname libc_getegid libc_getegid
//go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1318,7 +1245,6 @@ func Geteuid() (uid int) {
func libc_geteuid_trampoline()
-//go:linkname libc_geteuid libc_geteuid
//go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1331,7 +1257,6 @@ func Getgid() (gid int) {
func libc_getgid_trampoline()
-//go:linkname libc_getgid libc_getgid
//go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1347,7 +1272,6 @@ func Getpgid(pid int) (pgid int, err error) {
func libc_getpgid_trampoline()
-//go:linkname libc_getpgid libc_getpgid
//go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1360,7 +1284,6 @@ func Getpgrp() (pgrp int) {
func libc_getpgrp_trampoline()
-//go:linkname libc_getpgrp libc_getpgrp
//go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1373,7 +1296,6 @@ func Getpid() (pid int) {
func libc_getpid_trampoline()
-//go:linkname libc_getpid libc_getpid
//go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1386,7 +1308,6 @@ func Getppid() (ppid int) {
func libc_getppid_trampoline()
-//go:linkname libc_getppid libc_getppid
//go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1402,7 +1323,6 @@ func Getpriority(which int, who int) (prio int, err error) {
func libc_getpriority_trampoline()
-//go:linkname libc_getpriority libc_getpriority
//go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1417,7 +1337,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) {
func libc_getrlimit_trampoline()
-//go:linkname libc_getrlimit libc_getrlimit
//go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1432,7 +1351,6 @@ func Getrusage(who int, rusage *Rusage) (err error) {
func libc_getrusage_trampoline()
-//go:linkname libc_getrusage libc_getrusage
//go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1448,7 +1366,6 @@ func Getsid(pid int) (sid int, err error) {
func libc_getsid_trampoline()
-//go:linkname libc_getsid libc_getsid
//go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1463,7 +1380,6 @@ func Gettimeofday(tp *Timeval) (err error) {
func libc_gettimeofday_trampoline()
-//go:linkname libc_gettimeofday libc_gettimeofday
//go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1476,7 +1392,6 @@ func Getuid() (uid int) {
func libc_getuid_trampoline()
-//go:linkname libc_getuid libc_getuid
//go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1489,7 +1404,6 @@ func Issetugid() (tainted bool) {
func libc_issetugid_trampoline()
-//go:linkname libc_issetugid libc_issetugid
//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1505,7 +1419,6 @@ func Kqueue() (fd int, err error) {
func libc_kqueue_trampoline()
-//go:linkname libc_kqueue libc_kqueue
//go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1525,7 +1438,6 @@ func Lchown(path string, uid int, gid int) (err error) {
func libc_lchown_trampoline()
-//go:linkname libc_lchown libc_lchown
//go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1550,7 +1462,6 @@ func Link(path string, link string) (err error) {
func libc_link_trampoline()
-//go:linkname libc_link libc_link
//go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1575,7 +1486,6 @@ func Linkat(pathfd int, path string, linkfd int, link string, flags int) (err er
func libc_linkat_trampoline()
-//go:linkname libc_linkat libc_linkat
//go:cgo_import_dynamic libc_linkat linkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1590,7 +1500,6 @@ func Listen(s int, backlog int) (err error) {
func libc_listen_trampoline()
-//go:linkname libc_listen libc_listen
//go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1610,7 +1519,6 @@ func Mkdir(path string, mode uint32) (err error) {
func libc_mkdir_trampoline()
-//go:linkname libc_mkdir libc_mkdir
//go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1630,7 +1538,6 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
func libc_mkdirat_trampoline()
-//go:linkname libc_mkdirat libc_mkdirat
//go:cgo_import_dynamic libc_mkdirat mkdirat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1650,7 +1557,6 @@ func Mkfifo(path string, mode uint32) (err error) {
func libc_mkfifo_trampoline()
-//go:linkname libc_mkfifo libc_mkfifo
//go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1670,7 +1576,6 @@ func Mknod(path string, mode uint32, dev int) (err error) {
func libc_mknod_trampoline()
-//go:linkname libc_mknod libc_mknod
//go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1691,7 +1596,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
func libc_open_trampoline()
-//go:linkname libc_open libc_open
//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1712,7 +1616,6 @@ func Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
func libc_openat_trampoline()
-//go:linkname libc_openat libc_openat
//go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1733,7 +1636,6 @@ func Pathconf(path string, name int) (val int, err error) {
func libc_pathconf_trampoline()
-//go:linkname libc_pathconf libc_pathconf
//go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1755,7 +1657,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
func libc_pread_trampoline()
-//go:linkname libc_pread libc_pread
//go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1777,7 +1678,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
func libc_pwrite_trampoline()
-//go:linkname libc_pwrite libc_pwrite
//go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1799,7 +1699,6 @@ func read(fd int, p []byte) (n int, err error) {
func libc_read_trampoline()
-//go:linkname libc_read libc_read
//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1826,7 +1725,6 @@ func Readlink(path string, buf []byte) (n int, err error) {
func libc_readlink_trampoline()
-//go:linkname libc_readlink libc_readlink
//go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1853,7 +1751,6 @@ func Readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
func libc_readlinkat_trampoline()
-//go:linkname libc_readlinkat libc_readlinkat
//go:cgo_import_dynamic libc_readlinkat readlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1878,7 +1775,6 @@ func Rename(from string, to string) (err error) {
func libc_rename_trampoline()
-//go:linkname libc_rename libc_rename
//go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1903,7 +1799,6 @@ func Renameat(fromfd int, from string, tofd int, to string) (err error) {
func libc_renameat_trampoline()
-//go:linkname libc_renameat libc_renameat
//go:cgo_import_dynamic libc_renameat renameat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1923,7 +1818,6 @@ func Revoke(path string) (err error) {
func libc_revoke_trampoline()
-//go:linkname libc_revoke libc_revoke
//go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1943,7 +1837,6 @@ func Rmdir(path string) (err error) {
func libc_rmdir_trampoline()
-//go:linkname libc_rmdir libc_rmdir
//go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1959,7 +1852,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
func libc_lseek_trampoline()
-//go:linkname libc_lseek libc_lseek
//go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1975,7 +1867,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
func libc_select_trampoline()
-//go:linkname libc_select libc_select
//go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1990,7 +1881,6 @@ func Setegid(egid int) (err error) {
func libc_setegid_trampoline()
-//go:linkname libc_setegid libc_setegid
//go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2005,7 +1895,6 @@ func Seteuid(euid int) (err error) {
func libc_seteuid_trampoline()
-//go:linkname libc_seteuid libc_seteuid
//go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2020,7 +1909,6 @@ func Setgid(gid int) (err error) {
func libc_setgid_trampoline()
-//go:linkname libc_setgid libc_setgid
//go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2040,7 +1928,6 @@ func Setlogin(name string) (err error) {
func libc_setlogin_trampoline()
-//go:linkname libc_setlogin libc_setlogin
//go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2055,7 +1942,6 @@ func Setpgid(pid int, pgid int) (err error) {
func libc_setpgid_trampoline()
-//go:linkname libc_setpgid libc_setpgid
//go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2070,7 +1956,6 @@ func Setpriority(which int, who int, prio int) (err error) {
func libc_setpriority_trampoline()
-//go:linkname libc_setpriority libc_setpriority
//go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2085,7 +1970,6 @@ func Setprivexec(flag int) (err error) {
func libc_setprivexec_trampoline()
-//go:linkname libc_setprivexec libc_setprivexec
//go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2100,7 +1984,6 @@ func Setregid(rgid int, egid int) (err error) {
func libc_setregid_trampoline()
-//go:linkname libc_setregid libc_setregid
//go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2115,7 +1998,6 @@ func Setreuid(ruid int, euid int) (err error) {
func libc_setreuid_trampoline()
-//go:linkname libc_setreuid libc_setreuid
//go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2130,7 +2012,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) {
func libc_setrlimit_trampoline()
-//go:linkname libc_setrlimit libc_setrlimit
//go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2146,7 +2027,6 @@ func Setsid() (pid int, err error) {
func libc_setsid_trampoline()
-//go:linkname libc_setsid libc_setsid
//go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2161,7 +2041,6 @@ func Settimeofday(tp *Timeval) (err error) {
func libc_settimeofday_trampoline()
-//go:linkname libc_settimeofday libc_settimeofday
//go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2176,7 +2055,6 @@ func Setuid(uid int) (err error) {
func libc_setuid_trampoline()
-//go:linkname libc_setuid libc_setuid
//go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2201,7 +2079,6 @@ func Symlink(path string, link string) (err error) {
func libc_symlink_trampoline()
-//go:linkname libc_symlink libc_symlink
//go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2226,7 +2103,6 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
func libc_symlinkat_trampoline()
-//go:linkname libc_symlinkat libc_symlinkat
//go:cgo_import_dynamic libc_symlinkat symlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2241,7 +2117,6 @@ func Sync() (err error) {
func libc_sync_trampoline()
-//go:linkname libc_sync libc_sync
//go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2261,7 +2136,6 @@ func Truncate(path string, length int64) (err error) {
func libc_truncate_trampoline()
-//go:linkname libc_truncate libc_truncate
//go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2274,7 +2148,6 @@ func Umask(newmask int) (oldmask int) {
func libc_umask_trampoline()
-//go:linkname libc_umask libc_umask
//go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2294,7 +2167,6 @@ func Undelete(path string) (err error) {
func libc_undelete_trampoline()
-//go:linkname libc_undelete libc_undelete
//go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2314,7 +2186,6 @@ func Unlink(path string) (err error) {
func libc_unlink_trampoline()
-//go:linkname libc_unlink libc_unlink
//go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2334,7 +2205,6 @@ func Unlinkat(dirfd int, path string, flags int) (err error) {
func libc_unlinkat_trampoline()
-//go:linkname libc_unlinkat libc_unlinkat
//go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2354,7 +2224,6 @@ func Unmount(path string, flags int) (err error) {
func libc_unmount_trampoline()
-//go:linkname libc_unmount libc_unmount
//go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2376,7 +2245,6 @@ func write(fd int, p []byte) (n int, err error) {
func libc_write_trampoline()
-//go:linkname libc_write libc_write
//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2392,7 +2260,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (
func libc_mmap_trampoline()
-//go:linkname libc_mmap libc_mmap
//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2407,7 +2274,6 @@ func munmap(addr uintptr, length uintptr) (err error) {
func libc_munmap_trampoline()
-//go:linkname libc_munmap libc_munmap
//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2444,7 +2310,6 @@ func Fstat(fd int, stat *Stat_t) (err error) {
func libc_fstat64_trampoline()
-//go:linkname libc_fstat64 libc_fstat64
//go:cgo_import_dynamic libc_fstat64 fstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2464,7 +2329,6 @@ func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
func libc_fstatat64_trampoline()
-//go:linkname libc_fstatat64 libc_fstatat64
//go:cgo_import_dynamic libc_fstatat64 fstatat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2479,7 +2343,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) {
func libc_fstatfs64_trampoline()
-//go:linkname libc_fstatfs64 libc_fstatfs64
//go:cgo_import_dynamic libc_fstatfs64 fstatfs64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2495,7 +2358,6 @@ func getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) {
func libc_getfsstat64_trampoline()
-//go:linkname libc_getfsstat64 libc_getfsstat64
//go:cgo_import_dynamic libc_getfsstat64 getfsstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2515,7 +2377,6 @@ func Lstat(path string, stat *Stat_t) (err error) {
func libc_lstat64_trampoline()
-//go:linkname libc_lstat64 libc_lstat64
//go:cgo_import_dynamic libc_lstat64 lstat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2530,7 +2391,6 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
func libc_ptrace_trampoline()
-//go:linkname libc_ptrace libc_ptrace
//go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2550,7 +2410,6 @@ func Stat(path string, stat *Stat_t) (err error) {
func libc_stat64_trampoline()
-//go:linkname libc_stat64 libc_stat64
//go:cgo_import_dynamic libc_stat64 stat64 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2570,5 +2429,4 @@ func Statfs(path string, stat *Statfs_t) (err error) {
func libc_statfs64_trampoline()
-//go:linkname libc_statfs64 libc_statfs64
//go:cgo_import_dynamic libc_statfs64 statfs64 "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.go
index f519ce9afb..de4738fff8 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.1_13.go
@@ -24,7 +24,6 @@ func closedir(dir uintptr) (err error) {
func libc_closedir_trampoline()
-//go:linkname libc_closedir libc_closedir
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -37,5 +36,4 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
func libc_readdir_r_trampoline()
-//go:linkname libc_readdir_r libc_readdir_r
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go
index d6b5249c2f..8e79ad377b 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm.go
@@ -25,7 +25,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
func libc_getgroups_trampoline()
-//go:linkname libc_getgroups libc_getgroups
//go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -40,7 +39,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) {
func libc_setgroups_trampoline()
-//go:linkname libc_setgroups libc_setgroups
//go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -56,7 +54,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
func libc_wait4_trampoline()
-//go:linkname libc_wait4 libc_wait4
//go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -72,7 +69,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
func libc_accept_trampoline()
-//go:linkname libc_accept libc_accept
//go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -87,7 +83,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_bind_trampoline()
-//go:linkname libc_bind libc_bind
//go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -102,7 +97,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_connect_trampoline()
-//go:linkname libc_connect libc_connect
//go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -118,7 +112,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
func libc_socket_trampoline()
-//go:linkname libc_socket libc_socket
//go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -133,7 +126,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen
func libc_getsockopt_trampoline()
-//go:linkname libc_getsockopt libc_getsockopt
//go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -148,7 +140,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr)
func libc_setsockopt_trampoline()
-//go:linkname libc_setsockopt libc_setsockopt
//go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -163,7 +154,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getpeername_trampoline()
-//go:linkname libc_getpeername libc_getpeername
//go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -178,7 +168,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getsockname_trampoline()
-//go:linkname libc_getsockname libc_getsockname
//go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -193,7 +182,6 @@ func Shutdown(s int, how int) (err error) {
func libc_shutdown_trampoline()
-//go:linkname libc_shutdown libc_shutdown
//go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -208,7 +196,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
func libc_socketpair_trampoline()
-//go:linkname libc_socketpair libc_socketpair
//go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -230,7 +217,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
func libc_recvfrom_trampoline()
-//go:linkname libc_recvfrom libc_recvfrom
//go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -251,7 +237,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
func libc_sendto_trampoline()
-//go:linkname libc_sendto libc_sendto
//go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -267,7 +252,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_recvmsg_trampoline()
-//go:linkname libc_recvmsg libc_recvmsg
//go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -283,7 +267,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_sendmsg_trampoline()
-//go:linkname libc_sendmsg libc_sendmsg
//go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -299,7 +282,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne
func libc_kevent_trampoline()
-//go:linkname libc_kevent libc_kevent
//go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -319,7 +301,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) {
func libc_utimes_trampoline()
-//go:linkname libc_utimes libc_utimes
//go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -334,7 +315,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) {
func libc_futimes_trampoline()
-//go:linkname libc_futimes libc_futimes
//go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -350,7 +330,6 @@ func poll(fds *PollFd, nfds int, timeout int) (n int, err error) {
func libc_poll_trampoline()
-//go:linkname libc_poll libc_poll
//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -371,7 +350,6 @@ func Madvise(b []byte, behav int) (err error) {
func libc_madvise_trampoline()
-//go:linkname libc_madvise libc_madvise
//go:cgo_import_dynamic libc_madvise madvise "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -392,7 +370,6 @@ func Mlock(b []byte) (err error) {
func libc_mlock_trampoline()
-//go:linkname libc_mlock libc_mlock
//go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -407,7 +384,6 @@ func Mlockall(flags int) (err error) {
func libc_mlockall_trampoline()
-//go:linkname libc_mlockall libc_mlockall
//go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -428,7 +404,6 @@ func Mprotect(b []byte, prot int) (err error) {
func libc_mprotect_trampoline()
-//go:linkname libc_mprotect libc_mprotect
//go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -449,7 +424,6 @@ func Msync(b []byte, flags int) (err error) {
func libc_msync_trampoline()
-//go:linkname libc_msync libc_msync
//go:cgo_import_dynamic libc_msync msync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -470,7 +444,6 @@ func Munlock(b []byte) (err error) {
func libc_munlock_trampoline()
-//go:linkname libc_munlock libc_munlock
//go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -485,7 +458,6 @@ func Munlockall() (err error) {
func libc_munlockall_trampoline()
-//go:linkname libc_munlockall libc_munlockall
//go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -502,7 +474,6 @@ func pipe() (r int, w int, err error) {
func libc_pipe_trampoline()
-//go:linkname libc_pipe libc_pipe
//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -528,7 +499,6 @@ func getxattr(path string, attr string, dest *byte, size int, position uint32, o
func libc_getxattr_trampoline()
-//go:linkname libc_getxattr libc_getxattr
//go:cgo_import_dynamic libc_getxattr getxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -549,7 +519,6 @@ func fgetxattr(fd int, attr string, dest *byte, size int, position uint32, optio
func libc_fgetxattr_trampoline()
-//go:linkname libc_fgetxattr libc_fgetxattr
//go:cgo_import_dynamic libc_fgetxattr fgetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -574,7 +543,6 @@ func setxattr(path string, attr string, data *byte, size int, position uint32, o
func libc_setxattr_trampoline()
-//go:linkname libc_setxattr libc_setxattr
//go:cgo_import_dynamic libc_setxattr setxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -594,7 +562,6 @@ func fsetxattr(fd int, attr string, data *byte, size int, position uint32, optio
func libc_fsetxattr_trampoline()
-//go:linkname libc_fsetxattr libc_fsetxattr
//go:cgo_import_dynamic libc_fsetxattr fsetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -619,7 +586,6 @@ func removexattr(path string, attr string, options int) (err error) {
func libc_removexattr_trampoline()
-//go:linkname libc_removexattr libc_removexattr
//go:cgo_import_dynamic libc_removexattr removexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -639,7 +605,6 @@ func fremovexattr(fd int, attr string, options int) (err error) {
func libc_fremovexattr_trampoline()
-//go:linkname libc_fremovexattr libc_fremovexattr
//go:cgo_import_dynamic libc_fremovexattr fremovexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -660,7 +625,6 @@ func listxattr(path string, dest *byte, size int, options int) (sz int, err erro
func libc_listxattr_trampoline()
-//go:linkname libc_listxattr libc_listxattr
//go:cgo_import_dynamic libc_listxattr listxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -676,7 +640,6 @@ func flistxattr(fd int, dest *byte, size int, options int) (sz int, err error) {
func libc_flistxattr_trampoline()
-//go:linkname libc_flistxattr libc_flistxattr
//go:cgo_import_dynamic libc_flistxattr flistxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -691,7 +654,6 @@ func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintp
func libc_setattrlist_trampoline()
-//go:linkname libc_setattrlist libc_setattrlist
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -707,7 +669,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
func libc_fcntl_trampoline()
-//go:linkname libc_fcntl libc_fcntl
//go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -722,7 +683,6 @@ func kill(pid int, signum int, posix int) (err error) {
func libc_kill_trampoline()
-//go:linkname libc_kill libc_kill
//go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -737,7 +697,6 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
func libc_ioctl_trampoline()
-//go:linkname libc_ioctl libc_ioctl
//go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -758,7 +717,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
func libc_sysctl_trampoline()
-//go:linkname libc_sysctl libc_sysctl
//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -773,7 +731,6 @@ func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer
func libc_sendfile_trampoline()
-//go:linkname libc_sendfile libc_sendfile
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -793,7 +750,6 @@ func Access(path string, mode uint32) (err error) {
func libc_access_trampoline()
-//go:linkname libc_access libc_access
//go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -808,7 +764,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
func libc_adjtime_trampoline()
-//go:linkname libc_adjtime libc_adjtime
//go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -828,7 +783,6 @@ func Chdir(path string) (err error) {
func libc_chdir_trampoline()
-//go:linkname libc_chdir libc_chdir
//go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -848,7 +802,6 @@ func Chflags(path string, flags int) (err error) {
func libc_chflags_trampoline()
-//go:linkname libc_chflags libc_chflags
//go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -868,7 +821,6 @@ func Chmod(path string, mode uint32) (err error) {
func libc_chmod_trampoline()
-//go:linkname libc_chmod libc_chmod
//go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -888,7 +840,6 @@ func Chown(path string, uid int, gid int) (err error) {
func libc_chown_trampoline()
-//go:linkname libc_chown libc_chown
//go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -908,7 +859,6 @@ func Chroot(path string) (err error) {
func libc_chroot_trampoline()
-//go:linkname libc_chroot libc_chroot
//go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -923,7 +873,6 @@ func ClockGettime(clockid int32, time *Timespec) (err error) {
func libc_clock_gettime_trampoline()
-//go:linkname libc_clock_gettime libc_clock_gettime
//go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -938,7 +887,6 @@ func Close(fd int) (err error) {
func libc_close_trampoline()
-//go:linkname libc_close libc_close
//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -963,7 +911,6 @@ func Clonefile(src string, dst string, flags int) (err error) {
func libc_clonefile_trampoline()
-//go:linkname libc_clonefile libc_clonefile
//go:cgo_import_dynamic libc_clonefile clonefile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -988,7 +935,6 @@ func Clonefileat(srcDirfd int, src string, dstDirfd int, dst string, flags int)
func libc_clonefileat_trampoline()
-//go:linkname libc_clonefileat libc_clonefileat
//go:cgo_import_dynamic libc_clonefileat clonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1004,7 +950,6 @@ func Dup(fd int) (nfd int, err error) {
func libc_dup_trampoline()
-//go:linkname libc_dup libc_dup
//go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1019,7 +964,6 @@ func Dup2(from int, to int) (err error) {
func libc_dup2_trampoline()
-//go:linkname libc_dup2 libc_dup2
//go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1044,7 +988,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) {
func libc_exchangedata_trampoline()
-//go:linkname libc_exchangedata libc_exchangedata
//go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1056,7 +999,6 @@ func Exit(code int) {
func libc_exit_trampoline()
-//go:linkname libc_exit libc_exit
//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1076,7 +1018,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_faccessat_trampoline()
-//go:linkname libc_faccessat libc_faccessat
//go:cgo_import_dynamic libc_faccessat faccessat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1091,7 +1032,6 @@ func Fchdir(fd int) (err error) {
func libc_fchdir_trampoline()
-//go:linkname libc_fchdir libc_fchdir
//go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1106,7 +1046,6 @@ func Fchflags(fd int, flags int) (err error) {
func libc_fchflags_trampoline()
-//go:linkname libc_fchflags libc_fchflags
//go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1121,7 +1060,6 @@ func Fchmod(fd int, mode uint32) (err error) {
func libc_fchmod_trampoline()
-//go:linkname libc_fchmod libc_fchmod
//go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1141,7 +1079,6 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_fchmodat_trampoline()
-//go:linkname libc_fchmodat libc_fchmodat
//go:cgo_import_dynamic libc_fchmodat fchmodat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1156,7 +1093,6 @@ func Fchown(fd int, uid int, gid int) (err error) {
func libc_fchown_trampoline()
-//go:linkname libc_fchown libc_fchown
//go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1176,7 +1112,6 @@ func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
func libc_fchownat_trampoline()
-//go:linkname libc_fchownat libc_fchownat
//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1196,7 +1131,6 @@ func Fclonefileat(srcDirfd int, dstDirfd int, dst string, flags int) (err error)
func libc_fclonefileat_trampoline()
-//go:linkname libc_fclonefileat libc_fclonefileat
//go:cgo_import_dynamic libc_fclonefileat fclonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1211,7 +1145,6 @@ func Flock(fd int, how int) (err error) {
func libc_flock_trampoline()
-//go:linkname libc_flock libc_flock
//go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1227,7 +1160,6 @@ func Fpathconf(fd int, name int) (val int, err error) {
func libc_fpathconf_trampoline()
-//go:linkname libc_fpathconf libc_fpathconf
//go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1242,7 +1174,6 @@ func Fsync(fd int) (err error) {
func libc_fsync_trampoline()
-//go:linkname libc_fsync libc_fsync
//go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1257,7 +1188,6 @@ func Ftruncate(fd int, length int64) (err error) {
func libc_ftruncate_trampoline()
-//go:linkname libc_ftruncate libc_ftruncate
//go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1279,7 +1209,6 @@ func Getcwd(buf []byte) (n int, err error) {
func libc_getcwd_trampoline()
-//go:linkname libc_getcwd libc_getcwd
//go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1292,7 +1221,6 @@ func Getdtablesize() (size int) {
func libc_getdtablesize_trampoline()
-//go:linkname libc_getdtablesize libc_getdtablesize
//go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1305,7 +1233,6 @@ func Getegid() (egid int) {
func libc_getegid_trampoline()
-//go:linkname libc_getegid libc_getegid
//go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1318,7 +1245,6 @@ func Geteuid() (uid int) {
func libc_geteuid_trampoline()
-//go:linkname libc_geteuid libc_geteuid
//go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1331,7 +1257,6 @@ func Getgid() (gid int) {
func libc_getgid_trampoline()
-//go:linkname libc_getgid libc_getgid
//go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1347,7 +1272,6 @@ func Getpgid(pid int) (pgid int, err error) {
func libc_getpgid_trampoline()
-//go:linkname libc_getpgid libc_getpgid
//go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1360,7 +1284,6 @@ func Getpgrp() (pgrp int) {
func libc_getpgrp_trampoline()
-//go:linkname libc_getpgrp libc_getpgrp
//go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1373,7 +1296,6 @@ func Getpid() (pid int) {
func libc_getpid_trampoline()
-//go:linkname libc_getpid libc_getpid
//go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1386,7 +1308,6 @@ func Getppid() (ppid int) {
func libc_getppid_trampoline()
-//go:linkname libc_getppid libc_getppid
//go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1402,7 +1323,6 @@ func Getpriority(which int, who int) (prio int, err error) {
func libc_getpriority_trampoline()
-//go:linkname libc_getpriority libc_getpriority
//go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1417,7 +1337,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) {
func libc_getrlimit_trampoline()
-//go:linkname libc_getrlimit libc_getrlimit
//go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1432,7 +1351,6 @@ func Getrusage(who int, rusage *Rusage) (err error) {
func libc_getrusage_trampoline()
-//go:linkname libc_getrusage libc_getrusage
//go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1448,7 +1366,6 @@ func Getsid(pid int) (sid int, err error) {
func libc_getsid_trampoline()
-//go:linkname libc_getsid libc_getsid
//go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1463,7 +1380,6 @@ func Gettimeofday(tp *Timeval) (err error) {
func libc_gettimeofday_trampoline()
-//go:linkname libc_gettimeofday libc_gettimeofday
//go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1476,7 +1392,6 @@ func Getuid() (uid int) {
func libc_getuid_trampoline()
-//go:linkname libc_getuid libc_getuid
//go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1489,7 +1404,6 @@ func Issetugid() (tainted bool) {
func libc_issetugid_trampoline()
-//go:linkname libc_issetugid libc_issetugid
//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1505,7 +1419,6 @@ func Kqueue() (fd int, err error) {
func libc_kqueue_trampoline()
-//go:linkname libc_kqueue libc_kqueue
//go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1525,7 +1438,6 @@ func Lchown(path string, uid int, gid int) (err error) {
func libc_lchown_trampoline()
-//go:linkname libc_lchown libc_lchown
//go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1550,7 +1462,6 @@ func Link(path string, link string) (err error) {
func libc_link_trampoline()
-//go:linkname libc_link libc_link
//go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1575,7 +1486,6 @@ func Linkat(pathfd int, path string, linkfd int, link string, flags int) (err er
func libc_linkat_trampoline()
-//go:linkname libc_linkat libc_linkat
//go:cgo_import_dynamic libc_linkat linkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1590,7 +1500,6 @@ func Listen(s int, backlog int) (err error) {
func libc_listen_trampoline()
-//go:linkname libc_listen libc_listen
//go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1610,7 +1519,6 @@ func Mkdir(path string, mode uint32) (err error) {
func libc_mkdir_trampoline()
-//go:linkname libc_mkdir libc_mkdir
//go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1630,7 +1538,6 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
func libc_mkdirat_trampoline()
-//go:linkname libc_mkdirat libc_mkdirat
//go:cgo_import_dynamic libc_mkdirat mkdirat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1650,7 +1557,6 @@ func Mkfifo(path string, mode uint32) (err error) {
func libc_mkfifo_trampoline()
-//go:linkname libc_mkfifo libc_mkfifo
//go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1670,7 +1576,6 @@ func Mknod(path string, mode uint32, dev int) (err error) {
func libc_mknod_trampoline()
-//go:linkname libc_mknod libc_mknod
//go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1691,7 +1596,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
func libc_open_trampoline()
-//go:linkname libc_open libc_open
//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1712,7 +1616,6 @@ func Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
func libc_openat_trampoline()
-//go:linkname libc_openat libc_openat
//go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1733,7 +1636,6 @@ func Pathconf(path string, name int) (val int, err error) {
func libc_pathconf_trampoline()
-//go:linkname libc_pathconf libc_pathconf
//go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1755,7 +1657,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
func libc_pread_trampoline()
-//go:linkname libc_pread libc_pread
//go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1777,7 +1678,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
func libc_pwrite_trampoline()
-//go:linkname libc_pwrite libc_pwrite
//go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1799,7 +1699,6 @@ func read(fd int, p []byte) (n int, err error) {
func libc_read_trampoline()
-//go:linkname libc_read libc_read
//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1826,7 +1725,6 @@ func Readlink(path string, buf []byte) (n int, err error) {
func libc_readlink_trampoline()
-//go:linkname libc_readlink libc_readlink
//go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1853,7 +1751,6 @@ func Readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
func libc_readlinkat_trampoline()
-//go:linkname libc_readlinkat libc_readlinkat
//go:cgo_import_dynamic libc_readlinkat readlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1878,7 +1775,6 @@ func Rename(from string, to string) (err error) {
func libc_rename_trampoline()
-//go:linkname libc_rename libc_rename
//go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1903,7 +1799,6 @@ func Renameat(fromfd int, from string, tofd int, to string) (err error) {
func libc_renameat_trampoline()
-//go:linkname libc_renameat libc_renameat
//go:cgo_import_dynamic libc_renameat renameat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1923,7 +1818,6 @@ func Revoke(path string) (err error) {
func libc_revoke_trampoline()
-//go:linkname libc_revoke libc_revoke
//go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1943,7 +1837,6 @@ func Rmdir(path string) (err error) {
func libc_rmdir_trampoline()
-//go:linkname libc_rmdir libc_rmdir
//go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1959,7 +1852,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
func libc_lseek_trampoline()
-//go:linkname libc_lseek libc_lseek
//go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1975,7 +1867,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
func libc_select_trampoline()
-//go:linkname libc_select libc_select
//go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1990,7 +1881,6 @@ func Setegid(egid int) (err error) {
func libc_setegid_trampoline()
-//go:linkname libc_setegid libc_setegid
//go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2005,7 +1895,6 @@ func Seteuid(euid int) (err error) {
func libc_seteuid_trampoline()
-//go:linkname libc_seteuid libc_seteuid
//go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2020,7 +1909,6 @@ func Setgid(gid int) (err error) {
func libc_setgid_trampoline()
-//go:linkname libc_setgid libc_setgid
//go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2040,7 +1928,6 @@ func Setlogin(name string) (err error) {
func libc_setlogin_trampoline()
-//go:linkname libc_setlogin libc_setlogin
//go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2055,7 +1942,6 @@ func Setpgid(pid int, pgid int) (err error) {
func libc_setpgid_trampoline()
-//go:linkname libc_setpgid libc_setpgid
//go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2070,7 +1956,6 @@ func Setpriority(which int, who int, prio int) (err error) {
func libc_setpriority_trampoline()
-//go:linkname libc_setpriority libc_setpriority
//go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2085,7 +1970,6 @@ func Setprivexec(flag int) (err error) {
func libc_setprivexec_trampoline()
-//go:linkname libc_setprivexec libc_setprivexec
//go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2100,7 +1984,6 @@ func Setregid(rgid int, egid int) (err error) {
func libc_setregid_trampoline()
-//go:linkname libc_setregid libc_setregid
//go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2115,7 +1998,6 @@ func Setreuid(ruid int, euid int) (err error) {
func libc_setreuid_trampoline()
-//go:linkname libc_setreuid libc_setreuid
//go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2130,7 +2012,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) {
func libc_setrlimit_trampoline()
-//go:linkname libc_setrlimit libc_setrlimit
//go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2146,7 +2027,6 @@ func Setsid() (pid int, err error) {
func libc_setsid_trampoline()
-//go:linkname libc_setsid libc_setsid
//go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2161,7 +2041,6 @@ func Settimeofday(tp *Timeval) (err error) {
func libc_settimeofday_trampoline()
-//go:linkname libc_settimeofday libc_settimeofday
//go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2176,7 +2055,6 @@ func Setuid(uid int) (err error) {
func libc_setuid_trampoline()
-//go:linkname libc_setuid libc_setuid
//go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2201,7 +2079,6 @@ func Symlink(path string, link string) (err error) {
func libc_symlink_trampoline()
-//go:linkname libc_symlink libc_symlink
//go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2226,7 +2103,6 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
func libc_symlinkat_trampoline()
-//go:linkname libc_symlinkat libc_symlinkat
//go:cgo_import_dynamic libc_symlinkat symlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2241,7 +2117,6 @@ func Sync() (err error) {
func libc_sync_trampoline()
-//go:linkname libc_sync libc_sync
//go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2261,7 +2136,6 @@ func Truncate(path string, length int64) (err error) {
func libc_truncate_trampoline()
-//go:linkname libc_truncate libc_truncate
//go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2274,7 +2148,6 @@ func Umask(newmask int) (oldmask int) {
func libc_umask_trampoline()
-//go:linkname libc_umask libc_umask
//go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2294,7 +2167,6 @@ func Undelete(path string) (err error) {
func libc_undelete_trampoline()
-//go:linkname libc_undelete libc_undelete
//go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2314,7 +2186,6 @@ func Unlink(path string) (err error) {
func libc_unlink_trampoline()
-//go:linkname libc_unlink libc_unlink
//go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2334,7 +2205,6 @@ func Unlinkat(dirfd int, path string, flags int) (err error) {
func libc_unlinkat_trampoline()
-//go:linkname libc_unlinkat libc_unlinkat
//go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2354,7 +2224,6 @@ func Unmount(path string, flags int) (err error) {
func libc_unmount_trampoline()
-//go:linkname libc_unmount libc_unmount
//go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2376,7 +2245,6 @@ func write(fd int, p []byte) (n int, err error) {
func libc_write_trampoline()
-//go:linkname libc_write libc_write
//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2392,7 +2260,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (
func libc_mmap_trampoline()
-//go:linkname libc_mmap libc_mmap
//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2407,7 +2274,6 @@ func munmap(addr uintptr, length uintptr) (err error) {
func libc_munmap_trampoline()
-//go:linkname libc_munmap libc_munmap
//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2444,7 +2310,6 @@ func Fstat(fd int, stat *Stat_t) (err error) {
func libc_fstat_trampoline()
-//go:linkname libc_fstat libc_fstat
//go:cgo_import_dynamic libc_fstat fstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2464,7 +2329,6 @@ func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
func libc_fstatat_trampoline()
-//go:linkname libc_fstatat libc_fstatat
//go:cgo_import_dynamic libc_fstatat fstatat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2479,7 +2343,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) {
func libc_fstatfs_trampoline()
-//go:linkname libc_fstatfs libc_fstatfs
//go:cgo_import_dynamic libc_fstatfs fstatfs "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2495,7 +2358,6 @@ func getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) {
func libc_getfsstat_trampoline()
-//go:linkname libc_getfsstat libc_getfsstat
//go:cgo_import_dynamic libc_getfsstat getfsstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2515,7 +2377,6 @@ func Lstat(path string, stat *Stat_t) (err error) {
func libc_lstat_trampoline()
-//go:linkname libc_lstat libc_lstat
//go:cgo_import_dynamic libc_lstat lstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2535,7 +2396,6 @@ func Stat(path string, stat *Stat_t) (err error) {
func libc_stat_trampoline()
-//go:linkname libc_stat libc_stat
//go:cgo_import_dynamic libc_stat stat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2555,5 +2415,4 @@ func Statfs(path string, stat *Statfs_t) (err error) {
func libc_statfs_trampoline()
-//go:linkname libc_statfs libc_statfs
//go:cgo_import_dynamic libc_statfs statfs "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.go
index d64e6c806f..870eb37abf 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.go
@@ -24,7 +24,6 @@ func closedir(dir uintptr) (err error) {
func libc_closedir_trampoline()
-//go:linkname libc_closedir libc_closedir
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -37,5 +36,4 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
func libc_readdir_r_trampoline()
-//go:linkname libc_readdir_r libc_readdir_r
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
index 23b65a5301..23be592a9f 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
@@ -25,7 +25,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
func libc_getgroups_trampoline()
-//go:linkname libc_getgroups libc_getgroups
//go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -40,7 +39,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) {
func libc_setgroups_trampoline()
-//go:linkname libc_setgroups libc_setgroups
//go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -56,7 +54,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
func libc_wait4_trampoline()
-//go:linkname libc_wait4 libc_wait4
//go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -72,7 +69,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
func libc_accept_trampoline()
-//go:linkname libc_accept libc_accept
//go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -87,7 +83,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_bind_trampoline()
-//go:linkname libc_bind libc_bind
//go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -102,7 +97,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
func libc_connect_trampoline()
-//go:linkname libc_connect libc_connect
//go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -118,7 +112,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
func libc_socket_trampoline()
-//go:linkname libc_socket libc_socket
//go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -133,7 +126,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen
func libc_getsockopt_trampoline()
-//go:linkname libc_getsockopt libc_getsockopt
//go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -148,7 +140,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr)
func libc_setsockopt_trampoline()
-//go:linkname libc_setsockopt libc_setsockopt
//go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -163,7 +154,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getpeername_trampoline()
-//go:linkname libc_getpeername libc_getpeername
//go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -178,7 +168,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
func libc_getsockname_trampoline()
-//go:linkname libc_getsockname libc_getsockname
//go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -193,7 +182,6 @@ func Shutdown(s int, how int) (err error) {
func libc_shutdown_trampoline()
-//go:linkname libc_shutdown libc_shutdown
//go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -208,7 +196,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
func libc_socketpair_trampoline()
-//go:linkname libc_socketpair libc_socketpair
//go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -230,7 +217,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
func libc_recvfrom_trampoline()
-//go:linkname libc_recvfrom libc_recvfrom
//go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -251,7 +237,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
func libc_sendto_trampoline()
-//go:linkname libc_sendto libc_sendto
//go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -267,7 +252,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_recvmsg_trampoline()
-//go:linkname libc_recvmsg libc_recvmsg
//go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -283,7 +267,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
func libc_sendmsg_trampoline()
-//go:linkname libc_sendmsg libc_sendmsg
//go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -299,7 +282,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne
func libc_kevent_trampoline()
-//go:linkname libc_kevent libc_kevent
//go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -319,7 +301,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) {
func libc_utimes_trampoline()
-//go:linkname libc_utimes libc_utimes
//go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -334,7 +315,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) {
func libc_futimes_trampoline()
-//go:linkname libc_futimes libc_futimes
//go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -350,7 +330,6 @@ func poll(fds *PollFd, nfds int, timeout int) (n int, err error) {
func libc_poll_trampoline()
-//go:linkname libc_poll libc_poll
//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -371,7 +350,6 @@ func Madvise(b []byte, behav int) (err error) {
func libc_madvise_trampoline()
-//go:linkname libc_madvise libc_madvise
//go:cgo_import_dynamic libc_madvise madvise "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -392,7 +370,6 @@ func Mlock(b []byte) (err error) {
func libc_mlock_trampoline()
-//go:linkname libc_mlock libc_mlock
//go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -407,7 +384,6 @@ func Mlockall(flags int) (err error) {
func libc_mlockall_trampoline()
-//go:linkname libc_mlockall libc_mlockall
//go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -428,7 +404,6 @@ func Mprotect(b []byte, prot int) (err error) {
func libc_mprotect_trampoline()
-//go:linkname libc_mprotect libc_mprotect
//go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -449,7 +424,6 @@ func Msync(b []byte, flags int) (err error) {
func libc_msync_trampoline()
-//go:linkname libc_msync libc_msync
//go:cgo_import_dynamic libc_msync msync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -470,7 +444,6 @@ func Munlock(b []byte) (err error) {
func libc_munlock_trampoline()
-//go:linkname libc_munlock libc_munlock
//go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -485,7 +458,6 @@ func Munlockall() (err error) {
func libc_munlockall_trampoline()
-//go:linkname libc_munlockall libc_munlockall
//go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -502,7 +474,6 @@ func pipe() (r int, w int, err error) {
func libc_pipe_trampoline()
-//go:linkname libc_pipe libc_pipe
//go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -528,7 +499,6 @@ func getxattr(path string, attr string, dest *byte, size int, position uint32, o
func libc_getxattr_trampoline()
-//go:linkname libc_getxattr libc_getxattr
//go:cgo_import_dynamic libc_getxattr getxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -549,7 +519,6 @@ func fgetxattr(fd int, attr string, dest *byte, size int, position uint32, optio
func libc_fgetxattr_trampoline()
-//go:linkname libc_fgetxattr libc_fgetxattr
//go:cgo_import_dynamic libc_fgetxattr fgetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -574,7 +543,6 @@ func setxattr(path string, attr string, data *byte, size int, position uint32, o
func libc_setxattr_trampoline()
-//go:linkname libc_setxattr libc_setxattr
//go:cgo_import_dynamic libc_setxattr setxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -594,7 +562,6 @@ func fsetxattr(fd int, attr string, data *byte, size int, position uint32, optio
func libc_fsetxattr_trampoline()
-//go:linkname libc_fsetxattr libc_fsetxattr
//go:cgo_import_dynamic libc_fsetxattr fsetxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -619,7 +586,6 @@ func removexattr(path string, attr string, options int) (err error) {
func libc_removexattr_trampoline()
-//go:linkname libc_removexattr libc_removexattr
//go:cgo_import_dynamic libc_removexattr removexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -639,7 +605,6 @@ func fremovexattr(fd int, attr string, options int) (err error) {
func libc_fremovexattr_trampoline()
-//go:linkname libc_fremovexattr libc_fremovexattr
//go:cgo_import_dynamic libc_fremovexattr fremovexattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -660,7 +625,6 @@ func listxattr(path string, dest *byte, size int, options int) (sz int, err erro
func libc_listxattr_trampoline()
-//go:linkname libc_listxattr libc_listxattr
//go:cgo_import_dynamic libc_listxattr listxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -676,7 +640,6 @@ func flistxattr(fd int, dest *byte, size int, options int) (sz int, err error) {
func libc_flistxattr_trampoline()
-//go:linkname libc_flistxattr libc_flistxattr
//go:cgo_import_dynamic libc_flistxattr flistxattr "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -691,7 +654,6 @@ func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintp
func libc_setattrlist_trampoline()
-//go:linkname libc_setattrlist libc_setattrlist
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -707,7 +669,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
func libc_fcntl_trampoline()
-//go:linkname libc_fcntl libc_fcntl
//go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -722,7 +683,6 @@ func kill(pid int, signum int, posix int) (err error) {
func libc_kill_trampoline()
-//go:linkname libc_kill libc_kill
//go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -737,7 +697,6 @@ func ioctl(fd int, req uint, arg uintptr) (err error) {
func libc_ioctl_trampoline()
-//go:linkname libc_ioctl libc_ioctl
//go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -758,7 +717,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
func libc_sysctl_trampoline()
-//go:linkname libc_sysctl libc_sysctl
//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -773,7 +731,6 @@ func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer
func libc_sendfile_trampoline()
-//go:linkname libc_sendfile libc_sendfile
//go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -793,7 +750,6 @@ func Access(path string, mode uint32) (err error) {
func libc_access_trampoline()
-//go:linkname libc_access libc_access
//go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -808,7 +764,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
func libc_adjtime_trampoline()
-//go:linkname libc_adjtime libc_adjtime
//go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -828,7 +783,6 @@ func Chdir(path string) (err error) {
func libc_chdir_trampoline()
-//go:linkname libc_chdir libc_chdir
//go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -848,7 +802,6 @@ func Chflags(path string, flags int) (err error) {
func libc_chflags_trampoline()
-//go:linkname libc_chflags libc_chflags
//go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -868,7 +821,6 @@ func Chmod(path string, mode uint32) (err error) {
func libc_chmod_trampoline()
-//go:linkname libc_chmod libc_chmod
//go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -888,7 +840,6 @@ func Chown(path string, uid int, gid int) (err error) {
func libc_chown_trampoline()
-//go:linkname libc_chown libc_chown
//go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -908,7 +859,6 @@ func Chroot(path string) (err error) {
func libc_chroot_trampoline()
-//go:linkname libc_chroot libc_chroot
//go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -923,7 +873,6 @@ func ClockGettime(clockid int32, time *Timespec) (err error) {
func libc_clock_gettime_trampoline()
-//go:linkname libc_clock_gettime libc_clock_gettime
//go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -938,7 +887,6 @@ func Close(fd int) (err error) {
func libc_close_trampoline()
-//go:linkname libc_close libc_close
//go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -963,7 +911,6 @@ func Clonefile(src string, dst string, flags int) (err error) {
func libc_clonefile_trampoline()
-//go:linkname libc_clonefile libc_clonefile
//go:cgo_import_dynamic libc_clonefile clonefile "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -988,7 +935,6 @@ func Clonefileat(srcDirfd int, src string, dstDirfd int, dst string, flags int)
func libc_clonefileat_trampoline()
-//go:linkname libc_clonefileat libc_clonefileat
//go:cgo_import_dynamic libc_clonefileat clonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1004,7 +950,6 @@ func Dup(fd int) (nfd int, err error) {
func libc_dup_trampoline()
-//go:linkname libc_dup libc_dup
//go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1019,7 +964,6 @@ func Dup2(from int, to int) (err error) {
func libc_dup2_trampoline()
-//go:linkname libc_dup2 libc_dup2
//go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1044,7 +988,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) {
func libc_exchangedata_trampoline()
-//go:linkname libc_exchangedata libc_exchangedata
//go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1056,7 +999,6 @@ func Exit(code int) {
func libc_exit_trampoline()
-//go:linkname libc_exit libc_exit
//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1076,7 +1018,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_faccessat_trampoline()
-//go:linkname libc_faccessat libc_faccessat
//go:cgo_import_dynamic libc_faccessat faccessat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1091,7 +1032,6 @@ func Fchdir(fd int) (err error) {
func libc_fchdir_trampoline()
-//go:linkname libc_fchdir libc_fchdir
//go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1106,7 +1046,6 @@ func Fchflags(fd int, flags int) (err error) {
func libc_fchflags_trampoline()
-//go:linkname libc_fchflags libc_fchflags
//go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1121,7 +1060,6 @@ func Fchmod(fd int, mode uint32) (err error) {
func libc_fchmod_trampoline()
-//go:linkname libc_fchmod libc_fchmod
//go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1141,7 +1079,6 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
func libc_fchmodat_trampoline()
-//go:linkname libc_fchmodat libc_fchmodat
//go:cgo_import_dynamic libc_fchmodat fchmodat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1156,7 +1093,6 @@ func Fchown(fd int, uid int, gid int) (err error) {
func libc_fchown_trampoline()
-//go:linkname libc_fchown libc_fchown
//go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1176,7 +1112,6 @@ func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
func libc_fchownat_trampoline()
-//go:linkname libc_fchownat libc_fchownat
//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1196,7 +1131,6 @@ func Fclonefileat(srcDirfd int, dstDirfd int, dst string, flags int) (err error)
func libc_fclonefileat_trampoline()
-//go:linkname libc_fclonefileat libc_fclonefileat
//go:cgo_import_dynamic libc_fclonefileat fclonefileat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1211,7 +1145,6 @@ func Flock(fd int, how int) (err error) {
func libc_flock_trampoline()
-//go:linkname libc_flock libc_flock
//go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1227,7 +1160,6 @@ func Fpathconf(fd int, name int) (val int, err error) {
func libc_fpathconf_trampoline()
-//go:linkname libc_fpathconf libc_fpathconf
//go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1242,7 +1174,6 @@ func Fsync(fd int) (err error) {
func libc_fsync_trampoline()
-//go:linkname libc_fsync libc_fsync
//go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1257,7 +1188,6 @@ func Ftruncate(fd int, length int64) (err error) {
func libc_ftruncate_trampoline()
-//go:linkname libc_ftruncate libc_ftruncate
//go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1279,7 +1209,6 @@ func Getcwd(buf []byte) (n int, err error) {
func libc_getcwd_trampoline()
-//go:linkname libc_getcwd libc_getcwd
//go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1292,7 +1221,6 @@ func Getdtablesize() (size int) {
func libc_getdtablesize_trampoline()
-//go:linkname libc_getdtablesize libc_getdtablesize
//go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1305,7 +1233,6 @@ func Getegid() (egid int) {
func libc_getegid_trampoline()
-//go:linkname libc_getegid libc_getegid
//go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1318,7 +1245,6 @@ func Geteuid() (uid int) {
func libc_geteuid_trampoline()
-//go:linkname libc_geteuid libc_geteuid
//go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1331,7 +1257,6 @@ func Getgid() (gid int) {
func libc_getgid_trampoline()
-//go:linkname libc_getgid libc_getgid
//go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1347,7 +1272,6 @@ func Getpgid(pid int) (pgid int, err error) {
func libc_getpgid_trampoline()
-//go:linkname libc_getpgid libc_getpgid
//go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1360,7 +1284,6 @@ func Getpgrp() (pgrp int) {
func libc_getpgrp_trampoline()
-//go:linkname libc_getpgrp libc_getpgrp
//go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1373,7 +1296,6 @@ func Getpid() (pid int) {
func libc_getpid_trampoline()
-//go:linkname libc_getpid libc_getpid
//go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1386,7 +1308,6 @@ func Getppid() (ppid int) {
func libc_getppid_trampoline()
-//go:linkname libc_getppid libc_getppid
//go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1402,7 +1323,6 @@ func Getpriority(which int, who int) (prio int, err error) {
func libc_getpriority_trampoline()
-//go:linkname libc_getpriority libc_getpriority
//go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1417,7 +1337,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) {
func libc_getrlimit_trampoline()
-//go:linkname libc_getrlimit libc_getrlimit
//go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1432,7 +1351,6 @@ func Getrusage(who int, rusage *Rusage) (err error) {
func libc_getrusage_trampoline()
-//go:linkname libc_getrusage libc_getrusage
//go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1448,7 +1366,6 @@ func Getsid(pid int) (sid int, err error) {
func libc_getsid_trampoline()
-//go:linkname libc_getsid libc_getsid
//go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1463,7 +1380,6 @@ func Gettimeofday(tp *Timeval) (err error) {
func libc_gettimeofday_trampoline()
-//go:linkname libc_gettimeofday libc_gettimeofday
//go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1476,7 +1392,6 @@ func Getuid() (uid int) {
func libc_getuid_trampoline()
-//go:linkname libc_getuid libc_getuid
//go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1489,7 +1404,6 @@ func Issetugid() (tainted bool) {
func libc_issetugid_trampoline()
-//go:linkname libc_issetugid libc_issetugid
//go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1505,7 +1419,6 @@ func Kqueue() (fd int, err error) {
func libc_kqueue_trampoline()
-//go:linkname libc_kqueue libc_kqueue
//go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1525,7 +1438,6 @@ func Lchown(path string, uid int, gid int) (err error) {
func libc_lchown_trampoline()
-//go:linkname libc_lchown libc_lchown
//go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1550,7 +1462,6 @@ func Link(path string, link string) (err error) {
func libc_link_trampoline()
-//go:linkname libc_link libc_link
//go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1575,7 +1486,6 @@ func Linkat(pathfd int, path string, linkfd int, link string, flags int) (err er
func libc_linkat_trampoline()
-//go:linkname libc_linkat libc_linkat
//go:cgo_import_dynamic libc_linkat linkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1590,7 +1500,6 @@ func Listen(s int, backlog int) (err error) {
func libc_listen_trampoline()
-//go:linkname libc_listen libc_listen
//go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1610,7 +1519,6 @@ func Mkdir(path string, mode uint32) (err error) {
func libc_mkdir_trampoline()
-//go:linkname libc_mkdir libc_mkdir
//go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1630,7 +1538,6 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
func libc_mkdirat_trampoline()
-//go:linkname libc_mkdirat libc_mkdirat
//go:cgo_import_dynamic libc_mkdirat mkdirat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1650,7 +1557,6 @@ func Mkfifo(path string, mode uint32) (err error) {
func libc_mkfifo_trampoline()
-//go:linkname libc_mkfifo libc_mkfifo
//go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1670,7 +1576,6 @@ func Mknod(path string, mode uint32, dev int) (err error) {
func libc_mknod_trampoline()
-//go:linkname libc_mknod libc_mknod
//go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1691,7 +1596,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
func libc_open_trampoline()
-//go:linkname libc_open libc_open
//go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1712,7 +1616,6 @@ func Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) {
func libc_openat_trampoline()
-//go:linkname libc_openat libc_openat
//go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1733,7 +1636,6 @@ func Pathconf(path string, name int) (val int, err error) {
func libc_pathconf_trampoline()
-//go:linkname libc_pathconf libc_pathconf
//go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1755,7 +1657,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
func libc_pread_trampoline()
-//go:linkname libc_pread libc_pread
//go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1777,7 +1678,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
func libc_pwrite_trampoline()
-//go:linkname libc_pwrite libc_pwrite
//go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1799,7 +1699,6 @@ func read(fd int, p []byte) (n int, err error) {
func libc_read_trampoline()
-//go:linkname libc_read libc_read
//go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1826,7 +1725,6 @@ func Readlink(path string, buf []byte) (n int, err error) {
func libc_readlink_trampoline()
-//go:linkname libc_readlink libc_readlink
//go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1853,7 +1751,6 @@ func Readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
func libc_readlinkat_trampoline()
-//go:linkname libc_readlinkat libc_readlinkat
//go:cgo_import_dynamic libc_readlinkat readlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1878,7 +1775,6 @@ func Rename(from string, to string) (err error) {
func libc_rename_trampoline()
-//go:linkname libc_rename libc_rename
//go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1903,7 +1799,6 @@ func Renameat(fromfd int, from string, tofd int, to string) (err error) {
func libc_renameat_trampoline()
-//go:linkname libc_renameat libc_renameat
//go:cgo_import_dynamic libc_renameat renameat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1923,7 +1818,6 @@ func Revoke(path string) (err error) {
func libc_revoke_trampoline()
-//go:linkname libc_revoke libc_revoke
//go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1943,7 +1837,6 @@ func Rmdir(path string) (err error) {
func libc_rmdir_trampoline()
-//go:linkname libc_rmdir libc_rmdir
//go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1959,7 +1852,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
func libc_lseek_trampoline()
-//go:linkname libc_lseek libc_lseek
//go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1975,7 +1867,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
func libc_select_trampoline()
-//go:linkname libc_select libc_select
//go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1990,7 +1881,6 @@ func Setegid(egid int) (err error) {
func libc_setegid_trampoline()
-//go:linkname libc_setegid libc_setegid
//go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2005,7 +1895,6 @@ func Seteuid(euid int) (err error) {
func libc_seteuid_trampoline()
-//go:linkname libc_seteuid libc_seteuid
//go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2020,7 +1909,6 @@ func Setgid(gid int) (err error) {
func libc_setgid_trampoline()
-//go:linkname libc_setgid libc_setgid
//go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2040,7 +1928,6 @@ func Setlogin(name string) (err error) {
func libc_setlogin_trampoline()
-//go:linkname libc_setlogin libc_setlogin
//go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2055,7 +1942,6 @@ func Setpgid(pid int, pgid int) (err error) {
func libc_setpgid_trampoline()
-//go:linkname libc_setpgid libc_setpgid
//go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2070,7 +1956,6 @@ func Setpriority(which int, who int, prio int) (err error) {
func libc_setpriority_trampoline()
-//go:linkname libc_setpriority libc_setpriority
//go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2085,7 +1970,6 @@ func Setprivexec(flag int) (err error) {
func libc_setprivexec_trampoline()
-//go:linkname libc_setprivexec libc_setprivexec
//go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2100,7 +1984,6 @@ func Setregid(rgid int, egid int) (err error) {
func libc_setregid_trampoline()
-//go:linkname libc_setregid libc_setregid
//go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2115,7 +1998,6 @@ func Setreuid(ruid int, euid int) (err error) {
func libc_setreuid_trampoline()
-//go:linkname libc_setreuid libc_setreuid
//go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2130,7 +2012,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) {
func libc_setrlimit_trampoline()
-//go:linkname libc_setrlimit libc_setrlimit
//go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2146,7 +2027,6 @@ func Setsid() (pid int, err error) {
func libc_setsid_trampoline()
-//go:linkname libc_setsid libc_setsid
//go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2161,7 +2041,6 @@ func Settimeofday(tp *Timeval) (err error) {
func libc_settimeofday_trampoline()
-//go:linkname libc_settimeofday libc_settimeofday
//go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2176,7 +2055,6 @@ func Setuid(uid int) (err error) {
func libc_setuid_trampoline()
-//go:linkname libc_setuid libc_setuid
//go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2201,7 +2079,6 @@ func Symlink(path string, link string) (err error) {
func libc_symlink_trampoline()
-//go:linkname libc_symlink libc_symlink
//go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2226,7 +2103,6 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
func libc_symlinkat_trampoline()
-//go:linkname libc_symlinkat libc_symlinkat
//go:cgo_import_dynamic libc_symlinkat symlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2241,7 +2117,6 @@ func Sync() (err error) {
func libc_sync_trampoline()
-//go:linkname libc_sync libc_sync
//go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2261,7 +2136,6 @@ func Truncate(path string, length int64) (err error) {
func libc_truncate_trampoline()
-//go:linkname libc_truncate libc_truncate
//go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2274,7 +2148,6 @@ func Umask(newmask int) (oldmask int) {
func libc_umask_trampoline()
-//go:linkname libc_umask libc_umask
//go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2294,7 +2167,6 @@ func Undelete(path string) (err error) {
func libc_undelete_trampoline()
-//go:linkname libc_undelete libc_undelete
//go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2314,7 +2186,6 @@ func Unlink(path string) (err error) {
func libc_unlink_trampoline()
-//go:linkname libc_unlink libc_unlink
//go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2334,7 +2205,6 @@ func Unlinkat(dirfd int, path string, flags int) (err error) {
func libc_unlinkat_trampoline()
-//go:linkname libc_unlinkat libc_unlinkat
//go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2354,7 +2224,6 @@ func Unmount(path string, flags int) (err error) {
func libc_unmount_trampoline()
-//go:linkname libc_unmount libc_unmount
//go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2376,7 +2245,6 @@ func write(fd int, p []byte) (n int, err error) {
func libc_write_trampoline()
-//go:linkname libc_write libc_write
//go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2392,7 +2260,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (
func libc_mmap_trampoline()
-//go:linkname libc_mmap libc_mmap
//go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2407,7 +2274,6 @@ func munmap(addr uintptr, length uintptr) (err error) {
func libc_munmap_trampoline()
-//go:linkname libc_munmap libc_munmap
//go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2444,7 +2310,6 @@ func Fstat(fd int, stat *Stat_t) (err error) {
func libc_fstat_trampoline()
-//go:linkname libc_fstat libc_fstat
//go:cgo_import_dynamic libc_fstat fstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2464,7 +2329,6 @@ func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) {
func libc_fstatat_trampoline()
-//go:linkname libc_fstatat libc_fstatat
//go:cgo_import_dynamic libc_fstatat fstatat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2479,7 +2343,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) {
func libc_fstatfs_trampoline()
-//go:linkname libc_fstatfs libc_fstatfs
//go:cgo_import_dynamic libc_fstatfs fstatfs "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2495,7 +2358,6 @@ func getfsstat(buf unsafe.Pointer, size uintptr, flags int) (n int, err error) {
func libc_getfsstat_trampoline()
-//go:linkname libc_getfsstat libc_getfsstat
//go:cgo_import_dynamic libc_getfsstat getfsstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2515,7 +2377,6 @@ func Lstat(path string, stat *Stat_t) (err error) {
func libc_lstat_trampoline()
-//go:linkname libc_lstat libc_lstat
//go:cgo_import_dynamic libc_lstat lstat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2530,7 +2391,6 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
func libc_ptrace_trampoline()
-//go:linkname libc_ptrace libc_ptrace
//go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2550,7 +2410,6 @@ func Stat(path string, stat *Stat_t) (err error) {
func libc_stat_trampoline()
-//go:linkname libc_stat libc_stat
//go:cgo_import_dynamic libc_stat stat "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -2570,5 +2429,4 @@ func Statfs(path string, stat *Statfs_t) (err error) {
func libc_statfs_trampoline()
-//go:linkname libc_statfs libc_statfs
//go:cgo_import_dynamic libc_statfs statfs "/usr/lib/libSystem.B.dylib"
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go
index 830fbb35c0..725b4bee27 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_386.go
@@ -269,6 +269,7 @@ const (
SizeofSockaddrDatalink = 0x14
SizeofSockaddrCtl = 0x20
SizeofLinger = 0x8
+ SizeofIovec = 0x8
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x1c
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go
index e53a7c49ff..080ffce325 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go
@@ -274,6 +274,7 @@ const (
SizeofSockaddrDatalink = 0x14
SizeofSockaddrCtl = 0x20
SizeofLinger = 0x8
+ SizeofIovec = 0x10
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x30
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go
index 98be973ef9..f2a77bc4e2 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm.go
@@ -269,6 +269,7 @@ const (
SizeofSockaddrDatalink = 0x14
SizeofSockaddrCtl = 0x20
SizeofLinger = 0x8
+ SizeofIovec = 0x8
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x1c
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go
index ddae5afe1b..c9492428bf 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go
@@ -274,6 +274,7 @@ const (
SizeofSockaddrDatalink = 0x14
SizeofSockaddrCtl = 0x20
SizeofLinger = 0x8
+ SizeofIovec = 0x10
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x30
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/dll_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/dll_windows.go
index 9cd147b7e3..115341fba6 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/dll_windows.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/dll_windows.go
@@ -391,7 +391,6 @@ func loadLibraryEx(name string, system bool) (*DLL, error) {
var flags uintptr
if system {
if canDoSearchSystem32() {
- const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
flags = LOAD_LIBRARY_SEARCH_SYSTEM32
} else if isBaseName(name) {
// WindowsXP or unpatched Windows machine
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go
index 14906485f3..69eb462c59 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go
@@ -624,6 +624,7 @@ func (tml *Tokenmandatorylabel) Size() uint32 {
// Authorization Functions
//sys checkTokenMembership(tokenHandle Token, sidToCheck *SID, isMember *int32) (err error) = advapi32.CheckTokenMembership
+//sys isTokenRestricted(tokenHandle Token) (ret bool, err error) [!failretval] = advapi32.IsTokenRestricted
//sys OpenProcessToken(process Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
//sys OpenThreadToken(thread Handle, access uint32, openAsSelf bool, token *Token) (err error) = advapi32.OpenThreadToken
//sys ImpersonateSelf(impersonationlevel uint32) (err error) = advapi32.ImpersonateSelf
@@ -837,6 +838,16 @@ func (t Token) IsMember(sid *SID) (bool, error) {
return b != 0, nil
}
+// IsRestricted reports whether the access token t is a restricted token.
+func (t Token) IsRestricted() (isRestricted bool, err error) {
+ isRestricted, err = isTokenRestricted(t)
+ if !isRestricted && err == syscall.EINVAL {
+ // If err is EINVAL, this returned ERROR_SUCCESS indicating a non-restricted token.
+ err = nil
+ }
+ return
+}
+
const (
WTS_CONSOLE_CONNECT = 0x1
WTS_CONSOLE_DISCONNECT = 0x2
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/service.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/service.go
index f54ff90aac..b269850d06 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/service.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/service.go
@@ -128,6 +128,10 @@ const (
SERVICE_NOTIFY_CREATED = 0x00000080
SERVICE_NOTIFY_DELETED = 0x00000100
SERVICE_NOTIFY_DELETE_PENDING = 0x00000200
+
+ SC_EVENT_DATABASE_CHANGE = 0
+ SC_EVENT_PROPERTY_CHANGE = 1
+ SC_EVENT_STATUS_CHANGE = 2
)
type SERVICE_STATUS struct {
@@ -229,3 +233,5 @@ type QUERY_SERVICE_LOCK_STATUS struct {
//sys EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) = advapi32.EnumServicesStatusExW
//sys QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceStatusEx
//sys NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) = advapi32.NotifyServiceStatusChangeW
+//sys SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
+//sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go
index 86a46f7713..0f17fb75eb 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go
@@ -170,6 +170,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys GetProcAddress(module Handle, procname string) (proc uintptr, err error)
//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
+//sys SetDefaultDllDirectories(directoryFlags uint32) (err error)
+//sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
//sys GetVersion() (ver uint32, err error)
//sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
//sys ExitProcess(exitcode uint32)
@@ -258,7 +260,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) = mswsock.TransmitFile
//sys ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree bool, mask uint32, retlen *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) = kernel32.ReadDirectoryChangesW
//sys CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, err error) = crypt32.CertOpenSystemStoreW
-//sys CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptProv uintptr, flags uint32, para uintptr) (handle Handle, err error) [failretval==InvalidHandle] = crypt32.CertOpenStore
+//sys CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptProv uintptr, flags uint32, para uintptr) (handle Handle, err error) = crypt32.CertOpenStore
//sys CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (context *CertContext, err error) [failretval==nil] = crypt32.CertEnumCertificatesInStore
//sys CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) = crypt32.CertAddCertificateContextToStore
//sys CertCloseStore(store Handle, flags uint32) (err error) = crypt32.CertCloseStore
@@ -273,6 +275,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW
//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
//sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
+//sys RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, event Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
//sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go
index e7ae37f884..bbede40412 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go
@@ -1801,3 +1801,40 @@ const (
FileCaseSensitiveInfo = 23
FileNormalizedNameInfo = 24
)
+
+// LoadLibrary flags for determining from where to search for a DLL
+const (
+ DONT_RESOLVE_DLL_REFERENCES = 0x1
+ LOAD_LIBRARY_AS_DATAFILE = 0x2
+ LOAD_WITH_ALTERED_SEARCH_PATH = 0x8
+ LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x10
+ LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20
+ LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x40
+ LOAD_LIBRARY_REQUIRE_SIGNED_TARGET = 0x80
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100
+ LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x200
+ LOAD_LIBRARY_SEARCH_USER_DIRS = 0x400
+ LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x800
+ LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000
+ LOAD_LIBRARY_SAFE_CURRENT_DIRS = 0x00002000
+ LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER = 0x00004000
+ LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY = 0x00008000
+)
+
+// RegNotifyChangeKeyValue notifyFilter flags.
+const (
+ // REG_NOTIFY_CHANGE_NAME notifies the caller if a subkey is added or deleted.
+ REG_NOTIFY_CHANGE_NAME = 0x00000001
+
+ // REG_NOTIFY_CHANGE_ATTRIBUTES notifies the caller of changes to the attributes of the key, such as the security descriptor information.
+ REG_NOTIFY_CHANGE_ATTRIBUTES = 0x00000002
+
+ // REG_NOTIFY_CHANGE_LAST_SET notifies the caller of changes to a value of the key. This can include adding or deleting a value, or changing an existing value.
+ REG_NOTIFY_CHANGE_LAST_SET = 0x00000004
+
+ // REG_NOTIFY_CHANGE_SECURITY notifies the caller of changes to the security descriptor of the key.
+ REG_NOTIFY_CHANGE_SECURITY = 0x00000008
+
+ // REG_NOTIFY_THREAD_AGNOSTIC indicates that the lifetime of the registration must not be tied to the lifetime of the thread issuing the RegNotifyChangeKeyValue call. Note: This flag value is only supported in Windows 8 and later.
+ REG_NOTIFY_THREAD_AGNOSTIC = 0x10000000
+)
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index 8fbef7da66..72a91a5f16 100644
--- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -46,6 +46,7 @@ var (
modntdll = NewLazySystemDLL("ntdll.dll")
modole32 = NewLazySystemDLL("ole32.dll")
modpsapi = NewLazySystemDLL("psapi.dll")
+ modsechost = NewLazySystemDLL("sechost.dll")
modsecur32 = NewLazySystemDLL("secur32.dll")
modshell32 = NewLazySystemDLL("shell32.dll")
moduser32 = NewLazySystemDLL("user32.dll")
@@ -95,6 +96,7 @@ var (
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
procInitializeSecurityDescriptor = modadvapi32.NewProc("InitializeSecurityDescriptor")
procInitiateSystemShutdownExW = modadvapi32.NewProc("InitiateSystemShutdownExW")
+ procIsTokenRestricted = modadvapi32.NewProc("IsTokenRestricted")
procIsValidSecurityDescriptor = modadvapi32.NewProc("IsValidSecurityDescriptor")
procIsValidSid = modadvapi32.NewProc("IsValidSid")
procIsWellKnownSid = modadvapi32.NewProc("IsWellKnownSid")
@@ -115,6 +117,7 @@ var (
procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx")
procRegCloseKey = modadvapi32.NewProc("RegCloseKey")
procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
+ procRegNotifyChangeKeyValue = modadvapi32.NewProc("RegNotifyChangeKeyValue")
procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW")
procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
@@ -279,6 +282,8 @@ var (
procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition")
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW")
+ procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories")
+ procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW")
procSetEndOfFile = modkernel32.NewProc("SetEndOfFile")
procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW")
procSetErrorMode = modkernel32.NewProc("SetErrorMode")
@@ -326,6 +331,8 @@ var (
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procStringFromGUID2 = modole32.NewProc("StringFromGUID2")
procEnumProcesses = modpsapi.NewProc("EnumProcesses")
+ procSubscribeServiceChangeNotifications = modsechost.NewProc("SubscribeServiceChangeNotifications")
+ procUnsubscribeServiceChangeNotifications = modsechost.NewProc("UnsubscribeServiceChangeNotifications")
procGetUserNameExW = modsecur32.NewProc("GetUserNameExW")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW")
@@ -756,6 +763,15 @@ func InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint
return
}
+func isTokenRestricted(tokenHandle Token) (ret bool, err error) {
+ r0, _, e1 := syscall.Syscall(procIsTokenRestricted.Addr(), 1, uintptr(tokenHandle), 0, 0)
+ ret = r0 != 0
+ if !ret {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func isValidSecurityDescriptor(sd *SECURITY_DESCRIPTOR) (isValid bool) {
r0, _, _ := syscall.Syscall(procIsValidSecurityDescriptor.Addr(), 1, uintptr(unsafe.Pointer(sd)), 0, 0)
isValid = r0 != 0
@@ -916,6 +932,22 @@ func RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reser
return
}
+func RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, event Handle, asynchronous bool) (regerrno error) {
+ var _p0 uint32
+ if watchSubtree {
+ _p0 = 1
+ }
+ var _p1 uint32
+ if asynchronous {
+ _p1 = 1
+ }
+ r0, _, _ := syscall.Syscall6(procRegNotifyChangeKeyValue.Addr(), 5, uintptr(key), uintptr(_p0), uintptr(notifyFilter), uintptr(event), uintptr(_p1), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
func RegOpenKeyEx(key Handle, subkey *uint16, options uint32, desiredAccess uint32, result *Handle) (regerrno error) {
r0, _, _ := syscall.Syscall6(procRegOpenKeyExW.Addr(), 5, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(options), uintptr(desiredAccess), uintptr(unsafe.Pointer(result)), 0)
if r0 != 0 {
@@ -1181,7 +1213,7 @@ func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, a
func CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptProv uintptr, flags uint32, para uintptr) (handle Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCertOpenStore.Addr(), 5, uintptr(storeProvider), uintptr(msgAndCertEncodingType), uintptr(cryptProv), uintptr(flags), uintptr(para), 0)
handle = Handle(r0)
- if handle == InvalidHandle {
+ if handle == 0 {
err = errnoErr(e1)
}
return
@@ -2366,6 +2398,31 @@ func SetCurrentDirectory(path *uint16) (err error) {
return
}
+func SetDefaultDllDirectories(directoryFlags uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetDefaultDllDirectories.Addr(), 1, uintptr(directoryFlags), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func SetDllDirectory(path string) (err error) {
+ var _p0 *uint16
+ _p0, err = syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return
+ }
+ return _SetDllDirectory(_p0)
+}
+
+func _SetDllDirectory(path *uint16) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetDllDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
+ if r1 == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
func SetEndOfFile(handle Handle) (err error) {
r1, _, e1 := syscall.Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0)
if r1 == 0 {
@@ -2752,6 +2809,27 @@ func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) {
return
}
+func SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) {
+ ret = procSubscribeServiceChangeNotifications.Find()
+ if ret != nil {
+ return
+ }
+ r0, _, _ := syscall.Syscall6(procSubscribeServiceChangeNotifications.Addr(), 5, uintptr(service), uintptr(eventType), uintptr(callback), uintptr(callbackCtx), uintptr(unsafe.Pointer(subscription)), 0)
+ if r0 != 0 {
+ ret = syscall.Errno(r0)
+ }
+ return
+}
+
+func UnsubscribeServiceChangeNotifications(subscription uintptr) (err error) {
+ err = procUnsubscribeServiceChangeNotifications.Find()
+ if err != nil {
+ return
+ }
+ syscall.Syscall(procUnsubscribeServiceChangeNotifications.Addr(), 1, uintptr(subscription), 0, 0)
+ return
+}
+
func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
if r1&0xff == 0 {
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/AUTHORS b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/AUTHORS
new file mode 100644
index 0000000000..15167cd746
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTING.md b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTING.md
new file mode 100644
index 0000000000..d0485e887a
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+## Filing issues
+
+When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTORS b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTORS
new file mode 100644
index 0000000000..1c4577e968
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/LICENSE b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/LICENSE
new file mode 100644
index 0000000000..6a66aea5ea
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/PATENTS b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/PATENTS
new file mode 100644
index 0000000000..733099041f
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/README.md b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/README.md
new file mode 100644
index 0000000000..e0f390cb22
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/README.md
@@ -0,0 +1,17 @@
+# Go terminal/console support
+
+This repository provides Go terminal and console support packages.
+
+## Download/Install
+
+The easiest way to install is to run `go get -u golang.org/x/term`. You can
+also manually git clone the repository to `$GOPATH/src/golang.org/x/term`.
+
+## Report Issues / Send Patches
+
+This repository uses Gerrit for code changes. To learn how to submit changes to
+this repository, see https://golang.org/doc/contribute.html.
+
+The main issue tracker for the term repository is located at
+https://github.com/golang/go/issues. Prefix your issue with "x/term:" in the
+subject line, so it is easy to find.
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.mod b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.mod
new file mode 100644
index 0000000000..d45f52851e
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.mod
@@ -0,0 +1,5 @@
+module golang.org/x/term
+
+go 1.11
+
+require golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.sum b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.sum
new file mode 100644
index 0000000000..de9e09c654
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term.go
new file mode 100644
index 0000000000..69931cc84c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term.go
@@ -0,0 +1,58 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package term provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package term
+
+// State contains the state of a terminal.
+type State struct {
+ state
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ return isTerminal(fd)
+}
+
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ return makeRaw(fd)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ return getState(fd)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+ return restore(fd, oldState)
+}
+
+// GetSize returns the visible dimensions of the given terminal.
+//
+// These dimensions don't include any scrollback buffer height.
+func GetSize(fd int) (width, height int, err error) {
+ return getSize(fd)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ return readPassword(fd)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_plan9.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_plan9.go
new file mode 100644
index 0000000000..21afa55cdb
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_plan9.go
@@ -0,0 +1,42 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import (
+ "fmt"
+ "runtime"
+
+ "golang.org/x/sys/plan9"
+)
+
+type state struct{}
+
+func isTerminal(fd int) bool {
+ path, err := plan9.Fd2path(fd)
+ if err != nil {
+ return false
+ }
+ return path == "/dev/cons" || path == "/mnt/term/dev/cons"
+}
+
+func makeRaw(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getState(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func restore(fd int, state *State) error {
+ return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getSize(fd int) (width, height int, err error) {
+ return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func readPassword(fd int) ([]byte, error) {
+ return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_solaris.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_solaris.go
new file mode 100644
index 0000000000..b9da29744b
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_solaris.go
@@ -0,0 +1,111 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import (
+ "io"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+// State contains the state of a terminal.
+type state struct {
+ termios unix.Termios
+}
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+ return err == nil
+}
+
+func readPassword(fd int) ([]byte, error) {
+ // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
+ val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+ oldState := *val
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ newState.Lflag |= syscall.ICANON | syscall.ISIG
+ newState.Iflag |= syscall.ICRNL
+ err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
+ if err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(fd, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
+
+func makeRaw(fd int) (*State, error) {
+ // see http://cr.illumos.org/~webrev/andy_js/1060/
+ termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+
+ oldState := State{state{termios: *termios}}
+
+ termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+ termios.Oflag &^= unix.OPOST
+ termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+ termios.Cflag &^= unix.CSIZE | unix.PARENB
+ termios.Cflag |= unix.CS8
+ termios.Cc[unix.VMIN] = 1
+ termios.Cc[unix.VTIME] = 0
+
+ if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+func restore(fd int, oldState *State) error {
+ return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
+}
+
+func getState(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{state{termios: *termios}}, nil
+}
+
+func getSize(fd int) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return 0, 0, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix.go
new file mode 100644
index 0000000000..4c60e457d0
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix.go
@@ -0,0 +1,91 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd zos
+
+package term
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+type state struct {
+ termios unix.Termios
+}
+
+func isTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
+func makeRaw(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ oldState := State{state{termios: *termios}}
+
+ // This attempts to replicate the behaviour documented for cfmakeraw in
+ // the termios(3) manpage.
+ termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+ termios.Oflag &^= unix.OPOST
+ termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+ termios.Cflag &^= unix.CSIZE | unix.PARENB
+ termios.Cflag |= unix.CS8
+ termios.Cc[unix.VMIN] = 1
+ termios.Cc[unix.VTIME] = 0
+ if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+func getState(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{state{termios: *termios}}, nil
+}
+
+func restore(fd int, state *State) error {
+ return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
+}
+
+func getSize(fd int) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return -1, -1, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+ return unix.Read(int(r), buf)
+}
+
+func readPassword(fd int) ([]byte, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ newState := *termios
+ newState.Lflag &^= unix.ECHO
+ newState.Lflag |= unix.ICANON | unix.ISIG
+ newState.Iflag |= unix.ICRNL
+ if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
+
+ return readPasswordLine(passwordReader(fd))
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_aix.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_aix.go
new file mode 100644
index 0000000000..2d5efd26ad
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_aix.go
@@ -0,0 +1,10 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_bsd.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_bsd.go
new file mode 100644
index 0000000000..3342be00b4
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_bsd.go
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+const ioctlWriteTermios = unix.TIOCSETA
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_linux.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_linux.go
new file mode 100644
index 0000000000..2d5efd26ad
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_linux.go
@@ -0,0 +1,10 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_zos.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_zos.go
new file mode 100644
index 0000000000..b85ab89989
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unix_zos.go
@@ -0,0 +1,10 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unsupported.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unsupported.go
new file mode 100644
index 0000000000..8b5d1bad00
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_unsupported.go
@@ -0,0 +1,38 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9
+
+package term
+
+import (
+ "fmt"
+ "runtime"
+)
+
+type state struct{}
+
+func isTerminal(fd int) bool {
+ return false
+}
+
+func makeRaw(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getState(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func restore(fd int, state *State) error {
+ return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getSize(fd int) (width, height int, err error) {
+ return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func readPassword(fd int) ([]byte, error) {
+ return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_windows.go
new file mode 100644
index 0000000000..465f560604
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/term_windows.go
@@ -0,0 +1,79 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import (
+ "os"
+
+ "golang.org/x/sys/windows"
+)
+
+type state struct {
+ mode uint32
+}
+
+func isTerminal(fd int) bool {
+ var st uint32
+ err := windows.GetConsoleMode(windows.Handle(fd), &st)
+ return err == nil
+}
+
+func makeRaw(fd int) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+ if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
+ return nil, err
+ }
+ return &State{state{st}}, nil
+}
+
+func getState(fd int) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ return &State{state{st}}, nil
+}
+
+func restore(fd int, state *State) error {
+ return windows.SetConsoleMode(windows.Handle(fd), state.mode)
+}
+
+func getSize(fd int) (width, height int, err error) {
+ var info windows.ConsoleScreenBufferInfo
+ if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
+ return 0, 0, err
+ }
+ return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
+}
+
+func readPassword(fd int) ([]byte, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ old := st
+
+ st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT)
+ st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT)
+ if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
+ return nil, err
+ }
+
+ defer windows.SetConsoleMode(windows.Handle(fd), old)
+
+ var h windows.Handle
+ p, _ := windows.GetCurrentProcess()
+ if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
+ return nil, err
+ }
+
+ f := os.NewFile(uintptr(h), "stdin")
+ defer f.Close()
+ return readPasswordLine(f)
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go
new file mode 100644
index 0000000000..535ab8257c
--- /dev/null
+++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go
@@ -0,0 +1,987 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package term
+
+import (
+ "bytes"
+ "io"
+ "runtime"
+ "strconv"
+ "sync"
+ "unicode/utf8"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+ // Foreground colors
+ Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+ // Reset all attributes
+ Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+ Black: []byte{keyEscape, '[', '3', '0', 'm'},
+ Red: []byte{keyEscape, '[', '3', '1', 'm'},
+ Green: []byte{keyEscape, '[', '3', '2', 'm'},
+ Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
+ Blue: []byte{keyEscape, '[', '3', '4', 'm'},
+ Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+ Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
+ White: []byte{keyEscape, '[', '3', '7', 'm'},
+
+ Reset: []byte{keyEscape, '[', '0', 'm'},
+}
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal struct {
+ // AutoCompleteCallback, if non-null, is called for each keypress with
+ // the full input line and the current position of the cursor (in
+ // bytes, as an index into |line|). If it returns ok=false, the key
+ // press is processed normally. Otherwise it returns a replacement line
+ // and the new cursor position.
+ AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
+
+ // Escape contains a pointer to the escape codes for this terminal.
+ // It's always a valid pointer, although the escape codes themselves
+ // may be empty if the terminal doesn't support them.
+ Escape *EscapeCodes
+
+ // lock protects the terminal and the state in this object from
+ // concurrent processing of a key press and a Write() call.
+ lock sync.Mutex
+
+ c io.ReadWriter
+ prompt []rune
+
+ // line is the current line being entered.
+ line []rune
+ // pos is the logical position of the cursor in line
+ pos int
+ // echo is true if local echo is enabled
+ echo bool
+ // pasteActive is true iff there is a bracketed paste operation in
+ // progress.
+ pasteActive bool
+
+ // cursorX contains the current X value of the cursor where the left
+ // edge is 0. cursorY contains the row number where the first row of
+ // the current line is 0.
+ cursorX, cursorY int
+ // maxLine is the greatest value of cursorY so far.
+ maxLine int
+
+ termWidth, termHeight int
+
+ // outBuf contains the terminal data to be sent.
+ outBuf []byte
+ // remainder contains the remainder of any partial key sequences after
+ // a read. It aliases into inBuf.
+ remainder []byte
+ inBuf [256]byte
+
+ // history contains previously entered commands so that they can be
+ // accessed with the up and down keys.
+ history stRingBuffer
+ // historyIndex stores the currently accessed history entry, where zero
+ // means the immediately previous entry.
+ historyIndex int
+ // When navigating up and down the history it's possible to return to
+ // the incomplete, initial line. That value is stored in
+ // historyPending.
+ historyPending string
+}
+
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+ return &Terminal{
+ Escape: &vt100EscapeCodes,
+ c: c,
+ prompt: []rune(prompt),
+ termWidth: 80,
+ termHeight: 24,
+ echo: true,
+ historyIndex: -1,
+ }
+}
+
+const (
+ keyCtrlC = 3
+ keyCtrlD = 4
+ keyCtrlU = 21
+ keyEnter = '\r'
+ keyEscape = 27
+ keyBackspace = 127
+ keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
+ keyUp
+ keyDown
+ keyLeft
+ keyRight
+ keyAltLeft
+ keyAltRight
+ keyHome
+ keyEnd
+ keyDeleteWord
+ keyDeleteLine
+ keyClearScreen
+ keyPasteStart
+ keyPasteEnd
+)
+
+var (
+ crlf = []byte{'\r', '\n'}
+ pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
+ pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
+)
+
+// bytesToKey tries to parse a key sequence from b. If successful, it returns
+// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
+func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
+ if len(b) == 0 {
+ return utf8.RuneError, nil
+ }
+
+ if !pasteActive {
+ switch b[0] {
+ case 1: // ^A
+ return keyHome, b[1:]
+ case 2: // ^B
+ return keyLeft, b[1:]
+ case 5: // ^E
+ return keyEnd, b[1:]
+ case 6: // ^F
+ return keyRight, b[1:]
+ case 8: // ^H
+ return keyBackspace, b[1:]
+ case 11: // ^K
+ return keyDeleteLine, b[1:]
+ case 12: // ^L
+ return keyClearScreen, b[1:]
+ case 23: // ^W
+ return keyDeleteWord, b[1:]
+ case 14: // ^N
+ return keyDown, b[1:]
+ case 16: // ^P
+ return keyUp, b[1:]
+ }
+ }
+
+ if b[0] != keyEscape {
+ if !utf8.FullRune(b) {
+ return utf8.RuneError, b
+ }
+ r, l := utf8.DecodeRune(b)
+ return r, b[l:]
+ }
+
+ if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
+ switch b[2] {
+ case 'A':
+ return keyUp, b[3:]
+ case 'B':
+ return keyDown, b[3:]
+ case 'C':
+ return keyRight, b[3:]
+ case 'D':
+ return keyLeft, b[3:]
+ case 'H':
+ return keyHome, b[3:]
+ case 'F':
+ return keyEnd, b[3:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
+ switch b[5] {
+ case 'C':
+ return keyAltRight, b[6:]
+ case 'D':
+ return keyAltLeft, b[6:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
+ return keyPasteStart, b[6:]
+ }
+
+ if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
+ return keyPasteEnd, b[6:]
+ }
+
+ // If we get here then we have a key that we don't recognise, or a
+ // partial sequence. It's not clear how one should find the end of a
+ // sequence without knowing them all, but it seems that [a-zA-Z~] only
+ // appears at the end of a sequence.
+ for i, c := range b[0:] {
+ if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
+ return keyUnknown, b[i+1:]
+ }
+ }
+
+ return utf8.RuneError, b
+}
+
+// queue appends data to the end of t.outBuf
+func (t *Terminal) queue(data []rune) {
+ t.outBuf = append(t.outBuf, []byte(string(data))...)
+}
+
+var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
+var space = []rune{' '}
+
+func isPrintable(key rune) bool {
+ isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+ return key >= 32 && !isInSurrogateArea
+}
+
+// moveCursorToPos appends data to t.outBuf which will move the cursor to the
+// given, logical position in the text.
+func (t *Terminal) moveCursorToPos(pos int) {
+ if !t.echo {
+ return
+ }
+
+ x := visualLength(t.prompt) + pos
+ y := x / t.termWidth
+ x = x % t.termWidth
+
+ up := 0
+ if y < t.cursorY {
+ up = t.cursorY - y
+ }
+
+ down := 0
+ if y > t.cursorY {
+ down = y - t.cursorY
+ }
+
+ left := 0
+ if x < t.cursorX {
+ left = t.cursorX - x
+ }
+
+ right := 0
+ if x > t.cursorX {
+ right = x - t.cursorX
+ }
+
+ t.cursorX = x
+ t.cursorY = y
+ t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
+ m := []rune{}
+
+ // 1 unit up can be expressed as ^[[A or ^[A
+ // 5 units up can be expressed as ^[[5A
+
+ if up == 1 {
+ m = append(m, keyEscape, '[', 'A')
+ } else if up > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(up))...)
+ m = append(m, 'A')
+ }
+
+ if down == 1 {
+ m = append(m, keyEscape, '[', 'B')
+ } else if down > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(down))...)
+ m = append(m, 'B')
+ }
+
+ if right == 1 {
+ m = append(m, keyEscape, '[', 'C')
+ } else if right > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(right))...)
+ m = append(m, 'C')
+ }
+
+ if left == 1 {
+ m = append(m, keyEscape, '[', 'D')
+ } else if left > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(left))...)
+ m = append(m, 'D')
+ }
+
+ t.queue(m)
+}
+
+func (t *Terminal) clearLineToRight() {
+ op := []rune{keyEscape, '[', 'K'}
+ t.queue(op)
+}
+
+const maxLineLength = 4096
+
+func (t *Terminal) setLine(newLine []rune, newPos int) {
+ if t.echo {
+ t.moveCursorToPos(0)
+ t.writeLine(newLine)
+ for i := len(newLine); i < len(t.line); i++ {
+ t.writeLine(space)
+ }
+ t.moveCursorToPos(newPos)
+ }
+ t.line = newLine
+ t.pos = newPos
+}
+
+func (t *Terminal) advanceCursor(places int) {
+ t.cursorX += places
+ t.cursorY += t.cursorX / t.termWidth
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
+ }
+ t.cursorX = t.cursorX % t.termWidth
+
+ if places > 0 && t.cursorX == 0 {
+ // Normally terminals will advance the current position
+ // when writing a character. But that doesn't happen
+ // for the last character in a line. However, when
+ // writing a character (except a new line) that causes
+ // a line wrap, the position will be advanced two
+ // places.
+ //
+ // So, if we are stopping at the end of a line, we
+ // need to write a newline so that our cursor can be
+ // advanced to the next line.
+ t.outBuf = append(t.outBuf, '\r', '\n')
+ }
+}
+
+func (t *Terminal) eraseNPreviousChars(n int) {
+ if n == 0 {
+ return
+ }
+
+ if t.pos < n {
+ n = t.pos
+ }
+ t.pos -= n
+ t.moveCursorToPos(t.pos)
+
+ copy(t.line[t.pos:], t.line[n+t.pos:])
+ t.line = t.line[:len(t.line)-n]
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ for i := 0; i < n; i++ {
+ t.queue(space)
+ }
+ t.advanceCursor(n)
+ t.moveCursorToPos(t.pos)
+ }
+}
+
+// countToLeftWord returns then number of characters from the cursor to the
+// start of the previous word.
+func (t *Terminal) countToLeftWord() int {
+ if t.pos == 0 {
+ return 0
+ }
+
+ pos := t.pos - 1
+ for pos > 0 {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos--
+ }
+ for pos > 0 {
+ if t.line[pos] == ' ' {
+ pos++
+ break
+ }
+ pos--
+ }
+
+ return t.pos - pos
+}
+
+// countToRightWord returns then number of characters from the cursor to the
+// start of the next word.
+func (t *Terminal) countToRightWord() int {
+ pos := t.pos
+ for pos < len(t.line) {
+ if t.line[pos] == ' ' {
+ break
+ }
+ pos++
+ }
+ for pos < len(t.line) {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos++
+ }
+ return pos - t.pos
+}
+
+// visualLength returns the number of visible glyphs in s.
+func visualLength(runes []rune) int {
+ inEscapeSeq := false
+ length := 0
+
+ for _, r := range runes {
+ switch {
+ case inEscapeSeq:
+ if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
+ inEscapeSeq = false
+ }
+ case r == '\x1b':
+ inEscapeSeq = true
+ default:
+ length++
+ }
+ }
+
+ return length
+}
+
+// handleKey processes the given key and, optionally, returns a line of text
+// that the user has entered.
+func (t *Terminal) handleKey(key rune) (line string, ok bool) {
+ if t.pasteActive && key != keyEnter {
+ t.addKeyToLine(key)
+ return
+ }
+
+ switch key {
+ case keyBackspace:
+ if t.pos == 0 {
+ return
+ }
+ t.eraseNPreviousChars(1)
+ case keyAltLeft:
+ // move left by a word.
+ t.pos -= t.countToLeftWord()
+ t.moveCursorToPos(t.pos)
+ case keyAltRight:
+ // move right by a word.
+ t.pos += t.countToRightWord()
+ t.moveCursorToPos(t.pos)
+ case keyLeft:
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+ t.moveCursorToPos(t.pos)
+ case keyRight:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+ case keyHome:
+ if t.pos == 0 {
+ return
+ }
+ t.pos = 0
+ t.moveCursorToPos(t.pos)
+ case keyEnd:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos = len(t.line)
+ t.moveCursorToPos(t.pos)
+ case keyUp:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
+ if !ok {
+ return "", false
+ }
+ if t.historyIndex == -1 {
+ t.historyPending = string(t.line)
+ }
+ t.historyIndex++
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ case keyDown:
+ switch t.historyIndex {
+ case -1:
+ return
+ case 0:
+ runes := []rune(t.historyPending)
+ t.setLine(runes, len(runes))
+ t.historyIndex--
+ default:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
+ if ok {
+ t.historyIndex--
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ }
+ }
+ case keyEnter:
+ t.moveCursorToPos(len(t.line))
+ t.queue([]rune("\r\n"))
+ line = string(t.line)
+ ok = true
+ t.line = t.line[:0]
+ t.pos = 0
+ t.cursorX = 0
+ t.cursorY = 0
+ t.maxLine = 0
+ case keyDeleteWord:
+ // Delete zero or more spaces and then one or more characters.
+ t.eraseNPreviousChars(t.countToLeftWord())
+ case keyDeleteLine:
+ // Delete everything from the current cursor position to the
+ // end of line.
+ for i := t.pos; i < len(t.line); i++ {
+ t.queue(space)
+ t.advanceCursor(1)
+ }
+ t.line = t.line[:t.pos]
+ t.moveCursorToPos(t.pos)
+ case keyCtrlD:
+ // Erase the character under the current position.
+ // The EOF case when the line is empty is handled in
+ // readLine().
+ if t.pos < len(t.line) {
+ t.pos++
+ t.eraseNPreviousChars(1)
+ }
+ case keyCtrlU:
+ t.eraseNPreviousChars(t.pos)
+ case keyClearScreen:
+ // Erases the screen and moves the cursor to the home position.
+ t.queue([]rune("\x1b[2J\x1b[H"))
+ t.queue(t.prompt)
+ t.cursorX, t.cursorY = 0, 0
+ t.advanceCursor(visualLength(t.prompt))
+ t.setLine(t.line, t.pos)
+ default:
+ if t.AutoCompleteCallback != nil {
+ prefix := string(t.line[:t.pos])
+ suffix := string(t.line[t.pos:])
+
+ t.lock.Unlock()
+ newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
+ t.lock.Lock()
+
+ if completeOk {
+ t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
+ return
+ }
+ }
+ if !isPrintable(key) {
+ return
+ }
+ if len(t.line) == maxLineLength {
+ return
+ }
+ t.addKeyToLine(key)
+ }
+ return
+}
+
+// addKeyToLine inserts the given key at the current position in the current
+// line.
+func (t *Terminal) addKeyToLine(key rune) {
+ if len(t.line) == cap(t.line) {
+ newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
+ copy(newLine, t.line)
+ t.line = newLine
+ }
+ t.line = t.line[:len(t.line)+1]
+ copy(t.line[t.pos+1:], t.line[t.pos:])
+ t.line[t.pos] = key
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) writeLine(line []rune) {
+ for len(line) != 0 {
+ remainingOnLine := t.termWidth - t.cursorX
+ todo := len(line)
+ if todo > remainingOnLine {
+ todo = remainingOnLine
+ }
+ t.queue(line[:todo])
+ t.advanceCursor(visualLength(line[:todo]))
+ line = line[todo:]
+ }
+}
+
+// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
+func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
+ for len(buf) > 0 {
+ i := bytes.IndexByte(buf, '\n')
+ todo := len(buf)
+ if i >= 0 {
+ todo = i
+ }
+
+ var nn int
+ nn, err = w.Write(buf[:todo])
+ n += nn
+ if err != nil {
+ return n, err
+ }
+ buf = buf[todo:]
+
+ if i >= 0 {
+ if _, err = w.Write(crlf); err != nil {
+ return n, err
+ }
+ n++
+ buf = buf[1:]
+ }
+ }
+
+ return n, nil
+}
+
+func (t *Terminal) Write(buf []byte) (n int, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ // This is the easy case: there's nothing on the screen that we
+ // have to move out of the way.
+ return writeWithCRLF(t.c, buf)
+ }
+
+ // We have a prompt and possibly user input on the screen. We
+ // have to clear it first.
+ t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
+ t.cursorX = 0
+ t.clearLineToRight()
+
+ for t.cursorY > 0 {
+ t.move(1 /* up */, 0, 0, 0)
+ t.cursorY--
+ t.clearLineToRight()
+ }
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+
+ if n, err = writeWithCRLF(t.c, buf); err != nil {
+ return
+ }
+
+ t.writeLine(t.prompt)
+ if t.echo {
+ t.writeLine(t.line)
+ }
+
+ t.moveCursorToPos(t.pos)
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+ return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ oldPrompt := t.prompt
+ t.prompt = []rune(prompt)
+ t.echo = false
+
+ line, err = t.readLine()
+
+ t.prompt = oldPrompt
+ t.echo = true
+
+ return
+}
+
+// ReadLine returns a line of input from the terminal.
+func (t *Terminal) ReadLine() (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+ // t.lock must be held at this point
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ t.writeLine(t.prompt)
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ }
+
+ lineIsPasted := t.pasteActive
+
+ for {
+ rest := t.remainder
+ lineOk := false
+ for !lineOk {
+ var key rune
+ key, rest = bytesToKey(rest, t.pasteActive)
+ if key == utf8.RuneError {
+ break
+ }
+ if !t.pasteActive {
+ if key == keyCtrlD {
+ if len(t.line) == 0 {
+ return "", io.EOF
+ }
+ }
+ if key == keyCtrlC {
+ return "", io.EOF
+ }
+ if key == keyPasteStart {
+ t.pasteActive = true
+ if len(t.line) == 0 {
+ lineIsPasted = true
+ }
+ continue
+ }
+ } else if key == keyPasteEnd {
+ t.pasteActive = false
+ continue
+ }
+ if !t.pasteActive {
+ lineIsPasted = false
+ }
+ line, lineOk = t.handleKey(key)
+ }
+ if len(rest) > 0 {
+ n := copy(t.inBuf[:], rest)
+ t.remainder = t.inBuf[:n]
+ } else {
+ t.remainder = nil
+ }
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ if lineOk {
+ if t.echo {
+ t.historyIndex = -1
+ t.history.Add(line)
+ }
+ if lineIsPasted {
+ err = ErrPasteIndicator
+ }
+ return
+ }
+
+ // t.remainder is a slice at the beginning of t.inBuf
+ // containing a partial key sequence
+ readBuf := t.inBuf[len(t.remainder):]
+ var n int
+
+ t.lock.Unlock()
+ n, err = t.c.Read(readBuf)
+ t.lock.Lock()
+
+ if err != nil {
+ return
+ }
+
+ t.remainder = t.inBuf[:n+len(t.remainder)]
+ }
+}
+
+// SetPrompt sets the prompt to be used when reading subsequent lines.
+func (t *Terminal) SetPrompt(prompt string) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.prompt = []rune(prompt)
+}
+
+func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
+ // Move cursor to column zero at the start of the line.
+ t.move(t.cursorY, 0, t.cursorX, 0)
+ t.cursorX, t.cursorY = 0, 0
+ t.clearLineToRight()
+ for t.cursorY < numPrevLines {
+ // Move down a line
+ t.move(0, 1, 0, 0)
+ t.cursorY++
+ t.clearLineToRight()
+ }
+ // Move back to beginning.
+ t.move(t.cursorY, 0, 0, 0)
+ t.cursorX, t.cursorY = 0, 0
+
+ t.queue(t.prompt)
+ t.advanceCursor(visualLength(t.prompt))
+ t.writeLine(t.line)
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) SetSize(width, height int) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if width == 0 {
+ width = 1
+ }
+
+ oldWidth := t.termWidth
+ t.termWidth, t.termHeight = width, height
+
+ switch {
+ case width == oldWidth:
+ // If the width didn't change then nothing else needs to be
+ // done.
+ return nil
+ case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
+ // If there is nothing on current line and no prompt printed,
+ // just do nothing
+ return nil
+ case width < oldWidth:
+ // Some terminals (e.g. xterm) will truncate lines that were
+ // too long when shinking. Others, (e.g. gnome-terminal) will
+ // attempt to wrap them. For the former, repainting t.maxLine
+ // works great, but that behaviour goes badly wrong in the case
+ // of the latter because they have doubled every full line.
+
+ // We assume that we are working on a terminal that wraps lines
+ // and adjust the cursor position based on every previous line
+ // wrapping and turning into two. This causes the prompt on
+ // xterms to move upwards, which isn't great, but it avoids a
+ // huge mess with gnome-terminal.
+ if t.cursorX >= t.termWidth {
+ t.cursorX = t.termWidth - 1
+ }
+ t.cursorY *= 2
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
+ case width > oldWidth:
+ // If the terminal expands then our position calculations will
+ // be wrong in the future because we think the cursor is
+ // |t.pos| chars into the string, but there will be a gap at
+ // the end of any wrapped line.
+ //
+ // But the position will actually be correct until we move, so
+ // we can move back to the beginning and repaint everything.
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine)
+ }
+
+ _, err := t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ return err
+}
+
+type pasteIndicatorError struct{}
+
+func (pasteIndicatorError) Error() string {
+ return "terminal: ErrPasteIndicator not correctly handled"
+}
+
+// ErrPasteIndicator may be returned from ReadLine as the error, in addition
+// to valid line data. It indicates that bracketed paste mode is enabled and
+// that the returned line consists only of pasted data. Programs may wish to
+// interpret pasted data more literally than typed data.
+var ErrPasteIndicator = pasteIndicatorError{}
+
+// SetBracketedPasteMode requests that the terminal bracket paste operations
+// with markers. Not all terminals support this but, if it is supported, then
+// enabling this mode will stop any autocomplete callback from running due to
+// pastes. Additionally, any lines that are completely pasted will be returned
+// from ReadLine with the error set to ErrPasteIndicator.
+func (t *Terminal) SetBracketedPasteMode(on bool) {
+ if on {
+ io.WriteString(t.c, "\x1b[?2004h")
+ } else {
+ io.WriteString(t.c, "\x1b[?2004l")
+ }
+}
+
+// stRingBuffer is a ring buffer of strings.
+type stRingBuffer struct {
+ // entries contains max elements.
+ entries []string
+ max int
+ // head contains the index of the element most recently added to the ring.
+ head int
+ // size contains the number of elements in the ring.
+ size int
+}
+
+func (s *stRingBuffer) Add(a string) {
+ if s.entries == nil {
+ const defaultNumEntries = 100
+ s.entries = make([]string, defaultNumEntries)
+ s.max = defaultNumEntries
+ }
+
+ s.head = (s.head + 1) % s.max
+ s.entries[s.head] = a
+ if s.size < s.max {
+ s.size++
+ }
+}
+
+// NthPreviousEntry returns the value passed to the nth previous call to Add.
+// If n is zero then the immediately prior value is returned, if one, then the
+// next most recent, and so on. If such an element doesn't exist then ok is
+// false.
+func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
+ if n >= s.size {
+ return "", false
+ }
+ index := s.head - n
+ if index < 0 {
+ index += s.max
+ }
+ return s.entries[index], true
+}
+
+// readPasswordLine reads from reader until it finds \n or io.EOF.
+// The slice returned does not include the \n.
+// readPasswordLine also ignores any \r it finds.
+// Windows uses \r as end of line. So, on Windows, readPasswordLine
+// reads until it finds \r and ignores any \n it finds during processing.
+func readPasswordLine(reader io.Reader) ([]byte, error) {
+ var buf [1]byte
+ var ret []byte
+
+ for {
+ n, err := reader.Read(buf[:])
+ if n > 0 {
+ switch buf[0] {
+ case '\b':
+ if len(ret) > 0 {
+ ret = ret[:len(ret)-1]
+ }
+ case '\n':
+ if runtime.GOOS != "windows" {
+ return ret, nil
+ }
+ // otherwise ignore \n
+ case '\r':
+ if runtime.GOOS == "windows" {
+ return ret, nil
+ }
+ // otherwise ignore \r
+ default:
+ ret = append(ret, buf[0])
+ }
+ continue
+ }
+ if err != nil {
+ if err == io.EOF && len(ret) > 0 {
+ return ret, nil
+ }
+ return ret, err
+ }
+ }
+}
diff --git a/trunk/3rdparty/srs-bench/vendor/modules.txt b/trunk/3rdparty/srs-bench/vendor/modules.txt
index d8f8c47b46..d99520a763 100644
--- a/trunk/3rdparty/srs-bench/vendor/modules.txt
+++ b/trunk/3rdparty/srs-bench/vendor/modules.txt
@@ -1,5 +1,31 @@
+# github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83
+## explicit
+github.com/ghettovoice/gosip/log
+github.com/ghettovoice/gosip/sip
+github.com/ghettovoice/gosip/sip/parser
+github.com/ghettovoice/gosip/timing
+github.com/ghettovoice/gosip/transport
+github.com/ghettovoice/gosip/util
+# github.com/gobwas/httphead v0.1.0
+github.com/gobwas/httphead
+# github.com/gobwas/pool v0.2.1
+github.com/gobwas/pool
+github.com/gobwas/pool/internal/pmath
+github.com/gobwas/pool/pbufio
+github.com/gobwas/pool/pbytes
+# github.com/gobwas/ws v1.1.0-rc.1
+github.com/gobwas/ws
+github.com/gobwas/ws/wsutil
# github.com/google/uuid v1.1.5
github.com/google/uuid
+# github.com/konsorten/go-windows-terminal-sequences v1.0.2
+github.com/konsorten/go-windows-terminal-sequences
+# github.com/mattn/go-colorable v0.1.4
+github.com/mattn/go-colorable
+# github.com/mattn/go-isatty v0.0.8
+github.com/mattn/go-isatty
+# github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
+github.com/mgutz/ansi
# github.com/ossrs/go-oryx-lib v0.0.9
## explicit
github.com/ossrs/go-oryx-lib/aac
@@ -91,10 +117,25 @@ github.com/pion/webrtc/v3/pkg/media/oggwriter
github.com/pion/webrtc/v3/pkg/rtcerr
# github.com/pkg/errors v0.9.1
github.com/pkg/errors
+# github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
+github.com/satori/go.uuid
+# github.com/sirupsen/logrus v1.4.2
+github.com/sirupsen/logrus
+# github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5
+github.com/tevino/abool
+# github.com/x-cray/logrus-prefixed-formatter v0.5.2
+github.com/x-cray/logrus-prefixed-formatter
+# github.com/yapingcat/gomedia/codec v0.0.0-20220617074658-94762898dc25
+## explicit
+github.com/yapingcat/gomedia/codec
+# github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25
+## explicit
+github.com/yapingcat/gomedia/mpeg2
# golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/crypto/cryptobyte
golang.org/x/crypto/cryptobyte/asn1
golang.org/x/crypto/curve25519
+golang.org/x/crypto/ssh/terminal
# golang.org/x/net v0.0.0-20210119194325-5f4716e94777
golang.org/x/net/bpf
golang.org/x/net/dns/dnsmessage
@@ -103,10 +144,13 @@ golang.org/x/net/internal/socket
golang.org/x/net/internal/socks
golang.org/x/net/ipv4
golang.org/x/net/proxy
-# golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
+# golang.org/x/sys v0.0.0-20201214095126-aec9a390925b
golang.org/x/sys/internal/unsafeheader
+golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
+# golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
+golang.org/x/term
# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
golang.org/x/xerrors
golang.org/x/xerrors/internal
diff --git a/trunk/Dockerfile b/trunk/Dockerfile
index de93874da2..3c86c26813 100644
--- a/trunk/Dockerfile
+++ b/trunk/Dockerfile
@@ -24,7 +24,7 @@ WORKDIR /srs/trunk
# Build and install SRS.
# Note that SRT is enabled by default, so we configure without --srt=on.
-RUN ./configure --jobs=${JOBS} && make -j${JOBS} && make install
+RUN ./configure --jobs=${JOBS} --gb28181=on && make -j${JOBS} && make install
# All config files for SRS.
RUN cp -R conf /usr/local/srs/conf && \
diff --git a/trunk/Dockerfile.builds b/trunk/Dockerfile.builds
index 97d74b070f..baac52736c 100644
--- a/trunk/Dockerfile.builds
+++ b/trunk/Dockerfile.builds
@@ -1,59 +1,59 @@
########################################################
FROM ossrs/srs:dev-cache AS centos7-baseline
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off && make -j2
FROM ossrs/srs:dev-cache AS centos7-no-webrtc
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --rtc=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off --rtc=off && make -j2
FROM ossrs/srs:dev-cache AS centos7-no-asm
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --nasm=off --srtp-nasm=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off --nasm=off --srtp-nasm=off && make -j2
-FROM ossrs/srs:dev-cache AS centos7-srt
+FROM ossrs/srs:dev-cache AS centos7-all
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=on && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --gb28181=on && make -j2
FROM ossrs/srs:dev-cache AS centos7-ansi-no-ffmpeg
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --cxx11=off --cxx14=off --ffmpeg-fit=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off --cxx11=off --cxx14=off --ffmpeg-fit=off && make -j2
########################################################
FROM ossrs/srs:dev6-cache AS centos6-baseline
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --cxx11=off --cxx14=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off --cxx11=off --cxx14=off && make -j2
-FROM ossrs/srs:dev6-cache AS centos6-srt
+FROM ossrs/srs:dev6-cache AS centos6-all
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --cxx11=off --cxx14=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --gb28181=on --cxx11=off --cxx14=off && make -j2
########################################################
FROM ossrs/srs:ubuntu16-cache AS ubuntu16-baseline
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off && make -j2
-FROM ossrs/srs:ubuntu16-cache AS ubuntu16-srt
+FROM ossrs/srs:ubuntu16-cache AS ubuntu16-all
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=on && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --gb28181=on && make -j2
########################################################
FROM ossrs/srs:ubuntu18-cache AS ubuntu18-baseline
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off && make -j2
-FROM ossrs/srs:ubuntu18-cache AS ubuntu18-srt
+FROM ossrs/srs:ubuntu18-cache AS ubuntu18-all
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=on && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --gb28181=on && make -j2
########################################################
FROM ossrs/srs:ubuntu20-cache AS ubuntu20-baseline
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=off && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=off --gb28181=off && make -j2
-FROM ossrs/srs:ubuntu20-cache AS ubuntu20-srt
+FROM ossrs/srs:ubuntu20-cache AS ubuntu20-all
COPY . /srs
-RUN cd /srs/trunk && ./configure --jobs=2 --srt=on && make -j2
+RUN cd /srs/trunk && ./configure --jobs=2 --srt=on --gb28181=on && make -j2
########################################################
FROM ossrs/srs:ubuntu16-cross-arm AS ubuntu16-cross-armv7
diff --git a/trunk/Dockerfile.cov b/trunk/Dockerfile.cov
index 6a745d29f9..9793c3f89a 100644
--- a/trunk/Dockerfile.cov
+++ b/trunk/Dockerfile.cov
@@ -8,5 +8,5 @@ COPY . /srs
WORKDIR /srs/trunk
# Note that we must enable the gcc7 or link failed.
-RUN scl enable devtoolset-7 -- ./configure --srt=on --utest=on --gcov=on --jobs=2
+RUN scl enable devtoolset-7 -- ./configure --srt=on --gb28181=on --utest=on --gcov=on --jobs=2
RUN scl enable devtoolset-7 -- make -j2 utest
diff --git a/trunk/Dockerfile.test b/trunk/Dockerfile.test
index cd12950f78..ce833028eb 100644
--- a/trunk/Dockerfile.test
+++ b/trunk/Dockerfile.test
@@ -8,7 +8,7 @@ COPY . /srs
WORKDIR /srs/trunk
# Note that we must enable the gcc7 or link failed.
-RUN scl enable devtoolset-7 -- ./configure --srt=on --utest=on --jobs=2
+RUN scl enable devtoolset-7 -- ./configure --srt=on --gb28181=on --utest=on --jobs=2
RUN scl enable devtoolset-7 -- make -j2 utest
# Build benchmark tool.
diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh
index 73bc0ae3fa..80b0861822 100755
--- a/trunk/auto/auto_headers.sh
+++ b/trunk/auto/auto_headers.sh
@@ -92,6 +92,12 @@ else
srs_undefine_macro "SRS_HTTPS" $SRS_AUTO_HEADERS_H
fi
+if [ $SRS_GB28181 = YES ]; then
+ srs_define_macro "SRS_GB28181" $SRS_AUTO_HEADERS_H
+else
+ srs_undefine_macro "SRS_GB28181" $SRS_AUTO_HEADERS_H
+fi
+
if [ $SRS_UTEST = YES ]; then
srs_define_macro "SRS_UTEST" $SRS_AUTO_HEADERS_H
else
diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh
index 032a440fbc..1f765edbbf 100755
--- a/trunk/auto/options.sh
+++ b/trunk/auto/options.sh
@@ -6,6 +6,7 @@ help=no
SRS_HDS=NO
SRS_SRT=YES
SRS_RTC=YES
+SRS_GB28181=NO
SRS_CXX11=YES
SRS_CXX14=NO
SRS_NGINX=NO
@@ -124,6 +125,7 @@ Features:
--utest=on|off Whether build the utest. Default: $(value2switch $SRS_UTEST)
--srt=on|off Whether build the SRT. Default: $(value2switch $SRS_SRT)
--rtc=on|off Whether build the WebRTC. Default: $(value2switch $SRS_RTC)
+ --gb28181=on|off Whether build the GB28181. Default: $(value2switch $SRS_GB28181)
--cxx11=on|off Whether enable the C++11. Default: $(value2switch $SRS_CXX11)
--cxx14=on|off Whether enable the C++14. Default: $(value2switch $SRS_CXX14)
--ffmpeg-fit=on|off Whether enable the FFmpeg fit(source code). Default: $(value2switch $SRS_FFMPEG_FIT)
@@ -291,6 +293,7 @@ function parse_user_option() {
--simulator) SRS_SIMULATOR=$(switch2value $value) ;;
--generate-objs) SRS_GENERATE_OBJS=$(switch2value $value) ;;
--ffmpeg-fit) SRS_FFMPEG_FIT=$(switch2value $value) ;;
+ --gb28181) SRS_GB28181=$(switch2value $value) ;;
--cxx11) SRS_CXX11=$(switch2value $value) ;;
--cxx14) SRS_CXX14=$(switch2value $value) ;;
@@ -517,6 +520,7 @@ function regenerate_options() {
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cherrypy=$(value2switch $SRS_CHERRYPY)"
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srt=$(value2switch $SRS_SRT)"
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --rtc=$(value2switch $SRS_RTC)"
+ SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gb28181=$(value2switch $SRS_GB28181)"
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --simulator=$(value2switch $SRS_SIMULATOR)"
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx11=$(value2switch $SRS_CXX11)"
SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx14=$(value2switch $SRS_CXX14)"
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index 4a7cf3416a..3a1fb38e54 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -583,6 +583,52 @@ stream_caster {
listen 8936;
}
+# For GB28181 server, see https://github.com/ossrs/srs/issues/3176
+# For SIP specification, see https://www.ietf.org/rfc/rfc3261.html
+# For GB28181 2016 spec, see https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89
+stream_caster {
+ # Whether GB28181 server is enabled.
+ # Default: off
+ enabled off;
+ # The caster type of stream, the casters:
+ # gb28181, Push GB28181 stream to SRS.
+ caster gb28181;
+ # The output rtmp url.
+ # For gb28181 caster, the typically output url:
+ # rtmp://127.0.0.1/live/[stream]
+ # The available variables:
+ # [stream] The video channel codec ID.
+ output rtmp://127.0.0.1/live/[stream];
+ # The listen TCP/UDP port for stream caster.
+ # For gb28181 caster, listen at TCP/UDP port. for example, 9000.
+ # @remark We always enable bundle for media streams at this port.
+ listen 9000;
+ # SIP server for GB28181. Please note that this is only a demonstrated SIP server, please never use it in your
+ # online production environment. Instead please use [jsip](https://github.com/usnistgov/jsip) and there is a demo
+ # [srs-sip](https://github.com/ossrs/srs-sip) also base on it.
+ sip {
+ # Whether enable embedded SIP server.
+ # Default: on
+ enabled on;
+ # The SIP listen port, for both TCP and UDP protocol.
+ # Default: 5060
+ listen 5060;
+ # The SIP or media transport timeout in seconds.
+ # Default: 60
+ timeout 60;
+ # When media disconnect, the wait time in seconds to re-invite device to publish. During this wait time, device
+ # might send bye or unregister message(expire is 0), so that we will cancel the re-invite.
+ # Default: 5
+ reinvite 5;
+ # The exposed candidate IPs, response in SDP connection line. It can be:
+ # * Retrieve server IP automatically, from all network interfaces.
+ # $CANDIDATE Read the IP from ENV variable, use * if not set.
+ # x.x.x.x A specified IP address or DNS name, use * if 0.0.0.0.
+ # Default: *
+ candidate *;
+ }
+}
+
#############################################################################################
# other global sections
#############################################################################################
diff --git a/trunk/conf/gb28181.conf b/trunk/conf/gb28181.conf
new file mode 100644
index 0000000000..7ce7c606c7
--- /dev/null
+++ b/trunk/conf/gb28181.conf
@@ -0,0 +1,54 @@
+
+listen 1935;
+max_connections 1000;
+daemon off;
+srs_log_tank console;
+
+stream_caster {
+ enabled on;
+ caster gb28181;
+ output rtmp://127.0.0.1/live/[stream];
+ listen 9000;
+ sip {
+ enabled on;
+ listen 5060;
+ }
+}
+
+http_server {
+ enabled on;
+ listen 8080;
+ dir ./objs/nginx/html;
+}
+
+http_api {
+ enabled on;
+ listen 1985;
+}
+stats {
+ network 0;
+}
+rtc_server {
+ enabled on;
+ listen 8000; # UDP port
+ # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
+ candidate $CANDIDATE;
+}
+
+vhost __defaultVhost__ {
+ rtc {
+ enabled on;
+ # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
+ rtmp_to_rtc on;
+ # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
+ rtc_to_rtmp on;
+ }
+ http_remux {
+ enabled on;
+ mount [vhost]/[app]/[stream].flv;
+ }
+ hls {
+ enabled on;
+ }
+}
+
diff --git a/trunk/conf/regression-test.conf b/trunk/conf/regression-test.conf
index c17dc21b20..9f3022a8fd 100644
--- a/trunk/conf/regression-test.conf
+++ b/trunk/conf/regression-test.conf
@@ -1,9 +1,20 @@
listen 1935;
max_connections 1000;
-daemon on;
-disable_daemon_for_docker off;
-srs_log_tank file;
+daemon on; disable_daemon_for_docker off; srs_log_tank file;
+
+stream_caster {
+ enabled on;
+ caster gb28181;
+ output rtmp://127.0.0.1/live/[stream];
+ listen 9000;
+ sip {
+ enabled on;
+ listen 5060;
+ timeout 2.1;
+ reinvite 1.2;
+ }
+}
http_server {
enabled on;
diff --git a/trunk/configure b/trunk/configure
index 05fae37f6b..4d8af43f0b 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -211,7 +211,7 @@ MODULE_DEPENDS=("CORE")
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer"
"srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io"
- "srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts"
+ "srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts" "srs_kernel_ps"
"srs_kernel_stream" "srs_kernel_balance" "srs_kernel_mp4" "srs_kernel_file"
"srs_kernel_kbps")
if [[ $SRS_RTC == YES ]]; then
@@ -274,6 +274,9 @@ fi
if [[ $SRS_FFMPEG_FIT == YES ]]; then
MODULE_FILES+=("srs_app_rtc_codec")
fi
+if [[ $SRS_GB28181 == YES ]]; then
+ MODULE_FILES+=("srs_app_gb28181")
+fi
DEFINES=""
# add each modules for app
@@ -394,6 +397,9 @@ if [ $SRS_UTEST = YES ]; then
if [[ $SRS_SRT == YES ]]; then
MODULE_FILES+=("srs_utest_srt")
fi
+ if [[ $SRS_GB28181 == YES ]]; then
+ MODULE_FILES+=("srs_utest_gb28181")
+ fi
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSTRoot} ${LibSSLRoot})
if [[ $SRS_RTC == YES ]]; then
ModuleLibIncs+=(${LibSrtpRoot})
@@ -645,6 +651,11 @@ if [ $SRS_HDS = YES ]; then
else
echo -e "${GREEN}Warning: HDS is disabled.${BLACK}"
fi
+if [ $SRS_GB28181 = YES ]; then
+ echo -e "${YELLOW}Experiment: GB28181 is enabled. https://github.com/ossrs/srs/issues/3176${BLACK}"
+else
+ echo -e "${GREEN}Warning: GB28181 is disabled.${BLACK}"
+fi
if [ $SRS_SRT = YES ]; then
echo -e "${YELLOW}Experiment: SRT is enabled. https://github.com/ossrs/srs/issues/1147${BLACK}"
else
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index 3b08800762..f88c64c2e3 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 5.0 Changelog
+* v5.0, 2022-10-06, GB28181: Support GB28181-2016 protocol. v5.0.74
* v5.0, 2022-10-05, HTTP: Skip body and left message by upgrade. v5.0.73
* v5.0, 2022-10-02, ST: Support set context id while thread running. v5.0.72
* v5.0, 2022-09-30, RTC: Refine SDP to support GB28181 SSRC spec. v5.0.71
diff --git a/trunk/doc/Features.md b/trunk/doc/Features.md
index f353210d9f..9f36d9c2cf 100644
--- a/trunk/doc/Features.md
+++ b/trunk/doc/Features.md
@@ -56,13 +56,13 @@ The features of SRS.
- [x] Other: Support write log to tencent cloud CLS.
- [x] Other: [Experimental] Support pushing MPEG-TS over UDP, please read [bug #250](https://github.com/ossrs/srs/issues/250).
- [x] Other: [Experimental] Support pushing FLV over HTTP POST, please read wiki([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/streamer#push-http-flv-to-srs), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/streamer#push-http-flv-to-srs)).
+- [x] Other: [Experimental] Support push stream by GB28181, [#3176](https://github.com/ossrs/srs/issues/3176).
- [ ] System: Support Windows/Cygwin 64bits, [#2532](https://github.com/ossrs/srs/issues/2532).
- [ ] System: Support H.265 over RTMP and HLS, [#465](https://github.com/ossrs/srs/issues/465).
- [ ] System: Support source cleanup for idle streams, [#413](https://github.com/ossrs/srs/issues/413).
- [ ] Live: Support HLS variant, [#463](https://github.com/ossrs/srs/issues/463).
- [ ] RTC: Support IETF-QUIC for WebRTC Cluster, [#2091](https://github.com/ossrs/srs/issues/2091).
- [ ] RTC: Improve RTC performance to 5K by multiple threading, [#2188](https://github.com/ossrs/srs/issues/2188).
-- [ ] Other: Support push stream by GB28181, [#1500](https://github.com/ossrs/srs/issues/1500).
- [ ] Other: Support change user to run SRS, [#1111](https://github.com/ossrs/srs/issues/1111).
- [x] [Deprecated] Live: Support Adobe HDS(f4m), please read wiki([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-hds), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/delivery-hds)) and [#1535](https://github.com/ossrs/srs/issues/1535).
- [x] [Deprecated] Other: Support bandwidth testing, please read [#1535](https://github.com/ossrs/srs/issues/1535).
diff --git a/trunk/ide/srs_clion/CMakeLists.txt b/trunk/ide/srs_clion/CMakeLists.txt
index a21ccf5787..f87d6090a2 100755
--- a/trunk/ide/srs_clion/CMakeLists.txt
+++ b/trunk/ide/srs_clion/CMakeLists.txt
@@ -27,11 +27,11 @@ ProcessorCount(JOBS)
# We should always configure SRS for switching between branches.
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
EXECUTE_PROCESS(
- COMMAND ./configure --osx --srt=on --utest=on --jobs=${JOBS}
+ COMMAND ./configure --osx --srt=on --gb28181=on --utest=on --jobs=${JOBS}
WORKING_DIRECTORY ${SRS_DIR} RESULT_VARIABLE ret)
ELSE ()
EXECUTE_PROCESS(
- COMMAND ./configure --srt=on --utest=on --jobs=${JOBS}
+ COMMAND ./configure --srt=on --gb28181=on --utest=on --jobs=${JOBS}
WORKING_DIRECTORY ${SRS_DIR} RESULT_VARIABLE ret)
ENDIF ()
if(NOT ret EQUAL 0)
diff --git a/trunk/research/wireshark/gb28181.lua b/trunk/research/wireshark/gb28181.lua
new file mode 100644
index 0000000000..be1e1567f7
--- /dev/null
+++ b/trunk/research/wireshark/gb28181.lua
@@ -0,0 +1,14 @@
+-- For GB28181 TCP, RTP over RTP, see https://www.ietf.org/rfc/rfc4571.html
+--
+-- To apply this wireshark plugin:
+-- mkdir -p ~/.local/lib/wireshark/plugins
+-- ln -sf $(pwd)/gb28181.lua ~/.local/lib/wireshark/plugins/gb28181.lua
+
+do
+ -- RFC4517 RTP & RTCP over Connection-Oriented Transport
+ local rtp_dissector = Dissector.get("rtp.rfc4571")
+
+ local tcp_dissector_table = DissectorTable.get("tcp.port")
+ tcp_dissector_table:add(9000, rtp_dissector)
+end
+
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index caa5d567b4..c32df61872 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -266,6 +266,11 @@ bool srs_stream_caster_is_flv(string caster)
return caster == "flv";
}
+bool srs_stream_caster_is_gb28181(string caster)
+{
+ return caster == "gb28181";
+}
+
bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req)
{
static bool DEFAULT = true;
@@ -325,7 +330,61 @@ srs_error_t srs_config_transform_vhost(SrsConfDirective* root)
++it;
}
}
-
+
+ // SRS5.0, GB28181 allows unused config.
+ // stream_caster {
+ // caster gb28181; tcp_enable; rtp_port_min; rtp_port_max; wait_keyframe; rtp_idle_timeout;
+ // audio_enable; auto_create_channel;
+ // sip {
+ // serial; realm; ack_timeout; keepalive_timeout; invite_port_fixed; query_catalog_interval; auto_play;
+ // }
+ // }
+ if (dir->name == "stream_caster") {
+ for (vector::iterator it = dir->directives.begin(); it != dir->directives.end();) {
+ SrsConfDirective* conf = *it;
+ if (conf->name == "tcp_enable" || conf->name == "rtp_port_min" || conf->name == "rtp_port_max"
+ || conf->name == "wait_keyframe" || conf->name == "rtp_idle_timeout" || conf->name == "audio_enable"
+ || conf->name == "auto_create_channel"
+ ) {
+ srs_warn("transform: Config %s for GB is not used", conf->name.c_str());
+ it = dir->directives.erase(it); srs_freep(conf); continue;
+ }
+ ++it;
+ }
+
+ SrsConfDirective* sip = dir->get("sip");
+ if (sip) {
+ for (vector::iterator it = sip->directives.begin(); it != sip->directives.end();) {
+ SrsConfDirective* conf = *it;
+ if (conf->name == "serial" || conf->name == "realm" || conf->name == "ack_timeout"
+ || conf->name == "keepalive_timeout" || conf->name == "invite_port_fixed"
+ || conf->name == "query_catalog_interval" || conf->name == "auto_play"
+ ) {
+ srs_warn("transform: Config sip.%s for GB is not used", conf->name.c_str());
+ it = sip->directives.erase(it); srs_freep(conf); continue;
+ }
+ ++it;
+ }
+ }
+ }
+
+ // SRS 5.0, GB28181 moves config from:
+ // stream_caster { caster gb28181; host * }
+ // to:
+ // stream_caster { caster gb28181; sip { candidate *; } }
+ if (dir->name == "stream_caster") {
+ for (vector::iterator it = dir->directives.begin(); it != dir->directives.end();) {
+ SrsConfDirective* conf = *it;
+ if (conf->name == "host") {
+ srs_warn("transform: Config move host to sip.candidate for GB");
+ conf->name = "candidate"; dir->get_or_create("sip")->directives.push_back(conf->copy());
+ it = dir->directives.erase(it); srs_freep(conf); continue;
+ }
+ ++it;
+ }
+ }
+
+ // The bellow is vhost scope configurations.
if (!dir->is_vhost()) {
continue;
}
@@ -2437,21 +2496,15 @@ srs_error_t SrsConfig::check_normal_config()
for (int i = 0; stream_caster && i < (int)stream_caster->directives.size(); i++) {
SrsConfDirective* conf = stream_caster->at(i);
string n = conf->name;
- if (n != "enabled" && n != "caster" && n != "output"
- && n != "listen" && n != "tcp_enable" && n != "rtp_port_min" && n != "rtp_port_max"
- && n != "rtp_idle_timeout" && n != "sip"
- && n != "audio_enable" && n != "wait_keyframe" && n != "jitterbuffer_enable"
- && n != "host" && n != "auto_create_channel") {
+ if (n != "enabled" && n != "caster" && n != "output" && n != "listen" && n != "sip") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str());
}
if (n == "sip") {
for (int j = 0; j < (int)conf->directives.size(); j++) {
string m = conf->at(j)->name;
- if (m != "enabled" && m != "listen" && m != "ack_timeout" && m != "keepalive_timeout"
- && m != "host" && m != "serial" && m != "realm" && m != "auto_play" && m != "invite_port_fixed"
- && m != "query_catalog_interval") {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", m.c_str());
+ if (m != "enabled" && m != "listen" && m != "timeout" && m != "reinvite" && m != "candidate") {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.sip.%s", m.c_str());
}
}
}
@@ -3789,52 +3842,114 @@ int SrsConfig::get_stream_caster_listen(SrsConfDirective* conf)
return ::atoi(conf->arg0().c_str());
}
-bool SrsConfig::get_stream_caster_tcp_enable(SrsConfDirective* conf)
+bool SrsConfig::get_stream_caster_sip_enable(SrsConfDirective* conf)
{
- static bool DEFAULT = false;
+ static bool DEFAULT = true;
+
+ if (!conf) {
+ return DEFAULT;
+ }
- if (!conf) {
- return DEFAULT;
- }
+ conf = conf->get("sip");
+ if (!conf) {
+ return DEFAULT;
+ }
- conf = conf->get("tcp_enable");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
- }
+ conf = conf->get("enabled");
+ if (!conf || conf->arg0().empty()) {
+ return DEFAULT;
+ }
- return SRS_CONF_PERFER_FALSE(conf->arg0());
+ return SRS_CONF_PERFER_TRUE(conf->arg0());
}
-int SrsConfig::get_stream_caster_rtp_port_min(SrsConfDirective* conf)
+int SrsConfig::get_stream_caster_sip_listen(SrsConfDirective* conf)
{
- static int DEFAULT = 0;
-
+ static int DEFAULT = 5060;
+
if (!conf) {
return DEFAULT;
}
-
- conf = conf->get("rtp_port_min");
+
+ conf = conf->get("sip");
+ if (!conf) {
+ return DEFAULT;
+ }
+
+ conf = conf->get("listen");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
-
+
return ::atoi(conf->arg0().c_str());
}
-int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf)
+srs_utime_t SrsConfig::get_stream_caster_sip_timeout(SrsConfDirective* conf)
{
- static int DEFAULT = 0;
-
+ static srs_utime_t DEFAULT = 60 * SRS_UTIME_SECONDS;
+
if (!conf) {
return DEFAULT;
}
-
- conf = conf->get("rtp_port_max");
+
+ conf = conf->get("sip");
+ if (!conf) {
+ return DEFAULT;
+ }
+
+ conf = conf->get("timeout");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
-
- return ::atoi(conf->arg0().c_str());
+
+ return ::atof(conf->arg0().c_str()) * SRS_UTIME_SECONDS;
+}
+
+srs_utime_t SrsConfig::get_stream_caster_sip_reinvite(SrsConfDirective* conf)
+{
+ static srs_utime_t DEFAULT = 5 * SRS_UTIME_SECONDS;
+
+ if (!conf) {
+ return DEFAULT;
+ }
+
+ conf = conf->get("sip");
+ if (!conf) {
+ return DEFAULT;
+ }
+
+ conf = conf->get("reinvite");
+ if (!conf || conf->arg0().empty()) {
+ return DEFAULT;
+ }
+
+ return ::atof(conf->arg0().c_str()) * SRS_UTIME_SECONDS;
+}
+
+std::string SrsConfig::get_stream_caster_sip_candidate(SrsConfDirective* conf)
+{
+ static string DEFAULT = "*";
+
+ if (!conf) {
+ return DEFAULT;
+ }
+
+ conf = conf->get("candidate");
+ if (!conf || conf->arg0().empty()) {
+ return DEFAULT;
+ }
+
+ string eip = srs_getenv(conf->arg0());
+ if (!eip.empty()) {
+ return eip;
+ }
+
+ // If configed as ENV, but no ENV set, use default value.
+ if (srs_string_starts_with(conf->arg0(), "$")) {
+ return DEFAULT;
+ }
+
+ return conf->arg0();
}
bool SrsConfig::get_rtc_server_enabled()
diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp
index bff6e1759e..9d16e23fe3 100644
--- a/trunk/src/app/srs_app_config.hpp
+++ b/trunk/src/app/srs_app_config.hpp
@@ -101,6 +101,7 @@ extern bool srs_config_dvr_is_plan_segment(std::string plan);
extern bool srs_config_dvr_is_plan_session(std::string plan);
extern bool srs_stream_caster_is_udp(std::string caster);
extern bool srs_stream_caster_is_flv(std::string caster);
+extern bool srs_stream_caster_is_gb28181(std::string caster);
// Whether the dvr_apply active the stream specified by req.
extern bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req);
@@ -487,13 +488,16 @@ class SrsConfig
virtual std::string get_stream_caster_output(SrsConfDirective* conf);
// Get the listen port of stream caster.
virtual int get_stream_caster_listen(SrsConfDirective* conf);
- // Get the listen port type of stream caster.
- virtual bool get_stream_caster_tcp_enable(SrsConfDirective* conf);
- // Get the min udp port for rtp of stream caster rtsp.
- virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf);
- // Get the max udp port for rtp of stream caster rtsp.
- virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf);
-
+ // Get the sip.enabled configuration.
+ virtual bool get_stream_caster_sip_enable(SrsConfDirective* conf);
+ // Get the sip.listen port configuration.
+ virtual int get_stream_caster_sip_listen(SrsConfDirective* conf);
+ // Get the sip.timeout configuration.
+ virtual srs_utime_t get_stream_caster_sip_timeout(SrsConfDirective* conf);
+ // Get the sip.reinvite configuration.
+ virtual srs_utime_t get_stream_caster_sip_reinvite(SrsConfDirective* conf);
+ // Get the candidate for SDP.
+ virtual std::string get_stream_caster_sip_candidate(SrsConfDirective* conf);
// rtc section
public:
virtual bool get_rtc_server_enabled();
diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp
new file mode 100644
index 0000000000..454f8c2050
--- /dev/null
+++ b/trunk/src/app/srs_app_gb28181.cpp
@@ -0,0 +1,2484 @@
+//
+// Copyright (c) 2013-2022 The SRS Authors
+//
+// SPDX-License-Identifier: MIT or MulanPSL-2.0
+//
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+using namespace std;
+
+// See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.7
+#define SRS_GB_BRANCH_MAGIC "z9hG4bK"
+#define SRS_GB_SIP_PORT 5060
+#define SRS_GB_MAX_RECOVER 16
+#define SRS_GB_MAX_TIMEOUT 3
+#define SRS_GB_LARGE_PACKET 1500
+#define SRS_GB_SESSION_DRIVE_INTERVAL (300 * SRS_UTIME_MILLISECONDS)
+
+extern bool srs_is_rtcp(const uint8_t* data, size_t len);
+
+std::string srs_gb_session_state(SrsGbSessionState state)
+{
+ switch (state) {
+ case SrsGbSessionStateInit: return "Init";
+ case SrsGbSessionStateConnecting: return "Connecting";
+ case SrsGbSessionStateEstablished: return "Established";
+ default: return "Invalid";
+ }
+}
+
+std::string srs_gb_state(SrsGbSessionState ostate, SrsGbSessionState state)
+{
+ return srs_fmt("%s->%s", srs_gb_session_state(ostate).c_str(), srs_gb_session_state(state).c_str());
+}
+
+std::string srs_gb_sip_state(SrsGbSipState state)
+{
+ switch (state) {
+ case SrsGbSipStateInit: return "Init";
+ case SrsGbSipStateRegistered: return "Registered";
+ case SrsGbSipStateInviting: return "Inviting";
+ case SrsGbSipStateTrying: return "Trying";
+ case SrsGbSipStateStable: return "Stable";
+ case SrsGbSipStateReinviting: return "Re-inviting";
+ case SrsGbSipStateBye: return "Bye";
+ default: return "Invalid";
+ }
+}
+
+std::string srs_sip_state(SrsGbSipState ostate, SrsGbSipState state)
+{
+ return srs_fmt("%s->%s", srs_gb_sip_state(ostate).c_str(), srs_gb_sip_state(state).c_str());
+}
+
+ISrsGbSipConn::ISrsGbSipConn()
+{
+}
+
+ISrsGbSipConn::~ISrsGbSipConn()
+{
+}
+
+ISrsGbSipConnWrapper::ISrsGbSipConnWrapper()
+{
+}
+
+ISrsGbSipConnWrapper::~ISrsGbSipConnWrapper()
+{
+}
+
+ISrsGbMediaConn::ISrsGbMediaConn()
+{
+}
+
+ISrsGbMediaConn::~ISrsGbMediaConn()
+{
+}
+
+ISrsGbMediaConnWrapper::ISrsGbMediaConnWrapper()
+{
+}
+
+ISrsGbMediaConnWrapper::~ISrsGbMediaConnWrapper()
+{
+}
+
+SrsLazyGbSession::SrsLazyGbSession()
+{
+ sip_ = new ISrsGbSipConnWrapper();
+ media_ = new ISrsGbMediaConnWrapper();
+ muxer_ = new SrsGbMuxer(this);
+ state_ = SrsGbSessionStateInit;
+
+ connecting_starttime_ = 0;
+ connecting_timeout_ = 0;
+ nn_timeout_ = 0;
+ reinviting_starttime_ = 0;
+ reinvite_wait_ = 0;
+
+ ppp_ = new SrsAlonePithyPrint();
+ startime_ = srs_update_system_time();
+ total_packs_ = 0;
+ total_msgs_ = 0;
+ total_recovered_ = 0;
+ total_msgs_dropped_ = 0;
+ total_reserved_ = 0;
+
+ media_id_ = 0;
+ media_msgs_ = 0;
+ media_packs_ = 0;
+ media_starttime_ = startime_;
+ media_recovered_ = 0;
+ media_msgs_dropped_ = 0;
+ media_reserved_ = 0;
+
+ cid_ = _srs_context->generate_id();
+ _srs_context->set_id(cid_); // Also change current coroutine cid as session's.
+ trd_ = new SrsSTCoroutine("GBS", this, cid_);
+}
+
+SrsLazyGbSession::~SrsLazyGbSession()
+{
+ srs_freep(trd_);
+ srs_freep(sip_);
+ srs_freep(media_);
+ srs_freep(muxer_);
+ srs_freep(ppp_);
+}
+
+srs_error_t SrsLazyGbSession::initialize(SrsConfDirective* conf)
+{
+ srs_error_t err = srs_success;
+
+ pip_ = candidate_ = _srs_config->get_stream_caster_sip_candidate(conf);
+ if (candidate_ == "*") {
+ pip_ = srs_get_public_internet_address(true);
+ }
+
+ std::string output = _srs_config->get_stream_caster_output(conf);
+ if ((err = muxer_->initialize(output)) != srs_success) {
+ return srs_error_wrap(err, "muxer");
+ }
+
+ connecting_timeout_ = _srs_config->get_stream_caster_sip_timeout(conf);
+ reinvite_wait_ = _srs_config->get_stream_caster_sip_reinvite(conf);
+ srs_trace("Session: Start timeout=%dms, reinvite=%dms, candidate=%s, pip=%s, output=%s", srsu2msi(connecting_timeout_),
+ srsu2msi(reinvite_wait_), candidate_.c_str(), pip_.c_str(), output.c_str());
+
+ return err;
+}
+
+void SrsLazyGbSession::on_ps_pack(SrsPackContext* ctx, SrsPsPacket* ps, const std::vector& msgs)
+{
+ // Got a new context, that is new media transport.
+ if (media_id_ != ctx->media_id_) {
+ total_msgs_ += media_msgs_;
+ total_packs_ += media_packs_;
+ total_recovered_ += media_recovered_;
+ total_msgs_dropped_ += media_msgs_dropped_;
+ total_reserved_ += media_reserved_;
+
+ media_msgs_ = media_packs_ = 0;
+ media_recovered_ = media_msgs_dropped_ = 0;
+ media_reserved_ = 0;
+ }
+
+ // Update data for current context.
+ media_id_ = ctx->media_id_;
+ media_packs_++;
+ media_msgs_ += msgs.size();
+ media_starttime_ = ctx->media_startime_;
+ media_recovered_ = ctx->media_nn_recovered_;
+ media_msgs_dropped_ = ctx->media_nn_msgs_dropped_;
+ media_reserved_ = ctx->media_reserved_;
+
+ // Group all video in pack to a video frame, because only allows one video for each PS pack.
+ SrsTsMessage* video = new SrsTsMessage();
+ SrsAutoFree(SrsTsMessage, video);
+
+ for (vector::const_iterator it = msgs.begin(); it != msgs.end(); ++it) {
+ SrsTsMessage* msg = *it;
+
+ // Group all videos to one video.
+ if (msg->sid == SrsTsPESStreamIdVideoCommon) {
+ video->dts = msg->dts;
+ video->pts = msg->pts;
+ video->sid = msg->sid;
+ video->payload->append(msg->payload);
+ continue;
+ }
+
+ // Directly mux audio message.
+ srs_error_t err = muxer_->on_ts_message(msg);
+ if (err != srs_success) {
+ srs_warn("Muxer: Ignore audio err %s", srs_error_desc(err).c_str());
+ srs_freep(err);
+ }
+ }
+
+ // Send the generated video message.
+ if (video->payload->length() > 0) {
+ srs_error_t err = muxer_->on_ts_message(video);
+ if (err != srs_success) {
+ srs_warn("Muxer: Ignore video err %s", srs_error_desc(err).c_str());
+ srs_freep(err);
+ }
+ }
+}
+
+void SrsLazyGbSession::on_sip_transport(ISrsGbSipConnWrapper* sip)
+{
+ srs_freep(sip_);
+ sip_ = sip->copy();
+
+ // Change id of SIP and all its child coroutines.
+ sip_->resource()->set_cid(cid_);
+}
+
+ISrsGbSipConnWrapper* SrsLazyGbSession::sip_transport()
+{
+ return sip_;
+}
+
+void SrsLazyGbSession::on_media_transport(ISrsGbMediaConnWrapper* media)
+{
+ srs_freep(media_);
+ media_ = media->copy();
+
+ // Change id of SIP and all its child coroutines.
+ media_->resource()->set_cid(cid_);
+}
+
+std::string SrsLazyGbSession::pip()
+{
+ return pip_;
+}
+
+srs_error_t SrsLazyGbSession::start()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = trd_->start()) != srs_success) {
+ return srs_error_wrap(err, "coroutine");
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSession::cycle()
+{
+ srs_error_t err = do_cycle();
+
+ // Interrupt the SIP and media transport when session terminated.
+ sip_->resource()->interrupt();
+ media_->resource()->interrupt();
+
+ // Note that we added wrapper to manager, so we must free the wrapper, not this connection.
+ SrsLazyGbSessionWrapper* wrapper = dynamic_cast(gc_creator_wrapper());
+ srs_assert(wrapper); // The creator wrapper MUST never be null, because we created it.
+ _srs_gb_manager->remove(wrapper);
+
+ // success.
+ if (err == srs_success) {
+ srs_trace("client finished.");
+ return err;
+ }
+
+ // It maybe success with message.
+ if (srs_error_code(err) == ERROR_SUCCESS) {
+ srs_trace("client finished%s.", srs_error_summary(err).c_str());
+ srs_freep(err);
+ return err;
+ }
+
+ // client close peer.
+ // TODO: FIXME: Only reset the error when client closed it.
+ if (srs_is_client_gracefully_close(err)) {
+ srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
+ } else if (srs_is_server_gracefully_close(err)) {
+ srs_warn("server disconnect. ret=%d", srs_error_code(err));
+ } else {
+ srs_error("serve error %s", srs_error_desc(err).c_str());
+ }
+
+ srs_freep(err);
+ return srs_success;
+}
+
+srs_error_t SrsLazyGbSession::do_cycle()
+{
+ srs_error_t err = srs_success;
+
+ while (true) {
+ if ((err = trd_->pull()) != srs_success) {
+ return srs_error_wrap(err, "pull");
+ }
+
+ // Drive the state in a fixed interval.
+ srs_usleep(SRS_GB_SESSION_DRIVE_INTERVAL);
+
+ // Client send bye, we should dispose the session.
+ if (sip_->resource()->is_bye()) {
+ return err;
+ }
+
+ // Regular state, driven by state of SIP and transport.
+ if ((err = drive_state()) != srs_success) {
+ return srs_error_wrap(err, "drive");
+ }
+
+ ppp_->elapse();
+ if (ppp_->can_print()) {
+ int alive = srsu2msi(srs_update_system_time() - startime_) / 1000;
+ int pack_alive = srsu2msi(srs_update_system_time() - media_starttime_) / 1000;
+ srs_trace("Session: Alive=%ds, packs=%" PRId64 ", recover=%" PRId64 ", reserved=%" PRId64 ", msgs=%" PRId64 ", drop=%" PRId64 ", media(id=%u, alive=%ds, packs=%" PRId64 " recover=%" PRId64", reserved=%" PRId64 ", msgs=%" PRId64 ", drop=%" PRId64 ")",
+ alive, (total_packs_ + media_packs_), (total_recovered_ + media_recovered_), (total_reserved_ + media_reserved_),
+ (total_msgs_ + media_msgs_), (total_msgs_dropped_ + media_msgs_dropped_), media_id_, pack_alive, media_packs_,
+ media_recovered_, media_reserved_, media_msgs_, media_msgs_dropped_);
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSession::drive_state()
+{
+ srs_error_t err = srs_success;
+
+ #define SRS_GB_CHANGE_STATE_TO(state) { \
+ SrsGbSessionState ostate = set_state(state); \
+ srs_trace("Session: Change device=%s, state=%s", sip_->resource()->device_id().c_str(), \
+ srs_gb_state(ostate, state_).c_str()); \
+ }
+
+ if (state_ == SrsGbSessionStateInit) {
+ // Set to connecting, whatever media is connected or not, because the connecting state will handle it if media
+ // is connected, so we don't need to handle it here.
+ if (sip_->resource()->is_registered()) {
+ SRS_GB_CHANGE_STATE_TO(SrsGbSessionStateConnecting);
+ connecting_starttime_ = srs_update_system_time();
+ }
+
+ // Invite if media is not connected.
+ if (sip_->resource()->is_registered() && !media_->resource()->is_connected()) {
+ uint32_t ssrc = 0;
+ if ((err = sip_->resource()->invite_request(&ssrc)) != srs_success) {
+ return srs_error_wrap(err, "invite");
+ }
+
+ // Now, we're able to query session by ssrc, for media packets.
+ SrsLazyGbSessionWrapper* wrapper = dynamic_cast(gc_available_wrapper());
+ srs_assert(wrapper); // It MUST never be NULL, because this method is in the cycle of coroutine.
+ _srs_gb_manager->add_with_fast_id(ssrc, wrapper);
+ }
+ }
+
+ if (state_ == SrsGbSessionStateConnecting) {
+ if (srs_update_system_time() - connecting_starttime_ >= connecting_timeout_) {
+ if ((nn_timeout_++) > SRS_GB_MAX_TIMEOUT) {
+ return srs_error_new(ERROR_GB_TIMEOUT, "timeout");
+ }
+
+ srs_trace("Session: Connecting timeout, nn=%d, state=%s, sip=%s, media=%d", nn_timeout_, srs_gb_session_state(state_).c_str(),
+ srs_gb_sip_state(sip_->resource()->state()).c_str(), media_->resource()->is_connected());
+ sip_->resource()->reset_to_register();
+ SRS_GB_CHANGE_STATE_TO(SrsGbSessionStateInit);
+ }
+
+ if (sip_->resource()->is_stable() && media_->resource()->is_connected()) {
+ SRS_GB_CHANGE_STATE_TO(SrsGbSessionStateEstablished);
+ }
+ }
+
+ if (state_ == SrsGbSessionStateEstablished) {
+ if (sip_->resource()->is_bye()) {
+ srs_trace("Session: Dispose for client bye");
+ return err;
+ }
+
+ // When media disconnected, we wait for a while then reinvite.
+ if (!media_->resource()->is_connected()) {
+ if (!reinviting_starttime_) {
+ reinviting_starttime_ = srs_update_system_time();
+ }
+ if (srs_get_system_time() - reinviting_starttime_ > reinvite_wait_) {
+ reinviting_starttime_ = 0;
+ srs_trace("Session: Re-invite for disconnect, state=%s, sip=%s, media=%d", srs_gb_session_state(state_).c_str(),
+ srs_gb_sip_state(sip_->resource()->state()).c_str(), media_->resource()->is_connected());
+ sip_->resource()->reset_to_register();
+ SRS_GB_CHANGE_STATE_TO(SrsGbSessionStateInit);
+ }
+ }
+ }
+
+ return err;
+}
+
+SrsGbSessionState SrsLazyGbSession::set_state(SrsGbSessionState v)
+{
+ SrsGbSessionState state = state_;
+ state_ = v;
+ return state;
+}
+
+const SrsContextId& SrsLazyGbSession::get_id()
+{
+ return cid_;
+}
+
+std::string SrsLazyGbSession::desc()
+{
+ return "GBS";
+}
+
+SrsGbListener::SrsGbListener()
+{
+ conf_ = NULL;
+ sip_listener_ = new SrsTcpListener(this);
+ media_listener_ = new SrsTcpListener(this);
+}
+
+SrsGbListener::~SrsGbListener()
+{
+ srs_freep(conf_);
+ srs_freep(sip_listener_);
+ srs_freep(media_listener_);
+}
+
+srs_error_t SrsGbListener::initialize(SrsConfDirective* conf)
+{
+ srs_error_t err = srs_success;
+
+ srs_freep(conf_);
+ conf_ = conf->copy();
+
+ string ip = srs_any_address_for_listener();
+ if (true) {
+ int port = _srs_config->get_stream_caster_listen(conf);
+ media_listener_->set_endpoint(ip, port)->set_label("GB-TCP");
+ }
+
+ bool sip_enabled = _srs_config->get_stream_caster_sip_enable(conf);
+ if (!sip_enabled) {
+ return srs_error_new(ERROR_GB_CONFIG, "GB SIP is required");
+ }
+
+ int port = _srs_config->get_stream_caster_sip_listen(conf);
+ sip_listener_->set_endpoint(ip, port)->set_label("SIP-TCP");
+
+ return err;
+}
+
+srs_error_t SrsGbListener::listen()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = media_listener_->listen()) != srs_success) {
+ return srs_error_wrap(err, "listen");
+ }
+
+ if ((err = sip_listener_->listen()) != srs_success) {
+ return srs_error_wrap(err, "listen");
+ }
+
+ return err;
+}
+
+void SrsGbListener::close()
+{
+}
+
+srs_error_t SrsGbListener::on_tcp_client(ISrsListener* listener, srs_netfd_t stfd)
+{
+ srs_error_t err = srs_success;
+
+ // Handle TCP connections.
+ if (listener == sip_listener_) {
+ SrsLazyGbSipTcpConnWrapper* conn = new SrsLazyGbSipTcpConnWrapper();
+ SrsLazyGbSipTcpConn* resource = dynamic_cast(conn->resource());
+ resource->setup(conf_, sip_listener_, media_listener_, stfd);
+
+ if ((err = resource->start()) != srs_success) {
+ srs_freep(conn);
+ return srs_error_wrap(err, "gb sip");
+ }
+
+ _srs_gb_manager->add(conn, NULL);
+ } else if (listener == media_listener_) {
+ SrsLazyGbMediaTcpConnWrapper* conn = new SrsLazyGbMediaTcpConnWrapper();
+ SrsLazyGbMediaTcpConn* resource = dynamic_cast(conn->resource());
+ resource->setup(stfd);
+
+ if ((err = resource->start()) != srs_success) {
+ srs_freep(conn);
+ return srs_error_wrap(err, "gb media");
+ }
+
+ _srs_gb_manager->add(conn, NULL);
+ } else {
+ srs_warn("GB: Ignore TCP client");
+ srs_close_stfd(stfd);
+ }
+
+ return err;
+}
+
+SrsLazyGbSipTcpConn::SrsLazyGbSipTcpConn()
+{
+ session_ = NULL;
+ state_ = SrsGbSipStateInit;
+ register_ = new SrsSipMessage();
+ invite_ok_ = new SrsSipMessage();
+ ssrc_v_ = 0;
+
+ conf_ = NULL;
+ sip_listener_ = NULL;
+ media_listener_ = NULL;
+ conn_ = NULL;
+ receiver_ = NULL;
+ sender_ = NULL;
+
+ trd_ = new SrsSTCoroutine("sip", this);
+}
+
+SrsLazyGbSipTcpConn::~SrsLazyGbSipTcpConn()
+{
+ srs_freep(trd_);
+ srs_freep(receiver_);
+ srs_freep(sender_);
+ srs_freep(conn_);
+ srs_freep(session_);
+ srs_freep(register_);
+ srs_freep(invite_ok_);
+ srs_freep(conf_);
+}
+
+void SrsLazyGbSipTcpConn::setup(SrsConfDirective* conf, SrsTcpListener* sip, SrsTcpListener* media, srs_netfd_t stfd)
+{
+ srs_freep(conf_);
+ conf_ = conf->copy();
+
+ session_ = NULL;
+ sip_listener_ = sip;
+ media_listener_ = media;
+ conn_ = new SrsTcpConnection(stfd);
+ receiver_ = new SrsLazyGbSipTcpReceiver(this, conn_);
+ sender_ = new SrsLazyGbSipTcpSender(conn_);
+}
+
+std::string SrsLazyGbSipTcpConn::device_id()
+{
+ return register_->device_id();
+}
+
+void SrsLazyGbSipTcpConn::set_cid(const SrsContextId& cid)
+{
+ trd_->set_cid(cid);
+ receiver_->set_cid(cid);
+ sender_->set_cid(cid);
+}
+
+void SrsLazyGbSipTcpConn::query_ports(int* sip, int* media)
+{
+ if (sip) *sip = sip_listener_->port();
+ if (media) *media = media_listener_->port();
+}
+
+srs_error_t SrsLazyGbSipTcpConn::on_sip_message(SrsSipMessage* msg)
+{
+ srs_error_t err = srs_success;
+
+ // Finger out the GB session to handle SIP messages.
+ if (!session_ && (err = bind_session(msg, &session_)) != srs_success) {
+ return srs_error_wrap(err, "bind session");
+ }
+
+ // Ignore if session not found.
+ if (!session_) {
+ srs_warn("SIP: No session, drop message type=%d, id=%s, body=%s", msg->type_,
+ msg->device_id().c_str(), msg->body_escaped_.c_str());
+ return err;
+ }
+
+ // For state to use device id from register message.
+ if (msg->is_register()) {
+ srs_freep(register_); register_ = msg->copy(); // Cache the register request message.
+ }
+
+ // Drive state machine of SIP connection.
+ drive_state(msg);
+
+ // Notify session about the SIP message.
+ if (msg->is_register()) {
+ register_response(msg); // Response for REGISTER.
+ } else if (msg->is_message()) {
+ // Response for MESSAGE, the heartbeat message.
+ // Set 403 to require client register, see https://www.ietf.org/rfc/rfc3261.html#section-21.4
+ // Please note that it does not work for GB device, which just ignore 4xx packets like no response.
+ message_response(msg, (state_ == SrsGbSipStateInit ? HTTP_STATUS_FORBIDDEN : HTTP_STATUS_OK));
+ } else if (msg->is_invite_ok()) {
+ srs_freep(invite_ok_);
+ invite_ok_ = msg->copy(); // Cache the invite ok message.
+ invite_ack(msg); // Response for INVITE OK.
+ } else if (msg->is_bye()) {
+ bye_response(msg); // Response for Bye OK.
+ } else if (msg->is_trying() || msg->is_bye_ok()) {
+ // Ignore SIP message 100(Trying).
+ // Ignore BYE ok.
+ } else {
+ srs_warn("SIP: Ignore message type=%d, status=%d, method=%d, body=%s", msg->type_,
+ msg->status_, msg->method_, msg->body_escaped_.c_str());
+ }
+
+ return err;
+}
+
+void SrsLazyGbSipTcpConn::enqueue_sip_message(SrsSipMessage* msg)
+{
+ // Drive state machine when enqueue message.
+ drive_state(msg);
+
+ // TODO: Support SIP transaction and wait for response for request?
+ sender_->enqueue(msg);
+}
+
+void SrsLazyGbSipTcpConn::drive_state(SrsSipMessage* msg)
+{
+ srs_error_t err = srs_success;
+
+ #define SRS_GB_SIP_CHANGE_STATE_TO(state) { \
+ SrsGbSipState ostate = set_state(state); \
+ srs_trace("SIP: Change device=%s, state=%s", register_->device_id().c_str(), \
+ srs_sip_state(ostate, state_).c_str()); \
+ }
+
+ //const char* mt = msg->type_ == HTTP_REQUEST ? "REQUEST" : "RESPONSE";
+ //const char* mm = msg->type_ == HTTP_REQUEST ? http_method_str(msg->method_) : "Response";
+ //int ms = msg->type_ == HTTP_REQUEST ? 200 : msg->status_;
+ //srs_trace("SIP: Got message type=%s, method=%s, status=%d, expire=%d", mt, mm, ms, msg->expires_);
+
+ if (state_ == SrsGbSipStateInit) {
+ // The register message, we will invite it automatically.
+ if (msg->is_register() && msg->expires_ > 0) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateRegistered);
+ // Client bye or unregister, we should destroy the session because it might never publish again.
+ if (msg->is_register() && msg->expires_ == 0) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateBye);
+ // When got heartbeat message, we restore to stable state.
+ if (msg->is_message()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateStable);
+ }
+
+ if (state_ == SrsGbSipStateRegistered) {
+ if (msg->is_invite()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateInviting);
+ }
+
+ if (state_ == SrsGbSipStateInviting) {
+ if (msg->is_trying()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateTrying);
+ if (msg->is_invite_ok()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateStable);
+
+ // If device got invite request and disconnect, it might register again, we should re-invite.
+ if (msg->is_register()) {
+ srs_warn("SIP: Re-invite for got REGISTER in state=%s", srs_gb_sip_state(state_).c_str());
+ if ((err = invite_request(NULL)) != srs_success) {
+ // TODO: FIXME: Should fail the SIP session.
+ srs_freep(err);
+ }
+ }
+ }
+
+ if (state_ == SrsGbSipStateTrying) {
+ if (msg->is_invite_ok()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateStable);
+ }
+
+ if (state_ == SrsGbSipStateStable) {
+ // Client bye or unregister, we should destroy the session because it might never publish again.
+ if (msg->is_register() && msg->expires_ == 0) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateBye);
+ if (msg->is_bye()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateBye);
+ }
+
+ if (state_ == SrsGbSipStateReinviting) {
+ if (msg->is_bye_ok()) SRS_GB_SIP_CHANGE_STATE_TO(SrsGbSipStateInviting);
+ }
+}
+
+void SrsLazyGbSipTcpConn::register_response(SrsSipMessage* msg)
+{
+ SrsSipMessage* res = new SrsSipMessage();
+
+ res->type_ = HTTP_RESPONSE;
+ res->status_ = HTTP_STATUS_OK;
+ res->via_ = msg->via_;
+ res->from_ = msg->from_;
+ res->to_ = msg->to_;
+ res->cseq_ = msg->cseq_;
+ res->call_id_ = msg->call_id_;
+ res->contact_ = msg->contact_;
+ res->expires_ = msg->expires_;
+
+ enqueue_sip_message(res);
+}
+
+void SrsLazyGbSipTcpConn::message_response(SrsSipMessage* msg, http_status status)
+{
+ SrsSipMessage* res = new SrsSipMessage();
+
+ res->type_ = HTTP_RESPONSE;
+ res->status_ = status;
+ res->via_ = msg->via_;
+ res->from_ = msg->from_;
+ res->to_ = msg->to_;
+ res->cseq_ = msg->cseq_;
+ res->call_id_ = msg->call_id_;
+
+ enqueue_sip_message(res);
+}
+
+void SrsLazyGbSipTcpConn::invite_ack(SrsSipMessage* msg)
+{
+ string pip = session_->resource()->pip(); // Parse from CANDIDATE
+ int sip_port; query_ports(&sip_port, NULL);
+ string gb_device_id = srs_fmt("sip:%s@%s", msg->to_address_user_.c_str(), msg->to_address_host_.c_str());
+ string branch = srs_random_str(6);
+
+ SrsSipMessage* req = new SrsSipMessage();
+ req->type_ = HTTP_REQUEST;
+ req->method_ = HTTP_ACK;
+ req->request_uri_ = gb_device_id;
+ req->via_ = srs_fmt("SIP/2.0/TCP %s:%d;rport;branch=%s%s", pip.c_str(), sip_port, SRS_GB_BRANCH_MAGIC, branch.c_str());
+ req->from_ = msg->from_;
+ req->to_ = msg->to_;
+ req->cseq_ = srs_fmt("%d ACK", msg->cseq_number_);
+ req->call_id_ = msg->call_id_;
+ req->max_forwards_ = 70;
+
+ enqueue_sip_message(req);
+}
+
+void SrsLazyGbSipTcpConn::bye_response(SrsSipMessage* msg)
+{
+ SrsSipMessage* res = new SrsSipMessage();
+
+ res->type_ = HTTP_RESPONSE;
+ res->status_ = HTTP_STATUS_OK;
+ res->via_ = msg->via_;
+ res->from_ = msg->from_;
+ res->to_ = msg->to_;
+ res->cseq_ = msg->cseq_;
+ res->call_id_ = msg->call_id_;
+
+ enqueue_sip_message(res);
+}
+
+srs_error_t SrsLazyGbSipTcpConn::invite_request(uint32_t* pssrc)
+{
+ srs_error_t err = srs_success;
+
+ srs_assert(register_);
+
+ if (true) {
+ // Generate SSRC, detect conflict.
+ string ssrc = ssrc_str_;
+ for (int i = 0; ssrc.empty() && i < 16; i++) {
+ int flag = 0; // 0 is realtime.
+ string ssrc_str = srs_fmt("%d%s%04d", flag, register_->ssrc_domain_id().c_str(), srs_random() % 10000);
+ uint32_t ssrc_v = (uint32_t) ::atol(ssrc_str.c_str());
+ if (!_srs_gb_manager->find_by_fast_id(ssrc_v)) {
+ ssrc = ssrc_str;
+ break;
+ }
+ }
+ if (ssrc.empty()) {
+ return srs_error_new(ERROR_GB_SSRC_GENERATE, "Generate SSRC failed");
+ }
+
+ // Update and cache the SSRC for re-invite.
+ ssrc_str_ = ssrc;
+ ssrc_v_ = (uint32_t) ::atol(ssrc_str_.c_str());
+ if (pssrc) *pssrc = ssrc_v_;
+ }
+
+ string pip = session_->resource()->pip(); // Parse from CANDIDATE
+ int sip_port, media_port; query_ports(&sip_port, &media_port);
+ string srs_device_id = srs_fmt("sip:%s@%s", register_->request_uri_user_.c_str(), register_->request_uri_host_.c_str());
+ string gb_device_id = srs_fmt("sip:%s@%s", register_->from_address_user_.c_str(), register_->from_address_host_.c_str());
+ string subject = srs_fmt("%s:%s,%s:0", register_->from_address_user_.c_str(), ssrc_str_.c_str(), register_->request_uri_user_.c_str());
+ string branch = srs_random_str(6);
+ string tag = srs_random_str(8);
+ string call_id = srs_random_str(16);
+ int cseq = (int)(srs_random()%1000); // TODO: FIXME: Increase.
+
+ SrsSdp local_sdp;
+ local_sdp.version_ = "0";
+ local_sdp.username_ = register_->contact_user_;
+ local_sdp.session_id_ = "0";
+ local_sdp.session_version_ = "0";
+ local_sdp.nettype_ = "IN";
+ local_sdp.addrtype_ = "IP4";
+ local_sdp.unicast_address_ = pip; // Parse from CANDIDATE
+ local_sdp.session_name_ = "Play";
+ local_sdp.start_time_ = 0;
+ local_sdp.end_time_ = 0;
+ local_sdp.ice_lite_ = ""; // Disable this line.
+ local_sdp.connection_ = srs_fmt("c=IN IP4 %s", pip.c_str()); // Session level connection.
+
+ local_sdp.media_descs_.push_back(SrsMediaDesc("video"));
+ SrsMediaDesc& media = local_sdp.media_descs_.at(0);
+ media.port_ = media_port; // Read from config.
+ media.protos_ = "TCP/RTP/AVP";
+ media.connection_ = ""; // Disable media level connection.
+ media.recvonly_ = true;
+
+ media.payload_types_.push_back(SrsMediaPayloadType(96));
+ SrsMediaPayloadType& ps = media.payload_types_.at(0);
+ ps.encoding_name_ = "PS";
+ ps.clock_rate_ = 90000;
+
+ media.ssrc_infos_.push_back(SrsSSRCInfo());
+ SrsSSRCInfo& ssrc_info = media.ssrc_infos_.at(0);
+ ssrc_info.cname_ = ssrc_str_;
+ ssrc_info.ssrc_ = ssrc_v_;
+ ssrc_info.label_ = "gb28181";
+
+ ostringstream ss;
+ if ((err = local_sdp.encode(ss)) != srs_success) {
+ return srs_error_wrap(err, "encode sdp");
+ }
+
+ SrsSipMessage* req = new SrsSipMessage();
+ req->type_ = HTTP_REQUEST;
+ req->method_ = HTTP_INVITE;
+ req->request_uri_ = gb_device_id;
+ req->via_ = srs_fmt("SIP/2.0/TCP %s:%d;rport;branch=%s%s", pip.c_str(), sip_port, SRS_GB_BRANCH_MAGIC, branch.c_str());
+ req->from_ = srs_fmt("<%s>;tag=SRS%s", srs_device_id.c_str(), tag.c_str());
+ req->to_ = srs_fmt("<%s>", gb_device_id.c_str());
+ req->cseq_ = srs_fmt("%d INVITE", cseq);
+ req->call_id_ = call_id;
+ req->content_type_ = "Application/SDP";
+ req->contact_ = srs_fmt("<%s>", srs_device_id.c_str());
+ req->max_forwards_ = 70;
+ req->subject_ = subject;
+ req->set_body(ss.str());
+
+ enqueue_sip_message(req);
+ srs_trace("SIP: INVITE device=%s, branch=%s, tag=%s, call=%s, ssrc=%s, sdp is %s", gb_device_id.c_str(), branch.c_str(),
+ tag.c_str(), call_id.c_str(), ssrc_str_.c_str(), req->body_escaped_.c_str());
+
+ return err;
+}
+
+void SrsLazyGbSipTcpConn::interrupt()
+{
+ receiver_->interrupt();
+ sender_->interrupt();
+ trd_->interrupt();
+}
+
+SrsGbSipState SrsLazyGbSipTcpConn::state()
+{
+ return state_;
+}
+
+void SrsLazyGbSipTcpConn::reset_to_register()
+{
+ state_ = SrsGbSipStateRegistered;
+}
+
+bool SrsLazyGbSipTcpConn::is_registered()
+{
+ return state_ >= SrsGbSipStateRegistered && state_ <= SrsGbSipStateStable;
+}
+
+bool SrsLazyGbSipTcpConn::is_stable()
+{
+ return state_ == SrsGbSipStateStable;
+}
+
+bool SrsLazyGbSipTcpConn::is_bye()
+{
+ return state_ == SrsGbSipStateBye;
+}
+
+SrsGbSipState SrsLazyGbSipTcpConn::set_state(SrsGbSipState v)
+{
+ SrsGbSipState state = state_;
+ state_ = v;
+ return state;
+}
+
+const SrsContextId& SrsLazyGbSipTcpConn::get_id()
+{
+ return trd_->cid();
+}
+
+std::string SrsLazyGbSipTcpConn::desc()
+{
+ return "GB-SIP-TCP";
+}
+
+srs_error_t SrsLazyGbSipTcpConn::start()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = trd_->start()) != srs_success) {
+ return srs_error_wrap(err, "sip");
+ }
+
+ if ((err = receiver_->start()) != srs_success) {
+ return srs_error_wrap(err, "receiver");
+ }
+
+ if ((err = sender_->start()) != srs_success) {
+ return srs_error_wrap(err, "sender");
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpConn::cycle()
+{
+ srs_error_t err = do_cycle();
+
+ // Interrupt the receiver and sender coroutine.
+ receiver_->interrupt();
+ sender_->interrupt();
+
+ // Note that we added wrapper to manager, so we must free the wrapper, not this connection.
+ SrsLazyGbSipTcpConnWrapper* wrapper = dynamic_cast(gc_creator_wrapper());
+ srs_assert(wrapper); // The creator wrapper MUST never be null, because we created it.
+ _srs_gb_manager->remove(wrapper);
+
+ // success.
+ if (err == srs_success) {
+ srs_trace("client finished.");
+ return err;
+ }
+
+ // It maybe success with message.
+ if (srs_error_code(err) == ERROR_SUCCESS) {
+ srs_trace("client finished%s.", srs_error_summary(err).c_str());
+ srs_freep(err);
+ return err;
+ }
+
+ // client close peer.
+ // TODO: FIXME: Only reset the error when client closed it.
+ if (srs_is_client_gracefully_close(err)) {
+ srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
+ } else if (srs_is_server_gracefully_close(err)) {
+ srs_warn("server disconnect. ret=%d", srs_error_code(err));
+ } else {
+ srs_error("serve error %s", srs_error_desc(err).c_str());
+ }
+
+ srs_freep(err);
+ return srs_success;
+}
+
+srs_error_t SrsLazyGbSipTcpConn::do_cycle()
+{
+ srs_error_t err = srs_success;
+
+ while (true) {
+ if ((err = trd_->pull()) != srs_success) {
+ return srs_error_wrap(err, "pull");
+ }
+
+ // TODO: Handle other messages.
+ srs_usleep(SRS_UTIME_NO_TIMEOUT);
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpConn::bind_session(SrsSipMessage* msg, SrsLazyGbSessionWrapper** psession)
+{
+ srs_error_t err = srs_success;
+
+ string device = msg->device_id();
+ if (device.empty()) return err;
+
+ // Only create session for REGISTER request.
+ if (msg->type_ != HTTP_REQUEST || msg->method_ != HTTP_REGISTER) return err;
+
+ // The lazy-sweep wrapper for this resource.
+ SrsLazyGbSipTcpConnWrapper* wrapper = dynamic_cast(gc_available_wrapper());
+ srs_assert(wrapper); // It MUST never be NULL, because this method is in the cycle of coroutine of receiver.
+
+ // Find exists session for register, might be created by another object and still alive.
+ SrsLazyGbSessionWrapper* session = dynamic_cast(_srs_gb_manager->find_by_id(device));
+ if (!session) {
+ // Create new GB session.
+ session = new SrsLazyGbSessionWrapper();
+
+ if ((err = session->resource()->initialize(conf_)) != srs_success) {
+ srs_freep(session);
+ return srs_error_wrap(err, "initialize");
+ }
+
+ if ((err = session->resource()->start()) != srs_success) {
+ srs_freep(session);
+ return srs_error_wrap(err, "start");
+ }
+
+ _srs_gb_manager->add_with_id(device, session);
+ }
+
+ // Try to load state from previous SIP connection.
+ SrsLazyGbSipTcpConn* pre = dynamic_cast(session->resource()->sip_transport()->resource());
+ if (pre) {
+ state_ = pre->state_;
+ ssrc_str_ = pre->ssrc_str_;
+ ssrc_v_ = pre->ssrc_v_;
+ srs_freep(register_); register_ = pre->register_->copy();
+ srs_freep(invite_ok_); invite_ok_ = pre->invite_ok_->copy();
+ }
+
+ // Notice SIP session to use current SIP connection.
+ session->resource()->on_sip_transport(wrapper);
+ *psession = session->copy();
+
+ return err;
+}
+
+SrsLazyGbSipTcpReceiver::SrsLazyGbSipTcpReceiver(SrsLazyGbSipTcpConn* sip, SrsTcpConnection* conn)
+{
+ sip_ = sip;
+ conn_ = conn;
+ trd_ = new SrsSTCoroutine("sip-receiver", this);
+}
+
+SrsLazyGbSipTcpReceiver::~SrsLazyGbSipTcpReceiver()
+{
+ srs_freep(trd_);
+}
+
+void SrsLazyGbSipTcpReceiver::interrupt()
+{
+ trd_->interrupt();
+}
+
+void SrsLazyGbSipTcpReceiver::set_cid(const SrsContextId& cid)
+{
+ trd_->set_cid(cid);
+}
+
+srs_error_t SrsLazyGbSipTcpReceiver::start()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = trd_->start()) != srs_success) {
+ return srs_error_wrap(err, "coroutine");
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpReceiver::cycle()
+{
+ srs_error_t err = do_cycle();
+
+ // TODO: FIXME: Notify SIP transport to cleanup.
+ if (err != srs_success) {
+ srs_error("SIP: Receive err %s", srs_error_desc(err).c_str());
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpReceiver::do_cycle()
+{
+ srs_error_t err = srs_success;
+
+ SrsHttpParser* parser = new SrsHttpParser();
+ SrsAutoFree(SrsHttpParser, parser);
+
+ // We might get SIP request or response message.
+ if ((err = parser->initialize(HTTP_BOTH)) != srs_success) {
+ return srs_error_wrap(err, "init parser");
+ }
+
+ while (true) {
+ if ((err = trd_->pull()) != srs_success) {
+ return srs_error_wrap(err, "pull");
+ }
+
+ // Use HTTP parser to parse SIP messages.
+ ISrsHttpMessage* hmsg = NULL;
+ SrsAutoFree(ISrsHttpMessage, hmsg);
+ if ((err = parser->parse_message(conn_, &hmsg)) != srs_success) {
+ return srs_error_wrap(err, "parse message");
+ }
+
+ SrsSipMessage smsg;
+ if ((err = smsg.parse(hmsg)) != srs_success) {
+ srs_warn("SIP: Drop msg type=%d, method=%d, err is %s", hmsg->message_type(), hmsg->method(), srs_error_summary(err).c_str());
+ srs_freep(err); continue;
+ }
+
+ if ((err = sip_->on_sip_message(&smsg)) != srs_success) {
+ srs_warn("SIP: Ignore on msg err %s", srs_error_desc(err).c_str());
+ srs_freep(err); continue;
+ }
+ }
+
+ return err;
+}
+
+SrsLazyGbSipTcpSender::SrsLazyGbSipTcpSender(SrsTcpConnection* conn)
+{
+ conn_ = conn;
+ wait_ = srs_cond_new();
+ trd_ = new SrsSTCoroutine("sip-sender", this);
+}
+
+SrsLazyGbSipTcpSender::~SrsLazyGbSipTcpSender()
+{
+ srs_freep(trd_);
+ srs_cond_destroy(wait_);
+
+ for (vector::iterator it = msgs_.begin(); it != msgs_.end(); ++it) {
+ SrsSipMessage* msg = *it;
+ srs_freep(msg);
+ }
+}
+
+void SrsLazyGbSipTcpSender::enqueue(SrsSipMessage* msg)
+{
+ msgs_.push_back(msg);
+ srs_cond_signal(wait_);
+}
+
+void SrsLazyGbSipTcpSender::interrupt()
+{
+ trd_->interrupt();
+}
+
+void SrsLazyGbSipTcpSender::set_cid(const SrsContextId& cid)
+{
+ trd_->set_cid(cid);
+}
+
+srs_error_t SrsLazyGbSipTcpSender::start()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = trd_->start()) != srs_success) {
+ return srs_error_wrap(err, "coroutine");
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpSender::cycle()
+{
+ srs_error_t err = do_cycle();
+
+ // TODO: FIXME: Notify SIP transport to cleanup.
+ if (err != srs_success) {
+ srs_error("SIP: Send err %s", srs_error_desc(err).c_str());
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbSipTcpSender::do_cycle()
+{
+ srs_error_t err = srs_success;
+
+ while (true) {
+ if (msgs_.empty()) {
+ srs_cond_wait(wait_);
+ }
+
+ if ((err = trd_->pull()) != srs_success) {
+ return srs_error_wrap(err, "pull");
+ }
+
+ SrsSipMessage* msg = msgs_.front();
+ msgs_.erase(msgs_.begin());
+ SrsAutoFree(SrsSipMessage, msg);
+
+ if (msg->type_ == HTTP_RESPONSE) {
+ SrsSipResponseWriter res(conn_);
+ res.header()->set("Via", msg->via_);
+ res.header()->set("From", msg->from_);
+ res.header()->set("To", msg->to_);
+ res.header()->set("CSeq", msg->cseq_);
+ res.header()->set("Call-ID", msg->call_id_);
+ res.header()->set("User-Agent", RTMP_SIG_SRS_SERVER);
+ if (!msg->contact_.empty()) res.header()->set("Contact", msg->contact_);
+ if (msg->expires_ != UINT32_MAX) res.header()->set("Expires", srs_int2str(msg->expires_));
+
+ res.header()->set_content_length(msg->body_.length());
+ res.write_header(msg->status_);
+ if (!msg->body_.empty()) res.write((char*) msg->body_.c_str(), msg->body_.length());
+ if ((err = res.final_request()) != srs_success) {
+ return srs_error_wrap(err, "response");
+ }
+ } else if (msg->type_ == HTTP_REQUEST) {
+ SrsSipRequestWriter req(conn_);
+ req.header()->set("Via", msg->via_);
+ req.header()->set("From", msg->from_);
+ req.header()->set("To", msg->to_);
+ req.header()->set("CSeq", msg->cseq_);
+ req.header()->set("Call-ID", msg->call_id_);
+ req.header()->set("User-Agent", RTMP_SIG_SRS_SERVER);
+ if (!msg->contact_.empty()) req.header()->set("Contact", msg->contact_);
+ if (!msg->subject_.empty()) req.header()->set("Subject", msg->subject_);
+ if (msg->max_forwards_) req.header()->set("Max-Forwards", srs_int2str(msg->max_forwards_));
+
+ if (!msg->content_type_.empty()) req.header()->set_content_type(msg->content_type_);
+ req.header()->set_content_length(msg->body_.length());
+ req.write_header(http_method_str(msg->method_), msg->request_uri_);
+ if (!msg->body_.empty()) req.write((char*) msg->body_.c_str(), msg->body_.length());
+ if ((err = req.final_request()) != srs_success) {
+ return srs_error_wrap(err, "request");
+ }
+ } else {
+ srs_warn("SIP: Sender drop message type=%d, method=%s, body=%dB", msg->type_,
+ http_method_str(msg->method_), msg->body_.length());
+ }
+ }
+
+ return err;
+}
+
+ISrsPsPackHandler::ISrsPsPackHandler()
+{
+}
+
+ISrsPsPackHandler::~ISrsPsPackHandler()
+{
+}
+
+SrsLazyGbMediaTcpConn::SrsLazyGbMediaTcpConn()
+{
+ pack_ = new SrsPackContext(this);
+ trd_ = new SrsSTCoroutine("media", this);
+ buffer_ = new uint8_t[65535];
+ conn_ = NULL;
+
+ session_ = NULL;
+ connected_ = false;
+ nn_rtcp_ = 0;
+}
+
+SrsLazyGbMediaTcpConn::~SrsLazyGbMediaTcpConn()
+{
+ srs_freep(trd_);
+ srs_freep(conn_);
+ srs_freepa(buffer_);
+ srs_freep(pack_);
+ srs_freep(session_);
+}
+
+void SrsLazyGbMediaTcpConn::setup(srs_netfd_t stfd)
+{
+ srs_freep(conn_);
+ conn_ = new SrsTcpConnection(stfd);
+}
+
+bool SrsLazyGbMediaTcpConn::is_connected()
+{
+ return connected_;
+}
+
+void SrsLazyGbMediaTcpConn::interrupt()
+{
+ trd_->interrupt();
+}
+
+void SrsLazyGbMediaTcpConn::set_cid(const SrsContextId& cid)
+{
+ trd_->set_cid(cid);
+}
+
+const SrsContextId& SrsLazyGbMediaTcpConn::get_id()
+{
+ return _srs_context->get_id();
+}
+
+std::string SrsLazyGbMediaTcpConn::desc()
+{
+ return "GB-Media-TCP";
+}
+
+srs_error_t SrsLazyGbMediaTcpConn::start()
+{
+ srs_error_t err = srs_success;
+
+ if ((err = trd_->start()) != srs_success) {
+ return srs_error_wrap(err, "coroutine");
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbMediaTcpConn::cycle()
+{
+ srs_error_t err = do_cycle();
+
+ // Change state to disconnected.
+ connected_ = false;
+ srs_trace("PS: Media disconnect, code=%d", srs_error_code(err));
+
+ // Note that we added wrapper to manager, so we must free the wrapper, not this connection.
+ SrsLazyGbMediaTcpConnWrapper* wrapper = dynamic_cast(gc_creator_wrapper());
+ srs_assert(wrapper); // The creator wrapper MUST never be null, because we created it.
+ _srs_gb_manager->remove(wrapper);
+
+ // success.
+ if (err == srs_success) {
+ srs_trace("client finished.");
+ return err;
+ }
+
+ // It maybe success with message.
+ if (srs_error_code(err) == ERROR_SUCCESS) {
+ srs_trace("client finished%s.", srs_error_summary(err).c_str());
+ srs_freep(err);
+ return err;
+ }
+
+ // client close peer.
+ // TODO: FIXME: Only reset the error when client closed it.
+ if (srs_is_client_gracefully_close(err)) {
+ srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
+ } else if (srs_is_server_gracefully_close(err)) {
+ srs_warn("server disconnect. ret=%d", srs_error_code(err));
+ } else {
+ srs_error("serve error %s", srs_error_desc(err).c_str());
+ }
+
+ srs_freep(err);
+ return srs_success;
+}
+
+srs_error_t SrsLazyGbMediaTcpConn::do_cycle()
+{
+ srs_error_t err = srs_success;
+
+ // The PS context to decode all PS packets.
+ SrsRecoverablePsContext context;
+
+ // If bytes is not enough(defined by SRS_PS_MIN_REQUIRED), ignore.
+ context.ctx_.set_detect_ps_integrity(true);
+
+ // Previous left bytes, to parse in next loop.
+ uint32_t reserved = 0;
+
+ for (;;) {
+ if ((err = trd_->pull()) != srs_success) {
+ return srs_error_wrap(err, "pull");
+ }
+
+ // RFC4571, 2 bytes length.
+ uint16_t length = 0;
+ if (true) {
+ uint8_t lbuffer[2];
+ if ((err = conn_->read_fully(lbuffer, sizeof(lbuffer), NULL)) != srs_success) {
+ return srs_error_wrap(err, "read");
+ }
+
+ length = ((uint16_t)lbuffer[0]) << 8 | (uint16_t)lbuffer[1];
+ if (!length) {
+ return srs_error_new(ERROR_GB_PS_MEDIA, "Invalid length");
+ }
+ }
+
+ if (length > SRS_GB_LARGE_PACKET) {
+ const SrsPsDecodeHelper& h = context.ctx_.helper_;
+ srs_warn("PS: Large length=%u, previous-seq=%u, previous-ts=%u", length, h.rtp_seq_, h.rtp_ts_);
+ }
+
+ // Read length of bytes of RTP packet.
+ if ((err = conn_->read_fully(buffer_ + reserved, length, NULL)) != srs_success) {
+ return srs_error_wrap(err, "read");
+ }
+
+ // Drop all RTCP packets.
+ if (srs_is_rtcp(buffer_ + reserved, length)) {
+ nn_rtcp_++; srs_warn("PS: Drop RTCP packets nn=%d", nn_rtcp_);
+ continue;
+ }
+
+ // If no session, try to finger out it.
+ if (!session_) {
+ SrsRtpPacket rtp;
+ SrsBuffer b((char*)(buffer_ + reserved), length);
+ if ((err = rtp.decode(&b)) != srs_success) {
+ srs_warn("PS: Ignore packet length=%d for err %s", length, srs_error_desc(err).c_str());
+ srs_freep(err); // We ignore any error when decoding the RTP packet.
+ continue;
+ }
+
+ if ((err = bind_session(rtp.header.get_ssrc(), &session_)) != srs_success) {
+ return srs_error_wrap(err, "bind session");
+ }
+ }
+ if (!session_) {
+ srs_warn("PS: Ignore packet length=%d for no session", length);
+ continue; // Ignore any media packet when no session.
+ }
+
+ // Show tips about the buffer to parse.
+ if (reserved) {
+ string bytes = srs_string_dumps_hex((const char*)(buffer_ + reserved), length, 16);
+ srs_trace("PS: Consume reserved=%dB, length=%d, bytes=[%s]", reserved, length, bytes.c_str());
+ }
+
+ // Parse RTP over TCP, RFC4571.
+ SrsBuffer b((char*)buffer_, length + reserved);
+ if ((err = context.decode_rtp(&b, reserved, pack_)) != srs_success) {
+ return srs_error_wrap(err, "decode pack");
+ }
+
+ // There might some messages left to parse in next loop.
+ reserved = b.left();
+ if (reserved > 128) {
+ srs_warn("PS: Drop too many reserved=%d bytes", reserved);
+ reserved = 0; // Avoid reserving too much data.
+ }
+ if (reserved) {
+ string bytes = srs_string_dumps_hex(b.head(), reserved, 16);
+ srs_trace("PS: Reserved bytes for next loop, pos=%d, left=%d, total=%d, bytes=[%s]",
+ b.pos(), b.left(), b.size(), bytes.c_str());
+ // Copy the bytes left to the start of buffer.
+ b.read_bytes((char*)buffer_, reserved);
+ pack_->media_reserved_++;
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsLazyGbMediaTcpConn::on_ps_pack(SrsPsPacket* ps, const std::vector& msgs)
+{
+ srs_error_t err = srs_success;
+
+ // Change state to connected.
+ if (!connected_) {
+ connected_ = true;
+ srs_trace("PS: Media connected");
+ }
+
+ // Notify session about the media pack.
+ session_->resource()->on_ps_pack(pack_, ps, msgs);
+
+ //for (vector::const_iterator it = msgs.begin(); it != msgs.end(); ++it) {
+ // SrsTsMessage* msg = *it;
+ // uint8_t* p = (uint8_t*)msg->payload->bytes();
+ // srs_trace("PS: Handle message %s, dts=%" PRId64 ", payload=%dB, %#x, %#x, %#x, %#x, %#x, %#x, %#x, %#x",
+ // msg->is_video() ? "Video" : "Audio", msg->dts, msg->PES_packet_length,
+ // p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ //}
+
+ return err;
+}
+
+srs_error_t SrsLazyGbMediaTcpConn::bind_session(uint32_t ssrc, SrsLazyGbSessionWrapper** psession)
+{
+ srs_error_t err = srs_success;
+
+ if (!ssrc) return err;
+
+ // The lazy-sweep wrapper for this resource.
+ SrsLazyGbMediaTcpConnWrapper* wrapper = dynamic_cast(gc_available_wrapper());
+ srs_assert(wrapper); // It MUST never be NULL, because this method is in the cycle of coroutine.
+
+ // Find exists session for register, might be created by another object and still alive.
+ SrsLazyGbSessionWrapper* session = dynamic_cast(_srs_gb_manager->find_by_fast_id(ssrc));
+ if (!session) return err;
+
+ _srs_gb_manager->add_with_fast_id(ssrc, session);
+ session->resource()->on_media_transport(wrapper);
+ *psession = session->copy();
+
+ return err;
+}
+
+SrsMpegpsQueue::SrsMpegpsQueue()
+{
+ nb_audios = nb_videos = 0;
+}
+
+SrsMpegpsQueue::~SrsMpegpsQueue()
+{
+ std::map::iterator it;
+ for (it = msgs.begin(); it != msgs.end(); ++it) {
+ SrsSharedPtrMessage* msg = it->second;
+ srs_freep(msg);
+ }
+ msgs.clear();
+}
+
+srs_error_t SrsMpegpsQueue::push(SrsSharedPtrMessage* msg)
+{
+ srs_error_t err = srs_success;
+
+ // TODO: FIXME: use right way.
+ for (int i = 0; i < 10; i++) {
+ if (msgs.find(msg->timestamp) == msgs.end()) {
+ break;
+ }
+
+ // adjust the ts, add 1ms.
+ msg->timestamp += 1;
+
+ if (i >= 100) {
+ srs_warn("Muxer: free the msg for dts exists, dts=%" PRId64, msg->timestamp);
+ srs_freep(msg);
+ return err;
+ }
+ }
+
+ if (msg->is_audio()) {
+ nb_audios++;
+ }
+
+ if (msg->is_video()) {
+ nb_videos++;
+ }
+
+ msgs[msg->timestamp] = msg;
+
+ return err;
+}
+
+SrsSharedPtrMessage* SrsMpegpsQueue::dequeue()
+{
+ // got 2+ videos and audios, ok to dequeue.
+ bool av_ok = nb_videos >= 2 && nb_audios >= 2;
+ // 100 videos about 30s, while 300 audios about 30s
+ bool av_overflow = nb_videos > 100 || nb_audios > 300;
+
+ if (av_ok || av_overflow) {
+ std::map::iterator it = msgs.begin();
+ SrsSharedPtrMessage* msg = it->second;
+ msgs.erase(it);
+
+ if (msg->is_audio()) {
+ nb_audios--;
+ }
+
+ if (msg->is_video()) {
+ nb_videos--;
+ }
+
+ return msg;
+ }
+
+ return NULL;
+}
+
+SrsGbMuxer::SrsGbMuxer(SrsLazyGbSession* session)
+{
+ sdk_ = NULL;
+ session_ = session;
+
+ avc_ = new SrsRawH264Stream();
+ h264_sps_changed_ = false;
+ h264_pps_changed_ = false;
+ h264_sps_pps_sent_ = false;
+
+ aac_ = new SrsRawAacStream();
+
+ queue_ = new SrsMpegpsQueue();
+ pprint_ = SrsPithyPrint::create_caster();
+}
+
+SrsGbMuxer::~SrsGbMuxer()
+{
+ close();
+
+ srs_freep(avc_);
+}
+
+srs_error_t SrsGbMuxer::initialize(std::string output)
+{
+ srs_error_t err = srs_success;
+
+ output_ = output;
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::on_ts_message(SrsTsMessage* msg)
+{
+ srs_error_t err = srs_success;
+
+ SrsBuffer avs(msg->payload->bytes(), msg->payload->length());
+ if (msg->sid == SrsTsPESStreamIdVideoCommon) {
+ if ((err = on_ts_video(msg, &avs)) != srs_success) {
+ return srs_error_wrap(err, "ts: consume video");
+ }
+ } else {
+ if ((err = on_ts_audio(msg, &avs)) != srs_success) {
+ return srs_error_wrap(err, "ts: consume audio");
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::on_ts_video(SrsTsMessage* msg, SrsBuffer* avs)
+{
+ srs_error_t err = srs_success;
+
+ // ensure rtmp connected.
+ if ((err = connect()) != srs_success) {
+ return srs_error_wrap(err, "connect");
+ }
+
+ // ts tbn to flv tbn.
+ uint32_t dts = (uint32_t)(msg->dts / 90);
+ uint32_t pts = (uint32_t)(msg->dts / 90);
+
+ // send each frame.
+ while (!avs->empty()) {
+ char* frame = NULL;
+ int frame_size = 0;
+ if ((err = avc_->annexb_demux(avs, &frame, &frame_size)) != srs_success) {
+ return srs_error_wrap(err, "demux annexb");
+ }
+
+ // 5bits, 7.3.1 NAL unit syntax,
+ // ISO_IEC_14496-10-AVC-2003.pdf, page 44.
+ // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
+ SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f);
+
+ // ignore the nalu type sps(7), pps(8), aud(9)
+ if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter) {
+ continue;
+ }
+
+ // for sps
+ if (avc_->is_sps(frame, frame_size)) {
+ std::string sps;
+ if ((err = avc_->sps_demux(frame, frame_size, sps)) != srs_success) {
+ return srs_error_wrap(err, "demux sps");
+ }
+
+ if (h264_sps_ == sps) {
+ continue;
+ }
+ h264_sps_changed_ = true;
+ h264_sps_ = sps;
+
+ if ((err = write_h264_sps_pps(dts, pts)) != srs_success) {
+ return srs_error_wrap(err, "write sps/pps");
+ }
+ continue;
+ }
+
+ // for pps
+ if (avc_->is_pps(frame, frame_size)) {
+ std::string pps;
+ if ((err = avc_->pps_demux(frame, frame_size, pps)) != srs_success) {
+ return srs_error_wrap(err, "demux pps");
+ }
+
+ if (h264_pps_ == pps) {
+ continue;
+ }
+ h264_pps_changed_ = true;
+ h264_pps_ = pps;
+
+ if ((err = write_h264_sps_pps(dts, pts)) != srs_success) {
+ return srs_error_wrap(err, "write sps/pps");
+ }
+ continue;
+ }
+
+ // ibp frame.
+ // TODO: FIXME: we should group all frames to a rtmp/flv message from one ts message.
+ srs_info("Muxer: demux avc ibp frame size=%d, dts=%d", frame_size, dts);
+ if ((err = write_h264_ipb_frame(frame, frame_size, dts, pts)) != srs_success) {
+ return srs_error_wrap(err, "write frame");
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::write_h264_sps_pps(uint32_t dts, uint32_t pts)
+{
+ srs_error_t err = srs_success;
+
+ // TODO: FIMXE: there exists bug, see following comments.
+ // when sps or pps changed, update the sequence header,
+ // for the pps maybe not changed while sps changed.
+ // so, we must check when each video ts message frame parsed.
+ if (!h264_sps_changed_ || !h264_pps_changed_) {
+ return err;
+ }
+
+ // h264 raw to h264 packet.
+ std::string sh;
+ if ((err = avc_->mux_sequence_header(h264_sps_, h264_pps_, sh)) != srs_success) {
+ return srs_error_wrap(err, "mux sequence header");
+ }
+
+ // h264 packet to flv packet.
+ int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame;
+ int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader;
+ char* flv = NULL;
+ int nb_flv = 0;
+ if ((err = avc_->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) {
+ return srs_error_wrap(err, "avc to flv");
+ }
+
+ // the timestamp in rtmp message header is dts.
+ uint32_t timestamp = dts;
+ if ((err = rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv)) != srs_success) {
+ return srs_error_wrap(err, "write packet");
+ }
+
+ // reset sps and pps.
+ h264_sps_changed_ = false;
+ h264_pps_changed_ = false;
+ h264_sps_pps_sent_ = true;
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts)
+{
+ srs_error_t err = srs_success;
+
+ // when sps or pps not sent, ignore the packet.
+ if (!h264_sps_pps_sent_) {
+ return srs_error_new(ERROR_H264_DROP_BEFORE_SPS_PPS, "drop sps/pps");
+ }
+
+ // 5bits, 7.3.1 NAL unit syntax,
+ // ISO_IEC_14496-10-AVC-2003.pdf, page 44.
+ // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
+ SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f);
+
+ // for IDR frame, the frame is keyframe.
+ SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame;
+ if (nal_unit_type == SrsAvcNaluTypeIDR) {
+ frame_type = SrsVideoAvcFrameTypeKeyFrame;
+ }
+
+ std::string ibp;
+ if ((err = avc_->mux_ipb_frame(frame, frame_size, ibp)) != srs_success) {
+ return srs_error_wrap(err, "mux frame");
+ }
+
+ int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU;
+ char* flv = NULL;
+ int nb_flv = 0;
+ if ((err = avc_->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) {
+ return srs_error_wrap(err, "mux avc to flv");
+ }
+
+ // the timestamp in rtmp message header is dts.
+ uint32_t timestamp = dts;
+ return rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv);
+}
+
+srs_error_t SrsGbMuxer::on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs)
+{
+ srs_error_t err = srs_success;
+
+ // ensure rtmp connected.
+ if ((err = connect()) != srs_success) {
+ return srs_error_wrap(err, "connect");
+ }
+
+ // ts tbn to flv tbn.
+ uint32_t dts = (uint32_t)(msg->dts / 90);
+
+ // send each frame.
+ while (!avs->empty()) {
+ char* frame = NULL;
+ int frame_size = 0;
+ SrsRawAacStreamCodec codec;
+ if ((err = aac_->adts_demux(avs, &frame, &frame_size, codec)) != srs_success) {
+ return srs_error_wrap(err, "demux adts");
+ }
+
+ // ignore invalid frame,
+ // * atleast 1bytes for aac to decode the data.
+ if (frame_size <= 0) {
+ continue;
+ }
+ srs_info("Muxer: demux aac frame size=%d, dts=%d", frame_size, dts);
+
+ // generate sh.
+ if (aac_specific_config_.empty()) {
+ std::string sh;
+ if ((err = aac_->mux_sequence_header(&codec, sh)) != srs_success) {
+ return srs_error_wrap(err, "mux sequence header");
+ }
+ aac_specific_config_ = sh;
+
+ codec.aac_packet_type = 0;
+
+ if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, dts)) != srs_success) {
+ return srs_error_wrap(err, "write raw audio frame");
+ }
+ }
+
+ // audio raw data.
+ codec.aac_packet_type = 1;
+ if ((err = write_audio_raw_frame(frame, frame_size, &codec, dts)) != srs_success) {
+ return srs_error_wrap(err, "write audio raw frame");
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts)
+{
+ srs_error_t err = srs_success;
+
+ char* data = NULL;
+ int size = 0;
+ if ((err = aac_->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != srs_success) {
+ return srs_error_wrap(err, "mux aac to flv");
+ }
+
+ return rtmp_write_packet(SrsFrameTypeAudio, dts, data, size);
+}
+
+srs_error_t SrsGbMuxer::rtmp_write_packet(char type, uint32_t timestamp, char* data, int size)
+{
+ srs_error_t err = srs_success;
+
+ if ((err = connect()) != srs_success) {
+ return srs_error_wrap(err, "connect");
+ }
+
+ SrsSharedPtrMessage* msg = NULL;
+
+ if ((err = srs_rtmp_create_msg(type, timestamp, data, size, sdk_->sid(), &msg)) != srs_success) {
+ return srs_error_wrap(err, "create message");
+ }
+ srs_assert(msg);
+
+ // push msg to queue.
+ if ((err = queue_->push(msg)) != srs_success) {
+ return srs_error_wrap(err, "push to queue");
+ }
+
+ // for all ready msg, dequeue and send out.
+ for (;;) {
+ if ((msg = queue_->dequeue()) == NULL) {
+ break;
+ }
+
+ if (pprint_->can_print()) {
+ srs_trace("Muxer: send msg %s age=%d, dts=%" PRId64 ", size=%d",
+ msg->is_audio()? "A":msg->is_video()? "V":"N", pprint_->age(), msg->timestamp, msg->size);
+ }
+
+ // send out encoded msg.
+ if ((err = sdk_->send_and_free_message(msg)) != srs_success) {
+ close();
+ return srs_error_wrap(err, "send messages");
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsGbMuxer::connect()
+{
+ srs_error_t err = srs_success;
+
+ // Ignore when connected.
+ if (sdk_) {
+ return err;
+ }
+
+ string url = srs_string_replace(output_, "[stream]", session_->sip_transport()->resource()->device_id());
+ srs_trace("Muxer: Convert GB to RTMP %s", url.c_str());
+
+ srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT;
+ srs_utime_t sto = SRS_CONSTS_RTMP_PULSE;
+ sdk_ = new SrsSimpleRtmpClient(url, cto, sto);
+
+ if ((err = sdk_->connect()) != srs_success) {
+ close();
+ return srs_error_wrap(err, "connect %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto));
+ }
+
+ if ((err = sdk_->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) {
+ close();
+ return srs_error_wrap(err, "publish");
+ }
+
+ return err;
+}
+
+void SrsGbMuxer::close()
+{
+ srs_freep(sdk_);
+}
+
+SrsSipResponseWriter::SrsSipResponseWriter(ISrsProtocolReadWriter* io) : SrsHttpResponseWriter(io)
+{
+}
+
+SrsSipResponseWriter::~SrsSipResponseWriter()
+{
+}
+
+srs_error_t SrsSipResponseWriter::build_first_line(std::stringstream& ss, char* data, int size)
+{
+ // Write status line for response.
+ ss << "SIP/2.0 " << status << " " << srs_generate_http_status_text(status) << SRS_HTTP_CRLF;
+ return srs_success;
+}
+
+SrsSipRequestWriter::SrsSipRequestWriter(ISrsProtocolReadWriter* io) : SrsHttpRequestWriter(io)
+{
+}
+
+SrsSipRequestWriter::~SrsSipRequestWriter()
+{
+}
+
+srs_error_t SrsSipRequestWriter::build_first_line(std::stringstream& ss, char* data, int size)
+{
+ // Write status line for response.
+ ss << method_ << " " << path_ << " SIP/2.0" << SRS_HTTP_CRLF;
+ return srs_success;
+}
+
+SrsSipMessage::SrsSipMessage()
+{
+ method_ = HTTP_GET;
+ cseq_number_ = 0;
+ expires_ = UINT32_MAX; // Never use 0 because it means unregister.
+ max_forwards_ = 0;
+ via_send_by_port_ = SRS_GB_SIP_PORT;
+ contact_host_port_ = SRS_GB_SIP_PORT;
+}
+
+SrsSipMessage::~SrsSipMessage()
+{
+}
+
+SrsSipMessage* SrsSipMessage::copy()
+{
+ SrsSipMessage* cp = new SrsSipMessage();
+ *cp = *this;
+ return cp;
+}
+
+const std::string& SrsSipMessage::device_id()
+{
+ // If request is sent by device, then the "from" address must be the ID of device. While we use id to identify the
+ // requests of device, so we can use the "from" address.
+ return from_address_user_;
+}
+
+std::string SrsSipMessage::ssrc_domain_id()
+{
+ // The request uri user is GB domain, the 4-8 is used as domain id for SSRC, so the length must be 8+ bytes,
+ // see https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89
+ return (request_uri_user_.length() < 8) ? "00000" : request_uri_user_.substr(3, 5);
+}
+
+SrsSipMessage* SrsSipMessage::set_body(std::string v)
+{
+ body_ = v;
+ body_escaped_ = v;
+ body_escaped_ = srs_string_replace(body_escaped_, "\r", "\\r");
+ body_escaped_ = srs_string_replace(body_escaped_, "\n", "\\n");
+ return this;
+}
+
+srs_error_t SrsSipMessage::parse(ISrsHttpMessage* m)
+{
+ srs_error_t err = srs_success;
+
+ // Parse body if exists any. Note that we must read body even the message is invalid, because we might need to parse
+ // the next message when skip current invalid message.
+ string v;
+ ISrsHttpResponseReader* br = m->body_reader();
+ if (!br->eof() && (err = srs_ioutil_read_all(br, v)) != srs_success) {
+ return srs_error_wrap(err, "read body");
+ }
+
+ set_body(v);
+
+ // Parse the first line.
+ type_ = (http_parser_type)m->message_type();
+ if (type_ == HTTP_REQUEST) {
+ // Parse request line.
+ method_ = (http_method) m->method();
+ request_uri_ = srs_string_trim_start(m->path(), "/");
+ srs_sip_parse_address(request_uri_, request_uri_user_, request_uri_host_);
+ } else if (type_ == HTTP_RESPONSE) {
+ // Parse status line for response.
+ status_ = (http_status)m->status_code();
+ } else {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Invalid message type=%d", type_);
+ }
+
+ // Check fields for SIP request.
+ if (type_ == HTTP_REQUEST) {
+ if (method_ < HTTP_REGISTER || method_ > HTTP_BYE) {
+ return srs_error_new(ERROR_GB_SIP_MESSAGE, "Invalid method=%d(%s) of message", method_, http_method_str(method_));
+ }
+ if (request_uri_.empty()) return srs_error_new(ERROR_GB_SIP_MESSAGE, "No Request-URI in message");
+ }
+
+ // Get fields of SIP.
+ via_ = m->header()->get("Via");
+ from_ = m->header()->get("From");
+ to_ = m->header()->get("To");
+ call_id_ = m->header()->get("Call-ID");
+ cseq_ = m->header()->get("CSeq");
+ contact_ = m->header()->get("Contact");
+ subject_ = m->header()->get("Subject");
+ content_type_ = m->header()->content_type();
+
+ string expires = m->header()->get("Expires");
+ if (!expires.empty()) {
+ expires_ = (uint32_t)::atol(expires.c_str());
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.19
+ if (!expires_ && expires != "0") {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Invalid Expires=%s in header", expires.c_str());
+ }
+ }
+
+ string max_forwards = m->header()->get("Max-Forwards");
+ if (!max_forwards.empty() && max_forwards != "0") {
+ max_forwards_ = (uint32_t)::atol(max_forwards.c_str());
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.22
+ if (!max_forwards_) {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Invalid Max-Forwards=%s in header", max_forwards.c_str());
+ }
+ }
+
+ if (via_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "No Via in header");
+ if (from_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "No From in header");
+ if (to_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "No To in header");
+ if (call_id_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "No Call-ID in header");
+ if (cseq_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "No CSeq in header");
+
+ // Parse more information from fields.
+ if ((err = parse_via(via_)) != srs_success) {
+ return srs_error_wrap(err, "parse via=%s", via_.c_str());
+ }
+ if ((err = parse_from(from_)) != srs_success) {
+ return srs_error_wrap(err, "parse from=%s", from_.c_str());
+ }
+ if ((err = parse_to(to_)) != srs_success) {
+ return srs_error_wrap(err, "parse to=%s", to_.c_str());
+ }
+ if ((err = parse_cseq(cseq_)) != srs_success) {
+ return srs_error_wrap(err, "parse cseq=%s", cseq_.c_str());
+ }
+ if ((err = parse_contact(contact_)) != srs_success) {
+ return srs_error_wrap(err, "parse contact=%s", contact_.c_str());
+ }
+
+ srs_sip_parse_address(from_address_, from_address_user_, from_address_host_);
+ srs_sip_parse_address(to_address_, to_address_user_, to_address_host_);
+
+ // Except REGISTER, the initial Request-URI of the message SHOULD be set to the value of the URI in the To field.
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.1
+ if (type_ == HTTP_REQUEST && method_ != HTTP_REGISTER && to_address_user_ != request_uri_user_) {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "User of Request-URI=%s not in To=%s", request_uri_.c_str(), to_.c_str());
+ }
+
+ return err;
+}
+
+srs_error_t SrsSipMessage::parse_via(const std::string& via)
+{
+ srs_error_t err = srs_success;
+
+ if (!srs_string_starts_with(via, "SIP/2.0/")) {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Via protocol invalid");
+ }
+
+ if (srs_string_starts_with(via, "SIP/2.0/TCP")) {
+ via_transport_ = "TCP";
+ } else if (srs_string_starts_with(via, "SIP/2.0/UDP")) {
+ via_transport_ = "UDP";
+ } else {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Via transport invalid");
+ }
+
+ vector vs = srs_string_split(via, " ");
+ if (vs.size() <= 1) return srs_error_new(ERROR_GB_SIP_HEADER, "Via no send-by");
+
+ vector params = srs_string_split(vs[1], ";");
+ if (params.size() <= 1) return srs_error_new(ERROR_GB_SIP_HEADER, "Via no params");
+
+ via_send_by_ = params[0];
+ srs_parse_hostport(via_send_by_, via_send_by_address_, via_send_by_port_);
+
+ for (int i = 1; i < (int)params.size(); i++) {
+ string param = params[i];
+ if (srs_string_starts_with(param, "rport")) {
+ via_rport_ = param;
+ } else if (srs_string_starts_with(param, "branch")) {
+ via_branch_ = param;
+ }
+ }
+
+ // Before a request is sent, the client transport MUST insert a value of the "sent-by" field into the Via header
+ // field. See https://www.ietf.org/rfc/rfc3261.html#section-18.1.1
+ if (via_send_by_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "Via no sent-by");
+ // The Via header field value MUST contain a branch parameter.
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.7
+ if (via_branch_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "Via no branch");
+ // The branch ID inserted by an element compliant with this specification MUST always begin with the characters
+ // "z9hG4bK". See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.7
+ if (!srs_string_starts_with(via_branch_, string("branch=")+SRS_GB_BRANCH_MAGIC)) {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "Invalid branch=%s", via_branch_.c_str());
+ }
+
+ return err;
+}
+
+srs_error_t SrsSipMessage::parse_from(const std::string& from)
+{
+ srs_error_t err = srs_success;
+
+ vector params = srs_string_split(from, ";");
+ if (params.size() < 2) return srs_error_new(ERROR_GB_SIP_HEADER, "From no params");
+
+ from_address_ = params[0];
+ for (int i = 1; i < (int)params.size(); i++) {
+ string param = params[i];
+ if (srs_string_starts_with(param, "tag")) {
+ from_tag_ = param;
+ }
+ }
+
+ // The From field MUST contain a new "tag" parameter, chosen by the UAC.
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.3
+ if (from_tag_.empty()) return srs_error_new(ERROR_GB_SIP_HEADER, "From no tag");
+
+ return err;
+}
+
+srs_error_t SrsSipMessage::parse_to(const std::string& to)
+{
+ srs_error_t err = srs_success;
+
+ vector params = srs_string_split(to, ";");
+ if (params.size() < 1) return srs_error_new(ERROR_GB_SIP_HEADER, "To is empty");
+
+ to_address_ = params[0];
+ for (int i = 1; i < (int)params.size(); i++) {
+ string param = params[i];
+ if (srs_string_starts_with(param, "tag")) {
+ to_tag_ = param;
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsSipMessage::parse_cseq(const std::string& cseq)
+{
+ srs_error_t err = srs_success;
+
+ vector params = srs_string_split(cseq, " ");
+ if (params.size() < 2) return srs_error_new(ERROR_GB_SIP_HEADER, "CSeq is empty");
+
+ string sno = params[0];
+ if (sno != "0") {
+ cseq_number_ = (uint32_t)::atol(sno.c_str());
+
+ // The sequence number MUST be expressible as a 32-bit unsigned integer.
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.16
+ if (!cseq_number_) return srs_error_new(ERROR_GB_SIP_HEADER, "CSeq number is invalid");
+ }
+
+ cseq_method_ = params[1];
+ // The method part of CSeq is case-sensitive. See https://www.ietf.org/rfc/rfc3261.html#section-20.16
+ if (type_ == HTTP_REQUEST && string(http_method_str(method_)) != cseq_method_) {
+ return srs_error_new(ERROR_GB_SIP_HEADER, "CSeq method=%s is invalid, expect=%d(%s)", cseq_method_.c_str(), method_, http_method_str(method_));
+ }
+
+ return err;
+}
+
+srs_error_t SrsSipMessage::parse_contact(const std::string& contact)
+{
+ srs_error_t err = srs_success;
+
+ srs_sip_parse_address(contact, contact_user_, contact_host_);
+ srs_parse_hostport(contact_host_, contact_host_address_, contact_host_port_);
+
+ return err;
+}
+
+SrsPackContext::SrsPackContext(ISrsPsPackHandler* handler)
+{
+ static uint32_t gid = 0;
+ media_id_ = ++gid;
+
+ media_startime_ = srs_update_system_time();
+ media_nn_recovered_ = 0;
+ media_nn_msgs_dropped_ = 0;
+ media_reserved_ = 0;
+
+ ps_ = new SrsPsPacket(NULL);
+ handler_ = handler;
+}
+
+SrsPackContext::~SrsPackContext()
+{
+ clear();
+ srs_freep(ps_);
+}
+
+void SrsPackContext::clear()
+{
+ for (vector::iterator it = msgs_.begin(); it != msgs_.end(); ++it) {
+ SrsTsMessage* msg = *it;
+ srs_freep(msg);
+ }
+
+ msgs_.clear();
+}
+
+srs_error_t SrsPackContext::on_ts_message(SrsTsMessage* msg)
+{
+ srs_error_t err = srs_success;
+
+ SrsPsDecodeHelper* h = (SrsPsDecodeHelper*)msg->ps_helper_;
+ srs_assert(h && h->ctx_ && h->ps_);
+
+ // We got new pack header and an optional system header.
+ //if (ps_->id_ != h->ps_->id_) {
+ // stringstream ss;
+ // if (h->ps_->has_pack_header_) ss << srs_fmt(", clock=%" PRId64 ", rate=%d", h->ps_->system_clock_reference_base_, h->ps_->program_mux_rate_);
+ // if (h->ps_->has_system_header_) ss << srs_fmt(", rate_bound=%d, video_bound=%d, audio_bound=%d", h->ps_->rate_bound_, h->ps_->video_bound_, h->ps_->audio_bound_);
+ // srs_trace("PS: New pack header=%d, system=%d%s", h->ps_->has_pack_header_, h->ps_->has_system_header_, ss.str().c_str());
+ //}
+
+ // Correct DTS/PS to the last one.
+ if (!msgs_.empty() && (!msg->dts || !msg->pts)) {
+ SrsTsMessage* last = msgs_.back();
+ if (!msg->dts) msg->dts = last->dts;
+ if (!msg->pts) msg->pts = last->pts;
+ }
+
+ //uint8_t* p = (uint8_t*)msg->payload->bytes();
+ //srs_trace("PS: Got message %s, dts=%" PRId64 ", seq=%u, base=%" PRId64 ", payload=%dB, %#x, %#x, %#x, %#x, %#x, %#x, %#x, %#x",
+ // msg->is_video() ? "Video" : "Audio", msg->dts, h->rtp_seq_, h->ps_->system_clock_reference_base_, msg->PES_packet_length,
+ // p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+
+ // Notify about the previous pack.
+ if (ps_->id_ != h->ps_->id_) {
+ // Handle all messages of previous pack, note that we must free them.
+ if (!msgs_.empty()) {
+ err = handler_->on_ps_pack(ps_, msgs_);
+ clear();
+ }
+
+ // Directly copy the pack headers to current.
+ *ps_ = *h->ps_;
+ }
+
+ // Store the message to current pack.
+ msgs_.push_back(msg->detach());
+
+ return err;
+}
+
+void SrsPackContext::on_recover_mode(int nn_recover)
+{
+ // Only update stat for the first time.
+ if (nn_recover <= 1) {
+ media_nn_recovered_++;
+ }
+
+ // Always update the stat for messages.
+ if (!msgs_.empty()) {
+ media_nn_msgs_dropped_ += msgs_.size();
+ clear();
+ }
+}
+
+SrsRecoverablePsContext::SrsRecoverablePsContext()
+{
+ recover_ = 0;
+}
+
+SrsRecoverablePsContext::~SrsRecoverablePsContext()
+{
+}
+
+srs_error_t SrsRecoverablePsContext::decode_rtp(SrsBuffer* stream, int reserved, ISrsPsMessageHandler* handler)
+{
+ srs_error_t err = srs_success;
+
+ // Start to parse from the reserved bytes.
+ stream->skip(reserved);
+
+ SrsRtpPacket rtp;
+ int pos = stream->pos();
+ if ((err = rtp.decode(stream)) != srs_success) {
+ return enter_recover_mode(stream, handler, pos, err = srs_error_new(ERROR_GB_PS_HEADER, "decode rtp"));
+ }
+
+ SrsRtpRawPayload* rtp_raw = dynamic_cast(rtp.payload());
+ srs_assert(rtp_raw); // It must be a RTP RAW payload, by default.
+
+ // If got reserved bytes, move to the start of payload.
+ if (reserved) {
+ // Move the reserved bytes to the start of payload, from which we should parse.
+ char* src = stream->head() - stream->pos();
+ char* dst = stream->head() - reserved;
+ memmove(dst, src, reserved);
+
+ // The payload also should skip back to the reserved bytes.
+ rtp_raw->payload -= reserved;
+ rtp_raw->nn_payload += reserved;
+
+ // The stream also skip back to the not parsed bytes.
+ stream->skip(-1 * reserved);
+ }
+
+ SrsBuffer b((char*)rtp_raw->payload, rtp_raw->nn_payload);
+ //srs_trace("GB: Got RTP length=%d, payload=%d, seq=%u, ts=%d", length, rtp_raw->nn_payload, rtp.header.get_sequence(), rtp.header.get_timestamp());
+
+ ctx_.helper_.rtp_seq_ = rtp.header.get_sequence();
+ ctx_.helper_.rtp_ts_ = rtp.header.get_timestamp();
+ ctx_.helper_.rtp_pt_ = rtp.header.get_payload_type();
+ if ((err = decode(&b, handler)) != srs_success) {
+ return srs_error_wrap(err, "decode");
+ }
+
+ // Consume the stream, because there might be data left in stream.
+ stream->skip(b.pos());
+
+ return err;
+}
+
+srs_error_t SrsRecoverablePsContext::decode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
+{
+ srs_error_t err = srs_success;
+
+ // Ignore if empty packet.
+ if (stream->empty()) return err;
+
+ // For recover mode, we drop bytes util pack header(00 00 01 ba).
+ if (recover_) {
+ int pos = stream->pos();
+ if (!srs_skip_util_pack(stream)) {
+ stream->skip(pos - stream->pos());
+ return enter_recover_mode(stream, handler, pos, srs_error_new(ERROR_GB_PS_HEADER, "no pack"));
+ }
+ quit_recover_mode(stream, handler);
+ }
+
+ // Got packet to decode.
+ if ((err = ctx_.decode(stream, handler)) != srs_success) {
+ return enter_recover_mode(stream, handler, stream->pos(), srs_error_wrap(err, "decode pack"));
+ }
+
+ return err;
+}
+
+srs_error_t SrsRecoverablePsContext::enter_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler, int pos, srs_error_t err)
+{
+ // Enter recover mode. Increase the recover counter because we might fail for many times.
+ recover_++;
+
+ // Print the error information for debugging.
+ int npos = stream->pos();
+ stream->skip(pos - stream->pos());
+ string bytes = srs_string_dumps_hex(stream->head(), stream->left(), 8);
+
+ SrsPsDecodeHelper& h = ctx_.helper_;
+ uint16_t pack_seq = h.pack_first_seq_;
+ uint16_t pack_msgs = h.pack_nn_msgs_;
+ uint16_t lsopm = h.pack_pre_msg_last_seq_;
+ SrsTsMessage* last = ctx_.last();
+ srs_warn("PS: Enter recover=%d, seq=%u, ts=%u, pt=%u, pack=%u, msgs=%u, lsopm=%u, last=%u/%u, bytes=[%s], pos=%d, left=%d for err %s",
+ recover_, h.rtp_seq_, h.rtp_ts_, h.rtp_pt_, pack_seq, pack_msgs, lsopm, last->PES_packet_length, last->payload->length(),
+ bytes.c_str(), npos, stream->left(), srs_error_desc(err).c_str());
+
+ // If RTP packet exceed SRS_GB_LARGE_PACKET, which is large packet, might be correct length and impossible to
+ // recover, so we directly fail it and re-inivte.
+ if (stream->size() > SRS_GB_LARGE_PACKET) {
+ return srs_error_wrap(err, "no recover for large packet length=%dB", stream->size());
+ }
+
+ // Sometimes, we're unable to recover it, so we limit the max retry.
+ if (recover_ > SRS_GB_MAX_RECOVER) {
+ return srs_error_wrap(err, "exceed max recover, pack=%u, pack-seq=%u, seq=%u",
+ h.pack_id_, h.pack_first_seq_, h.rtp_seq_);
+ }
+
+ // Reap and dispose last incomplete message.
+ SrsTsMessage* msg = ctx_.reap(); srs_freep(msg);
+ // Skip all left bytes in buffer, reset error because recovered.
+ stream->skip(stream->left()); srs_freep(err);
+
+ // Notify handler to cleanup previous messages in pack.
+ handler->on_recover_mode(recover_);
+
+ return err;
+}
+
+void SrsRecoverablePsContext::quit_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
+{
+ string bytes = srs_string_dumps_hex(stream->head(), stream->left(), 8);
+ srs_warn("PS: Quit recover=%d, seq=%u, bytes=[%s], pos=%d, left=%d", recover_, ctx_.helper_.rtp_seq_,
+ bytes.c_str(), stream->pos(), stream->left());
+ recover_ = 0;
+}
+
+bool srs_skip_util_pack(SrsBuffer* stream)
+{
+ while (stream->require(4)) {
+ uint8_t* p = (uint8_t*)stream->head();
+
+ // When searching pack header from payload, mostly not zero.
+ if (p[0] != 0x00 && p[1] != 0x00 && p[2] != 0x00 && p[3] != 0x00) {
+ stream->skip(4);
+ } else if (p[0] != 0x00 && p[1] != 0x00 && p[2] != 0x00) {
+ stream->skip(3);
+ } else if (p[0] != 0x00 && p[1] != 0x00) {
+ stream->skip(2);
+ } else {
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
+ return true;
+ }
+ stream->skip(1);
+ }
+ }
+
+ return false;
+}
+
+void srs_sip_parse_address(const std::string& address, std::string& user, std::string& host)
+{
+ string v = address;
+
+ size_t pos;
+
+ if ((pos = v.find("<")) != string::npos) {
+ v = v.substr(pos + 1);
+ }
+ if ((pos = v.find(">")) != string::npos) {
+ v = v.substr(0, pos);
+ }
+ if ((pos = v.find("sip:")) != string::npos) {
+ v = v.substr(4);
+ }
+
+ user = v;
+ if ((pos = v.find("@")) != string::npos) {
+ user = v.substr(0, pos);
+ host = v.substr(pos + 1);
+ }
+}
+
+SrsResourceManager* _srs_gb_manager = NULL;
+
diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp
new file mode 100644
index 0000000000..12c5e14818
--- /dev/null
+++ b/trunk/src/app/srs_app_gb28181.hpp
@@ -0,0 +1,736 @@
+//
+// Copyright (c) 2013-2022 The SRS Authors
+//
+// SPDX-License-Identifier: MIT or MulanPSL-2.0
+//
+
+#ifndef SRS_APP_GB28181_HPP
+#define SRS_APP_GB28181_HPP
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+class SrsConfDirective;
+class SrsTcpListener;
+class SrsResourceManager;
+class SrsTcpConnection;
+class SrsCoroutine;
+class SrsPackContext;
+class SrsBuffer;
+class SrsSipMessage;
+class SrsLazyGbSession;
+class SrsLazyGbSipTcpConn;
+class SrsLazyGbMediaTcpConn;
+class SrsLazyGbSipTcpConnWrapper;
+class SrsLazyGbMediaTcpConnWrapper;
+class SrsLazyGbSipTcpReceiver;
+class SrsLazyGbSipTcpSender;
+class SrsAlonePithyPrint;
+class SrsGbMuxer;
+class SrsSimpleRtmpClient;
+struct SrsRawAacStreamCodec;
+class SrsRawH264Stream;
+class SrsSharedPtrMessage;
+class SrsPithyPrint;
+class SrsRawAacStream;
+
+// The state machine for GB session.
+// init:
+// to connecting: sip is registered. Note that also send invite request if media not connected.
+// connecting:
+// to established: sip is stable, media is connected.
+// established:
+// init: media is not connected.
+// dispose session: sip is bye.
+// Please see SrsLazyGbSession::drive_state for detail.
+enum SrsGbSessionState
+{
+ SrsGbSessionStateInit = 0,
+ SrsGbSessionStateConnecting,
+ SrsGbSessionStateEstablished,
+};
+std::string srs_gb_session_state(SrsGbSessionState state);
+
+// The state machine for GB SIP connection.
+// init:
+// to registered: Got REGISTER SIP message.
+// to stable: Got MESSAGE SIP message, when SIP TCP connection re-connect.
+// registered:
+// to inviting: Sent INVITE SIP message.
+// inviting:
+// to trying: Got TRYING SIP message.
+// to stable: Got INVITE 200 OK and sent INVITE OK response.
+// trying:
+// to stable: Got INVITE 200 OK and sent INVITE OK response.
+// stable:
+// to re-inviting: Sent bye to device before re-inviting.
+// to bye: Got bye SIP message from device.
+// re-inviting:
+// to inviting: Got bye OK response from deivce.
+// Please see SrsLazyGbSipTcpConn::drive_state for detail.
+enum SrsGbSipState
+{
+ SrsGbSipStateInit = 0,
+ SrsGbSipStateRegistered,
+ SrsGbSipStateInviting,
+ SrsGbSipStateTrying,
+ SrsGbSipStateReinviting,
+ SrsGbSipStateStable,
+ SrsGbSipStateBye,
+};
+std::string srs_gb_sip_state(SrsGbSipState state);
+
+// The interface for GB SIP or HTTP-API connection.
+class ISrsGbSipConn
+{
+public:
+ ISrsGbSipConn();
+ virtual ~ISrsGbSipConn();
+public:
+ // Interrupt the transport, because session is disposing.
+ virtual void interrupt() {}
+ // Get the device id of device, also used as RTMP stream name.
+ virtual std::string device_id() { return "livestream"; }
+ // Get the state of SIP.
+ virtual SrsGbSipState state() { return SrsGbSipStateInit; }
+ // Reset the SIP state to registered, for re-inviting.
+ virtual void reset_to_register() {}
+ // Whether device is already registered, which might drive the session to connecting state.
+ virtual bool is_registered() { return false; }
+ // Whether device is stable state, which means it's sending heartbeat message.
+ virtual bool is_stable() { return false; }
+ // Whether device is request to bye, which means there might be no stream ever and so the session should be
+ // disposed. This is the control event from client device.
+ virtual bool is_bye() { return false; }
+ // Send invite to device, for SIP it should be an "INVITE" request message. Output the ssrc as ID of session, for
+ // media connection to load from SSRC while receiving and handling RTP packets.
+ virtual srs_error_t invite_request(uint32_t* pssrc) { return srs_success; }
+ // Change id of coroutine.
+ virtual void set_cid(const SrsContextId& cid) {}
+};
+
+// The wrapper for ISrsGbSipConn.
+class ISrsGbSipConnWrapper
+{
+private:
+ ISrsGbSipConn dummy_;
+public:
+ ISrsGbSipConnWrapper();
+ virtual ~ISrsGbSipConnWrapper();
+public:
+ virtual ISrsGbSipConn* resource() { return &dummy_; }
+ virtual ISrsGbSipConnWrapper* copy() { return new ISrsGbSipConnWrapper(); }
+};
+
+// The interface for GB media over TCP or UDP transport.
+class ISrsGbMediaConn
+{
+public:
+ ISrsGbMediaConn();
+ virtual ~ISrsGbMediaConn();
+public:
+ // Interrupt the transport, because session is disposing.
+ virtual void interrupt() {}
+ // Whether media transport is connected. SRS will invite client to publish stream if not connected.
+ virtual bool is_connected() { return false; }
+ // Change id of coroutine.
+ virtual void set_cid(const SrsContextId& cid) {}
+};
+
+// The wrapper for ISrsGbMediaConn.
+class ISrsGbMediaConnWrapper
+{
+private:
+ ISrsGbMediaConn dummy_;
+public:
+ ISrsGbMediaConnWrapper();
+ virtual ~ISrsGbMediaConnWrapper();
+public:
+ virtual ISrsGbMediaConn* resource() { return &dummy_; }
+ virtual ISrsGbMediaConnWrapper* copy() { return new ISrsGbMediaConnWrapper(); }
+};
+
+// The main logic object for GB, the session.
+class SrsLazyGbSession : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
+{
+private:
+ SrsCoroutine* trd_;
+ SrsContextId cid_;
+private:
+ SrsGbSessionState state_;
+ ISrsGbSipConnWrapper* sip_;
+ ISrsGbMediaConnWrapper* media_;
+ SrsGbMuxer* muxer_;
+private:
+ // The candidate for SDP in configuration.
+ std::string candidate_;
+ // The public IP for SDP, generated by SRS.
+ std::string pip_;
+ // When wait for SIP and media connecting, timeout if exceed.
+ srs_utime_t connecting_starttime_;
+ // The max timeout for connecting.
+ srs_utime_t connecting_timeout_;
+ // The time we enter reinviting state.
+ srs_utime_t reinviting_starttime_;
+ // The wait time for re-invite.
+ srs_utime_t reinvite_wait_;
+ // The number of timeout, dispose session if exceed.
+ uint32_t nn_timeout_;
+private:
+ SrsAlonePithyPrint* ppp_;
+ srs_utime_t startime_;
+ uint64_t total_packs_;
+ uint64_t total_msgs_;
+ uint64_t total_recovered_;
+ uint64_t total_msgs_dropped_;
+ uint64_t total_reserved_;
+private:
+ uint32_t media_id_;
+ srs_utime_t media_starttime_;
+ uint64_t media_msgs_;
+ uint64_t media_packs_;
+ uint64_t media_recovered_;
+ uint64_t media_msgs_dropped_;
+ uint64_t media_reserved_;
+private:
+ friend class SrsLazyObjectWrapper;
+ SrsLazyGbSession();
+public:
+ virtual ~SrsLazyGbSession();
+public:
+ // Initialize the GB session.
+ srs_error_t initialize(SrsConfDirective* conf);
+ // When got a pack of messages.
+ void on_ps_pack(SrsPackContext* ctx, SrsPsPacket* ps, const std::vector& msgs);
+ // When got available SIP transport.
+ void on_sip_transport(ISrsGbSipConnWrapper* sip);
+ ISrsGbSipConnWrapper* sip_transport();
+ // When got available media transport.
+ void on_media_transport(ISrsGbMediaConnWrapper* media);
+ // Get the candidate for SDP generation, the public IP address for device to connect to.
+ std::string pip();
+// Interface ISrsStartable
+public:
+ virtual srs_error_t start();
+// Interface ISrsOneCycleThreadHandler
+public:
+ virtual srs_error_t cycle();
+private:
+ virtual srs_error_t do_cycle();
+ srs_error_t drive_state();
+private:
+ SrsGbSessionState set_state(SrsGbSessionState v);
+// Interface ISrsResource
+public:
+ virtual const SrsContextId& get_id();
+ virtual std::string desc();
+};
+
+// Lazy-sweep wrapper for GB session.
+class SrsLazyGbSessionWrapper : public ISrsResource
+{
+ SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSession, SrsLazyGbSessionWrapper, SrsLazyGbSession);
+};
+
+// The SIP and Media listener for GB.
+class SrsGbListener : public ISrsListener, public ISrsTcpHandler
+{
+private:
+ SrsConfDirective* conf_;
+ SrsTcpListener* media_listener_;
+ SrsTcpListener* sip_listener_;
+public:
+ SrsGbListener();
+ virtual ~SrsGbListener();
+public:
+ srs_error_t initialize(SrsConfDirective* conf);
+ srs_error_t listen();
+ void close();
+// Interface ISrsTcpHandler
+public:
+ virtual srs_error_t on_tcp_client(ISrsListener* listener, srs_netfd_t stfd);
+};
+
+// A GB28181 TCP SIP connection.
+class SrsLazyGbSipTcpConn : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
+ , public ISrsGbSipConn
+{
+private:
+ SrsGbSipState state_;
+ SrsLazyGbSessionWrapper* session_;
+ SrsSipMessage* register_;
+ SrsSipMessage* invite_ok_;
+private:
+ std::string ssrc_str_;
+ uint32_t ssrc_v_;
+private:
+ SrsConfDirective* conf_;
+ SrsTcpListener* sip_listener_;
+ SrsTcpListener* media_listener_;
+private:
+ SrsTcpConnection* conn_;
+ SrsLazyGbSipTcpReceiver* receiver_;
+ SrsLazyGbSipTcpSender* sender_;
+ SrsCoroutine* trd_;
+private:
+ friend class SrsLazyObjectWrapper;
+ SrsLazyGbSipTcpConn();
+public:
+ virtual ~SrsLazyGbSipTcpConn();
+public:
+ // Setup object, to keep empty constructor.
+ void setup(SrsConfDirective* conf, SrsTcpListener* sip, SrsTcpListener* media, srs_netfd_t stfd);
+ // Get the SIP device id.
+ std::string device_id();
+ // Set the cid of all coroutines.
+ virtual void set_cid(const SrsContextId& cid);
+private:
+ // Get the sip and media listen port.
+ void query_ports(int* sip, int* media);
+public:
+ // When got a SIP message.
+ srs_error_t on_sip_message(SrsSipMessage* msg);
+ // Enqueue a SIP message to send, which might be a request or response.
+ void enqueue_sip_message(SrsSipMessage* msg);
+private:
+ void drive_state(SrsSipMessage* msg);
+ void register_response(SrsSipMessage* msg);
+ void message_response(SrsSipMessage* msg, http_status status);
+ void invite_ack(SrsSipMessage* msg);
+ void bye_response(SrsSipMessage* msg);
+public:
+ srs_error_t invite_request(uint32_t* pssrc);
+public:
+ // Interrupt transport by session.
+ void interrupt();
+ // Get the SIP state.
+ SrsGbSipState state();
+ // Reset the SIP state to registered, for re-inviting.
+ void reset_to_register();
+ // Whether SIP state is registered or more.
+ bool is_registered();
+ // Whether SIP state is stable state, established.
+ bool is_stable();
+ // Whether SIP is bye bye.
+ bool is_bye();
+private:
+ SrsGbSipState set_state(SrsGbSipState v);
+// Interface ISrsResource
+public:
+ virtual const SrsContextId& get_id();
+ virtual std::string desc();
+// Interface ISrsStartable
+public:
+ virtual srs_error_t start();
+// Interface ISrsOneCycleThreadHandler
+public:
+ virtual srs_error_t cycle();
+private:
+ virtual srs_error_t do_cycle();
+private:
+ // Create session if no one, or bind to an existed session.
+ srs_error_t bind_session(SrsSipMessage* msg, SrsLazyGbSessionWrapper** psession);
+};
+
+// Lazy-sweep wrapper for GB SIP TCP connection.
+class SrsLazyGbSipTcpConnWrapper : public ISrsResource, public ISrsGbSipConnWrapper
+{
+ SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSipTcpConn, ISrsGbSipConnWrapper, ISrsGbSipConn);
+};
+
+// Start a coroutine to receive SIP messages.
+class SrsLazyGbSipTcpReceiver : public ISrsStartable, public ISrsCoroutineHandler
+{
+private:
+ SrsCoroutine* trd_;
+ SrsTcpConnection* conn_;
+ SrsLazyGbSipTcpConn* sip_;
+public:
+ SrsLazyGbSipTcpReceiver(SrsLazyGbSipTcpConn* sip, SrsTcpConnection* conn);
+ virtual ~SrsLazyGbSipTcpReceiver();
+public:
+ // Interrupt the receiver coroutine.
+ void interrupt();
+ // Set the cid of all coroutines.
+ virtual void set_cid(const SrsContextId& cid);
+// Interface ISrsStartable
+public:
+ virtual srs_error_t start();
+// Interface ISrsOneCycleThreadHandler
+public:
+ virtual srs_error_t cycle();
+private:
+ srs_error_t do_cycle();
+};
+
+// Start a coroutine to send out SIP messages.
+class SrsLazyGbSipTcpSender : public ISrsStartable, public ISrsCoroutineHandler
+{
+private:
+ SrsCoroutine* trd_;
+ SrsTcpConnection* conn_;
+private:
+ std::vector msgs_;
+ srs_cond_t wait_;
+public:
+ SrsLazyGbSipTcpSender(SrsTcpConnection* conn);
+ virtual ~SrsLazyGbSipTcpSender();
+public:
+ // Push message to queue, and sender will send out in dedicate coroutine.
+ void enqueue(SrsSipMessage* msg);
+ // Interrupt the sender coroutine.
+ void interrupt();
+ // Set the cid of all coroutines.
+ virtual void set_cid(const SrsContextId& cid);
+// Interface ISrsStartable
+public:
+ virtual srs_error_t start();
+// Interface ISrsOneCycleThreadHandler
+public:
+ virtual srs_error_t cycle();
+private:
+ srs_error_t do_cycle();
+};
+
+// The handler for a pack of PS PES packets.
+class ISrsPsPackHandler
+{
+public:
+ ISrsPsPackHandler();
+ virtual ~ISrsPsPackHandler();
+public:
+ // When got a pack of PS/TS messages, contains one video frame and optional SPS/PPS message, one or generally more
+ // audio frames. Note that the ps contains the pack information.
+ virtual srs_error_t on_ps_pack(SrsPsPacket* ps, const std::vector& msgs) = 0;
+};
+
+// A GB28181 TCP media connection, for PS stream.
+class SrsLazyGbMediaTcpConn : public SrsLazyObject, public ISrsResource, public ISrsStartable, public ISrsCoroutineHandler
+ , public ISrsPsPackHandler, public ISrsGbMediaConn
+{
+private:
+ bool connected_;
+ SrsLazyGbSessionWrapper* session_;
+ uint32_t nn_rtcp_;
+private:
+ SrsPackContext* pack_;
+ SrsTcpConnection* conn_;
+ SrsCoroutine* trd_;
+ uint8_t* buffer_;
+private:
+ friend class SrsLazyObjectWrapper;
+ SrsLazyGbMediaTcpConn();
+public:
+ virtual ~SrsLazyGbMediaTcpConn();
+public:
+ // Setup object, to keep empty constructor.
+ void setup(srs_netfd_t stfd);
+ // Whether media is connected.
+ bool is_connected();
+ // Interrupt transport by session.
+ void interrupt();
+ // Set the cid of all coroutines.
+ virtual void set_cid(const SrsContextId& cid);
+// Interface ISrsResource
+public:
+ virtual const SrsContextId& get_id();
+ virtual std::string desc();
+// Interface ISrsStartable
+public:
+ virtual srs_error_t start();
+// Interface ISrsOneCycleThreadHandler
+public:
+ virtual srs_error_t cycle();
+private:
+ virtual srs_error_t do_cycle();
+// Interface ISrsPsPackHandler
+public:
+ virtual srs_error_t on_ps_pack(SrsPsPacket* ps, const std::vector& msgs);
+private:
+ // Create session if no one, or bind to an existed session.
+ srs_error_t bind_session(uint32_t ssrc, SrsLazyGbSessionWrapper** psession);
+};
+
+// Lazy-sweep wrapper for GB Media TCP connection.
+class SrsLazyGbMediaTcpConnWrapper : public ISrsResource, public ISrsGbMediaConnWrapper
+{
+ SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbMediaTcpConn, ISrsGbMediaConnWrapper, ISrsGbMediaConn);
+};
+
+// The queue for mpegts over udp to send packets.
+// For the aac in mpegts contains many flv packets in a pes packet,
+// we must recalc the timestamp.
+class SrsMpegpsQueue
+{
+private:
+ // The key: dts, value: msg.
+ std::map msgs;
+ int nb_audios;
+ int nb_videos;
+public:
+ SrsMpegpsQueue();
+ virtual ~SrsMpegpsQueue();
+public:
+ virtual srs_error_t push(SrsSharedPtrMessage* msg);
+ virtual SrsSharedPtrMessage* dequeue();
+};
+
+// Mux GB28181 to RTMP.
+class SrsGbMuxer
+{
+private:
+ SrsLazyGbSession* session_;
+ std::string output_;
+ SrsSimpleRtmpClient* sdk_;
+private:
+ SrsRawH264Stream* avc_;
+ std::string h264_sps_;
+ bool h264_sps_changed_;
+ std::string h264_pps_;
+ bool h264_pps_changed_;
+ bool h264_sps_pps_sent_;
+private:
+ SrsRawAacStream* aac_;
+ std::string aac_specific_config_;
+private:
+ SrsMpegpsQueue* queue_;
+ SrsPithyPrint* pprint_;
+public:
+ SrsGbMuxer(SrsLazyGbSession* session);
+ virtual ~SrsGbMuxer();
+public:
+ srs_error_t initialize(std::string output);
+ srs_error_t on_ts_message(SrsTsMessage* msg);
+private:
+ virtual srs_error_t on_ts_video(SrsTsMessage* msg, SrsBuffer* avs);
+ virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_t pts);
+ virtual srs_error_t write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts);
+ virtual srs_error_t on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs);
+ virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts);
+ virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size);
+private:
+ // Connect to RTMP server.
+ virtual srs_error_t connect();
+ // Close the connection to RTMP server.
+ virtual void close();
+};
+
+// Response writer for SIP.
+class SrsSipResponseWriter : public SrsHttpResponseWriter
+{
+public:
+ SrsSipResponseWriter(ISrsProtocolReadWriter* io);
+ virtual ~SrsSipResponseWriter();
+// Interface ISrsHttpFirstLineWriter
+public:
+ virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
+};
+
+// Request writer for SIP.
+class SrsSipRequestWriter : public SrsHttpRequestWriter
+{
+public:
+ SrsSipRequestWriter(ISrsProtocolReadWriter* io);
+ virtual ~SrsSipRequestWriter();
+// Interface ISrsHttpFirstLineWriter
+public:
+ virtual srs_error_t build_first_line(std::stringstream& ss, char* data, int size);
+};
+
+// SIP message, covert with HTTP message.
+class SrsSipMessage
+{
+public:
+ // SIP message type, request or response.
+ http_parser_type type_;
+ // For SIP request.
+ http_method method_; // For example: HTTP_INVITE
+ std::string request_uri_; // For example: sip:34020000001320000001@3402000000
+ std::string request_uri_user_; // For example: 34020000001320000001
+ std::string request_uri_host_; // For example: 3402000000
+ // For SIP response.
+ http_status status_; // For example: HTTP_STATUS_OK
+public:
+ // The Via header field indicates the path taken by the request so far and indicates the path that should be
+ // followed in routing responses. See https://www.ietf.org/rfc/rfc3261.html#section-20.42
+ // @param transport TCP or UDP, the transport protocol.
+ // @param send_by The host and port which send the packet.
+ // @param branch Transaction identifier, see https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.7
+ // @param rport For UDP to traverse NATs, see https://www.ietf.org/rfc/rfc3581.html
+ std::string via_; // For example: SIP/2.0/TCP 192.168.3.82:5060;rport;branch=z9hG4bK0l31rx
+ std::string via_transport_; // For example: TCP
+ std::string via_send_by_; // For example: 192.168.3.82:5060
+ std::string via_send_by_address_; // For example: 192.168.3.82
+ int via_send_by_port_; // For example: 5060
+ std::string via_branch_; // For example: branch=z9hG4bK0l31rx
+ std::string via_rport_; // For example: rport
+public:
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.20
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.3
+ std::string from_; // For example: ;tag=SRSk1er282t
+ std::string from_address_; // For example:
+ std::string from_address_user_; // For example: 34020000002000000001
+ std::string from_address_host_; // For example: 3402000000
+ std::string from_tag_; // For example: tag=SRSk1er282t
+public:
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.2
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.39
+ std::string to_; // For example: ;tag=SRSk1er282t
+ std::string to_address_; // For example:
+ std::string to_address_user_; // For example: 34020000001320000001
+ std::string to_address_host_; // For example: 3402000000
+ std::string to_tag_; // For example: tag=SRSk1er282t
+public:
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.4
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.8
+ std::string call_id_; // For example: 854k7337207yxpfj
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.8
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.10
+ std::string contact_; // For example:
+ std::string contact_user_; // For example:
+ std::string contact_host_; // For example: 3402000000
+ std::string contact_host_address_; // For example: 3402000000
+ int contact_host_port_; // For example: 5060
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.19
+ uint32_t expires_;
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.6
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.22
+ uint32_t max_forwards_;
+public:
+ // See https://www.ietf.org/rfc/rfc3261.html#section-8.1.1.5
+ // See https://www.ietf.org/rfc/rfc3261.html#section-20.16
+ std::string cseq_;
+ uint32_t cseq_number_;
+ std::string cseq_method_;
+public:
+ // See https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89
+ std::string subject_;
+ // The content type.
+ std::string content_type_;
+public:
+ // SIP message body.
+ std::string body_;
+ // Escape \r to \\r, \n to \\n.
+ std::string body_escaped_;
+public:
+ SrsSipMessage();
+ virtual ~SrsSipMessage();
+public:
+ SrsSipMessage* copy();
+ const std::string& device_id();
+ std::string ssrc_domain_id();
+ SrsSipMessage* set_body(std::string v);
+ srs_error_t parse(ISrsHttpMessage* m);
+private:
+ srs_error_t parse_via(const std::string& via);
+ srs_error_t parse_from(const std::string& from);
+ srs_error_t parse_to(const std::string& to);
+ srs_error_t parse_cseq(const std::string& cseq);
+ srs_error_t parse_contact(const std::string& contact);
+public:
+ bool is_register() {
+ return type_ == HTTP_REQUEST && method_ == HTTP_REGISTER;
+ }
+ bool is_message() {
+ return type_ == HTTP_REQUEST && method_ == HTTP_MESSAGE;
+ }
+ bool is_invite() {
+ return type_ == HTTP_REQUEST && method_ == HTTP_INVITE;
+ }
+ bool is_trying() {
+ return type_ == HTTP_RESPONSE && cseq_method_ == "INVITE" && status_ == HTTP_STATUS_CONTINUE;
+ }
+ bool is_invite_ok() {
+ return type_ == HTTP_RESPONSE && cseq_method_ == "INVITE" && status_ == HTTP_STATUS_OK;
+ }
+ bool is_bye() {
+ return type_ == HTTP_REQUEST && method_ == HTTP_BYE;
+ }
+ bool is_bye_ok() {
+ return type_ == HTTP_RESPONSE && cseq_method_ == "BYE" && status_ == HTTP_STATUS_OK;
+ }
+};
+
+// Recoverable PS context for GB28181.
+class SrsRecoverablePsContext
+{
+public:
+ SrsPsContext ctx_;
+private:
+ // If decoding error, enter the recover mode. Drop all left bytes util next pack header.
+ int recover_;
+public:
+ SrsRecoverablePsContext();
+ virtual ~SrsRecoverablePsContext();
+public:
+ // Decode the PS stream in RTP payload. Note that there might be first reserved bytes of RAW data, which is not
+ // parsed by previous decoding, we should move to the start of payload bytes.
+ virtual srs_error_t decode_rtp(SrsBuffer* stream, int reserved, ISrsPsMessageHandler* handler);
+private:
+ // Decode the RTP payload as PS pack stream.
+ virtual srs_error_t decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
+ // When got error, drop data and enter recover mode.
+ srs_error_t enter_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler, int pos, srs_error_t err);
+ // Quit Recover mode when got pack header.
+ void quit_recover_mode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
+};
+
+// The PS pack context, for GB28181 to process based on PS pack, which contains a video and audios messages. For large
+// video frame, it might be split to multiple PES packets, which must be group to one video frame.
+// Please note that a pack might contain multiple audio frames, so size of audio PES packet should not exceed 64KB,
+// which is limited by the 16 bits PES_packet_length.
+// We also correct the timestamp, or DTS/PTS of video frames, which might be 0 if more than one video PES packets in a
+// PS pack stream.
+class SrsPackContext : public ISrsPsMessageHandler
+{
+public:
+ // Each media transport only use one context, so the context id is the media id.
+ uint32_t media_id_;
+ srs_utime_t media_startime_;
+ uint64_t media_nn_recovered_;
+ uint64_t media_nn_msgs_dropped_;
+ uint64_t media_reserved_;
+private:
+ // To process a pack of TS/PS messages.
+ ISrsPsPackHandler* handler_;
+ // Note that it might be freed, so never use its fields.
+ SrsPsPacket* ps_;
+ // The messages in current PS pack.
+ std::vector msgs_;
+public:
+ SrsPackContext(ISrsPsPackHandler* handler);
+ virtual ~SrsPackContext();
+private:
+ void clear();
+// Interface ISrsPsMessageHandler
+public:
+ virtual srs_error_t on_ts_message(SrsTsMessage* msg);
+ virtual void on_recover_mode(int nn_recover);
+};
+
+// Find the pack header which starts with bytes (00 00 01 ba).
+extern bool srs_skip_util_pack(SrsBuffer* stream);
+
+// Parse the address from SIP string, for example:
+// sip:bob@ossrs.io:5060
+//
+// Bob
+// Parsed as:
+// user: bob
+// host: ossrs.io:5060
+// Note that the host can be parsed by srs_parse_hostport as host(ossrs.io) and port(5060).
+extern void srs_sip_parse_address(const std::string& address, std::string& user, std::string& host);
+
+// Manager for GB connections.
+extern SrsResourceManager* _srs_gb_manager;
+
+#endif
+
diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp
index 8ae334a844..b74f784f3f 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -40,6 +40,9 @@ using namespace std;
#ifdef SRS_RTC
#include
#endif
+#ifdef SRS_GB28181
+#include
+#endif
SrsSignalManager* SrsSignalManager::instance = NULL;
@@ -336,7 +339,10 @@ SrsServer::SrsServer()
stream_caster_flv_listener_ = new SrsHttpFlvListener();
stream_caster_mpegts_ = new SrsUdpCasterListener();
exporter_listener_ = new SrsTcpListener(this);
-
+#ifdef SRS_GB28181
+ stream_caster_gb28181_ = new SrsGbListener();
+#endif
+
// donot new object in constructor,
// for some global instance is not ready now,
// new these objects in initialize instead.
@@ -392,6 +398,9 @@ void SrsServer::destroy()
srs_freep(stream_caster_flv_listener_);
srs_freep(stream_caster_mpegts_);
srs_freep(exporter_listener_);
+#ifdef SRS_GB28181
+ srs_freep(stream_caster_gb28181_);
+#endif
}
void SrsServer::dispose()
@@ -408,6 +417,9 @@ void SrsServer::dispose()
stream_caster_flv_listener_->close();
stream_caster_mpegts_->close();
exporter_listener_->close();
+#ifdef SRS_GB28181
+ stream_caster_gb28181_->close();
+#endif
// Fast stop to notify FFMPEG to quit, wait for a while then fast kill.
ingester->dispose();
@@ -436,6 +448,9 @@ void SrsServer::gracefully_dispose()
stream_caster_flv_listener_->close();
stream_caster_mpegts_->close();
exporter_listener_->close();
+#ifdef SRS_GB28181
+ stream_caster_gb28181_->close();
+#endif
srs_trace("listeners closed");
// Fast stop to notify FFMPEG to quit, wait for a while then fast kill.
@@ -632,6 +647,15 @@ srs_error_t SrsServer::listen()
if ((err = stream_caster_flv_listener_->initialize(conf)) != srs_success) {
return srs_error_wrap(err, "initialize");
}
+ } else if (srs_stream_caster_is_gb28181(caster)) {
+ #ifdef SRS_GB28181
+ listener = stream_caster_gb28181_;
+ if ((err = stream_caster_gb28181_->initialize(conf)) != srs_success) {
+ return srs_error_wrap(err, "initialize");
+ }
+ #else
+ return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "Please enable GB by: ./configure --gb28181=on");
+ #endif
} else {
return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "invalid caster %s", caster.c_str());
}
@@ -1318,6 +1342,17 @@ srs_error_t SrsServerAdapter::run(SrsWaitGroup* wg)
return srs_error_wrap(err, "start");
}
+#ifdef SRS_GB28181
+ if ((err = _srs_gb_manager->start()) != srs_success) {
+ return srs_error_wrap(err, "start manager");
+ }
+#endif
+
+ SrsSweepGc* gc = dynamic_cast(_srs_gc);
+ if ((err = gc->start()) != srs_success) {
+ return srs_error_wrap(err, "start gc");
+ }
+
return err;
}
diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp
index 4bde69c1d8..791d31125d 100644
--- a/trunk/src/app/srs_app_server.hpp
+++ b/trunk/src/app/srs_app_server.hpp
@@ -40,6 +40,7 @@ class SrsWaitGroup;
class SrsMultipleTcpListeners;
class SrsHttpFlvListener;
class SrsUdpCasterListener;
+class SrsGbListener;
// Convert signal to io,
// @see: st-1.9/docs/notes.html
@@ -136,6 +137,10 @@ class SrsServer : public ISrsReloadHandler, public ISrsLiveSourceHandler, public
// Exporter server listener, over TCP. Please note that metrics request of HTTP is served by this
// listener, and it might be reused by HTTP API.
SrsTcpListener* exporter_listener_;
+#ifdef SRS_GB28181
+ // Stream Caster for GB28181.
+ SrsGbListener* stream_caster_gb28181_;
+#endif
private:
// Signal manager which convert gignal to io message.
SrsSignalManager* signal_manager;
diff --git a/trunk/src/app/srs_app_threads.cpp b/trunk/src/app/srs_app_threads.cpp
index 387c17cf37..f763b6840b 100644
--- a/trunk/src/app/srs_app_threads.cpp
+++ b/trunk/src/app/srs_app_threads.cpp
@@ -25,6 +25,9 @@
#ifdef SRS_SRT
#include
#endif
+#ifdef SRS_GB28181
+#include
+#endif
#include
#include
@@ -329,6 +332,9 @@ srs_error_t srs_global_initialize()
_srs_rtc_manager = new SrsResourceManager("RTC", true);
_srs_rtc_dtls_certificate = new SrsDtlsCertificate();
+#endif
+#ifdef SRS_GB28181
+ _srs_gb_manager = new SrsResourceManager("GB", true);
#endif
_srs_gc = new SrsSweepGc();
diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp
index b1cd49b35e..7dc2419b8e 100644
--- a/trunk/src/core/srs_core_version5.hpp
+++ b/trunk/src/core/srs_core_version5.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
-#define VERSION_REVISION 73
+#define VERSION_REVISION 74
#endif
diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp
index b68a948680..785549f902 100644
--- a/trunk/src/kernel/srs_kernel_error.hpp
+++ b/trunk/src/kernel/srs_kernel_error.hpp
@@ -307,7 +307,15 @@
XX(ERROR_HTTPS_HANDSHAKE , 4042, "HttpsHandshake", "Failed to do handshake for HTTPS") \
XX(ERROR_HTTPS_READ , 4043, "HttpsRead", "Failed to read data from HTTPS stream") \
XX(ERROR_HTTPS_WRITE , 4044, "HttpsWrite", "Failed to write data to HTTPS stream") \
- XX(ERROR_HTTPS_KEY_CRT , 4045, "HttpsSslFile", "Failed to load SSL key or crt file for HTTPS")
+ XX(ERROR_HTTPS_KEY_CRT , 4045, "HttpsSslFile", "Failed to load SSL key or crt file for HTTPS") \
+ XX(ERROR_GB_SIP_HEADER , 4046, "GbHeaderCallId", "Missing field of SIP header for GB28181") \
+ XX(ERROR_GB_SIP_MESSAGE , 4047, "GbHeaderCallId", "Invalid SIP message for GB28181") \
+ XX(ERROR_GB_PS_HEADER , 4048, "GbPsHeader", "Invalid PS header for GB28181") \
+ XX(ERROR_GB_PS_PSE , 4049, "GbPsPSE", "Invalid PS PSE for GB28181") \
+ XX(ERROR_GB_PS_MEDIA , 4050, "GbPsMedia", "Invalid PS Media packet for GB28181") \
+ XX(ERROR_GB_SSRC_GENERATE , 4051, "GbSsrcGenerate", "Failed to generate SSRC for GB28181") \
+ XX(ERROR_GB_CONFIG , 4052, "GbConfig", "Invalid configuration for GB28181") \
+ XX(ERROR_GB_TIMEOUT , 4053, "GbTimeout", "SIP or media connection timeout for GB28181") \
/**************************************************/
/* RTC protocol error. */
diff --git a/trunk/src/kernel/srs_kernel_ps.cpp b/trunk/src/kernel/srs_kernel_ps.cpp
new file mode 100644
index 0000000000..bc28a2d3d8
--- /dev/null
+++ b/trunk/src/kernel/srs_kernel_ps.cpp
@@ -0,0 +1,491 @@
+//
+// Copyright (c) 2013-2022 The SRS Authors
+//
+// SPDX-License-Identifier: MIT or MulanPSL-2.0
+//
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+using namespace std;
+
+// The minimum required bytes to parse a PS packet.
+#define SRS_PS_MIN_REQUIRED 32
+
+SrsPsDecodeHelper::SrsPsDecodeHelper()
+{;
+ rtp_seq_ = 0;
+ rtp_ts_ = 0;
+ rtp_pt_ = 0;
+ pack_id_ = 0;
+ pack_first_seq_ = 0;
+ pack_pre_msg_last_seq_ = 0;
+ pack_nn_msgs_ = 0;
+
+ ctx_ = NULL;
+ ps_ = NULL;
+}
+
+ISrsPsMessageHandler::ISrsPsMessageHandler()
+{
+}
+
+ISrsPsMessageHandler::~ISrsPsMessageHandler()
+{
+}
+
+SrsPsContext::SrsPsContext()
+{
+ last_ = NULL;
+ current_ = NULL;
+ helper_.ctx_ = this;
+ detect_ps_integrity_ = false;
+}
+
+SrsPsContext::~SrsPsContext()
+{
+ srs_freep(last_);
+ srs_freep(current_);
+}
+
+void SrsPsContext::set_detect_ps_integrity(bool v)
+{
+ detect_ps_integrity_ = v;
+}
+
+SrsTsMessage* SrsPsContext::last()
+{
+ if (!last_) {
+ last_ = new SrsTsMessage();
+ last_->ps_helper_ = &helper_;
+ }
+ return last_;
+}
+
+SrsTsMessage* SrsPsContext::reap()
+{
+ SrsTsMessage* msg = last_;
+ last_ = new SrsTsMessage();
+ last_->ps_helper_ = &helper_;
+ return msg;
+}
+
+srs_error_t SrsPsContext::decode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
+{
+ srs_error_t err = srs_success;
+
+ // Decode PS packet one by one.
+ while (!stream->empty()) {
+ // If new PS packet but not enough packet, ignore.
+ if (detect_ps_integrity_ && stream->left() >= 4) {
+ uint8_t* p = (uint8_t*)stream->head();
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && stream->left() <= SRS_PS_MIN_REQUIRED) {
+ break;
+ }
+ }
+
+ // Try to decode the stream by one PS packet.
+ // See Table 2-32 – Program Stream pack, hls-mpeg-ts-iso13818-1.pdf, page 73
+ if ((err = do_decode(stream, handler)) != srs_success) {
+ return srs_error_wrap(err, "decode");
+ }
+
+ // Use unit start as 1, however it has no effect because PES_packet_length should never be 0 for PS packet.
+ const int payload_unit_start_indicator = 1;
+ if (!last()->completed(payload_unit_start_indicator)) {
+ continue; // Ignore if message not completed.
+ }
+
+ // Reap the last completed PS message.
+ SrsTsMessage* msg = reap();
+ SrsAutoFree(SrsTsMessage, msg);
+
+ if (msg->sid == SrsTsPESStreamIdProgramStreamMap) {
+ if (!msg->payload || !msg->payload->length()) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "empty PSM payload");
+ }
+
+ // Decode PSM(Program Stream map) from PES packet payload.
+ SrsBuffer buf(msg->payload->bytes(), msg->payload->length());
+
+ SrsPsPsmPacket psm;
+ if ((err = psm.decode(&buf)) != srs_success) {
+ return srs_error_wrap(err, "decode psm");
+ }
+
+ srs_info("PS: Ignore PSM for video=%#x, audio=%#x", psm.video_elementary_stream_id_, psm.audio_elementary_stream_id_);
+ //context_->set(psm.video_elementary_stream_id_, SrsTsPidApplyVideo);
+ //context_->set(psm.audio_elementary_stream_id_, SrsTsPidApplyAudio);
+ } else if (msg->is_video() || msg->is_audio()) {
+ // Update the total messages in pack.
+ helper_.pack_pre_msg_last_seq_ = helper_.rtp_seq_;
+ helper_.pack_nn_msgs_++;
+
+ //srs_error("PS: Got message %s, dts=%" PRId64 ", payload=%dB", msg->is_video() ? "Video" : "Audio", msg->dts/9000, msg->PES_packet_length);
+ if (handler && (err = handler->on_ts_message(msg)) != srs_success) {
+ return srs_error_wrap(err, "handle PS message");
+ }
+ } else {
+ srs_info("PS: Ignore message sid=%#x", msg->sid);
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsPsContext::do_decode(SrsBuffer* stream, ISrsPsMessageHandler* handler)
+{
+ srs_error_t err = srs_success;
+
+ // If last message not completed, the bytes in stream must be payload.
+ if (!last()->fresh()) {
+ if ((err = last()->dump(stream, NULL)) != srs_success) {
+ return srs_error_wrap(err, "dump pes");
+ }
+ return err;
+ }
+
+ // For PS pack, must always start with 00 00 01 XX.
+ // See Table 2-32 – Program Stream pack, hls-mpeg-ts-iso13818-1.pdf, page 73
+ if (!stream->require(4)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ }
+ uint8_t* p = (uint8_t*)stream->head();
+
+ // For normal mode, should start with 00 00 01, for pack or system header or PES packet.
+ if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "Invalid PS stream %#x %#x %#x", p[0], p[1], p[2]);
+ }
+
+ // If pack start code, it's a net PS pack stream.
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
+ srs_freep(current_);
+ }
+ if (!current_) {
+ current_ = new SrsPsPacket(this);
+ current_->id_ |= helper_.rtp_seq_; // The low 16 bits is reserved for RTP seq.
+ helper_.pack_id_ = current_->id_;
+ helper_.pack_first_seq_ = helper_.rtp_seq_;
+ // Set the helper for decoder, pass-by values.
+ helper_.ps_ = current_;
+ helper_.pack_nn_msgs_ = 0;
+ }
+
+ // Try to decode the PS pack stream.
+ int pos = stream->pos();
+ if ((err = current_->decode(stream)) != srs_success) {
+ err = srs_error_wrap(err, "decode start=%d, pos=%d, left=%d", pos, stream->pos(), stream->left());
+ stream->skip(pos - stream->pos());
+ return err;
+ }
+
+ return err;
+}
+
+SrsPsPacket::SrsPsPacket(SrsPsContext* context)
+{
+ context_ = context;
+ has_pack_header_ = has_system_header_ = false;
+
+ static uint32_t gid = 0;
+ id_ = ((gid++) << 16) & 0xffff0000;
+
+ pack_start_code_ = 0;
+ system_clock_reference_base_ = 0;
+ system_clock_reference_extension_ = 0;
+ program_mux_rate_ = 0;
+ pack_stuffing_length_ = 0;
+
+ system_header_start_code_ = 0;
+ header_length_ = 0;
+ rate_bound_ = 0;
+ audio_bound_ = 0;
+ CSPS_flag_ = 0;
+ system_audio_lock_flag_ = 0;
+ system_video_lock_flag_ = 0;
+ video_bound_ = 0;
+ packet_rate_restriction_flag_ = 0;
+ audio_stream_id_ = 0;
+ audio_buffer_bound_scale_ = 0;
+ audio_buffer_size_bound_ = 0;
+ video_stream_id_ = 0;
+ video_buffer_bound_scale_ = 0;
+ video_buffer_size_bound_ = 0;
+}
+
+SrsPsPacket::~SrsPsPacket()
+{
+}
+
+srs_error_t SrsPsPacket::decode(SrsBuffer* stream)
+{
+ srs_error_t err = srs_success;
+
+ // Program Stream pack header.
+ if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ uint8_t* p = (uint8_t*)stream->head();
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
+ if ((err = decode_pack(stream)) != srs_success) {
+ return srs_error_wrap(err, "pack");
+ }
+ has_pack_header_ = true;
+ }
+
+ // Program stream system header.
+ if (stream->empty()) return err; // Parsed done, OK.
+ if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ p = (uint8_t*)stream->head();
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xbb) {
+ if ((err = decode_system(stream)) != srs_success) {
+ return srs_error_wrap(err, "system");
+ }
+ has_system_header_ = true;
+ }
+
+ // Packet start code prefix.
+ while (!stream->empty()) {
+ if (!stream->require(4)) return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ p = (uint8_t*)stream->head();
+ if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01) break;
+ if (p[3] == 0xba || p[3] == 0xbb) break; // Reparse for pack or system header.
+
+ SrsMpegPES pes;
+ if ((err = pes.decode(stream)) != srs_success) {
+ return srs_error_wrap(err, "decode pes");
+ }
+
+ SrsTsMessage* lm = context_->last();
+
+ // The stream id should never change for PS stream.
+ if (lm->sid != (SrsTsPESStreamId)0 && lm->sid != (SrsTsPESStreamId)pes.stream_id) {
+ return srs_error_new(ERROR_GB_PS_PSE, "PS stream id change from %#x to %#x", lm->sid, pes.stream_id);
+ }
+ lm->sid = (SrsTsPESStreamId)pes.stream_id;
+
+ if (pes.PTS_DTS_flags == 0x02 || pes.PTS_DTS_flags == 0x03) {
+ lm->dts = pes.dts;
+ lm->pts = pes.pts;
+ }
+ if (pes.has_payload_) {
+ // The size of PS message, should be always a positive value.
+ lm->PES_packet_length = pes.nb_payload_;
+ if ((err = lm->dump(stream, &pes.nb_bytes)) != srs_success) {
+ return srs_error_wrap(err, "dump pes");
+ }
+ }
+
+ // Use unit start as 1, however it has no effect because PES_packet_length should never be 0 for PS packet.
+ const int payload_unit_start_indicator = 1;
+ if (lm->completed(payload_unit_start_indicator)) {
+ return err; // OK, got one message, let PS context handle it.
+ }
+ }
+
+ return err;
+}
+
+srs_error_t SrsPsPacket::decode_pack(SrsBuffer* stream)
+{
+ srs_error_t err = srs_success;
+
+ // 14 bytes fixed header.
+ if (!stream->require(14)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "ps requires 14 only %d bytes", stream->left());
+ }
+
+ pack_start_code_ = stream->read_4bytes();
+ srs_assert(pack_start_code_ == 0x000001ba);
+
+ uint64_t r0 = stream->read_4bytes();
+ uint16_t r1 = stream->read_2bytes();
+ system_clock_reference_extension_ = (r1 >> 1) & 0x1ff;
+ system_clock_reference_base_ = 0x00
+ | ((uint64_t) ((r0 >> 27) & 0x07) << 30) // 3bits
+ | ((uint64_t) ((r0 >> 11) & 0x7fff) << 15) // 15bits
+ | ((uint64_t) (r0 & 0x03ff) << 5) // 10bits
+ | (uint64_t) ((r1 >> 11) & 0x1f); // 5bits
+
+ program_mux_rate_ = stream->read_3bytes();
+ program_mux_rate_ = (program_mux_rate_ >> 2) & 0x3fffff;
+
+ pack_stuffing_length_ = stream->read_1bytes();
+ pack_stuffing_length_ &= 0x07;
+ //srs_warn("PS: New pack header clock=%" PRId64 ", rate=%d", system_clock_reference_base_, program_mux_rate_);
+
+ if (!stream->require(pack_stuffing_length_)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", pack_stuffing_length_, stream->left());
+ }
+ stream->skip(pack_stuffing_length_);
+
+ return err;
+}
+
+srs_error_t SrsPsPacket::decode_system(SrsBuffer* stream)
+{
+ srs_error_t err = srs_success;
+
+ system_header_start_code_ = stream->read_4bytes();
+ srs_assert(system_header_start_code_ == 0x000001bb);
+
+ if (!stream->require(8)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 8 only %d bytes", stream->left());
+ }
+
+ header_length_ = stream->read_2bytes();
+ if (!stream->require(header_length_)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", header_length_, stream->left());
+ }
+
+ SrsBuffer b(stream->head(), header_length_);
+ stream->skip(header_length_);
+
+ rate_bound_ = b.read_3bytes();
+ rate_bound_ = (rate_bound_ >> 1) & 0x3fffff;
+
+ CSPS_flag_ = b.read_1bytes();
+ audio_bound_ = (CSPS_flag_ >> 2) & 0x3f;
+ CSPS_flag_ &= 0x01;
+
+ video_bound_ = b.read_1bytes();
+ system_audio_lock_flag_ = (video_bound_ >> 7) & 0x01;
+ system_video_lock_flag_ = (video_bound_ >> 6) & 0x01;
+ video_bound_ &= 0x1f;
+
+ packet_rate_restriction_flag_ = b.read_1bytes();
+ packet_rate_restriction_flag_ = (packet_rate_restriction_flag_ >> 5) & 0x01;
+ //srs_warn("PS: New system header rate_bound=%d, video_bound=%d, audio_bound=%d", rate_bound_, video_bound_, audio_bound_);
+
+ // Parse stream_id and buffer information.
+ while (!b.empty()) {
+ uint8_t r2 = (uint8_t) b.head()[0];
+ if ((r2 & 0x80) != 0x80) break;
+
+ if (!b.require(3)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 3 only %d bytes", b.left());
+ }
+
+ SrsTsPESStreamId stream_id = (SrsTsPESStreamId)(uint8_t)b.read_1bytes();
+ uint16_t buffer_size_bound = b.read_2bytes();
+ uint8_t buffer_bound_scale = (uint8_t)((buffer_size_bound>>13) & 0x01);
+ buffer_size_bound &= 0x1fff;
+
+ if (((stream_id>>4) & 0x0f) == SrsTsPESStreamIdVideoChecker) {
+ video_stream_id_ = stream_id;
+ video_buffer_bound_scale_ = buffer_bound_scale;
+ video_buffer_size_bound_ = buffer_size_bound;
+ } else if (((stream_id>>5) & 0x07) == SrsTsPESStreamIdAudioChecker) {
+ audio_stream_id_ = stream_id;
+ audio_buffer_bound_scale_ = buffer_bound_scale;
+ audio_buffer_size_bound_ = buffer_size_bound;
+ } else {
+ srs_info("PS: Ignore stream_id=%#x, buffer_bound_scale=%d, buffer_size_bound=%d", stream_id, buffer_bound_scale, buffer_size_bound);
+ }
+ }
+
+ return err;
+}
+
+SrsPsPsmPacket::SrsPsPsmPacket()
+{
+ current_next_indicator_ = 0;
+ program_stream_map_version_ = 0;
+ program_stream_info_length_ = 0;
+ elementary_stream_map_length_ = 0;
+ video_stream_type_ = 0;
+ video_elementary_stream_id_ = 0;
+ video_elementary_stream_info_length_ = 0;
+ audio_stream_type_ = 0;
+ audio_elementary_stream_id_ = 0;
+ audio_elementary_stream_info_length_ = 0;
+ CRC_32_ = 0;
+}
+
+SrsPsPsmPacket::~SrsPsPsmPacket()
+{
+}
+
+srs_error_t SrsPsPsmPacket::decode(SrsBuffer* stream)
+{
+ srs_error_t err = srs_success;
+
+ // From first util program_stream_info_length field, at least 4 bytes.
+ if (!stream->require(4)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ }
+
+ uint16_t r0 = stream->read_2bytes();
+ if ((r0&0x01) != 0x01) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "invalid marker of 0x%#x", r0);
+ }
+
+ program_stream_map_version_ = (uint8_t)(r0&0x1f);
+ current_next_indicator_ = (uint8_t)((r0>>7) & 0x01);
+ if (!current_next_indicator_) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "invalid indicator of 0x%#x", r0);
+ }
+
+ program_stream_info_length_ = stream->read_2bytes();
+ if (!stream->require(program_stream_info_length_)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", program_stream_info_length_, stream->left());
+ }
+ stream->skip(program_stream_info_length_);
+
+ // The number of ES map count, 2 bytes.
+ if (!stream->require(2)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 2 only %d bytes", stream->left());
+ }
+ elementary_stream_map_length_ = stream->read_2bytes();
+ if (!stream->require(elementary_stream_map_length_)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", elementary_stream_map_length_, stream->left());
+ }
+
+ SrsBuffer b(stream->head(), elementary_stream_map_length_);
+ stream->skip(elementary_stream_map_length_);
+
+ while (!b.empty()) {
+ if (!b.require(4)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", b.left());
+ }
+
+ SrsTsStream stream_type = (SrsTsStream)(uint8_t)b.read_1bytes();
+ uint8_t elementary_stream_id = b.read_1bytes();
+ uint16_t elementary_stream_info_length = b.read_2bytes();
+ if (!b.require(elementary_stream_info_length)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires %d only %d bytes", elementary_stream_info_length, b.left());
+ }
+ // Descriptor defined as bellow section, but we ignore it:
+ // Table 2-40 – Video stream descriptor, hls-mpeg-ts-iso13818-1.pdf, page 82
+ // Table 2-42 – Audio stream descriptor, hls-mpeg-ts-iso13818-1.pdf, page 83
+ b.skip(elementary_stream_info_length);
+ srs_info("PS: Ignore %d bytes descriptor for stream=%#x", elementary_stream_info_length, stream_type);
+
+ if (stream_type == SrsTsStreamVideoH264) {
+ video_stream_type_ = stream_type;
+ video_elementary_stream_id_ = elementary_stream_id;
+ video_elementary_stream_info_length_ = elementary_stream_info_length;
+ } else if (stream_type == SrsTsStreamAudioAAC) {
+ audio_stream_type_ = stream_type;
+ audio_elementary_stream_id_ = elementary_stream_id;
+ audio_elementary_stream_info_length_ = elementary_stream_info_length;
+ } else {
+ srs_trace("PS: Ignore stream_type=%#x, es_id=%d, es_info=%d", stream_type, elementary_stream_id, elementary_stream_info_length);
+ }
+ }
+
+ // The last CRC32.
+ if (!stream->require(4)) {
+ return srs_error_new(ERROR_GB_PS_HEADER, "requires 4 only %d bytes", stream->left());
+ }
+ CRC_32_ = stream->read_4bytes();
+
+ return err;
+}
+
diff --git a/trunk/src/kernel/srs_kernel_ps.hpp b/trunk/src/kernel/srs_kernel_ps.hpp
new file mode 100644
index 0000000000..948fd9c10d
--- /dev/null
+++ b/trunk/src/kernel/srs_kernel_ps.hpp
@@ -0,0 +1,314 @@
+//
+// Copyright (c) 2013-2022 The SRS Authors
+//
+// SPDX-License-Identifier: MIT or MulanPSL-2.0
+//
+
+#ifndef SRS_KERNEL_PS_HPP
+#define SRS_KERNEL_PS_HPP
+
+#include
+
+#include
+
+class SrsPsPacket;
+class SrsPsContext;
+
+// The helper for PS decoding.
+struct SrsPsDecodeHelper
+{
+public:
+ // For debugging to get the RTP packet source. Not used in context.
+ uint16_t rtp_seq_;
+ // For debugging to get the RTP packet timestamp. Not used in context.
+ uint32_t rtp_ts_;
+ // For debugging to get the RTP packet payload type. Not used in context.
+ uint8_t rtp_pt_;
+public:
+ // For debugging, current pack id. Not used in context.
+ uint32_t pack_id_;
+ // For debugging, the first sequence of current pack. Not used in context.
+ uint16_t pack_first_seq_;
+ // For debugging, the last sequence of previous message in current pack. Not used in context.
+ uint16_t pack_pre_msg_last_seq_;
+ // For debugging, the number of messages in pack. Not used in context.
+ uint16_t pack_nn_msgs_;
+public:
+ // The PS context for decoding.
+ SrsPsContext* ctx_;
+ // The PS packet for decoding.
+ SrsPsPacket* ps_;
+public:
+ SrsPsDecodeHelper();
+};
+
+// The PS message handler.
+class ISrsPsMessageHandler : public ISrsTsHandler
+{
+public:
+ ISrsPsMessageHandler();
+ virtual ~ISrsPsMessageHandler();
+public:
+ // When enter recover mode, user should drop all messages in pack. The nn_recover indicates the number of retry
+ // during recovery, that is 0 for the first time, and 1 for the second time.
+ virtual void on_recover_mode(int nn_recover) = 0;
+};
+
+// The PS context, to process PS PES stream.
+class SrsPsContext
+{
+public:
+ SrsPsDecodeHelper helper_;
+private:
+ // The last decoding PS(TS) message.
+ SrsTsMessage* last_;
+ // The current parsing PS packet context.
+ SrsPsPacket* current_;
+ // Whether detect PS packet header integrity.
+ bool detect_ps_integrity_;
+public:
+ SrsPsContext();
+ virtual ~SrsPsContext();
+public:
+ // Set whether detecting PS header integrity.
+ void set_detect_ps_integrity(bool v);
+ // Get the last PS(TS) message. Create one if not exists.
+ SrsTsMessage* last();
+ // Reap the last message and create a fresh one.
+ SrsTsMessage* reap();
+public:
+ // Feed with ts packets, decode as ts message, callback handler if got one ts message.
+ // A ts video message can be decoded to NALUs by SrsRawH264Stream::annexb_demux.
+ // A ts audio message can be decoded to RAW frame by SrsRawAacStream::adts_demux.
+ // @param handler The ts message handler to process the msg.
+ // @remark We will consume all bytes in stream.
+ virtual srs_error_t decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
+private:
+ srs_error_t do_decode(SrsBuffer* stream, ISrsPsMessageHandler* handler);
+};
+
+// The packet in ps stream.
+// 2.5.3.3 Pack layer of Program Stream, hls-mpeg-ts-iso13818-1.pdf, page 73
+class SrsPsPacket
+{
+public:
+ SrsPsContext* context_;
+public:
+ // The global ID of pack header. The low 16 bits is reserved for seq. Automatically generate the high bits in
+ // constructor.
+ uint32_t id_;
+ // Whether the PS pack stream has pack and system header.
+ bool has_pack_header_;
+ bool has_system_header_;
+// Table 2-33 – Program Stream pack header, hls-mpeg-ts-iso13818-1.pdf, page 73
+public:
+ // 4B
+ // The pack_start_code is the bit string '0000 0000 0000 0000 0000 0001 1011 1010' (0x000001BA). It identifies the
+ // beginning of a pack.
+ uint32_t pack_start_code_; // 32bits
+
+ // 6B
+ // 2bits const '01'
+ // 3bits system_clock_reference_base [32..30]
+ // 1bit marker_bit '1'
+ // 15bits system_clock_reference_base [29..15]
+ // 1bit marker_bit '1'
+ // 22bits system_clock_reference_base [14..0]
+ // 1bit marker_bit '1'
+ // 9bits system_clock_reference_extension
+ // 1bit marker_bit '1'
+ // The system clock reference (SCR) is a 42-bit field coded in two parts. The first part,
+ // system_clock_reference_base, is a 33-bit field whose value is given by SCR_base(i) as given in equation 2-19.
+ // The second part, system_clock_reference_extension, is a 9-bit field whose value is given by SCR_ext(i), as given
+ // in equation 2-20. The SCR indicates the intended time of arrival of the byte containing the last bit of the
+ // system_clock_reference_base at the input of the program target decoder.
+ uint64_t system_clock_reference_base_; // 32bits
+ uint16_t system_clock_reference_extension_; // 9bits
+
+ // 3B
+ // 22bits program_mux_rate
+ // 1bit marker_bit '1'
+ // 1bit marker_bit '1'
+ // This is a 22-bit integer specifying the rate at which the P-STD receives the Program Stream during the pack in
+ // which it is included. The value of program_mux_rate is measured in units of 50 bytes/second. The value 0 is
+ // forbidden. The value represented in program_mux_rate is used to define the time of arrival of bytes at the input
+ // to the P-STD in 2.5.2. The value encoded in the program_mux_rate field may vary from pack to pack in an ITU-T
+ // Rec. H.222.0 | ISO/IEC 13818-1 program multiplexed stream.
+ uint32_t program_mux_rate_; // 22bits
+
+ // 1B
+ // 5bits reserved
+ // 3bits pack_stuffing_length
+ // A 3-bit integer specifying the number of stuffing bytes which follow this field.
+ uint8_t pack_stuffing_length_; // 3bits
+// Table 2-34 – Program Stream system header, hls-mpeg-ts-iso13818-1.pdf, page 74
+public:
+ // 4B
+ // The system_header_start_code is the bit string '0000 0000 0000 0000 0000 0001 1011 1011' (0x000001BB). It
+ // identifies the beginning of a system header.
+ uint32_t system_header_start_code_;
+
+ // 2B
+ // This 16-bit field indicates the length in bytes of the system header following the header_length field. Future
+ // extensions of this Specification may extend the system header.
+ uint16_t header_length_;
+
+ // 3B
+ // 1bit marker_bit '1'
+ // 22bits rate_bound
+ // 1bit marker_bit '1'
+ // A 22-bit field. The rate_bound is an integer value greater than or equal to the maximum value of the
+ // program_mux_rate field coded in any pack of the Program Stream. It may be used by a decoder to assess whether it
+ // is capable of decoding the entire stream.
+ uint32_t rate_bound_;
+
+ // 1B
+ // 6bits audio_bound
+ // 1bit fixed_flag
+ // 1bit CSPS_flag
+ // A 6-bit field. The audio_bound is an integer in the inclusive range from 0 to 32 and is set to a value greater
+ // than or equal to the maximum number of ISO/IEC 13818-3 and ISO/IEC 11172-3 audio streams in the Program Stream
+ // for which the decoding processes are simultaneously active. For the purpose of this subclause, the decoding
+ // process of an ISO/IEC 13818-3 or ISO/IEC 11172-3 audio stream is active if the STD buffer is not empty or if a
+ // Presentation Unit is being presented in the P-STD model.
+ uint8_t audio_bound_;
+ // The CSPS_flag is a 1-bit field. If its value is set to '1' the Program Stream meets the constraints defined in
+ // 2.7.9.
+ uint8_t CSPS_flag_;
+
+ // 1B
+ // 1bit system_audio_lock_flag
+ // 1bit system_video_lock_flag
+ // 1bit marker_bit '1'
+ // 5bits video_bound
+ // The system_audio_lock_flag is a 1-bit field indicating that there is a specified, constant rational relationship
+ // between the audio sampling rate and the system_clock_frequency in the system target decoder. The
+ // system_clock_frequency is defined in 2.5.2.1 and the audio sampling rate is specified in ISO/IEC 13818-3. The
+ // system_audio_lock_flag may only be set to '1' if, for all presentation units in all audio elementary streams in
+ // the Program Stream, the ratio of system_clock_frequency to the actual audio sampling rate, SCASR, is constant and
+ // equal to the value indicated in the following table at the nominal sampling rate indicated in the audio stream.
+ uint8_t system_audio_lock_flag_;
+ // The system_video_lock_flag is a 1-bit field indicating that there is a specified, constant rational relationship
+ // between the video frame rate and the system clock frequency in the system target decoder. Subclause 2.5.2.1
+ // defines system_clock_frequency and the video frame rate is specified in ITU-T Rec. H.262 | ISO/IEC 13818-2. The
+ // system_video_lock_flag may only be set to '1' if, for all presentation units in all video elementary streams in
+ // the ITU-T Rec. H.222.0 | ISO/IEC 13818-1 program, the ratio of system_clock_frequency to the actual video frame
+ // rate, SCFR, is constant and equal to the value indicated in the following table at the nominal frame rate
+ // indicated in the video stream.
+ uint8_t system_video_lock_flag_;
+ // The video_bound is a 5-bit integer in the inclusive range from 0 to 16 and is set to a value greater than or
+ // equal to the maximum number of ITU-T Rec. H.262 | ISO/IEC 13818-2 and ISO/IEC 11172-2 streams in the Program
+ // Stream of which the decoding processes are simultaneously active. For the purpose of this subclause, the decoding
+ // process of an ITU-T Rec. H.262 | ISO/IEC 13818-2 and ISO/IEC 11172-2 video stream is active if the P-STD buffer
+ // is not empty, or if a Presentation Unit is being presented in the P-STD model, or if the reorder buffer is not
+ // empty.
+ uint8_t video_bound_;
+
+ // 1B
+ // 1bit packet_rate_restriction_flag
+ // 5bits reserved_bits
+ // The packet_rate_restriction_flag is a 1-bit flag. If the CSPS flag is set to '1', the
+ // packet_rate_restriction_flag indicates which constraint is applicable to the packet rate, as specified in 2.7.9.
+ // If the CSPS flag is set to value of '0', then the meaning of the packet_rate_restriction_flag is undefined.
+ uint8_t packet_rate_restriction_flag_;
+
+ // 3B
+ // 8bits stream_id
+ // 2bits fixed '11'
+ // 1bit buffer_bound_scale
+ // 13bits buffer_size_bound
+ // Has some audio or video stream, by the next bit is 1.
+ // Note that we ignore other streams except audio and video.
+ //
+ // The stream_id is an 8-bit field that indicates the coding and elementary stream number of the stream to which the
+ // following P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields refer.
+ // If stream_id equals '1011 1000' the P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields following the
+ // stream_id refer to all audio streams in the Program Stream.
+ // If stream_id equals '1011 1001' the P-STD_buffer_bound_scale and P-STD_buffer_size_bound fields following the
+ // stream_id refer to all video streams in the Program Stream.
+ // If the stream_id takes on any other value it shall be a byte value greater than or equal to '1011 1100' and shall
+ // be interpreted as referring to the stream coding and elementary stream number according to Table 2-18.
+ //
+ uint8_t audio_stream_id_;
+ uint8_t audio_buffer_bound_scale_;
+ uint16_t audio_buffer_size_bound_;
+ //
+ uint8_t video_stream_id_;
+ uint8_t video_buffer_bound_scale_;
+ uint16_t video_buffer_size_bound_;
+public:
+ SrsPsPacket(SrsPsContext* context);
+ virtual ~SrsPsPacket();
+public:
+ virtual srs_error_t decode(SrsBuffer* stream);
+private:
+ virtual srs_error_t decode_pack(SrsBuffer* stream);
+ virtual srs_error_t decode_system(SrsBuffer* stream);
+};
+
+// The Program Stream Map (PSM) provides a description of the elementary streams in the Program Stream and their
+// relationship to one another.
+// 2.5.4 Program Stream map, hls-mpeg-ts-iso13818-1.pdf, page 77
+class SrsPsPsmPacket
+{
+public:
+ // 2B
+ // 1bit current_next_indicator
+ // 2bits reserved
+ // 5bits program_stream_map_version
+ // 7bits reserved
+ // 1bit marker_bit '1'
+ // This is a 1-bit field, when set to '1' indicates that the Program Stream Map sent is currently applicable. When
+ // the bit is set to '0', it indicates that the Program Stream Map sent is not yet applicable and shall be the next
+ // table to become valid.
+ uint8_t current_next_indicator_;
+ // This 5-bit field is the version number of the whole Program Stream Map. The version number shall be incremented
+ // by 1 modulo 32 whenever the definition of the Program Stream Map changes. When the current_next_indicator is set
+ // to '1', then the program_stream_map_version shall be that of the currently applicable Program Stream Map. When
+ // the current_next_indicator is set to '0', then the program_stream_map_version shall be that of the next
+ // applicable Program Stream Map.
+ uint8_t program_stream_map_version_;
+
+ // 2B + [program_stream_info_length_]B
+ // The program_stream_info_length is a 16-bit field indicating the total length of the descriptors immediately
+ // following this field.
+ uint16_t program_stream_info_length_;
+
+ // 2B
+ // This is a 16-bit field specifying the total length, in bytes, of all elementary stream information in this
+ // program stream map. It includes the stream_type, elementary_stream_id, and elementary_stream_info_length fields.
+ uint16_t elementary_stream_map_length_;
+
+ // 4B + [elementary_stream_info_length_]B
+ // 8bits stream_type
+ // 8bits elementary_stream_id
+ // 16bits elementary_stream_info_length
+ // This 8-bit field specifies the type of the stream according to Table 2-29. The stream_type field shall only
+ // identify elementary streams contained in PES packets. A value of 0x05 is prohibited.
+ // The elementary_stream_id is an 8-bit field indicating the value of the stream_id field in the PES packet headers
+ // of PES packets in which this elementary stream is stored.
+ // The elementary_stream_info_length is a 16-bit field indicating the length in bytes of the descriptors immediately
+ // following this field.
+ // Definition for the descriptor() fields may be found in 2.6.
+ //
+ uint8_t video_stream_type_;
+ uint8_t video_elementary_stream_id_;
+ uint16_t video_elementary_stream_info_length_;
+ //
+ uint8_t audio_stream_type_;
+ uint8_t audio_elementary_stream_id_;
+ uint16_t audio_elementary_stream_info_length_;
+
+ // 4B
+ // This is a 32-bit field that contains the CRC value that gives a zero output of the registers in the decoder
+ // defined in Annex A after processing the entire program stream map.
+ uint32_t CRC_32_;
+public:
+ SrsPsPsmPacket();
+ virtual ~SrsPsPsmPacket();
+public:
+ virtual srs_error_t decode(SrsBuffer* stream);
+};
+
+#endif
+
diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp
index fb10356dbe..2b3e43ab53 100644
--- a/trunk/src/kernel/srs_kernel_ts.cpp
+++ b/trunk/src/kernel/srs_kernel_ts.cpp
@@ -68,6 +68,7 @@ SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p)
{
channel = c;
packet = p;
+ ps_helper_ = NULL;
dts = pts = 0;
sid = (SrsTsPESStreamId)0x00;
@@ -108,7 +109,7 @@ srs_error_t SrsTsMessage::dump(SrsBuffer* stream, int* pnb_bytes)
stream->skip(nb_bytes);
}
- *pnb_bytes = nb_bytes;
+ if (pnb_bytes) *pnb_bytes = nb_bytes;
return err;
}
@@ -123,7 +124,9 @@ bool SrsTsMessage::completed(int8_t payload_unit_start_indicator)
bool SrsTsMessage::fresh()
{
- return payload->length() == 0;
+ // Note that both must be 0. For PS stream, the payload might be empty but PES_packet_length is not, see
+ // PsPacketDecodePrivateStream of KernelPSTest. For TS stream, both should be 0 in the same time.
+ return PES_packet_length == 0 && payload->length() == 0;
}
bool SrsTsMessage::is_audio()
@@ -1566,6 +1569,11 @@ srs_error_t SrsMpegPES::decode(SrsBuffer* stream)
// PES_packet_data_byte
// }
+ // For PS, the PES packet should never be empty, because there is no continuity for PS packet.
+ if (PES_packet_length <= 0) {
+ return srs_error_new(ERROR_GB_PS_PSE, "ts: Invalid PES_packet_length=%d for PS", PES_packet_length);
+ }
+
// The pos_packet equals to stream pos, so the PES_packet_length is actually the payload length.
nb_payload_ = PES_packet_length;
has_payload_ = true;
diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp
index 8efcb9fcc5..dd7bb83fd4 100644
--- a/trunk/src/kernel/srs_kernel_ts.hpp
+++ b/trunk/src/kernel/srs_kernel_ts.hpp
@@ -219,6 +219,8 @@ class SrsTsMessage
// For decoder only, the ts message does not use them, for user to get the channel and packet.
SrsTsChannel* channel;
SrsTsPacket* packet;
+ // For decoder only, the ts message does not use them, to get the RTP packet source.
+ void* ps_helper_;
public:
// The audio cache buffer start pts, to flush audio if full.
// @remark the pts is not the adjust one, it's the orignal pts.
@@ -228,6 +230,9 @@ class SrsTsMessage
bool write_pcr;
// Whether got discontinuity ts, for example, sequence header changed.
bool is_discontinuity;
+public:
+ // The chunk id of TS packet.
+ uint8_t continuity_counter;
public:
// The timestamp in 90khz
int64_t dts;
@@ -237,8 +242,6 @@ class SrsTsMessage
SrsTsPESStreamId sid;
// The size of payload, 0 indicates the length() of payload.
uint16_t PES_packet_length;
- // The chunk id.
- uint8_t continuity_counter;
// The payload bytes.
SrsSimpleStream* payload;
public:
diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp
index 9ea1c61a8d..70bb305aa0 100644
--- a/trunk/src/utest/srs_utest_config.cpp
+++ b/trunk/src/utest/srs_utest_config.cpp
@@ -2857,46 +2857,6 @@ VOID TEST(ConfigMainTest, CheckStreamCaster)
EXPECT_EQ(8080, conf.get_stream_caster_listen(arr.at(0)));
}
-
- if (true) {
- MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;"));
-
- vector arr = conf.get_stream_casters();
- ASSERT_EQ(1, (int)arr.size());
-
- EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_min(arr.at(0)));
- }
-
- if (true) {
- MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_min 8080;}"));
-
- vector arr = conf.get_stream_casters();
- ASSERT_EQ(1, (int)arr.size());
-
- EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_min(arr.at(0)));
- }
-
- if (true) {
- MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster;"));
-
- vector arr = conf.get_stream_casters();
- ASSERT_EQ(1, (int)arr.size());
-
- EXPECT_EQ(0, (int)conf.get_stream_caster_rtp_port_max(arr.at(0)));
- }
-
- if (true) {
- MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "stream_caster {rtp_port_max 8080;}"));
-
- vector arr = conf.get_stream_casters();
- ASSERT_EQ(1, (int)arr.size());
-
- EXPECT_EQ(8080, conf.get_stream_caster_rtp_port_max(arr.at(0)));
- }
}
VOID TEST(ConfigMainTest, CheckVhostConfig2)
diff --git a/trunk/src/utest/srs_utest_gb28181.cpp b/trunk/src/utest/srs_utest_gb28181.cpp
new file mode 100644
index 0000000000..fed82bb977
--- /dev/null
+++ b/trunk/src/utest/srs_utest_gb28181.cpp
@@ -0,0 +1,1946 @@
+//
+// Copyright (c) 2013-2022 The SRS Authors
+//
+// SPDX-License-Identifier: MIT or MulanPSL-2.0
+//
+#include
+
+#include
+using namespace std;
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern void srs_sip_parse_address(const std::string& address, std::string& user, std::string& host);
+
+VOID TEST(ProtocolGbSipTest, SipParseAddress)
+{
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("", user, host);
+ EXPECT_STREQ("", user.c_str());
+ EXPECT_STREQ("", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("sip:", user, host);
+ EXPECT_STREQ("", user.c_str());
+ EXPECT_STREQ("", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("sip:bob", user, host);
+ EXPECT_STREQ("bob", user.c_str());
+ EXPECT_STREQ("", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("sip:bob@", user, host);
+ EXPECT_STREQ("bob", user.c_str());
+ EXPECT_STREQ("", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("sip:bob@host.com", user, host);
+ EXPECT_STREQ("bob", user.c_str());
+ EXPECT_STREQ("host.com", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("Bob ", user, host);
+ EXPECT_STREQ("bob", user.c_str());
+ EXPECT_STREQ("host.com", host.c_str());
+ }
+
+ if (true) {
+ string user, host;
+ srs_sip_parse_address("Bob ", user, host);
+ EXPECT_STREQ("bob", user.c_str());
+ EXPECT_STREQ("host.com", host.c_str());
+ }
+}
+
+VOID TEST(ProtocolGbSipTest, SipViaBranchMagic)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("REGISTER sip:registrar.biloxi.com SIP/2.0\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=xxxx\r\n");
+ r.in_bytes.push_back("To: Bob \r\n");
+ r.in_bytes.push_back("From: Bob ;tag=456248\r\n");
+ r.in_bytes.push_back("Call-ID: 843817637684230@998sdasdh09\r\n");
+ r.in_bytes.push_back("CSeq: 1826 REGISTER\r\n");
+ r.in_bytes.push_back("Content-Length: 0\r\n");
+ r.in_bytes.push_back("\r\n");
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_FAILED(smsg.parse(msg));
+}
+
+VOID TEST(ProtocolGbSipTest, SipRegisterRequest)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.1
+ MockMSegmentsReader source;
+ source.in_bytes.push_back("REGISTER sip:registrar.biloxi.com SIP/2.0\r\n");
+ source.in_bytes.push_back("Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7\r\n");
+ source.in_bytes.push_back("Max-Forwards: 70\r\n");
+ source.in_bytes.push_back("To: Bob \r\n");
+ source.in_bytes.push_back("From: Bob ;tag=456248\r\n");
+ source.in_bytes.push_back("Call-ID: 843817637684230@998sdasdh09\r\n");
+ source.in_bytes.push_back("CSeq: 1826 REGISTER\r\n");
+ source.in_bytes.push_back("Contact: \r\n");
+ source.in_bytes.push_back("Expires: 7200\r\n");
+ source.in_bytes.push_back("Content-Length: 0\r\n");
+ source.in_bytes.push_back("\r\n");
+
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_REQUEST));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_REGISTER, msg->method());
+ EXPECT_STREQ("/sip:registrar.biloxi.com", msg->path().c_str());
+ EXPECT_STREQ("SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("70", msg->header()->get("Max-Forwards").c_str());
+ EXPECT_STREQ("Bob ", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Bob ;tag=456248", msg->header()->get("From").c_str());
+ EXPECT_STREQ("843817637684230@998sdasdh09", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("1826 REGISTER", msg->header()->get("CSeq").c_str());
+ EXPECT_STREQ("", msg->header()->get("Contact").c_str());
+ EXPECT_STREQ("7200", msg->header()->get("Expires").c_str());
+ EXPECT_EQ(0, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_REQUEST, smsg.type_);
+ EXPECT_EQ(HTTP_REGISTER, smsg.method_);
+ EXPECT_STREQ("sip:registrar.biloxi.com", smsg.request_uri_.c_str());
+ EXPECT_STREQ("SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("bobspc.biloxi.com:5060", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("bobspc.biloxi.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds7", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Bob ;tag=456248", smsg.from_.c_str());
+ EXPECT_STREQ("Bob ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=456248", smsg.from_tag_.c_str());
+ EXPECT_STREQ("843817637684230@998sdasdh09", smsg.call_id_.c_str());
+ EXPECT_STREQ("", smsg.contact_.c_str());
+ EXPECT_STREQ("bob", smsg.contact_user_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_address_.c_str());
+ EXPECT_EQ(5060, smsg.contact_host_port_);
+ EXPECT_STREQ("1826 REGISTER", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)1826, smsg.cseq_number_);
+ EXPECT_EQ((uint32_t)7200, smsg.expires_);
+ EXPECT_EQ((uint32_t)70, smsg.max_forwards_);
+ }
+
+ // Parse in HTTP_REQUEST mode.
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_REQUEST));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_REQUEST, (http_parser_type)msg->message_type());
+ EXPECT_EQ(HTTP_REGISTER, msg->method());
+ EXPECT_STREQ("/sip:registrar.biloxi.com", msg->path().c_str());
+ }
+
+ // Parse in HTTP_BOTH mode.
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_REQUEST, (http_parser_type)msg->message_type());
+ EXPECT_EQ(HTTP_REGISTER, msg->method());
+ EXPECT_STREQ("/sip:registrar.biloxi.com", msg->path().c_str());
+ }
+}
+
+VOID TEST(ProtocolGbSipTest, SipRegisterResponse)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.1
+ MockMSegmentsReader source;
+ source.in_bytes.push_back("SIP/2.0 200 OK\r\n");
+ source.in_bytes.push_back("Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7;received=192.0.2.4\r\n");
+ source.in_bytes.push_back("To: Bob ;tag=2493k59kd\r\n");
+ source.in_bytes.push_back("From: Bob ;tag=456248\r\n");
+ source.in_bytes.push_back("Call-ID: 843817637684230@998sdasdh09\r\n");
+ source.in_bytes.push_back("CSeq: 1826 REGISTER\r\n");
+ source.in_bytes.push_back("Contact: \r\n");
+ source.in_bytes.push_back("Expires: 7200\r\n");
+ source.in_bytes.push_back("Content-Length: 0\r\n");
+ source.in_bytes.push_back("\r\n");
+
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_RESPONSE));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_RESPONSE, (http_parser_type) msg->message_type());
+ EXPECT_EQ(200, msg->status_code());
+ EXPECT_STREQ("SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7;received=192.0.2.4",
+ msg->header()->get("Via").c_str());
+ EXPECT_STREQ("Bob ;tag=2493k59kd", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Bob ;tag=456248", msg->header()->get("From").c_str());
+ EXPECT_STREQ("843817637684230@998sdasdh09", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("1826 REGISTER", msg->header()->get("CSeq").c_str());
+ EXPECT_STREQ("", msg->header()->get("Contact").c_str());
+ EXPECT_STREQ("7200", msg->header()->get("Expires").c_str());
+ EXPECT_EQ(0, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_RESPONSE, smsg.type_);
+ EXPECT_EQ(HTTP_STATUS_OK, smsg.status_);
+ EXPECT_STREQ("SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7;received=192.0.2.4", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("bobspc.biloxi.com:5060", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("bobspc.biloxi.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds7", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ;tag=2493k59kd", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("tag=2493k59kd", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Bob ;tag=456248", smsg.from_.c_str());
+ EXPECT_STREQ("Bob ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=456248", smsg.from_tag_.c_str());
+ EXPECT_STREQ("843817637684230@998sdasdh09", smsg.call_id_.c_str());
+ EXPECT_STREQ("", smsg.contact_.c_str());
+ EXPECT_STREQ("bob", smsg.contact_user_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_address_.c_str());
+ EXPECT_EQ(5060, smsg.contact_host_port_);
+ EXPECT_STREQ("1826 REGISTER", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)1826, smsg.cseq_number_);
+ }
+
+ // Parse in HTTP_RESPONSE mode.
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_RESPONSE));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_RESPONSE, (http_parser_type)msg->message_type());
+ EXPECT_EQ(200, msg->status_code());
+ }
+
+ // Parse in HTTP_BOTH mode.
+ if (true) {
+ MockMSegmentsReader r = source;
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_RESPONSE, (http_parser_type)msg->message_type());
+ EXPECT_EQ(200, msg->status_code());
+ }
+}
+
+VOID TEST(ProtocolGbSipTest, SipSessionUacInviteRequest)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("INVITE sip:bob@biloxi.com SIP/2.0\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\r\n");
+ r.in_bytes.push_back("Max-Forwards: 70\r\n");
+ r.in_bytes.push_back("To: Bob \r\n");
+ r.in_bytes.push_back("From: Alice ;tag=1928301774\r\n");
+ r.in_bytes.push_back("Call-ID: a84b4c76e66710\r\n");
+ r.in_bytes.push_back("CSeq: 314159 INVITE\r\n");
+ r.in_bytes.push_back("Contact: \r\n");
+ r.in_bytes.push_back("Content-Type: application/sdp\r\n");
+ r.in_bytes.push_back("Content-Length: 142\r\n");
+ r.in_bytes.push_back("\r\n");
+ r.in_bytes.push_back(string(142, 'x'));
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_INVITE, msg->method());
+ EXPECT_STREQ("/sip:bob@biloxi.com", msg->path().c_str());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("70", msg->header()->get("Max-Forwards").c_str());
+ EXPECT_STREQ("Bob ", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", msg->header()->get("From").c_str());
+ EXPECT_STREQ("a84b4c76e66710", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("314159 INVITE", msg->header()->get("CSeq").c_str());
+ EXPECT_STREQ("", msg->header()->get("Contact").c_str());
+ EXPECT_EQ(142, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_REQUEST, smsg.type_);
+ EXPECT_EQ(HTTP_INVITE, smsg.method_);
+ EXPECT_STREQ("sip:bob@biloxi.com", smsg.request_uri_.c_str());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds8", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", smsg.from_.c_str());
+ EXPECT_STREQ("Alice ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=1928301774", smsg.from_tag_.c_str());
+ EXPECT_STREQ("a84b4c76e66710", smsg.call_id_.c_str());
+ EXPECT_STREQ("", smsg.contact_.c_str());
+ EXPECT_STREQ("alice", smsg.contact_user_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.contact_host_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.contact_host_address_.c_str());
+ EXPECT_EQ(5060, smsg.contact_host_port_);
+ EXPECT_STREQ("314159 INVITE", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)314159, smsg.cseq_number_);
+ EXPECT_EQ((uint32_t)70, smsg.max_forwards_);
+ EXPECT_EQ((size_t)142, smsg.body_.length());
+}
+
+VOID TEST(ProtocolGbSipTest, SipSessionUasTryingResponse)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("SIP/2.0 100 Trying\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1\r\n");
+ r.in_bytes.push_back("To: Bob \r\n");
+ r.in_bytes.push_back("From: Alice ;tag=1928301774\r\n");
+ r.in_bytes.push_back("Call-ID: a84b4c76e66710\r\n");
+ r.in_bytes.push_back("CSeq: 314159 INVITE\r\n");
+ r.in_bytes.push_back("Content-Length: 0\r\n");
+ r.in_bytes.push_back("\r\n");
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_RESPONSE));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_RESPONSE, (http_parser_type) msg->message_type());
+ EXPECT_EQ(100, msg->status_code());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("Bob ", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", msg->header()->get("From").c_str());
+ EXPECT_STREQ("a84b4c76e66710", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("314159 INVITE", msg->header()->get("CSeq").c_str());
+ EXPECT_EQ(0, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_RESPONSE, smsg.type_);
+ EXPECT_EQ(100, smsg.status_);
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds8", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", smsg.from_.c_str());
+ EXPECT_STREQ("Alice ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=1928301774", smsg.from_tag_.c_str());
+ EXPECT_STREQ("a84b4c76e66710", smsg.call_id_.c_str());
+ EXPECT_STREQ("314159 INVITE", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)314159, smsg.cseq_number_);
+}
+
+VOID TEST(ProtocolGbSipTest, SipSessionUas200OkResponse)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("SIP/2.0 200 OK\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1;received=192.0.2.3\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1\r\n");
+ r.in_bytes.push_back("To: Bob ;tag=a6c85cf\r\n");
+ r.in_bytes.push_back("From: Alice ;tag=1928301774\r\n");
+ r.in_bytes.push_back("Call-ID: a84b4c76e66710\r\n");
+ r.in_bytes.push_back("CSeq: 314159 INVITE\r\n");
+ r.in_bytes.push_back("Contact: \r\n");
+ r.in_bytes.push_back("Content-Type: application/sdp\r\n");
+ r.in_bytes.push_back("Content-Length: 131\r\n");
+ r.in_bytes.push_back("\r\n");
+ r.in_bytes.push_back(string(131, 'x'));
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_RESPONSE));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_RESPONSE, (http_parser_type) msg->message_type());
+ EXPECT_EQ(200, msg->status_code());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("Bob ;tag=a6c85cf", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", msg->header()->get("From").c_str());
+ EXPECT_STREQ("", msg->header()->get("Contact").c_str());
+ EXPECT_STREQ("a84b4c76e66710", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("314159 INVITE", msg->header()->get("CSeq").c_str());
+ EXPECT_EQ(131, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_RESPONSE, smsg.type_);
+ EXPECT_EQ(200, smsg.status_);
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8;received=192.0.2.1", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds8", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ;tag=a6c85cf", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("tag=a6c85cf", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", smsg.from_.c_str());
+ EXPECT_STREQ("Alice ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=1928301774", smsg.from_tag_.c_str());
+ EXPECT_STREQ("", smsg.contact_.c_str());
+ EXPECT_STREQ("bob", smsg.contact_user_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.contact_host_address_.c_str());
+ EXPECT_EQ(5060, smsg.contact_host_port_);
+ EXPECT_STREQ("a84b4c76e66710", smsg.call_id_.c_str());
+ EXPECT_STREQ("314159 INVITE", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)314159, smsg.cseq_number_);
+ EXPECT_EQ((size_t)131, smsg.body_.length());
+}
+
+VOID TEST(ProtocolGbSipTest, SipSessionUacAckRequest)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("ACK sip:bob@192.0.2.4 SIP/2.0\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9\r\n");
+ r.in_bytes.push_back("Max-Forwards: 70\r\n");
+ r.in_bytes.push_back("To: Bob ;tag=a6c85cf\r\n");
+ r.in_bytes.push_back("From: Alice ;tag=1928301774\r\n");
+ r.in_bytes.push_back("Call-ID: a84b4c76e66710\r\n");
+ r.in_bytes.push_back("CSeq: 314159 ACK\r\n");
+ r.in_bytes.push_back("Content-Length: 0\r\n");
+ r.in_bytes.push_back("\r\n");
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_ACK, msg->method());
+ EXPECT_STREQ("/sip:bob@192.0.2.4", msg->path().c_str());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("70", msg->header()->get("Max-Forwards").c_str());
+ EXPECT_STREQ("Bob ;tag=a6c85cf", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", msg->header()->get("From").c_str());
+ EXPECT_STREQ("a84b4c76e66710", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("314159 ACK", msg->header()->get("CSeq").c_str());
+ EXPECT_EQ(0, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_REQUEST, smsg.type_);
+ EXPECT_EQ(HTTP_ACK, smsg.method_);
+ EXPECT_STREQ("sip:bob@192.0.2.4", smsg.request_uri_.c_str());
+ EXPECT_STREQ("SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("pc33.atlanta.com", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds9", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Bob ;tag=a6c85cf", smsg.to_.c_str());
+ EXPECT_STREQ("Bob ", smsg.to_address_.c_str());
+ EXPECT_STREQ("tag=a6c85cf", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", smsg.from_.c_str());
+ EXPECT_STREQ("Alice ", smsg.from_address_.c_str());
+ EXPECT_STREQ("tag=1928301774", smsg.from_tag_.c_str());
+ EXPECT_STREQ("a84b4c76e66710", smsg.call_id_.c_str());
+ EXPECT_STREQ("314159 ACK", smsg.cseq_.c_str());
+ EXPECT_EQ((uint32_t)314159, smsg.cseq_number_);
+ EXPECT_EQ((uint32_t)70, smsg.max_forwards_);
+}
+
+VOID TEST(ProtocolGbSipTest, SipSessionUacByeRequest)
+{
+ srs_error_t err = srs_success;
+
+ // See https://www.ietf.org/rfc/rfc3261.html#section-24.2
+ MockMSegmentsReader r;
+ r.in_bytes.push_back("BYE sip:alice@pc33.atlanta.com SIP/2.0\r\n");
+ r.in_bytes.push_back("Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10\r\n");
+ r.in_bytes.push_back("Max-Forwards: 70\r\n");
+ r.in_bytes.push_back("From: Bob ;tag=a6c85cf\r\n");
+ r.in_bytes.push_back("To: Alice ;tag=1928301774\r\n");
+ r.in_bytes.push_back("Call-ID: a84b4c76e66710\r\n");
+ r.in_bytes.push_back("CSeq: 231 BYE\r\n");
+ r.in_bytes.push_back("Content-Length: 0\r\n");
+ r.in_bytes.push_back("\r\n");
+
+ SrsHttpParser p;
+ HELPER_ASSERT_SUCCESS(p.initialize(HTTP_BOTH));
+
+ ISrsHttpMessage* msg = NULL;
+ SrsAutoFree(ISrsHttpMessage, msg);
+ HELPER_ASSERT_SUCCESS(p.parse_message(&r, &msg));
+ EXPECT_EQ(HTTP_BYE, msg->method());
+ EXPECT_STREQ("/sip:alice@pc33.atlanta.com", msg->path().c_str());
+ EXPECT_STREQ("SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10", msg->header()->get("Via").c_str());
+ EXPECT_STREQ("70", msg->header()->get("Max-Forwards").c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", msg->header()->get("To").c_str());
+ EXPECT_STREQ("Bob ;tag=a6c85cf", msg->header()->get("From").c_str());
+ EXPECT_STREQ("a84b4c76e66710", msg->header()->get("Call-ID").c_str());
+ EXPECT_STREQ("231 BYE", msg->header()->get("CSeq").c_str());
+ EXPECT_EQ(0, msg->content_length());
+
+ SrsSipMessage smsg;
+ HELPER_ASSERT_SUCCESS(smsg.parse(msg));
+ EXPECT_EQ(HTTP_REQUEST, smsg.type_);
+ EXPECT_EQ(HTTP_BYE, smsg.method_);
+ EXPECT_STREQ("sip:alice@pc33.atlanta.com", smsg.request_uri_.c_str());
+ EXPECT_STREQ("SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10", smsg.via_.c_str());
+ EXPECT_STREQ("UDP", smsg.via_transport_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.via_send_by_.c_str());
+ EXPECT_STREQ("192.0.2.4", smsg.via_send_by_address_.c_str());
+ EXPECT_EQ(5060, smsg.via_send_by_port_);
+ EXPECT_STREQ("branch=z9hG4bKnashds10", smsg.via_branch_.c_str());
+ EXPECT_STREQ("Alice ;tag=1928301774", smsg.to_.c_str());
+ EXPECT_STREQ("Alice ", smsg.to_address_.c_str());
+ EXPECT_STREQ("tag=1928301774", smsg.to_tag_.c_str());
+ EXPECT_STREQ("Bob