NEW PROTOCOL: Support viewing stream over RTSP. v7.0.47 (#4333)
## Introduce This PR adds support for viewing streams via the RTSP protocol. Note that it only supports viewing streams, not publishing streams via RTSP. Currently, only publishing via RTMP is supported, which is then converted to RTSP. Further work is needed to support publishing RTC/SRT streams and converting them to RTSP. ## Usage Build and run SRS with RTSP support: ``` cd srs/trunk && ./configure --rtsp=on && make -j16 ./objs/srs -c conf/rtsp.conf ``` Push stream via RTMP by FFmpeg: ``` ffmpeg -re -i doc/source.flv -c copy -f flv rtmp://localhost/live/livestream ``` View the stream via RTSP protocol, try UDP first, then use TCP: ``` ffplay -i rtsp://localhost:8554/live/livestream ``` Or specify the transport protocol with TCP: ``` ffplay -rtsp_transport tcp -i rtsp://localhost:8554/live/livestream ``` ## Unit Test Run utest for RTSP: ``` ./configure --utest=on & make utest -j16 ./objs/srs_utest ``` ## Regression Test You need to start SRS for regression testing. ``` ./objs/srs -c conf/regression-test-for-clion.conf ``` Then run regression tests for RTSP. ``` cd srs/trunk/3rdparty/srs-bench go test ./srs -mod=vendor -v -count=1 -run=TestRtmpPublish_RtspPlay ``` ## Blackbox Test For blackbox testing, SRS will be started by utest, so there is no need to start SRS manually. ``` cd srs/trunk/3rdparty/srs-bench go test ./blackbox -mod=vendor -v -count=1 -run=TestFast_RtmpPublish_RtspPlay_Basic ``` ## UDP Transport As UDP requires port allocation, this PR doesn't support delivering media stream via UDP transport, so it will fail if you try to use UDP as transport: ``` ffplay -rtsp_transport udp -i rtsp://localhost:8554/live/livestream [rtsp @ 0x7fbc99a14880] method SETUP failed: 461 Unsupported Transport rtsp://localhost:8554/live/livestream: Protocol not supported [2025-07-05 21:30:52.738][WARN][14916][7d7gf623][35] RTSP: setup failed: code=2057 (RtspTransportNotSupported) : UDP transport not supported, only TCP/interleaved mode is supported ``` There are no plans to support UDP transport for RTSP. In the real world, UDP is rarely used; the vast majority of RTSP traffic uses TCP. ## Play Before Publish RTSP supports audio with AAC and OPUS codecs, which is significantly different from RTMP or WebRTC. RTSP uses commands to exchange SDP and specify the audio track to play, unlike WHEP or HTTP-FLV, which use the query string of the URL. RTSP depends on the player’s behavior, making it very difficult to use and describe. Considering the feature that allows playing the stream before publishing it, it requires generating some default parameters in the SDP. For OPUS, the sample rate is 48 kHz with 2 channels, while AAC is more complex, especially regarding the sample rate, which may be 44.1 kHz, 32 kHz, or 48 kHz. Therefore, for RTSP, we cannot support play-then-publish. Instead, there must already be a stream when playing it, so that the audio codec is determined. ## Opus Codec No Opus codec support for RTSP, because for RTC2RTSP, it always converts RTC to RTMP frames, then converts them to RTSP packets. Therefore, the audio codec is always AAC after converting RTC to RTMP. This means the bridge architecture needs some changes. We need a new bridge that binds to the target protocol. For example, RTC2RTMP converts the audio codec, but RTC2RTSP keeps the original audio codec. Furthermore, the RTC2RTMP bridge should also support bypassing the Opus codec if we use enhanced-RTMP, which supports the Opus audio codec. I think it should be configurable to either transcode or bypass the audio codec. However, this is not relevant to RTSP. ## AI Contributor Below commits are contributed by AI: * [AI: Remove support for media transport via UDP.](755686229f) * [AI: Add crutial logs for each RTSP stage.](9c8cbe7bde) * [AI: Support AAC doec for RTSP.](7d7cc12bae) * [AI: Add option --rtsp for RTSP.](f67414d9ee) * [AI: Extract SrsRtpVideoBuilder for RTC and RTSP.](562e76b904) --------- Co-authored-by: Jacob Su <suzp1984@gmail.com> Co-authored-by: winlin <winlinvip@gmail.com>
This commit is contained in:
parent
6208b6fe61
commit
5dc292ce64
119
trunk/3rdparty/srs-bench/blackbox/rtsp_test.go
vendored
Normal file
119
trunk/3rdparty/srs-bench/blackbox/rtsp_test.go
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// The MIT License (MIT)
|
||||
//
|
||||
// # Copyright (c) 2025 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 blackbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ossrs/go-oryx-lib/errors"
|
||||
"github.com/ossrs/go-oryx-lib/logger"
|
||||
)
|
||||
|
||||
func TestFast_RtmpPublish_RtspPlay_Basic(t *testing.T) {
|
||||
// This case is run in parallel.
|
||||
t.Parallel()
|
||||
|
||||
// Setup the max timeout for this case.
|
||||
ctx, cancel := context.WithTimeout(logger.WithContext(context.Background()), time.Duration(*srsTimeout)*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
// Check a set of errors.
|
||||
var r0, r1, r2, r3, r4, r5 error
|
||||
defer func(ctx context.Context) {
|
||||
if err := filterTestError(ctx.Err(), r0, r1, r2, r3, r4, r5); err != nil {
|
||||
t.Errorf("Fail for err %+v", err)
|
||||
} else {
|
||||
logger.Tf(ctx, "test done with err %+v", err)
|
||||
}
|
||||
}(ctx)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
// Start SRS server and wait for it to be ready.
|
||||
svr := NewSRSServer(func(v *srsServer) {
|
||||
v.envs = []string{
|
||||
"SRS_RTSP_SERVER_ENABLED=on",
|
||||
"SRS_VHOST_RTSP_ENABLED=on",
|
||||
"SRS_VHOST_RTSP_RTMP_TO_RTSP=on",
|
||||
}
|
||||
})
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r0 = svr.Run(ctx, cancel)
|
||||
}()
|
||||
|
||||
// Start FFmpeg to publish stream.
|
||||
streamID := fmt.Sprintf("stream-%v-%v", os.Getpid(), rand.Int())
|
||||
streamURL := fmt.Sprintf("rtmp://localhost:%v/live/%v", svr.RTMPPort(), streamID)
|
||||
ffmpeg := NewFFmpeg(func(v *ffmpegClient) {
|
||||
v.args = []string{
|
||||
"-stream_loop", "-1", "-re", "-i", *srsPublishAvatar, "-c", "copy", "-f", "flv", streamURL,
|
||||
}
|
||||
})
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-svr.ReadyCtx().Done()
|
||||
r1 = ffmpeg.Run(ctx, cancel)
|
||||
}()
|
||||
|
||||
// Start FFprobe to detect and verify stream.
|
||||
duration := time.Duration(*srsFFprobeDuration) * time.Millisecond
|
||||
ffprobe := NewFFprobe(func(v *ffprobeClient) {
|
||||
v.dvrFile = path.Join(svr.WorkDir(), "objs", fmt.Sprintf("srs-ffprobe-%v.mp4", streamID))
|
||||
v.streamURL = fmt.Sprintf("rtsp://localhost:%v/live/%v", svr.RTSPPort(), streamID)
|
||||
v.duration, v.timeout = duration, time.Duration(*srsFFprobeTimeout)*time.Millisecond
|
||||
})
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-svr.ReadyCtx().Done()
|
||||
r2 = ffprobe.Run(ctx, cancel)
|
||||
}()
|
||||
|
||||
// Fast quit for probe done.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-ffprobe.ProbeDoneCtx().Done():
|
||||
defer cancel()
|
||||
|
||||
str, m := ffprobe.Result()
|
||||
if len(m.Streams) != 2 {
|
||||
r3 = errors.Errorf("invalid streams=%v, %v, %v", len(m.Streams), m.String(), str)
|
||||
}
|
||||
|
||||
if ts := 90; m.Format.ProbeScore < ts {
|
||||
r4 = errors.Errorf("low score=%v < %v, %v, %v", m.Format.ProbeScore, ts, m.String(), str)
|
||||
}
|
||||
if dv := m.Duration(); dv < duration {
|
||||
r5 = errors.Errorf("short duration=%v < %v, %v, %v", dv, duration, m.String(), str)
|
||||
}
|
||||
}
|
||||
}
|
||||
13
trunk/3rdparty/srs-bench/blackbox/util.go
vendored
13
trunk/3rdparty/srs-bench/blackbox/util.go
vendored
|
|
@ -422,6 +422,8 @@ type SRSServer interface {
|
|||
APIPort() int
|
||||
// SRTPort is the SRT UDP port.
|
||||
SRTPort() int
|
||||
// RTSPPort is the RTSP port.
|
||||
RTSPPort() int
|
||||
}
|
||||
|
||||
// srsServer is a SRS server instance.
|
||||
|
|
@ -450,6 +452,8 @@ type srsServer struct {
|
|||
httpListen int
|
||||
// SRT UDP server listen port.
|
||||
srtListen int
|
||||
// RTSP server listen port.
|
||||
rtspListen int
|
||||
|
||||
// The envs from user.
|
||||
envs []string
|
||||
|
|
@ -476,6 +480,7 @@ func NewSRSServer(opts ...func(v *srsServer)) SRSServer {
|
|||
v.apiListen = allocator.Allocate()
|
||||
v.httpListen = allocator.Allocate()
|
||||
v.srtListen = allocator.Allocate()
|
||||
v.rtspListen = allocator.Allocate()
|
||||
|
||||
// Do cleanup.
|
||||
v.process.onDispose = func(ctx context.Context, bs *backendService) error {
|
||||
|
|
@ -483,7 +488,7 @@ func NewSRSServer(opts ...func(v *srsServer)) SRSServer {
|
|||
allocator.Free(v.apiListen)
|
||||
allocator.Free(v.httpListen)
|
||||
allocator.Free(v.srtListen)
|
||||
|
||||
allocator.Free(v.rtspListen)
|
||||
if _, err := os.Stat(v.workDir); err == nil {
|
||||
os.RemoveAll(v.workDir)
|
||||
}
|
||||
|
|
@ -520,6 +525,10 @@ func (v *srsServer) SRTPort() int {
|
|||
return v.srtListen
|
||||
}
|
||||
|
||||
func (v *srsServer) RTSPPort() int {
|
||||
return v.rtspListen
|
||||
}
|
||||
|
||||
func (v *srsServer) WorkDir() string {
|
||||
return v.workDir
|
||||
}
|
||||
|
|
@ -575,6 +584,8 @@ func (v *srsServer) Run(ctx context.Context, cancel context.CancelFunc) error {
|
|||
fmt.Sprintf("SRS_HTTP_SERVER_LISTEN=%v", v.httpListen),
|
||||
// Setup the SRT server listen port.
|
||||
fmt.Sprintf("SRS_SRT_SERVER_LISTEN=%v", v.srtListen),
|
||||
// Setup the RTSP server listen port.
|
||||
fmt.Sprintf("SRS_RTSP_SERVER_LISTEN=%v", v.rtspListen),
|
||||
}...)
|
||||
// Rewrite envs by case.
|
||||
if v.envs != nil {
|
||||
|
|
|
|||
2
trunk/3rdparty/srs-bench/go.mod
vendored
2
trunk/3rdparty/srs-bench/go.mod
vendored
|
|
@ -3,6 +3,7 @@ module github.com/ossrs/srs-bench
|
|||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/bluenviron/gortsplib/v4 v4.13.1
|
||||
github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/haivision/srtgo v0.0.0-20230627061225-a70d53fcd618
|
||||
|
|
@ -21,6 +22,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/bluenviron/mediacommon/v2 v2.1.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.1.0-rc.1 // indirect
|
||||
|
|
|
|||
4
trunk/3rdparty/srs-bench/go.sum
vendored
4
trunk/3rdparty/srs-bench/go.sum
vendored
|
|
@ -1,3 +1,7 @@
|
|||
github.com/bluenviron/gortsplib/v4 v4.13.1 h1:FpkfzLTWgeC4C3ytfNZ9ezes+MA4ZSp8rAvxJP5RnLY=
|
||||
github.com/bluenviron/gortsplib/v4 v4.13.1/go.mod h1:nJVGKKG8KEkt7KKuckZIXQ1FHevSbvdV7y5UcpLmORw=
|
||||
github.com/bluenviron/mediacommon/v2 v2.1.0 h1:NtlRCaAo7gnCcO+EHHeFJxSIt+v8uNbvJqlH1Gk2PZM=
|
||||
github.com/bluenviron/mediacommon/v2 v2.1.0/go.mod h1:iHEz1SFIet6zBwAQoh1a92vTQ3dV3LpVFbom6/SLz3k=
|
||||
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=
|
||||
|
|
|
|||
80
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
80
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
|
|
@ -30,11 +30,13 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||
"github.com/ossrs/go-oryx-lib/avc"
|
||||
"github.com/ossrs/go-oryx-lib/flv"
|
||||
"github.com/ossrs/go-oryx-lib/logger"
|
||||
"github.com/ossrs/go-oryx-lib/rtmp"
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
@ -190,6 +192,82 @@ func TestRtmpPublish_RtcPlay_AVC(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRtmpPublish_RtspPlay(t *testing.T) {
|
||||
ctx := logger.WithContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||
|
||||
var r0, r1 error
|
||||
err := func() (err error) {
|
||||
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||
rtmpUrl := fmt.Sprintf("rtmp://%v/live/%v", *srsServer, streamSuffix)
|
||||
|
||||
// TODO: 8554
|
||||
rtspUrl := fmt.Sprintf("rtsp://%v:8554/live/%v", *srsServer, streamSuffix)
|
||||
|
||||
// Publisher connect to a RTMP stream.
|
||||
publisher := NewRTMPPublisher()
|
||||
defer publisher.Close()
|
||||
|
||||
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
player := NewRTSPPlayer()
|
||||
defer player.Close()
|
||||
|
||||
// Run publisher and player
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
publisherReady, publisherReadyCancel := context.WithCancel(context.Background())
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
publisherReadyCancel()
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-publisherReady.Done()
|
||||
|
||||
if err := player.Play(ctx, rtspUrl); err != nil {
|
||||
r1 = err
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
var nnPackets int
|
||||
player.onRTPPacket = func(media *description.Media, format uint8, packet *rtp.Packet) error {
|
||||
logger.Tf(ctx, "RTSP: recv rtp packet #%v, payload type=%v, size=%vB",
|
||||
nnPackets, packet.PayloadType, len(packet.Payload))
|
||||
if nnPackets += 1; nnPackets > 50 {
|
||||
cancel()
|
||||
}
|
||||
// TODO: Further validate the RTP packets.
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||
t.Errorf("err %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRtmpPublish_MultipleSequences(t *testing.T) {
|
||||
ctx := logger.WithContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||
|
|
@ -235,7 +313,7 @@ func TestRtmpPublish_MultipleSequences(t *testing.T) {
|
|||
}
|
||||
|
||||
// Ingore the duplicated sps/pps.
|
||||
if isAvccrEquals(previousAvccr, avccr) {
|
||||
if IsAvccrEquals(previousAvccr, avccr) {
|
||||
return nil
|
||||
}
|
||||
previousAvccr = avccr
|
||||
|
|
|
|||
89
trunk/3rdparty/srs-bench/srs/util.go
vendored
89
trunk/3rdparty/srs-bench/srs/util.go
vendored
|
|
@ -44,6 +44,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||
"github.com/ossrs/go-oryx-lib/amf0"
|
||||
"github.com/ossrs/go-oryx-lib/avc"
|
||||
"github.com/ossrs/go-oryx-lib/flv"
|
||||
|
|
@ -2220,7 +2224,90 @@ func (v *FLVPlayer) consume(ctx context.Context) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func isAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool {
|
||||
// RTSPPlayer
|
||||
type RTSPPlayer struct {
|
||||
rtspUrl string
|
||||
|
||||
client *gortsplib.Client
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
onRTPPacket func(media *description.Media, format uint8, packet *rtp.Packet) error
|
||||
}
|
||||
|
||||
func NewRTSPPlayer() *RTSPPlayer {
|
||||
return &RTSPPlayer{}
|
||||
}
|
||||
|
||||
func (v *RTSPPlayer) Close() error {
|
||||
if v.cancel != nil {
|
||||
v.cancel()
|
||||
}
|
||||
if v.client != nil {
|
||||
v.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *RTSPPlayer) Play(ctx context.Context, rtspUrl string) error {
|
||||
v.rtspUrl = rtspUrl
|
||||
v.ctx, v.cancel = context.WithCancel(ctx)
|
||||
|
||||
transport := gortsplib.Transport(gortsplib.TransportTCP)
|
||||
v.client = &gortsplib.Client{
|
||||
Transport: &transport,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
u, err := base.ParseURL(rtspUrl)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "parse url %v", rtspUrl)
|
||||
}
|
||||
|
||||
err = v.client.Start(u.Scheme, u.Host)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "connect rtsp %v", rtspUrl)
|
||||
}
|
||||
|
||||
desc, _, err := v.client.Describe(u)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "describe rtsp %v", rtspUrl)
|
||||
}
|
||||
|
||||
err = v.client.SetupAll(desc.BaseURL, desc.Medias)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "setup rtsp %v", rtspUrl)
|
||||
}
|
||||
|
||||
v.client.OnPacketRTPAny(func(media *description.Media, forma format.Format, packet *rtp.Packet) {
|
||||
if v.onRTPPacket != nil {
|
||||
if err := v.onRTPPacket(media, uint8(forma.ClockRate()), packet); err != nil {
|
||||
logger.Wf(ctx, "rtsp: on rtp error %+v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_, err = v.client.Play(nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "play rtsp %v", rtspUrl)
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := v.client.Wait()
|
||||
if err != nil {
|
||||
logger.Wf(ctx, "rtsp: client error %+v", err)
|
||||
}
|
||||
if v.cancel != nil {
|
||||
v.cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
1
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.dockerignore
generated
vendored
Normal file
1
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.dockerignore
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.git
|
||||
1
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.gitignore
generated
vendored
Normal file
1
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/coverage*.txt
|
||||
73
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.golangci.yml
generated
vendored
Normal file
73
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- bodyclose
|
||||
#- contextcheck
|
||||
- copyloopvar
|
||||
- dupl
|
||||
- errorlint
|
||||
- gochecknoinits
|
||||
- gocritic
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- lll
|
||||
- misspell
|
||||
- nilerr
|
||||
- prealloc
|
||||
- predeclared
|
||||
- revive
|
||||
- usestdlibvars
|
||||
- unconvert
|
||||
#- usetesting
|
||||
- tparallel
|
||||
- wastedassign
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- io.Copy
|
||||
- (io.Closer).Close
|
||||
- (io.Writer).Write
|
||||
- (hash.Hash).Write
|
||||
- (net.Conn).Close
|
||||
- (net.Conn).SetReadDeadline
|
||||
- (net.Conn).SetWriteDeadline
|
||||
- (*net.TCPConn).SetKeepAlive
|
||||
- (*net.TCPConn).SetKeepAlivePeriod
|
||||
- (*net.TCPConn).SetNoDelay
|
||||
- (net.Listener).Close
|
||||
- (net.PacketConn).Close
|
||||
- (net.PacketConn).SetReadDeadline
|
||||
- (net.PacketConn).SetWriteDeadline
|
||||
- (net/http.ResponseWriter).Write
|
||||
- (*net/http.Server).Serve
|
||||
- (*net/http.Server).ServeTLS
|
||||
- (*net/http.Server).Shutdown
|
||||
- os.Chdir
|
||||
- os.Mkdir
|
||||
- os.MkdirAll
|
||||
- os.Remove
|
||||
- os.RemoveAll
|
||||
- os.Setenv
|
||||
- os.Unsetenv
|
||||
- (*os.File).WriteString
|
||||
- (*os.File).Close
|
||||
- (github.com/datarhei/gosrt.Conn).Close
|
||||
- (github.com/datarhei/gosrt.Conn).SetReadDeadline
|
||||
- (github.com/datarhei/gosrt.Conn).SetWriteDeadline
|
||||
- (*github.com/bluenviron/gortsplib/v4.Client).Close
|
||||
- (*github.com/bluenviron/gortsplib/v4.Server).Close
|
||||
- (*github.com/bluenviron/gortsplib/v4.ServerSession).Close
|
||||
- (*github.com/bluenviron/gortsplib/v4.ServerStream).Close
|
||||
- (*github.com/bluenviron/gortsplib/v4.ServerConn).Close
|
||||
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- fieldalignment
|
||||
- reflectvaluecompare
|
||||
21
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/LICENSE
generated
vendored
Normal file
21
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 aler9
|
||||
|
||||
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.
|
||||
26
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/Makefile
generated
vendored
Normal file
26
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
BASE_IMAGE = golang:1.24-alpine3.20
|
||||
LINT_IMAGE = golangci/golangci-lint:v1.64.5
|
||||
|
||||
.PHONY: $(shell ls)
|
||||
|
||||
help:
|
||||
@echo "usage: make [action]"
|
||||
@echo ""
|
||||
@echo "available actions:"
|
||||
@echo ""
|
||||
@echo " mod-tidy run go mod tidy"
|
||||
@echo " format format source files"
|
||||
@echo " test run tests"
|
||||
@echo " test-32 run tests on a 32-bit system"
|
||||
@echo " test-e2e run end-to-end tests"
|
||||
@echo " lint run linter"
|
||||
@echo " bench run benchmarks"
|
||||
@echo ""
|
||||
|
||||
blank :=
|
||||
define NL
|
||||
|
||||
$(blank)
|
||||
endef
|
||||
|
||||
include scripts/*.mk
|
||||
177
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/README.md
generated
vendored
Normal file
177
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# gortsplib
|
||||
|
||||
[](https://github.com/bluenviron/gortsplib/actions/workflows/test.yml)
|
||||
[](https://github.com/bluenviron/gortsplib/actions/workflows/lint.yml)
|
||||
[](https://goreportcard.com/report/github.com/bluenviron/gortsplib)
|
||||
[](https://app.codecov.io/gh/bluenviron/gortsplib/tree/main)
|
||||
[](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4#pkg-index)
|
||||
|
||||
RTSP 1.0 client and server library for the Go programming language, written for [MediaMTX](https://github.com/bluenviron/mediamtx).
|
||||
|
||||
Go ≥ 1.21 is required.
|
||||
|
||||
Features:
|
||||
|
||||
* Client
|
||||
* Query servers about available media streams
|
||||
* Read media streams from a server ("play")
|
||||
* Read streams with the UDP, UDP-multicast or TCP transport protocol
|
||||
* Read TLS-encrypted streams (TCP only)
|
||||
* Switch transport protocol automatically
|
||||
* Read selected media streams
|
||||
* Pause or seek without disconnecting from the server
|
||||
* Write to ONVIF back channels
|
||||
* Get PTS (relative) timestamp of incoming packets
|
||||
* Get NTP (absolute) timestamp of incoming packets
|
||||
* Write media streams to a server ("record")
|
||||
* Write streams with the UDP or TCP transport protocol
|
||||
* Write TLS-encrypted streams (TCP only)
|
||||
* Switch transport protocol automatically
|
||||
* Pause without disconnecting from the server
|
||||
* Server
|
||||
* Handle requests from clients
|
||||
* Validate client credentials
|
||||
* Read media streams from clients ("record")
|
||||
* Read streams with the UDP or TCP transport protocol
|
||||
* Read TLS-encrypted streams (TCP only)
|
||||
* Get PTS (relative) timestamp of incoming packets
|
||||
* Get NTP (absolute) timestamp of incoming packets
|
||||
* Serve media streams to clients ("play")
|
||||
* Write streams with the UDP, UDP-multicast or TCP transport protocol
|
||||
* Write TLS-encrypted streams (TCP only)
|
||||
* Compute and provide SSRC, RTP-Info to clients
|
||||
* Utilities
|
||||
* Parse RTSP elements
|
||||
* Encode/decode RTP packets into/from codec-specific frames
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Examples](#examples)
|
||||
* [API Documentation](#api-documentation)
|
||||
* [RTP Payload Formats](#rtp-payload-formats)
|
||||
* [Specifications](#specifications)
|
||||
* [Related projects](#related-projects)
|
||||
|
||||
## Examples
|
||||
|
||||
* [client-query](examples/client-query/main.go)
|
||||
* [client-play](examples/client-play/main.go)
|
||||
* [client-play-timestamp](examples/client-play-timestamp/main.go)
|
||||
* [client-play-options](examples/client-play-options/main.go)
|
||||
* [client-play-pause](examples/client-play-pause/main.go)
|
||||
* [client-play-to-record](examples/client-play-to-record/main.go)
|
||||
* [client-play-backchannel](examples/client-play-backchannel/main.go)
|
||||
* [client-play-format-av1](examples/client-play-format-av1/main.go)
|
||||
* [client-play-format-av1-to-jpeg](examples/client-play-format-av1-to-jpeg/main.go)
|
||||
* [client-play-format-g711](examples/client-play-format-g711/main.go)
|
||||
* [client-play-format-h264](examples/client-play-format-h264/main.go)
|
||||
* [client-play-format-h264-to-jpeg](examples/client-play-format-h264-to-jpeg/main.go)
|
||||
* [client-play-format-h264-to-disk](examples/client-play-format-h264-to-disk/main.go)
|
||||
* [client-play-format-h264-mpeg4audio-to-disk](examples/client-play-format-h264-mpeg4audio-to-disk/main.go)
|
||||
* [client-play-format-h265](examples/client-play-format-h265/main.go)
|
||||
* [client-play-format-h265-to-jpeg](examples/client-play-format-h265-to-jpeg/main.go)
|
||||
* [client-play-format-h265-to-disk](examples/client-play-format-h265-to-disk/main.go)
|
||||
* [client-play-format-lpcm](examples/client-play-format-lpcm/main.go)
|
||||
* [client-play-format-mjpeg](examples/client-play-format-mjpeg/main.go)
|
||||
* [client-play-format-mpeg4audio](examples/client-play-format-mpeg4audio/main.go)
|
||||
* [client-play-format-mpeg4audio-to-disk](examples/client-play-format-mpeg4audio-to-disk/main.go)
|
||||
* [client-play-format-opus](examples/client-play-format-opus/main.go)
|
||||
* [client-play-format-opus-to-disk](examples/client-play-format-opus-to-disk/main.go)
|
||||
* [client-play-format-vp8](examples/client-play-format-vp8/main.go)
|
||||
* [client-play-format-vp9](examples/client-play-format-vp9/main.go)
|
||||
* [client-record-options](examples/client-record-options/main.go)
|
||||
* [client-record-pause](examples/client-record-pause/main.go)
|
||||
* [client-record-format-av1](examples/client-record-format-av1/main.go)
|
||||
* [client-record-format-g711](examples/client-record-format-g711/main.go)
|
||||
* [client-record-format-h264](examples/client-record-format-h264/main.go)
|
||||
* [client-record-format-h264-from-disk](examples/client-record-format-h264-from-disk/main.go)
|
||||
* [client-record-format-h265](examples/client-record-format-h265/main.go)
|
||||
* [client-record-format-lpcm](examples/client-record-format-lpcm/main.go)
|
||||
* [client-record-format-mjpeg](examples/client-record-format-mjpeg/main.go)
|
||||
* [client-record-format-mpeg4audio](examples/client-record-format-mpeg4audio/main.go)
|
||||
* [client-record-format-opus](examples/client-record-format-opus/main.go)
|
||||
* [client-record-format-vp8](examples/client-record-format-vp8/main.go)
|
||||
* [client-record-format-vp9](examples/client-record-format-vp9/main.go)
|
||||
* [server](examples/server/main.go)
|
||||
* [server-tls](examples/server-tls/main.go)
|
||||
* [server-auth](examples/server-auth/main.go)
|
||||
* [server-h264-to-disk](examples/server-h264-to-disk/main.go)
|
||||
* [server-h264-from-disk](examples/server-h264-from-disk/main.go)
|
||||
* [proxy](examples/proxy/main.go)
|
||||
|
||||
## API Documentation
|
||||
|
||||
[Click to open the API Documentation](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4#pkg-index)
|
||||
|
||||
## RTP Payload Formats
|
||||
|
||||
In RTSP, media streams are transmitted by using RTP packets, which are encoded in a specific, codec-dependent, format. This library supports formats for the following codecs:
|
||||
|
||||
### Video
|
||||
|
||||
|codec|documentation|encoder and decoder available|
|
||||
|------|-------------|-----------------------------|
|
||||
|AV1|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#AV1)|:heavy_check_mark:|
|
||||
|VP9|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#VP9)|:heavy_check_mark:|
|
||||
|VP8|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#VP8)|:heavy_check_mark:|
|
||||
|H265|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#H265)|:heavy_check_mark:|
|
||||
|H264|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#H264)|:heavy_check_mark:|
|
||||
|MPEG-4 Video (H263, Xvid)|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MPEG4Video)|:heavy_check_mark:|
|
||||
|MPEG-1/2 Video|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MPEG1Video)|:heavy_check_mark:|
|
||||
|M-JPEG|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MJPEG)|:heavy_check_mark:|
|
||||
|
||||
### Audio
|
||||
|
||||
|codec|documentation|encoder and decoder available|
|
||||
|------|-------------|-----------------------------|
|
||||
|Opus|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#Opus)|:heavy_check_mark:|
|
||||
|Vorbis|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#Vorbis)||
|
||||
|MPEG-4 Audio (AAC)|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MPEG4Audio)|:heavy_check_mark:|
|
||||
|MPEG-1/2 Audio (MP3)|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MPEG1Audio)|:heavy_check_mark:|
|
||||
|AC-3|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#AC3)|:heavy_check_mark:|
|
||||
|Speex|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#Speex)||
|
||||
|G726|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#G726)||
|
||||
|G722|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#G722)|:heavy_check_mark:|
|
||||
|G711 (PCMA, PCMU)|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#G711)|:heavy_check_mark:|
|
||||
|LPCM|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#LPCM)|:heavy_check_mark:|
|
||||
|
||||
### Other
|
||||
|
||||
|codec|documentation|encoder and decoder available|
|
||||
|------|-------------|-----------------------------|
|
||||
|MPEG-TS|[link](https://pkg.go.dev/github.com/bluenviron/gortsplib/v4/pkg/format#MPEGTS)||
|
||||
|
||||
## Specifications
|
||||
|
||||
|name|area|
|
||||
|----|----|
|
||||
|[RFC2326, RTSP 1.0](https://datatracker.ietf.org/doc/html/rfc2326)|protocol|
|
||||
|[RFC7826, RTSP 2.0](https://datatracker.ietf.org/doc/html/rfc7826)|protocol|
|
||||
|[RFC8866, SDP: Session Description Protocol](https://datatracker.ietf.org/doc/html/rfc8866)|SDP|
|
||||
|[RTP Payload Format For AV1 (v1.0)](https://aomediacodec.github.io/av1-rtp-spec/)|payload formats / AV1|
|
||||
|[RTP Payload Format for VP9 Video](https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16)|payload formats / VP9|
|
||||
|[RFC7741, RTP Payload Format for VP8 Video](https://datatracker.ietf.org/doc/html/rfc7741)|payload formats / VP8|
|
||||
|[RFC7798, RTP Payload Format for High Efficiency Video Coding (HEVC)](https://datatracker.ietf.org/doc/html/rfc7798)|payload formats / H265|
|
||||
|[RFC6184, RTP Payload Format for H.264 Video](https://datatracker.ietf.org/doc/html/rfc6184)|payload formats / H264|
|
||||
|[RFC3640, RTP Payload Format for Transport of MPEG-4 Elementary Streams](https://datatracker.ietf.org/doc/html/rfc3640)|payload formats / MPEG-4 audio, MPEG-4 video|
|
||||
|[RFC2250, RTP Payload Format for MPEG1/MPEG2 Video](https://datatracker.ietf.org/doc/html/rfc2250)|payload formats / MPEG-1 video, MPEG-2 audio, MPEG-TS|
|
||||
|[RFC2435, RTP Payload Format for JPEG-compressed Video](https://datatracker.ietf.org/doc/html/rfc2435)|payload formats / M-JPEG|
|
||||
|[RFC7587, RTP Payload Format for the Opus Speech and Audio Codec](https://datatracker.ietf.org/doc/html/rfc7587)|payload formats / Opus|
|
||||
|[Multiopus in libwebrtc](https://webrtc-review.googlesource.com/c/src/+/129768)|payload formats / Opus|
|
||||
|[RFC5215, RTP Payload Format for Vorbis Encoded Audio](https://datatracker.ietf.org/doc/html/rfc5215)|payload formats / Vorbis|
|
||||
|[RFC4184, RTP Payload Format for AC-3 Audio](https://datatracker.ietf.org/doc/html/rfc4184)|payload formats / AC-3|
|
||||
|[RFC6416, RTP Payload Format for MPEG-4 Audio/Visual Streams](https://datatracker.ietf.org/doc/html/rfc6416)|payload formats / MPEG-4 audio|
|
||||
|[RFC5574, RTP Payload Format for the Speex Codec](https://datatracker.ietf.org/doc/html/rfc5574)|payload formats / Speex|
|
||||
|[RFC3551, RTP Profile for Audio and Video Conferences with Minimal Control](https://datatracker.ietf.org/doc/html/rfc3551)|payload formats / G726, G722, G711, LPCM|
|
||||
|[RFC3190, RTP Payload Format for 12-bit DAT Audio and 20- and 24-bit Linear Sampled Audio](https://datatracker.ietf.org/doc/html/rfc3190)|payload formats / LPCM|
|
||||
|[Codec specifications](https://github.com/bluenviron/mediacommon#specifications)|codecs|
|
||||
|[Golang project layout](https://github.com/golang-standards/project-layout)|project layout|
|
||||
|
||||
## Related projects
|
||||
|
||||
* [MediaMTX](https://github.com/bluenviron/mediamtx)
|
||||
* [gohlslib](https://github.com/bluenviron/gohlslib)
|
||||
* [mediacommon](https://github.com/bluenviron/mediacommon)
|
||||
* [pion/sdp (SDP library used internally)](https://github.com/pion/sdp)
|
||||
* [pion/rtp (RTP library used internally)](https://github.com/pion/rtp)
|
||||
* [pion/rtcp (RTCP library used internally)](https://github.com/pion/rtcp)
|
||||
58
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/async_processor.go
generated
vendored
Normal file
58
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/async_processor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
|
||||
)
|
||||
|
||||
// this is an asynchronous queue processor
|
||||
// that allows to detach the routine that is reading a stream
|
||||
// from the routine that is writing a stream.
|
||||
type asyncProcessor struct {
|
||||
bufferSize int
|
||||
|
||||
running bool
|
||||
buffer *ringbuffer.RingBuffer
|
||||
stopError error
|
||||
|
||||
chStopped chan struct{}
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) initialize() {
|
||||
w.buffer, _ = ringbuffer.New(uint64(w.bufferSize))
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) close() {
|
||||
if w.running {
|
||||
w.buffer.Close()
|
||||
<-w.chStopped
|
||||
}
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) start() {
|
||||
w.running = true
|
||||
w.chStopped = make(chan struct{})
|
||||
go w.run()
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) run() {
|
||||
w.stopError = w.runInner()
|
||||
close(w.chStopped)
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) runInner() error {
|
||||
for {
|
||||
tmp, ok := w.buffer.Pull()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := tmp.(func() error)()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *asyncProcessor) push(cb func() error) bool {
|
||||
return w.buffer.Push(cb)
|
||||
}
|
||||
2256
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client.go
generated
vendored
Normal file
2256
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
159
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_format.go
generated
vendored
Normal file
159
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/rtcpreceiver"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/rtcpsender"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/rtplossdetector"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/rtpreorderer"
|
||||
)
|
||||
|
||||
type clientFormat struct {
|
||||
cm *clientMedia
|
||||
format format.Format
|
||||
onPacketRTP OnPacketRTPFunc
|
||||
|
||||
udpReorderer *rtpreorderer.Reorderer // play
|
||||
tcpLossDetector *rtplossdetector.LossDetector // play
|
||||
rtcpReceiver *rtcpreceiver.RTCPReceiver // play
|
||||
rtcpSender *rtcpsender.RTCPSender // record or back channel
|
||||
writePacketRTPInQueue func([]byte) error
|
||||
rtpPacketsReceived *uint64
|
||||
rtpPacketsSent *uint64
|
||||
rtpPacketsLost *uint64
|
||||
}
|
||||
|
||||
func (cf *clientFormat) initialize() {
|
||||
cf.rtpPacketsReceived = new(uint64)
|
||||
cf.rtpPacketsSent = new(uint64)
|
||||
cf.rtpPacketsLost = new(uint64)
|
||||
}
|
||||
|
||||
func (cf *clientFormat) start() {
|
||||
if cf.cm.udpRTPListener != nil {
|
||||
cf.writePacketRTPInQueue = cf.writePacketRTPInQueueUDP
|
||||
} else {
|
||||
cf.writePacketRTPInQueue = cf.writePacketRTPInQueueTCP
|
||||
}
|
||||
|
||||
if cf.cm.c.state == clientStateRecord || cf.cm.media.IsBackChannel {
|
||||
cf.rtcpSender = &rtcpsender.RTCPSender{
|
||||
ClockRate: cf.format.ClockRate(),
|
||||
Period: cf.cm.c.senderReportPeriod,
|
||||
TimeNow: cf.cm.c.timeNow,
|
||||
WritePacketRTCP: func(pkt rtcp.Packet) {
|
||||
if !cf.cm.c.DisableRTCPSenderReports {
|
||||
cf.cm.c.WritePacketRTCP(cf.cm.media, pkt) //nolint:errcheck
|
||||
}
|
||||
},
|
||||
}
|
||||
cf.rtcpSender.Initialize()
|
||||
} else {
|
||||
if cf.cm.udpRTPListener != nil {
|
||||
cf.udpReorderer = &rtpreorderer.Reorderer{}
|
||||
cf.udpReorderer.Initialize()
|
||||
} else {
|
||||
cf.tcpLossDetector = &rtplossdetector.LossDetector{}
|
||||
}
|
||||
|
||||
cf.rtcpReceiver = &rtcpreceiver.RTCPReceiver{
|
||||
ClockRate: cf.format.ClockRate(),
|
||||
Period: cf.cm.c.receiverReportPeriod,
|
||||
TimeNow: cf.cm.c.timeNow,
|
||||
WritePacketRTCP: func(pkt rtcp.Packet) {
|
||||
if cf.cm.udpRTPListener != nil {
|
||||
cf.cm.c.WritePacketRTCP(cf.cm.media, pkt) //nolint:errcheck
|
||||
}
|
||||
},
|
||||
}
|
||||
err := cf.rtcpReceiver.Initialize()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cf *clientFormat) stop() {
|
||||
if cf.rtcpReceiver != nil {
|
||||
cf.rtcpReceiver.Close()
|
||||
cf.rtcpReceiver = nil
|
||||
}
|
||||
|
||||
if cf.rtcpSender != nil {
|
||||
cf.rtcpSender.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (cf *clientFormat) readPacketRTPUDP(pkt *rtp.Packet) {
|
||||
packets, lost := cf.udpReorderer.Process(pkt)
|
||||
if lost != 0 {
|
||||
cf.handlePacketsLost(uint64(lost))
|
||||
// do not return
|
||||
}
|
||||
|
||||
now := cf.cm.c.timeNow()
|
||||
|
||||
for _, pkt := range packets {
|
||||
cf.handlePacketRTP(pkt, now)
|
||||
}
|
||||
}
|
||||
|
||||
func (cf *clientFormat) readPacketRTPTCP(pkt *rtp.Packet) {
|
||||
lost := cf.tcpLossDetector.Process(pkt)
|
||||
if lost != 0 {
|
||||
cf.handlePacketsLost(uint64(lost))
|
||||
// do not return
|
||||
}
|
||||
|
||||
now := cf.cm.c.timeNow()
|
||||
|
||||
cf.handlePacketRTP(pkt, now)
|
||||
}
|
||||
|
||||
func (cf *clientFormat) handlePacketRTP(pkt *rtp.Packet, now time.Time) {
|
||||
err := cf.rtcpReceiver.ProcessPacket(pkt, now, cf.format.PTSEqualsDTS(pkt))
|
||||
if err != nil {
|
||||
cf.cm.onPacketRTPDecodeError(err)
|
||||
return
|
||||
}
|
||||
|
||||
atomic.AddUint64(cf.rtpPacketsReceived, 1)
|
||||
|
||||
cf.onPacketRTP(pkt)
|
||||
}
|
||||
|
||||
func (cf *clientFormat) handlePacketsLost(lost uint64) {
|
||||
atomic.AddUint64(cf.rtpPacketsLost, lost)
|
||||
cf.cm.c.OnPacketsLost(lost)
|
||||
}
|
||||
|
||||
func (cf *clientFormat) writePacketRTPInQueueUDP(payload []byte) error {
|
||||
err := cf.cm.udpRTPListener.write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.AddUint64(cf.cm.bytesSent, uint64(len(payload)))
|
||||
atomic.AddUint64(cf.rtpPacketsSent, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cf *clientFormat) writePacketRTPInQueueTCP(payload []byte) error {
|
||||
cf.cm.c.tcpFrame.Channel = cf.cm.tcpChannel
|
||||
cf.cm.c.tcpFrame.Payload = payload
|
||||
cf.cm.c.nconn.SetWriteDeadline(time.Now().Add(cf.cm.c.WriteTimeout))
|
||||
err := cf.cm.c.conn.WriteInterleavedFrame(cf.cm.c.tcpFrame, cf.cm.c.tcpBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.AddUint64(cf.cm.bytesSent, uint64(len(payload)))
|
||||
atomic.AddUint64(cf.rtpPacketsSent, 1)
|
||||
return nil
|
||||
}
|
||||
361
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_media.go
generated
vendored
Normal file
361
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_media.go
generated
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||
)
|
||||
|
||||
type clientMedia struct {
|
||||
c *Client
|
||||
media *description.Media
|
||||
|
||||
onPacketRTCP OnPacketRTCPFunc
|
||||
formats map[uint8]*clientFormat
|
||||
tcpChannel int
|
||||
udpRTPListener *clientUDPListener
|
||||
udpRTCPListener *clientUDPListener
|
||||
writePacketRTCPInQueue func([]byte) error
|
||||
bytesReceived *uint64
|
||||
bytesSent *uint64
|
||||
rtpPacketsInError *uint64
|
||||
rtcpPacketsReceived *uint64
|
||||
rtcpPacketsSent *uint64
|
||||
rtcpPacketsInError *uint64
|
||||
}
|
||||
|
||||
func (cm *clientMedia) initialize() {
|
||||
cm.onPacketRTCP = func(rtcp.Packet) {}
|
||||
cm.bytesReceived = new(uint64)
|
||||
cm.bytesSent = new(uint64)
|
||||
cm.rtpPacketsInError = new(uint64)
|
||||
cm.rtcpPacketsReceived = new(uint64)
|
||||
cm.rtcpPacketsSent = new(uint64)
|
||||
cm.rtcpPacketsInError = new(uint64)
|
||||
|
||||
cm.formats = make(map[uint8]*clientFormat)
|
||||
|
||||
for _, forma := range cm.media.Formats {
|
||||
f := &clientFormat{
|
||||
cm: cm,
|
||||
format: forma,
|
||||
onPacketRTP: func(*rtp.Packet) {},
|
||||
}
|
||||
f.initialize()
|
||||
cm.formats[forma.PayloadType()] = f
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *clientMedia) close() {
|
||||
if cm.udpRTPListener != nil {
|
||||
cm.udpRTPListener.close()
|
||||
cm.udpRTCPListener.close()
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *clientMedia) createUDPListeners(
|
||||
multicastEnable bool,
|
||||
multicastSourceIP net.IP,
|
||||
rtpAddress string,
|
||||
rtcpAddress string,
|
||||
) error {
|
||||
if rtpAddress != ":0" {
|
||||
l1 := &clientUDPListener{
|
||||
c: cm.c,
|
||||
multicastEnable: multicastEnable,
|
||||
multicastSourceIP: multicastSourceIP,
|
||||
address: rtpAddress,
|
||||
}
|
||||
err := l1.initialize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l2 := &clientUDPListener{
|
||||
c: cm.c,
|
||||
multicastEnable: multicastEnable,
|
||||
multicastSourceIP: multicastSourceIP,
|
||||
address: rtcpAddress,
|
||||
}
|
||||
err = l2.initialize()
|
||||
if err != nil {
|
||||
l1.close()
|
||||
return err
|
||||
}
|
||||
|
||||
cm.udpRTPListener, cm.udpRTCPListener = l1, l2
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
cm.udpRTPListener, cm.udpRTCPListener, err = createUDPListenerPair(cm.c)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cm *clientMedia) start() {
|
||||
if cm.udpRTPListener != nil {
|
||||
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP
|
||||
|
||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
||||
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPRecord
|
||||
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPRecord
|
||||
} else {
|
||||
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPPlay
|
||||
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPPlay
|
||||
}
|
||||
} else {
|
||||
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueTCP
|
||||
|
||||
if cm.c.tcpCallbackByChannel == nil {
|
||||
cm.c.tcpCallbackByChannel = make(map[int]readFunc)
|
||||
}
|
||||
|
||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
||||
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPRecord
|
||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPRecord
|
||||
} else {
|
||||
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPPlay
|
||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPPlay
|
||||
}
|
||||
}
|
||||
|
||||
for _, ct := range cm.formats {
|
||||
ct.start()
|
||||
}
|
||||
|
||||
if cm.udpRTPListener != nil {
|
||||
cm.udpRTPListener.start()
|
||||
cm.udpRTCPListener.start()
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *clientMedia) stop() {
|
||||
if cm.udpRTPListener != nil {
|
||||
cm.udpRTPListener.stop()
|
||||
cm.udpRTCPListener.stop()
|
||||
}
|
||||
|
||||
for _, ct := range cm.formats {
|
||||
ct.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *clientMedia) findFormatWithSSRC(ssrc uint32) *clientFormat {
|
||||
for _, format := range cm.formats {
|
||||
stats := format.rtcpReceiver.Stats()
|
||||
if stats != nil && stats.RemoteSSRC == ssrc {
|
||||
return format
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *clientMedia) writePacketRTCPInQueueUDP(payload []byte) error {
|
||||
err := cm.udpRTCPListener.write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.AddUint64(cm.bytesSent, uint64(len(payload)))
|
||||
atomic.AddUint64(cm.rtcpPacketsSent, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *clientMedia) writePacketRTCPInQueueTCP(payload []byte) error {
|
||||
cm.c.tcpFrame.Channel = cm.tcpChannel + 1
|
||||
cm.c.tcpFrame.Payload = payload
|
||||
cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout))
|
||||
err := cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.AddUint64(cm.bytesSent, uint64(len(payload)))
|
||||
atomic.AddUint64(cm.rtcpPacketsSent, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTPTCPPlay(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
now := cm.c.timeNow()
|
||||
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
||||
|
||||
pkt := &rtp.Packet{}
|
||||
err := pkt.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
forma, ok := cm.formats[pkt.PayloadType]
|
||||
if !ok {
|
||||
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||
return false
|
||||
}
|
||||
|
||||
forma.readPacketRTPTCP(pkt)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTCPTCPPlay(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
now := cm.c.timeNow()
|
||||
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
||||
|
||||
if len(payload) > udpMaxPayloadSize {
|
||||
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||
return false
|
||||
}
|
||||
|
||||
packets, err := rtcp.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTCPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||
|
||||
for _, pkt := range packets {
|
||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||
format := cm.findFormatWithSSRC(sr.SSRC)
|
||||
if format != nil {
|
||||
format.rtcpReceiver.ProcessSenderReport(sr, now)
|
||||
}
|
||||
}
|
||||
|
||||
cm.onPacketRTCP(pkt)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTPTCPRecord(_ []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTCPTCPRecord(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
if len(payload) > udpMaxPayloadSize {
|
||||
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||
return false
|
||||
}
|
||||
|
||||
packets, err := rtcp.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTCPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||
|
||||
for _, pkt := range packets {
|
||||
cm.onPacketRTCP(pkt)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTPUDPPlay(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketTooBigUDP{})
|
||||
return false
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{}
|
||||
err := pkt.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
forma, ok := cm.formats[pkt.PayloadType]
|
||||
if !ok {
|
||||
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||
return false
|
||||
}
|
||||
|
||||
forma.readPacketRTPUDP(pkt)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTCPUDPPlay(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
||||
return false
|
||||
}
|
||||
|
||||
packets, err := rtcp.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTCPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
now := cm.c.timeNow()
|
||||
|
||||
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||
|
||||
for _, pkt := range packets {
|
||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||
format := cm.findFormatWithSSRC(sr.SSRC)
|
||||
if format != nil {
|
||||
format.rtcpReceiver.ProcessSenderReport(sr, now)
|
||||
}
|
||||
}
|
||||
|
||||
cm.onPacketRTCP(pkt)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTPUDPRecord(_ []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cm *clientMedia) readPacketRTCPUDPRecord(payload []byte) bool {
|
||||
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||
|
||||
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
||||
return false
|
||||
}
|
||||
|
||||
packets, err := rtcp.Unmarshal(payload)
|
||||
if err != nil {
|
||||
cm.onPacketRTCPDecodeError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||
|
||||
for _, pkt := range packets {
|
||||
cm.onPacketRTCP(pkt)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cm *clientMedia) onPacketRTPDecodeError(err error) {
|
||||
atomic.AddUint64(cm.rtpPacketsInError, 1)
|
||||
cm.c.OnDecodeError(err)
|
||||
}
|
||||
|
||||
func (cm *clientMedia) onPacketRTCPDecodeError(err error) {
|
||||
atomic.AddUint64(cm.rtcpPacketsInError, 1)
|
||||
cm.c.OnDecodeError(err)
|
||||
}
|
||||
79
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_reader.go
generated
vendored
Normal file
79
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_reader.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||
)
|
||||
|
||||
type clientReader struct {
|
||||
c *Client
|
||||
|
||||
mutex sync.Mutex
|
||||
allowInterleavedFrames bool
|
||||
|
||||
chResponse chan *base.Response
|
||||
chRequest chan *base.Request
|
||||
chError chan error
|
||||
}
|
||||
|
||||
func (r *clientReader) start() {
|
||||
r.chResponse = make(chan *base.Response)
|
||||
r.chRequest = make(chan *base.Request)
|
||||
r.chError = make(chan error)
|
||||
|
||||
go r.run()
|
||||
}
|
||||
|
||||
func (r *clientReader) setAllowInterleavedFrames(v bool) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
r.allowInterleavedFrames = v
|
||||
}
|
||||
|
||||
func (r *clientReader) wait() {
|
||||
for {
|
||||
select {
|
||||
case <-r.chError:
|
||||
return
|
||||
|
||||
case <-r.chResponse:
|
||||
case <-r.chRequest:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *clientReader) run() {
|
||||
r.chError <- r.runInner()
|
||||
}
|
||||
|
||||
func (r *clientReader) runInner() error {
|
||||
for {
|
||||
what, err := r.c.conn.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch what := what.(type) {
|
||||
case *base.Response:
|
||||
r.chResponse <- what
|
||||
|
||||
case *base.Request:
|
||||
r.chRequest <- what
|
||||
|
||||
case *base.InterleavedFrame:
|
||||
r.mutex.Lock()
|
||||
|
||||
if !r.allowInterleavedFrames {
|
||||
r.mutex.Unlock()
|
||||
return liberrors.ErrClientUnexpectedFrame{}
|
||||
}
|
||||
|
||||
if cb, ok := r.c.tcpCallbackByChannel[what.Channel]; ok {
|
||||
cb(what.Payload)
|
||||
}
|
||||
r.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
7
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_stats.go
generated
vendored
Normal file
7
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_stats.go
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package gortsplib
|
||||
|
||||
// ClientStats are client statistics
|
||||
type ClientStats struct {
|
||||
Conn StatsConn
|
||||
Session StatsSession
|
||||
}
|
||||
188
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_udp_listener.go
generated
vendored
Normal file
188
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/client_udp_listener.go
generated
vendored
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/multicast"
|
||||
)
|
||||
|
||||
func int64Ptr(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func randInRange(maxVal int) (int, error) {
|
||||
b := big.NewInt(int64(maxVal + 1))
|
||||
n, err := rand.Int(rand.Reader, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(n.Int64()), nil
|
||||
}
|
||||
|
||||
func createUDPListenerPair(c *Client) (*clientUDPListener, *clientUDPListener, error) {
|
||||
// choose two consecutive ports in range 65535-10000
|
||||
// RTP port must be even and RTCP port odd
|
||||
for {
|
||||
v, err := randInRange((65535 - 10000) / 2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rtpPort := v*2 + 10000
|
||||
rtcpPort := rtpPort + 1
|
||||
|
||||
rtpListener := &clientUDPListener{
|
||||
c: c,
|
||||
multicastEnable: false,
|
||||
multicastSourceIP: nil,
|
||||
address: net.JoinHostPort("", strconv.FormatInt(int64(rtpPort), 10)),
|
||||
}
|
||||
err = rtpListener.initialize()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rtcpListener := &clientUDPListener{
|
||||
c: c,
|
||||
multicastEnable: false,
|
||||
multicastSourceIP: nil,
|
||||
address: net.JoinHostPort("", strconv.FormatInt(int64(rtcpPort), 10)),
|
||||
}
|
||||
err = rtcpListener.initialize()
|
||||
if err != nil {
|
||||
rtpListener.close()
|
||||
continue
|
||||
}
|
||||
|
||||
return rtpListener, rtcpListener, nil
|
||||
}
|
||||
}
|
||||
|
||||
type packetConn interface {
|
||||
net.PacketConn
|
||||
SetReadBuffer(int) error
|
||||
}
|
||||
|
||||
type clientUDPListener struct {
|
||||
c *Client
|
||||
multicastEnable bool
|
||||
multicastSourceIP net.IP
|
||||
address string
|
||||
|
||||
pc packetConn
|
||||
readFunc readFunc
|
||||
readIP net.IP
|
||||
readPort int
|
||||
writeAddr *net.UDPAddr
|
||||
|
||||
running bool
|
||||
lastPacketTime *int64
|
||||
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) initialize() error {
|
||||
if u.multicastEnable {
|
||||
intf, err := multicast.InterfaceForSource(u.multicastSourceIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.pc, err = multicast.NewSingleConn(intf, u.address, u.c.ListenPacket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
tmp, err := u.c.ListenPacket(restrictNetwork("udp", u.address))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.pc = tmp.(*net.UDPConn)
|
||||
}
|
||||
|
||||
err := u.pc.SetReadBuffer(udpKernelReadBufferSize)
|
||||
if err != nil {
|
||||
u.pc.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
u.lastPacketTime = int64Ptr(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) close() {
|
||||
if u.running {
|
||||
u.stop()
|
||||
}
|
||||
u.pc.Close()
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) port() int {
|
||||
return u.pc.LocalAddr().(*net.UDPAddr).Port
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) start() {
|
||||
u.running = true
|
||||
u.pc.SetReadDeadline(time.Time{})
|
||||
u.done = make(chan struct{})
|
||||
go u.run()
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) stop() {
|
||||
u.pc.SetReadDeadline(time.Now())
|
||||
<-u.done
|
||||
u.running = false
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) run() {
|
||||
defer close(u.done)
|
||||
|
||||
var buf []byte
|
||||
|
||||
createNewBuffer := func() {
|
||||
buf = make([]byte, udpMaxPayloadSize+1)
|
||||
}
|
||||
|
||||
createNewBuffer()
|
||||
|
||||
for {
|
||||
n, addr, err := u.pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uaddr := addr.(*net.UDPAddr)
|
||||
|
||||
if !u.readIP.Equal(uaddr.IP) {
|
||||
continue
|
||||
}
|
||||
|
||||
// in case of anyPortEnable, store the port of the first packet we receive.
|
||||
// this reduces security issues
|
||||
if u.c.AnyPortEnable && u.readPort == 0 {
|
||||
u.readPort = uaddr.Port
|
||||
} else if u.readPort != uaddr.Port {
|
||||
continue
|
||||
}
|
||||
|
||||
now := u.c.timeNow()
|
||||
atomic.StoreInt64(u.lastPacketTime, now.Unix())
|
||||
|
||||
if u.readFunc(buf[:n]) {
|
||||
createNewBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *clientUDPListener) write(payload []byte) error {
|
||||
// no mutex is needed here since Write() has an internal lock.
|
||||
// https://github.com/golang/go/issues/27203#issuecomment-534386117
|
||||
u.pc.SetWriteDeadline(time.Now().Add(u.c.WriteTimeout))
|
||||
_, err := u.pc.WriteTo(payload, u.writeAddr)
|
||||
return err
|
||||
}
|
||||
9
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/constants.go
generated
vendored
Normal file
9
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/constants.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package gortsplib
|
||||
|
||||
const (
|
||||
// same size as GStreamer's rtspsrc
|
||||
udpKernelReadBufferSize = 0x80000
|
||||
|
||||
// 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header)
|
||||
udpMaxPayloadSize = 1472
|
||||
)
|
||||
11
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/empty_timer.go
generated
vendored
Normal file
11
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/empty_timer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package gortsplib
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func emptyTimer() *time.Timer {
|
||||
t := time.NewTimer(0)
|
||||
<-t.C
|
||||
return t
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/auth.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/auth.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package auth contains utilities to perform authentication.
|
||||
package auth
|
||||
17
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/nonce.go
generated
vendored
Normal file
17
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/nonce.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// GenerateNonce generates a nonce that can be used in Validate().
|
||||
func GenerateNonce() (string, error) {
|
||||
byts := make([]byte, 16)
|
||||
_, err := rand.Read(byts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(byts), nil
|
||||
}
|
||||
89
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/sender.go
generated
vendored
Normal file
89
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/sender.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||
)
|
||||
|
||||
// NewSender allocates a Sender.
|
||||
//
|
||||
// Deprecated: replaced by Sender.Initialize().
|
||||
func NewSender(wwwAuth base.HeaderValue, user string, pass string) (*Sender, error) {
|
||||
s := &Sender{
|
||||
WWWAuth: wwwAuth,
|
||||
User: user,
|
||||
Pass: pass,
|
||||
}
|
||||
err := s.Initialize()
|
||||
return s, err
|
||||
}
|
||||
|
||||
// Sender allows to send credentials.
|
||||
// It requires a WWW-Authenticate header (provided by the server)
|
||||
// and a set of credentials.
|
||||
type Sender struct {
|
||||
WWWAuth base.HeaderValue
|
||||
User string
|
||||
Pass string
|
||||
|
||||
authHeader *headers.Authenticate
|
||||
}
|
||||
|
||||
// Initialize initializes a Sender.
|
||||
func (se *Sender) Initialize() error {
|
||||
for _, v := range se.WWWAuth {
|
||||
var auth headers.Authenticate
|
||||
err := auth.Unmarshal(base.HeaderValue{v})
|
||||
if err != nil {
|
||||
continue // ignore unrecognized headers
|
||||
}
|
||||
|
||||
if se.authHeader == nil ||
|
||||
(auth.Algorithm != nil && *auth.Algorithm == headers.AuthAlgorithmSHA256) ||
|
||||
(se.authHeader.Method == headers.AuthMethodBasic) {
|
||||
se.authHeader = &auth
|
||||
}
|
||||
}
|
||||
|
||||
if se.authHeader == nil {
|
||||
return fmt.Errorf("no authentication methods available")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddAuthorization adds the Authorization header to a Request.
|
||||
func (se *Sender) AddAuthorization(req *base.Request) {
|
||||
urStr := req.URL.CloneWithoutCredentials().String()
|
||||
|
||||
h := headers.Authorization{
|
||||
Method: se.authHeader.Method,
|
||||
}
|
||||
|
||||
h.Username = se.User
|
||||
|
||||
if se.authHeader.Method == headers.AuthMethodBasic {
|
||||
h.BasicPass = se.Pass
|
||||
} else { // digest
|
||||
h.Realm = se.authHeader.Realm
|
||||
h.Nonce = se.authHeader.Nonce
|
||||
h.URI = urStr
|
||||
h.Algorithm = se.authHeader.Algorithm
|
||||
|
||||
if se.authHeader.Algorithm == nil || *se.authHeader.Algorithm == headers.AuthAlgorithmMD5 {
|
||||
h.Response = md5Hex(md5Hex(se.User+":"+se.authHeader.Realm+":"+se.Pass) + ":" +
|
||||
se.authHeader.Nonce + ":" + md5Hex(string(req.Method)+":"+urStr))
|
||||
} else { // sha256
|
||||
h.Response = sha256Hex(sha256Hex(se.User+":"+se.authHeader.Realm+":"+se.Pass) + ":" +
|
||||
se.authHeader.Nonce + ":" + sha256Hex(string(req.Method)+":"+urStr))
|
||||
}
|
||||
}
|
||||
|
||||
if req.Header == nil {
|
||||
req.Header = make(base.Header)
|
||||
}
|
||||
|
||||
req.Header["Authorization"] = h.Marshal()
|
||||
}
|
||||
33
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/validate.go
generated
vendored
Normal file
33
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/validate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
)
|
||||
|
||||
// ValidateMethod is a validation method.
|
||||
//
|
||||
// Deprecated: replaced by VerifyMethod
|
||||
type ValidateMethod = VerifyMethod
|
||||
|
||||
// validation methods.
|
||||
//
|
||||
// Deprecated.
|
||||
const (
|
||||
ValidateMethodBasic = VerifyMethodBasic
|
||||
ValidateMethodDigestMD5 = VerifyMethodDigestMD5
|
||||
ValidateMethodSHA256 = VerifyMethodDigestSHA256
|
||||
)
|
||||
|
||||
// Validate validates a request sent by a client.
|
||||
//
|
||||
// Deprecated: replaced by Verify.
|
||||
func Validate(
|
||||
req *base.Request,
|
||||
user string,
|
||||
pass string,
|
||||
methods []ValidateMethod,
|
||||
realm string,
|
||||
nonce string,
|
||||
) error {
|
||||
return Verify(req, user, pass, methods, realm, nonce)
|
||||
}
|
||||
135
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/verify.go
generated
vendored
Normal file
135
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/verify.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||
)
|
||||
|
||||
var reControlAttribute = regexp.MustCompile("^(.+/)trackID=[0-9]+$")
|
||||
|
||||
func md5Hex(in string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(in))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func sha256Hex(in string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(in))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func contains(list []VerifyMethod, item VerifyMethod) bool {
|
||||
for _, i := range list {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func urlMatches(expected string, received string, isSetup bool) bool {
|
||||
if received == expected {
|
||||
return true
|
||||
}
|
||||
|
||||
// in SETUP requests, VLC uses the base URL of the stream
|
||||
// instead of the URL of the track.
|
||||
// Strip the control attribute to obtain the URL of the stream.
|
||||
if isSetup {
|
||||
if m := reControlAttribute.FindStringSubmatch(expected); m != nil && received == m[1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// VerifyMethod is a validation method.
|
||||
type VerifyMethod int
|
||||
|
||||
// validation methods.
|
||||
const (
|
||||
VerifyMethodBasic VerifyMethod = iota
|
||||
VerifyMethodDigestMD5
|
||||
VerifyMethodDigestSHA256
|
||||
)
|
||||
|
||||
// Verify verifies a request sent by a client.
|
||||
func Verify(
|
||||
req *base.Request,
|
||||
user string,
|
||||
pass string,
|
||||
methods []VerifyMethod,
|
||||
realm string,
|
||||
nonce string,
|
||||
) error {
|
||||
if methods == nil {
|
||||
// disable VerifyMethodDigestSHA256 unless explicitly set
|
||||
// since it prevents FFmpeg from authenticating
|
||||
methods = []VerifyMethod{VerifyMethodBasic, VerifyMethodDigestMD5}
|
||||
}
|
||||
|
||||
var auth headers.Authorization
|
||||
err := auth.Unmarshal(req.Header["Authorization"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case auth.Method == headers.AuthMethodDigest &&
|
||||
(contains(methods, VerifyMethodDigestMD5) &&
|
||||
(auth.Algorithm == nil || *auth.Algorithm == headers.AuthAlgorithmMD5) ||
|
||||
contains(methods, VerifyMethodDigestSHA256) &&
|
||||
auth.Algorithm != nil && *auth.Algorithm == headers.AuthAlgorithmSHA256):
|
||||
if auth.Nonce != nonce {
|
||||
return fmt.Errorf("wrong nonce")
|
||||
}
|
||||
|
||||
if auth.Realm != realm {
|
||||
return fmt.Errorf("wrong realm")
|
||||
}
|
||||
|
||||
if auth.Username != user {
|
||||
return fmt.Errorf("authentication failed")
|
||||
}
|
||||
|
||||
if !urlMatches(req.URL.String(), auth.URI, req.Method == base.Setup) {
|
||||
return fmt.Errorf("wrong URL")
|
||||
}
|
||||
|
||||
var response string
|
||||
|
||||
if auth.Algorithm == nil || *auth.Algorithm == headers.AuthAlgorithmMD5 {
|
||||
response = md5Hex(md5Hex(user+":"+realm+":"+pass) +
|
||||
":" + nonce + ":" + md5Hex(string(req.Method)+":"+auth.URI))
|
||||
} else { // sha256
|
||||
response = sha256Hex(sha256Hex(user+":"+realm+":"+pass) +
|
||||
":" + nonce + ":" + sha256Hex(string(req.Method)+":"+auth.URI))
|
||||
}
|
||||
|
||||
if auth.Response != response {
|
||||
return fmt.Errorf("authentication failed")
|
||||
}
|
||||
|
||||
case auth.Method == headers.AuthMethodBasic && contains(methods, VerifyMethodBasic):
|
||||
if auth.Username != user {
|
||||
return fmt.Errorf("authentication failed")
|
||||
}
|
||||
|
||||
if auth.BasicPass != pass {
|
||||
return fmt.Errorf("authentication failed")
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("no supported authentication methods found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
51
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/www_authenticate.go
generated
vendored
Normal file
51
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/auth/www_authenticate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||
)
|
||||
|
||||
// GenerateWWWAuthenticate generates a WWW-Authenticate header.
|
||||
func GenerateWWWAuthenticate(methods []ValidateMethod, realm string, nonce string) base.HeaderValue {
|
||||
if methods == nil {
|
||||
// disable VerifyMethodDigestSHA256 unless explicitly set
|
||||
// since it prevents FFmpeg from authenticating
|
||||
methods = []VerifyMethod{VerifyMethodBasic, VerifyMethodDigestMD5}
|
||||
}
|
||||
|
||||
var ret base.HeaderValue
|
||||
|
||||
for _, m := range methods {
|
||||
var a base.HeaderValue
|
||||
|
||||
switch m {
|
||||
case ValidateMethodBasic:
|
||||
a = headers.Authenticate{
|
||||
Method: headers.AuthMethodBasic,
|
||||
Realm: realm,
|
||||
}.Marshal()
|
||||
|
||||
case ValidateMethodDigestMD5:
|
||||
aa := headers.AuthAlgorithmMD5
|
||||
a = headers.Authenticate{
|
||||
Method: headers.AuthMethodDigest,
|
||||
Realm: realm,
|
||||
Nonce: nonce,
|
||||
Algorithm: &aa,
|
||||
}.Marshal()
|
||||
|
||||
default: // sha256
|
||||
aa := headers.AuthAlgorithmSHA256
|
||||
a = headers.Authenticate{
|
||||
Method: headers.AuthMethodDigest,
|
||||
Realm: realm,
|
||||
Nonce: nonce,
|
||||
Algorithm: &aa,
|
||||
}.Marshal()
|
||||
}
|
||||
|
||||
ret = append(ret, a...)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
54
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/body.go
generated
vendored
Normal file
54
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/body.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
rtspMaxContentLength = 128 * 1024
|
||||
)
|
||||
|
||||
type body []byte
|
||||
|
||||
func (b *body) unmarshal(header Header, rb *bufio.Reader) error {
|
||||
cls, ok := header["Content-Length"]
|
||||
if !ok || len(cls) != 1 {
|
||||
*b = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
cl, err := strconv.ParseUint(cls[0], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid Content-Length")
|
||||
}
|
||||
|
||||
if cl > rtspMaxContentLength {
|
||||
return fmt.Errorf("Content-Length exceeds %d (it's %d)",
|
||||
rtspMaxContentLength, cl)
|
||||
}
|
||||
|
||||
*b = make([]byte, cl)
|
||||
n, err := io.ReadFull(rb, *b)
|
||||
if err != nil && n != len(*b) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b body) marshalSize() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b body) marshalTo(buf []byte) int {
|
||||
return copy(buf, b)
|
||||
}
|
||||
|
||||
func (b body) marshal() []byte {
|
||||
buf := make([]byte, b.marshalSize())
|
||||
b.marshalTo(buf)
|
||||
return buf
|
||||
}
|
||||
148
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/header.go
generated
vendored
Normal file
148
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/header.go
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
headerMaxEntryCount = 255
|
||||
headerMaxKeyLength = 512
|
||||
headerMaxValueLength = 2048
|
||||
)
|
||||
|
||||
func headerKeyNormalize(in string) string {
|
||||
switch strings.ToLower(in) {
|
||||
case "rtp-info":
|
||||
return "RTP-Info"
|
||||
|
||||
case "www-authenticate":
|
||||
return "WWW-Authenticate"
|
||||
|
||||
case "cseq":
|
||||
return "CSeq"
|
||||
}
|
||||
return http.CanonicalHeaderKey(in)
|
||||
}
|
||||
|
||||
// HeaderValue is an header value.
|
||||
type HeaderValue []string
|
||||
|
||||
// Header is a RTSP reader, present in both Requests and Responses.
|
||||
type Header map[string]HeaderValue
|
||||
|
||||
func (h *Header) unmarshal(br *bufio.Reader) error {
|
||||
*h = make(Header)
|
||||
count := 0
|
||||
|
||||
for {
|
||||
byt, err := br.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if byt == '\r' {
|
||||
err = readByteEqual(br, '\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if count >= headerMaxEntryCount {
|
||||
return fmt.Errorf("headers count exceeds %d", headerMaxEntryCount)
|
||||
}
|
||||
|
||||
key := string([]byte{byt})
|
||||
byts, err := readBytesLimited(br, ':', headerMaxKeyLength-1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("value is missing")
|
||||
}
|
||||
|
||||
key += string(byts[:len(byts)-1])
|
||||
key = headerKeyNormalize(key)
|
||||
|
||||
// https://tools.ietf.org/html/rfc2616
|
||||
// The field value MAY be preceded by any amount of spaces
|
||||
for {
|
||||
byt, err = br.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if byt != ' ' {
|
||||
break
|
||||
}
|
||||
}
|
||||
br.UnreadByte() //nolint:errcheck
|
||||
|
||||
byts, err = readBytesLimited(br, '\r', headerMaxValueLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val := string(byts[:len(byts)-1])
|
||||
|
||||
err = readByteEqual(br, '\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*h)[key] = append((*h)[key], val)
|
||||
count++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h Header) marshalSize() int {
|
||||
// sort headers by key
|
||||
// in order to obtain deterministic results
|
||||
keys := make([]string, len(h))
|
||||
for key := range h {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
n := 0
|
||||
|
||||
for _, key := range keys {
|
||||
for _, val := range h[key] {
|
||||
n += len([]byte(key + ": " + val + "\r\n"))
|
||||
}
|
||||
}
|
||||
|
||||
n += 2
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (h Header) marshalTo(buf []byte) int {
|
||||
// sort headers by key
|
||||
// in order to obtain deterministic results
|
||||
keys := make([]string, len(h))
|
||||
for key := range h {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
pos := 0
|
||||
|
||||
for _, key := range keys {
|
||||
for _, val := range h[key] {
|
||||
pos += copy(buf[pos:], []byte(key+": "+val+"\r\n"))
|
||||
}
|
||||
}
|
||||
|
||||
pos += copy(buf[pos:], []byte("\r\n"))
|
||||
|
||||
return pos
|
||||
}
|
||||
|
||||
func (h Header) marshal() []byte {
|
||||
buf := make([]byte, h.marshalSize())
|
||||
h.marshalTo(buf)
|
||||
return buf
|
||||
}
|
||||
72
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/interleaved_frame.go
generated
vendored
Normal file
72
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/interleaved_frame.go
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// InterleavedFrameMagicByte is the first byte of an interleaved frame.
|
||||
InterleavedFrameMagicByte = 0x24
|
||||
)
|
||||
|
||||
// InterleavedFrame is an interleaved frame, and allows to transfer binary data
|
||||
// within RTSP/TCP connections. It is used to send and receive RTP and RTCP packets with TCP.
|
||||
type InterleavedFrame struct {
|
||||
// channel ID
|
||||
Channel int
|
||||
|
||||
// payload
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal decodes an interleaved frame.
|
||||
func (f *InterleavedFrame) Unmarshal(br *bufio.Reader) error {
|
||||
var header [4]byte
|
||||
_, err := io.ReadFull(br, header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if header[0] != InterleavedFrameMagicByte {
|
||||
return fmt.Errorf("invalid magic byte (0x%.2x)", header[0])
|
||||
}
|
||||
|
||||
// it's useless to check payloadLen since it's limited to 65535
|
||||
payloadLen := int(uint16(header[2])<<8 | uint16(header[3]))
|
||||
|
||||
f.Channel = int(header[1])
|
||||
f.Payload = make([]byte, payloadLen)
|
||||
|
||||
_, err = io.ReadFull(br, f.Payload)
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of an InterleavedFrame.
|
||||
func (f InterleavedFrame) MarshalSize() int {
|
||||
return 4 + len(f.Payload)
|
||||
}
|
||||
|
||||
// MarshalTo writes an InterleavedFrame.
|
||||
func (f InterleavedFrame) MarshalTo(buf []byte) (int, error) {
|
||||
pos := 0
|
||||
|
||||
pos += copy(buf[pos:], []byte{0x24, byte(f.Channel)})
|
||||
|
||||
payloadLen := len(f.Payload)
|
||||
buf[pos] = byte(payloadLen >> 8)
|
||||
buf[pos+1] = byte(payloadLen)
|
||||
pos += 2
|
||||
|
||||
pos += copy(buf[pos:], f.Payload)
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Marshal writes an InterleavedFrame.
|
||||
func (f InterleavedFrame) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, f.MarshalSize())
|
||||
_, err := f.MarshalTo(buf)
|
||||
return buf, err
|
||||
}
|
||||
17
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/path.go
generated
vendored
Normal file
17
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/path.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathSplitQuery splits a path from a query.
|
||||
//
|
||||
// Deprecated: not useful anymore.
|
||||
func PathSplitQuery(pathAndQuery string) (string, string) {
|
||||
i := strings.Index(pathAndQuery, "?")
|
||||
if i >= 0 {
|
||||
return pathAndQuery[:i], pathAndQuery[i+1:]
|
||||
}
|
||||
|
||||
return pathAndQuery, ""
|
||||
}
|
||||
173
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/request.go
generated
vendored
Normal file
173
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// Package base contains the primitives of the RTSP protocol.
|
||||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
rtspProtocol10 = "RTSP/1.0"
|
||||
requestMaxMethodLength = 64
|
||||
requestMaxURLLength = 2048
|
||||
requestMaxProtocolLength = 64
|
||||
)
|
||||
|
||||
// Method is the method of a RTSP request.
|
||||
type Method string
|
||||
|
||||
// methods.
|
||||
const (
|
||||
Announce Method = "ANNOUNCE"
|
||||
Describe Method = "DESCRIBE"
|
||||
GetParameter Method = "GET_PARAMETER"
|
||||
Options Method = "OPTIONS"
|
||||
Pause Method = "PAUSE"
|
||||
Play Method = "PLAY"
|
||||
Record Method = "RECORD"
|
||||
Setup Method = "SETUP"
|
||||
SetParameter Method = "SET_PARAMETER"
|
||||
Teardown Method = "TEARDOWN"
|
||||
)
|
||||
|
||||
// Request is a RTSP request.
|
||||
type Request struct {
|
||||
// request method
|
||||
Method Method
|
||||
|
||||
// request url
|
||||
URL *URL
|
||||
|
||||
// map of header values
|
||||
Header Header
|
||||
|
||||
// optional body
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Unmarshal reads a request.
|
||||
func (req *Request) Unmarshal(br *bufio.Reader) error {
|
||||
byts, err := readBytesLimited(br, ' ', requestMaxMethodLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Method = Method(byts[:len(byts)-1])
|
||||
|
||||
if req.Method == "" {
|
||||
return fmt.Errorf("empty method")
|
||||
}
|
||||
|
||||
byts, err = readBytesLimited(br, ' ', requestMaxURLLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawURL := string(byts[:len(byts)-1])
|
||||
|
||||
if rawURL != "*" {
|
||||
var ur *URL
|
||||
ur, err = ParseURL(rawURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid URL (%v)", rawURL)
|
||||
}
|
||||
req.URL = ur
|
||||
} else {
|
||||
req.URL = nil
|
||||
}
|
||||
|
||||
byts, err = readBytesLimited(br, '\r', requestMaxProtocolLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proto := byts[:len(byts)-1]
|
||||
|
||||
if string(proto) != rtspProtocol10 {
|
||||
return fmt.Errorf("expected '%s', got %v", rtspProtocol10, proto)
|
||||
}
|
||||
|
||||
err = readByteEqual(br, '\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = req.Header.unmarshal(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = (*body)(&req.Body).unmarshal(req.Header, br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of a Request.
|
||||
func (req Request) MarshalSize() int {
|
||||
n := len(req.Method) + 1
|
||||
|
||||
if req.URL != nil {
|
||||
n += len(req.URL.CloneWithoutCredentials().String())
|
||||
} else {
|
||||
n++
|
||||
}
|
||||
|
||||
n += 1 + len(rtspProtocol10) + 2
|
||||
|
||||
if len(req.Body) != 0 {
|
||||
req.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(req.Body)), 10)}
|
||||
}
|
||||
|
||||
n += req.Header.marshalSize()
|
||||
|
||||
n += body(req.Body).marshalSize()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// MarshalTo writes a Request.
|
||||
func (req Request) MarshalTo(buf []byte) (int, error) {
|
||||
pos := 0
|
||||
|
||||
pos += copy(buf[pos:], []byte(req.Method))
|
||||
buf[pos] = ' '
|
||||
pos++
|
||||
|
||||
if req.URL != nil {
|
||||
pos += copy(buf[pos:], []byte(req.URL.CloneWithoutCredentials().String()))
|
||||
} else {
|
||||
pos += copy(buf[pos:], []byte("*"))
|
||||
}
|
||||
|
||||
buf[pos] = ' '
|
||||
pos++
|
||||
pos += copy(buf[pos:], rtspProtocol10)
|
||||
buf[pos] = '\r'
|
||||
pos++
|
||||
buf[pos] = '\n'
|
||||
pos++
|
||||
|
||||
if len(req.Body) != 0 {
|
||||
req.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(req.Body)), 10)}
|
||||
}
|
||||
|
||||
pos += req.Header.marshalTo(buf[pos:])
|
||||
|
||||
pos += body(req.Body).marshalTo(buf[pos:])
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Marshal writes a Request.
|
||||
func (req Request) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, req.MarshalSize())
|
||||
_, err := req.MarshalTo(buf)
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (req Request) String() string {
|
||||
buf, _ := req.Marshal()
|
||||
return string(buf)
|
||||
}
|
||||
254
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/response.go
generated
vendored
Normal file
254
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/response.go
generated
vendored
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// StatusCode is the status code of a RTSP response.
|
||||
type StatusCode int
|
||||
|
||||
// status codes.
|
||||
const (
|
||||
StatusContinue StatusCode = 100
|
||||
StatusOK StatusCode = 200
|
||||
StatusMovedPermanently StatusCode = 301
|
||||
StatusFound StatusCode = 302
|
||||
StatusSeeOther StatusCode = 303
|
||||
StatusNotModified StatusCode = 304
|
||||
StatusUseProxy StatusCode = 305
|
||||
StatusBadRequest StatusCode = 400
|
||||
StatusUnauthorized StatusCode = 401
|
||||
StatusPaymentRequired StatusCode = 402
|
||||
StatusForbidden StatusCode = 403
|
||||
StatusNotFound StatusCode = 404
|
||||
StatusMethodNotAllowed StatusCode = 405
|
||||
StatusNotAcceptable StatusCode = 406
|
||||
StatusProxyAuthRequired StatusCode = 407
|
||||
StatusRequestTimeout StatusCode = 408
|
||||
StatusGone StatusCode = 410
|
||||
StatusPreconditionFailed StatusCode = 412
|
||||
StatusRequestEntityTooLarge StatusCode = 413
|
||||
StatusRequestURITooLong StatusCode = 414
|
||||
StatusUnsupportedMediaType StatusCode = 415
|
||||
StatusParameterNotUnderstood StatusCode = 451
|
||||
StatusNotEnoughBandwidth StatusCode = 453
|
||||
StatusSessionNotFound StatusCode = 454
|
||||
StatusMethodNotValidInThisState StatusCode = 455
|
||||
StatusHeaderFieldNotValidForResource StatusCode = 456
|
||||
StatusInvalidRange StatusCode = 457
|
||||
StatusParameterIsReadOnly StatusCode = 458
|
||||
StatusAggregateOperationNotAllowed StatusCode = 459
|
||||
StatusOnlyAggregateOperationAllowed StatusCode = 460
|
||||
StatusUnsupportedTransport StatusCode = 461
|
||||
StatusDestinationUnreachable StatusCode = 462
|
||||
StatusDestinationProhibited StatusCode = 463
|
||||
StatusDataTransportNotReadyYet StatusCode = 464
|
||||
StatusNotificationReasonUnknown StatusCode = 465
|
||||
StatusKeyManagementError StatusCode = 466
|
||||
StatusConnectionAuthorizationRequired StatusCode = 470
|
||||
StatusConnectionCredentialsNotAccepted StatusCode = 471
|
||||
StatusFailureToEstablishSecureConnection StatusCode = 472
|
||||
StatusInternalServerError StatusCode = 500
|
||||
StatusNotImplemented StatusCode = 501
|
||||
StatusBadGateway StatusCode = 502
|
||||
StatusServiceUnavailable StatusCode = 503
|
||||
StatusGatewayTimeout StatusCode = 504
|
||||
StatusRTSPVersionNotSupported StatusCode = 505
|
||||
StatusOptionNotSupported StatusCode = 551
|
||||
StatusProxyUnavailable StatusCode = 553
|
||||
)
|
||||
|
||||
// StatusMessages contains the status messages associated with each status code.
|
||||
var StatusMessages = statusMessages
|
||||
|
||||
var statusMessages = map[StatusCode]string{
|
||||
StatusContinue: "Continue",
|
||||
|
||||
StatusOK: "OK",
|
||||
|
||||
StatusMovedPermanently: "Moved Permanently",
|
||||
StatusFound: "Found",
|
||||
StatusSeeOther: "See Other",
|
||||
StatusNotModified: "Not Modified",
|
||||
StatusUseProxy: "Use Proxy",
|
||||
|
||||
StatusBadRequest: "Bad Request",
|
||||
StatusUnauthorized: "Unauthorized",
|
||||
StatusPaymentRequired: "Payment Required",
|
||||
StatusForbidden: "Forbidden",
|
||||
StatusNotFound: "Not Found",
|
||||
StatusMethodNotAllowed: "Method Not Allowed",
|
||||
StatusNotAcceptable: "Not Acceptable",
|
||||
StatusProxyAuthRequired: "Proxy Auth Required",
|
||||
StatusRequestTimeout: "Request Timeout",
|
||||
StatusGone: "Gone",
|
||||
StatusPreconditionFailed: "Precondition Failed",
|
||||
StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
StatusRequestURITooLong: "Request URI Too Long",
|
||||
StatusUnsupportedMediaType: "Unsupported Media Type",
|
||||
StatusParameterNotUnderstood: "Parameter Not Understood",
|
||||
StatusNotEnoughBandwidth: "Not Enough Bandwidth",
|
||||
StatusSessionNotFound: "Session Not Found",
|
||||
StatusMethodNotValidInThisState: "Method Not Valid In This State",
|
||||
StatusHeaderFieldNotValidForResource: "Header Field Not Valid for Resource",
|
||||
StatusInvalidRange: "Invalid Range",
|
||||
StatusParameterIsReadOnly: "Parameter Is Read-Only",
|
||||
StatusAggregateOperationNotAllowed: "Aggregate Operation Not Allowed",
|
||||
StatusOnlyAggregateOperationAllowed: "Only Aggregate Operation Allowed",
|
||||
StatusUnsupportedTransport: "Unsupported Transport",
|
||||
StatusDestinationUnreachable: "Destination Unreachable",
|
||||
StatusDestinationProhibited: "Destination Prohibited",
|
||||
StatusDataTransportNotReadyYet: "Data Transport Not Ready Yet",
|
||||
StatusNotificationReasonUnknown: "Notification Reason Unknown",
|
||||
StatusKeyManagementError: "Key Management Error",
|
||||
StatusConnectionAuthorizationRequired: "Connection Authorization Required",
|
||||
StatusConnectionCredentialsNotAccepted: "Connection Credentials Not Accepted",
|
||||
StatusFailureToEstablishSecureConnection: "Failure to Establish Secure Connection",
|
||||
|
||||
StatusInternalServerError: "Internal Server Error",
|
||||
StatusNotImplemented: "Not Implemented",
|
||||
StatusBadGateway: "Bad Gateway",
|
||||
StatusServiceUnavailable: "Service Unavailable",
|
||||
StatusGatewayTimeout: "Gateway Timeout",
|
||||
StatusRTSPVersionNotSupported: "RTSP Version Not Supported",
|
||||
StatusOptionNotSupported: "Option Not Supported",
|
||||
StatusProxyUnavailable: "Proxy Unavailable",
|
||||
}
|
||||
|
||||
// Response is a RTSP response.
|
||||
type Response struct {
|
||||
// numeric status code
|
||||
StatusCode StatusCode
|
||||
|
||||
// status message
|
||||
StatusMessage string
|
||||
|
||||
// map of header values
|
||||
Header Header
|
||||
|
||||
// optional body
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Unmarshal reads a response.
|
||||
func (res *Response) Unmarshal(br *bufio.Reader) error {
|
||||
byts, err := readBytesLimited(br, ' ', 255)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proto := byts[:len(byts)-1]
|
||||
|
||||
if string(proto) != rtspProtocol10 {
|
||||
return fmt.Errorf("expected '%s', got %v", rtspProtocol10, proto)
|
||||
}
|
||||
|
||||
byts, err = readBytesLimited(br, ' ', 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statusCodeStr := string(byts[:len(byts)-1])
|
||||
|
||||
tmp, err := strconv.ParseUint(statusCodeStr, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse status code")
|
||||
}
|
||||
res.StatusCode = StatusCode(tmp)
|
||||
|
||||
byts, err = readBytesLimited(br, '\r', 255)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res.StatusMessage = string(byts[:len(byts)-1])
|
||||
|
||||
if len(res.StatusMessage) == 0 {
|
||||
return fmt.Errorf("empty status message")
|
||||
}
|
||||
|
||||
err = readByteEqual(br, '\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = res.Header.unmarshal(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = (*body)(&res.Body).unmarshal(res.Header, br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of a Response.
|
||||
func (res Response) MarshalSize() int {
|
||||
n := 0
|
||||
|
||||
if res.StatusMessage == "" {
|
||||
if status, ok := statusMessages[res.StatusCode]; ok {
|
||||
res.StatusMessage = status
|
||||
}
|
||||
}
|
||||
|
||||
n += len(rtspProtocol10) + 1 + len(strconv.FormatInt(int64(res.StatusCode), 10)) + 1 + len(res.StatusMessage) + 2
|
||||
|
||||
if len(res.Body) != 0 {
|
||||
res.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(res.Body)), 10)}
|
||||
}
|
||||
|
||||
n += res.Header.marshalSize()
|
||||
|
||||
n += body(res.Body).marshalSize()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// MarshalTo writes a Response.
|
||||
func (res Response) MarshalTo(buf []byte) (int, error) {
|
||||
if res.StatusMessage == "" {
|
||||
if status, ok := statusMessages[res.StatusCode]; ok {
|
||||
res.StatusMessage = status
|
||||
}
|
||||
}
|
||||
|
||||
pos := 0
|
||||
|
||||
pos += copy(buf[pos:], []byte(rtspProtocol10))
|
||||
buf[pos] = ' '
|
||||
pos++
|
||||
pos += copy(buf[pos:], []byte(strconv.FormatInt(int64(res.StatusCode), 10)))
|
||||
buf[pos] = ' '
|
||||
pos++
|
||||
pos += copy(buf[pos:], []byte(res.StatusMessage))
|
||||
buf[pos] = '\r'
|
||||
pos++
|
||||
buf[pos] = '\n'
|
||||
pos++
|
||||
|
||||
if len(res.Body) != 0 {
|
||||
res.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(res.Body)), 10)}
|
||||
}
|
||||
|
||||
pos += res.Header.marshalTo(buf[pos:])
|
||||
|
||||
pos += body(res.Body).marshalTo(buf[pos:])
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Marshal writes a Response.
|
||||
func (res Response) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, res.MarshalSize())
|
||||
_, err := res.MarshalTo(buf)
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (res Response) String() string {
|
||||
buf, _ := res.Marshal()
|
||||
return string(buf)
|
||||
}
|
||||
107
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/url.go
generated
vendored
Normal file
107
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/url.go
generated
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URL is a RTSP URL.
|
||||
// This is basically an HTTP URL with some additional functions to handle
|
||||
// control attributes.
|
||||
type URL url.URL
|
||||
|
||||
var escapeRegexp = regexp.MustCompile(`^(.+?)://(.*?)@(.*?)/(.*?)$`)
|
||||
|
||||
// ParseURL parses a RTSP URL.
|
||||
func ParseURL(s string) (*URL, error) {
|
||||
// https://github.com/golang/go/issues/30611
|
||||
m := escapeRegexp.FindStringSubmatch(s)
|
||||
if m != nil {
|
||||
m[3] = strings.ReplaceAll(m[3], "%25", "%")
|
||||
m[3] = strings.ReplaceAll(m[3], "%", "%25")
|
||||
s = m[1] + "://" + m[2] + "@" + m[3] + "/" + m[4]
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Scheme != "rtsp" && u.Scheme != "rtsps" {
|
||||
return nil, fmt.Errorf("unsupported scheme '%s'", u.Scheme)
|
||||
}
|
||||
|
||||
if u.Opaque != "" {
|
||||
return nil, fmt.Errorf("URLs with opaque data are not supported")
|
||||
}
|
||||
|
||||
if u.Fragment != "" {
|
||||
return nil, fmt.Errorf("URLs with fragments are not supported")
|
||||
}
|
||||
|
||||
return (*URL)(u), nil
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (u *URL) String() string {
|
||||
return (*url.URL)(u).String()
|
||||
}
|
||||
|
||||
// Clone clones a URL.
|
||||
func (u *URL) Clone() *URL {
|
||||
return (*URL)(&url.URL{
|
||||
Scheme: u.Scheme,
|
||||
User: u.User,
|
||||
Host: u.Host,
|
||||
Path: u.Path,
|
||||
RawPath: u.RawPath,
|
||||
ForceQuery: u.ForceQuery,
|
||||
RawQuery: u.RawQuery,
|
||||
})
|
||||
}
|
||||
|
||||
// CloneWithoutCredentials clones a URL without its credentials.
|
||||
func (u *URL) CloneWithoutCredentials() *URL {
|
||||
return (*URL)(&url.URL{
|
||||
Scheme: u.Scheme,
|
||||
Host: u.Host,
|
||||
Path: u.Path,
|
||||
RawPath: u.RawPath,
|
||||
ForceQuery: u.ForceQuery,
|
||||
RawQuery: u.RawQuery,
|
||||
})
|
||||
}
|
||||
|
||||
// RTSPPathAndQuery returns the path and query of a RTSP URL.
|
||||
//
|
||||
// Deprecated: not useful anymore.
|
||||
func (u *URL) RTSPPathAndQuery() (string, bool) {
|
||||
var pathAndQuery string
|
||||
if u.RawPath != "" {
|
||||
pathAndQuery = u.RawPath
|
||||
} else {
|
||||
pathAndQuery = u.Path
|
||||
}
|
||||
if u.RawQuery != "" {
|
||||
pathAndQuery += "?" + u.RawQuery
|
||||
}
|
||||
|
||||
return pathAndQuery, true
|
||||
}
|
||||
|
||||
// Hostname returns u.Host, stripping any valid port number if present.
|
||||
//
|
||||
// If the result is enclosed in square brackets, as literal IPv6 addresses are,
|
||||
// the square brackets are removed from the result.
|
||||
func (u *URL) Hostname() string {
|
||||
return (*url.URL)(u).Hostname()
|
||||
}
|
||||
|
||||
// Port returns the port part of u.Host, without the leading colon.
|
||||
//
|
||||
// If u.Host doesn't contain a valid numeric port, Port returns an empty string.
|
||||
func (u *URL) Port() string {
|
||||
return (*url.URL)(u).Port()
|
||||
}
|
||||
34
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/utils.go
generated
vendored
Normal file
34
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/base/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func readByteEqual(rb *bufio.Reader, cmp byte) error {
|
||||
byt, err := rb.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if byt != cmp {
|
||||
return fmt.Errorf("expected '%c', got '%c'", cmp, byt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readBytesLimited(rb *bufio.Reader, delim byte, n int) ([]byte, error) {
|
||||
for i := 1; i <= n; i++ {
|
||||
byts, err := rb.Peek(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if byts[len(byts)-1] == delim {
|
||||
rb.Discard(len(byts)) //nolint:errcheck
|
||||
return byts, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("buffer length exceeds %d", n)
|
||||
}
|
||||
54
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/bytecounter/bytecounter.go
generated
vendored
Normal file
54
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/bytecounter/bytecounter.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Package bytecounter contains a io.ReadWriter wrapper that allows to count read and written bytes.
|
||||
package bytecounter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ByteCounter is a io.ReadWriter wrapper that allows to count read and written bytes.
|
||||
type ByteCounter struct {
|
||||
rw io.ReadWriter
|
||||
received *uint64
|
||||
sent *uint64
|
||||
}
|
||||
|
||||
// New allocates a ByteCounter.
|
||||
func New(rw io.ReadWriter, received *uint64, sent *uint64) *ByteCounter {
|
||||
if received == nil {
|
||||
received = new(uint64)
|
||||
}
|
||||
if sent == nil {
|
||||
sent = new(uint64)
|
||||
}
|
||||
|
||||
return &ByteCounter{
|
||||
rw: rw,
|
||||
received: received,
|
||||
sent: sent,
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements io.ReadWriter.
|
||||
func (bc *ByteCounter) Read(p []byte) (int, error) {
|
||||
n, err := bc.rw.Read(p)
|
||||
atomic.AddUint64(bc.received, uint64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write implements io.ReadWriter.
|
||||
func (bc *ByteCounter) Write(p []byte) (int, error) {
|
||||
n, err := bc.rw.Write(p)
|
||||
atomic.AddUint64(bc.sent, uint64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BytesReceived returns the number of bytes received.
|
||||
func (bc *ByteCounter) BytesReceived() uint64 {
|
||||
return atomic.LoadUint64(bc.received)
|
||||
}
|
||||
|
||||
// BytesSent returns the number of bytes sent.
|
||||
func (bc *ByteCounter) BytesSent() uint64 {
|
||||
return atomic.LoadUint64(bc.sent)
|
||||
}
|
||||
105
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/conn/conn.go
generated
vendored
Normal file
105
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/conn/conn.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Package conn contains a RTSP connection implementation.
|
||||
package conn
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
)
|
||||
|
||||
const (
|
||||
readBufferSize = 4096
|
||||
)
|
||||
|
||||
// Conn is a RTSP connection.
|
||||
type Conn struct {
|
||||
w io.Writer
|
||||
br *bufio.Reader
|
||||
|
||||
// reuse interleaved frames. they should never be passed to secondary routines
|
||||
fr base.InterleavedFrame
|
||||
}
|
||||
|
||||
// NewConn allocates a Conn.
|
||||
func NewConn(rw io.ReadWriter) *Conn {
|
||||
return &Conn{
|
||||
w: rw,
|
||||
br: bufio.NewReaderSize(rw, readBufferSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads a Request, a Response or an Interleaved frame.
|
||||
func (c *Conn) Read() (interface{}, error) {
|
||||
for {
|
||||
byts, err := c.br.Peek(2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if byts[0] == base.InterleavedFrameMagicByte {
|
||||
return c.ReadInterleavedFrame()
|
||||
}
|
||||
|
||||
if byts[0] == 'R' && byts[1] == 'T' {
|
||||
return c.ReadResponse()
|
||||
}
|
||||
|
||||
if (byts[0] == 'A' && byts[1] == 'N') ||
|
||||
(byts[0] == 'D' && byts[1] == 'E') ||
|
||||
(byts[0] == 'G' && byts[1] == 'E') ||
|
||||
(byts[0] == 'O' && byts[1] == 'P') ||
|
||||
(byts[0] == 'P' && byts[1] == 'A') ||
|
||||
(byts[0] == 'P' && byts[1] == 'L') ||
|
||||
(byts[0] == 'R' && byts[1] == 'E') ||
|
||||
(byts[0] == 'S' && byts[1] == 'E') ||
|
||||
(byts[0] == 'T' && byts[1] == 'E') {
|
||||
return c.ReadRequest()
|
||||
}
|
||||
|
||||
if _, err := c.br.Discard(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadRequest reads a Request.
|
||||
func (c *Conn) ReadRequest() (*base.Request, error) {
|
||||
var req base.Request
|
||||
err := req.Unmarshal(c.br)
|
||||
return &req, err
|
||||
}
|
||||
|
||||
// ReadResponse reads a Response.
|
||||
func (c *Conn) ReadResponse() (*base.Response, error) {
|
||||
var res base.Response
|
||||
err := res.Unmarshal(c.br)
|
||||
return &res, err
|
||||
}
|
||||
|
||||
// ReadInterleavedFrame reads a InterleavedFrame.
|
||||
func (c *Conn) ReadInterleavedFrame() (*base.InterleavedFrame, error) {
|
||||
err := c.fr.Unmarshal(c.br)
|
||||
return &c.fr, err
|
||||
}
|
||||
|
||||
// WriteRequest writes a request.
|
||||
func (c *Conn) WriteRequest(req *base.Request) error {
|
||||
buf, _ := req.Marshal()
|
||||
_, err := c.w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteResponse writes a response.
|
||||
func (c *Conn) WriteResponse(res *base.Response) error {
|
||||
buf, _ := res.Marshal()
|
||||
_, err := c.w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteInterleavedFrame writes an interleaved frame.
|
||||
func (c *Conn) WriteInterleavedFrame(fr *base.InterleavedFrame, buf []byte) error {
|
||||
n, _ := fr.MarshalTo(buf)
|
||||
_, err := c.w.Write(buf[:n])
|
||||
return err
|
||||
}
|
||||
218
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/description/media.go
generated
vendored
Normal file
218
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/description/media.go
generated
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
// Package description contains objects to describe streams.
|
||||
package description
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
psdp "github.com/pion/sdp/v3"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||
)
|
||||
|
||||
func getAttribute(attributes []psdp.Attribute, key string) string {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == key {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isBackChannel(attributes []psdp.Attribute) bool {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == "sendonly" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sortedKeys(fmtp map[string]string) []string {
|
||||
keys := make([]string, len(fmtp))
|
||||
i := 0
|
||||
for key := range fmtp {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func isAlphaNumeric(v string) bool {
|
||||
for _, r := range v {
|
||||
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MediaType is the type of a media stream.
|
||||
type MediaType string
|
||||
|
||||
// media types.
|
||||
const (
|
||||
MediaTypeVideo MediaType = "video"
|
||||
MediaTypeAudio MediaType = "audio"
|
||||
MediaTypeApplication MediaType = "application"
|
||||
)
|
||||
|
||||
// Media is a media stream.
|
||||
// It contains one or more formats.
|
||||
type Media struct {
|
||||
// Media type.
|
||||
Type MediaType
|
||||
|
||||
// Media ID (optional).
|
||||
ID string
|
||||
|
||||
// Whether this media is a back channel.
|
||||
IsBackChannel bool
|
||||
|
||||
// Control attribute.
|
||||
Control string
|
||||
|
||||
// Formats contained into the media.
|
||||
Formats []format.Format
|
||||
}
|
||||
|
||||
// Unmarshal decodes the media from the SDP format.
|
||||
func (m *Media) Unmarshal(md *psdp.MediaDescription) error {
|
||||
m.Type = MediaType(md.MediaName.Media)
|
||||
|
||||
m.ID = getAttribute(md.Attributes, "mid")
|
||||
if m.ID != "" && !isAlphaNumeric(m.ID) {
|
||||
return fmt.Errorf("invalid mid: %v", m.ID)
|
||||
}
|
||||
|
||||
m.IsBackChannel = isBackChannel(md.Attributes)
|
||||
m.Control = getAttribute(md.Attributes, "control")
|
||||
|
||||
m.Formats = nil
|
||||
|
||||
for _, payloadType := range md.MediaName.Formats {
|
||||
format, err := format.Unmarshal(md, payloadType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Formats = append(m.Formats, format)
|
||||
}
|
||||
|
||||
if m.Formats == nil {
|
||||
return fmt.Errorf("no formats found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal encodes the media in SDP format.
|
||||
func (m Media) Marshal() *psdp.MediaDescription {
|
||||
md := &psdp.MediaDescription{
|
||||
MediaName: psdp.MediaName{
|
||||
Media: string(m.Type),
|
||||
Protos: []string{"RTP", "AVP"},
|
||||
},
|
||||
}
|
||||
|
||||
if m.ID != "" {
|
||||
md.Attributes = append(md.Attributes, psdp.Attribute{
|
||||
Key: "mid",
|
||||
Value: m.ID,
|
||||
})
|
||||
}
|
||||
|
||||
if m.IsBackChannel {
|
||||
md.Attributes = append(md.Attributes, psdp.Attribute{
|
||||
Key: "sendonly",
|
||||
})
|
||||
}
|
||||
|
||||
md.Attributes = append(md.Attributes, psdp.Attribute{
|
||||
Key: "control",
|
||||
Value: m.Control,
|
||||
})
|
||||
|
||||
for _, forma := range m.Formats {
|
||||
typ := strconv.FormatUint(uint64(forma.PayloadType()), 10)
|
||||
md.MediaName.Formats = append(md.MediaName.Formats, typ)
|
||||
|
||||
rtpmap := forma.RTPMap()
|
||||
if rtpmap != "" {
|
||||
md.Attributes = append(md.Attributes, psdp.Attribute{
|
||||
Key: "rtpmap",
|
||||
Value: typ + " " + rtpmap,
|
||||
})
|
||||
}
|
||||
|
||||
fmtp := forma.FMTP()
|
||||
if len(fmtp) != 0 {
|
||||
tmp := make([]string, len(fmtp))
|
||||
for i, key := range sortedKeys(fmtp) {
|
||||
tmp[i] = key + "=" + fmtp[key]
|
||||
}
|
||||
|
||||
md.Attributes = append(md.Attributes, psdp.Attribute{
|
||||
Key: "fmtp",
|
||||
Value: typ + " " + strings.Join(tmp, "; "),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return md
|
||||
}
|
||||
|
||||
// URL returns the absolute URL of the media.
|
||||
func (m Media) URL(contentBase *base.URL) (*base.URL, error) {
|
||||
if contentBase == nil {
|
||||
return nil, fmt.Errorf("Content-Base header not provided")
|
||||
}
|
||||
|
||||
// no control attribute, use base URL
|
||||
if m.Control == "" {
|
||||
return contentBase, nil
|
||||
}
|
||||
|
||||
// control attribute contains an absolute path
|
||||
if strings.HasPrefix(m.Control, "rtsp://") ||
|
||||
strings.HasPrefix(m.Control, "rtsps://") {
|
||||
ur, err := base.ParseURL(m.Control)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy host and credentials
|
||||
ur.Host = contentBase.Host
|
||||
ur.User = contentBase.User
|
||||
return ur, nil
|
||||
}
|
||||
|
||||
// control attribute contains a relative control attribute
|
||||
// insert the control attribute at the end of the URL
|
||||
// if there's a query, insert it after the query
|
||||
// otherwise insert it after the path
|
||||
strURL := contentBase.String()
|
||||
if m.Control[0] != '?' && m.Control[0] != '/' && !strings.HasSuffix(strURL, "/") {
|
||||
strURL += "/"
|
||||
}
|
||||
|
||||
ur, _ := base.ParseURL(strURL + m.Control)
|
||||
return ur, nil
|
||||
}
|
||||
|
||||
// FindFormat finds a certain format among all the formats in the media.
|
||||
func (m Media) FindFormat(forma interface{}) bool {
|
||||
for _, formak := range m.Formats {
|
||||
if reflect.TypeOf(formak) == reflect.TypeOf(forma).Elem() {
|
||||
reflect.ValueOf(forma).Elem().Set(reflect.ValueOf(formak))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
164
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/description/session.go
generated
vendored
Normal file
164
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/description/session.go
generated
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package description
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
psdp "github.com/pion/sdp/v3"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
|
||||
)
|
||||
|
||||
func atLeastOneHasMID(medias []*Media) bool {
|
||||
for _, media := range medias {
|
||||
if media.ID != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func atLeastOneDoesntHaveMID(medias []*Media) bool {
|
||||
for _, media := range medias {
|
||||
if media.ID == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasMediaWithID(medias []*Media, id string) bool {
|
||||
for _, media := range medias {
|
||||
if media.ID == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SessionFECGroup is a FEC group.
|
||||
type SessionFECGroup []string
|
||||
|
||||
// Session is the description of a RTSP stream.
|
||||
type Session struct {
|
||||
// Base URL of the stream (read only).
|
||||
BaseURL *base.URL
|
||||
|
||||
// Title of the stream (optional).
|
||||
Title string
|
||||
|
||||
// FEC groups (RFC5109).
|
||||
FECGroups []SessionFECGroup
|
||||
|
||||
// Media streams.
|
||||
Medias []*Media
|
||||
}
|
||||
|
||||
// FindFormat finds a certain format among all the formats in all the medias of the stream.
|
||||
// If the format is found, it is inserted into forma, and its media is returned.
|
||||
func (d *Session) FindFormat(forma interface{}) *Media {
|
||||
for _, media := range d.Medias {
|
||||
ok := media.FindFormat(forma)
|
||||
if ok {
|
||||
return media
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes the description from SDP.
|
||||
func (d *Session) Unmarshal(ssd *sdp.SessionDescription) error {
|
||||
d.Title = string(ssd.SessionName)
|
||||
if d.Title == " " {
|
||||
d.Title = ""
|
||||
}
|
||||
|
||||
d.Medias = make([]*Media, len(ssd.MediaDescriptions))
|
||||
|
||||
for i, md := range ssd.MediaDescriptions {
|
||||
var m Media
|
||||
err := m.Unmarshal(md)
|
||||
if err != nil {
|
||||
return fmt.Errorf("media %d is invalid: %w", i+1, err)
|
||||
}
|
||||
|
||||
if m.ID != "" && hasMediaWithID(d.Medias[:i], m.ID) {
|
||||
return fmt.Errorf("duplicate media IDs")
|
||||
}
|
||||
|
||||
d.Medias[i] = &m
|
||||
}
|
||||
|
||||
if atLeastOneHasMID(d.Medias) && atLeastOneDoesntHaveMID(d.Medias) {
|
||||
return fmt.Errorf("media IDs sent partially")
|
||||
}
|
||||
|
||||
for _, attr := range ssd.Attributes {
|
||||
if attr.Key == "group" && strings.HasPrefix(attr.Value, "FEC ") {
|
||||
group := SessionFECGroup(strings.Split(attr.Value[len("FEC "):], " "))
|
||||
|
||||
for _, id := range group {
|
||||
if !hasMediaWithID(d.Medias, id) {
|
||||
return fmt.Errorf("FEC group points to an invalid media ID: %v", id)
|
||||
}
|
||||
}
|
||||
|
||||
d.FECGroups = append(d.FECGroups, group)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal encodes the description in SDP.
|
||||
func (d Session) Marshal(multicast bool) ([]byte, error) {
|
||||
var sessionName psdp.SessionName
|
||||
if d.Title != "" {
|
||||
sessionName = psdp.SessionName(d.Title)
|
||||
} else {
|
||||
// RFC 4566: If a session has no meaningful name, the
|
||||
// value "s= " SHOULD be used (i.e., a single space as the session name).
|
||||
sessionName = psdp.SessionName(" ")
|
||||
}
|
||||
|
||||
var address string
|
||||
if multicast {
|
||||
address = "224.1.0.0"
|
||||
} else {
|
||||
address = "0.0.0.0"
|
||||
}
|
||||
|
||||
sout := &sdp.SessionDescription{
|
||||
SessionName: sessionName,
|
||||
Origin: psdp.Origin{
|
||||
Username: "-",
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
UnicastAddress: "127.0.0.1",
|
||||
},
|
||||
// required by Darwin Streaming Server
|
||||
ConnectionInformation: &psdp.ConnectionInformation{
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
Address: &psdp.Address{Address: address},
|
||||
},
|
||||
TimeDescriptions: []psdp.TimeDescription{
|
||||
{Timing: psdp.Timing{StartTime: 0, StopTime: 0}},
|
||||
},
|
||||
MediaDescriptions: make([]*psdp.MediaDescription, len(d.Medias)),
|
||||
}
|
||||
|
||||
for i, media := range d.Medias {
|
||||
sout.MediaDescriptions[i] = media.Marshal()
|
||||
}
|
||||
|
||||
for _, group := range d.FECGroups {
|
||||
sout.Attributes = append(sout.Attributes, psdp.Attribute{
|
||||
Key: "group",
|
||||
Value: "FEC " + strings.Join(group, " "),
|
||||
})
|
||||
}
|
||||
|
||||
return sout.Marshal()
|
||||
}
|
||||
101
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/ac3.go
generated
vendored
Normal file
101
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/ac3.go
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3"
|
||||
)
|
||||
|
||||
// AC3 is the RTP format for the AC-3 codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc4184
|
||||
type AC3 struct {
|
||||
PayloadTyp uint8
|
||||
SampleRate int
|
||||
ChannelCount int
|
||||
}
|
||||
|
||||
func (f *AC3) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
tmp := strings.SplitN(ctx.clock, "/", 2)
|
||||
|
||||
tmp1, err := strconv.ParseUint(tmp[0], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SampleRate = int(tmp1)
|
||||
|
||||
if len(tmp) >= 2 {
|
||||
tmp1, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ChannelCount = int(tmp1)
|
||||
} else {
|
||||
// RFC4184: If the "channels" parameter
|
||||
// is omitted, a default maximum value of 6 is implied.
|
||||
f.ChannelCount = 6
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *AC3) Codec() string {
|
||||
return "AC-3"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *AC3) ClockRate() int {
|
||||
return f.SampleRate
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *AC3) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *AC3) RTPMap() string {
|
||||
return "AC3/" + strconv.FormatInt(int64(f.SampleRate), 10) +
|
||||
"/" + strconv.FormatInt(int64(f.ChannelCount), 10)
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *AC3) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *AC3) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *AC3) CreateDecoder() (*rtpac3.Decoder, error) {
|
||||
d := &rtpac3.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *AC3) CreateEncoder() (*rtpac3.Encoder, error) {
|
||||
e := &rtpac3.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
124
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/av1.go
generated
vendored
Normal file
124
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/av1.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package format //nolint:dupl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1"
|
||||
)
|
||||
|
||||
// AV1 is the RTP format for the AV1 codec.
|
||||
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
|
||||
type AV1 struct {
|
||||
PayloadTyp uint8
|
||||
LevelIdx *int
|
||||
Profile *int
|
||||
Tier *int
|
||||
}
|
||||
|
||||
func (f *AV1) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "level-idx":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid level-idx: %v", val)
|
||||
}
|
||||
|
||||
v2 := int(n)
|
||||
f.LevelIdx = &v2
|
||||
|
||||
case "profile":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid profile: %v", val)
|
||||
}
|
||||
|
||||
v2 := int(n)
|
||||
f.Profile = &v2
|
||||
|
||||
case "tier":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid tier: %v", val)
|
||||
}
|
||||
|
||||
v2 := int(n)
|
||||
f.Tier = &v2
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *AV1) Codec() string {
|
||||
return "AV1"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *AV1) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *AV1) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *AV1) RTPMap() string {
|
||||
return "AV1/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *AV1) FMTP() map[string]string {
|
||||
fmtp := make(map[string]string)
|
||||
|
||||
if f.LevelIdx != nil {
|
||||
fmtp["level-idx"] = strconv.FormatInt(int64(*f.LevelIdx), 10)
|
||||
}
|
||||
if f.Profile != nil {
|
||||
fmtp["profile"] = strconv.FormatInt(int64(*f.Profile), 10)
|
||||
}
|
||||
if f.Tier != nil {
|
||||
fmtp["tier"] = strconv.FormatInt(int64(*f.Tier), 10)
|
||||
}
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *AV1) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *AV1) CreateDecoder() (*rtpav1.Decoder, error) {
|
||||
d := &rtpav1.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *AV1) CreateEncoder() (*rtpav1.Encoder, error) {
|
||||
e := &rtpav1.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
234
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/format.go
generated
vendored
Normal file
234
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
// Package format contains RTP format definitions, decoders and encoders.
|
||||
package format
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
psdp "github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
smartPayloadTypeRegexp = regexp.MustCompile("^smart/[0-9]/[0-9]+$")
|
||||
smartRtpmapRegexp = regexp.MustCompile("^([0-9]+) (.+)/[0-9]+$")
|
||||
)
|
||||
|
||||
func replaceSmartPayloadType(payloadType string, attributes []psdp.Attribute) string {
|
||||
re1 := smartPayloadTypeRegexp.FindStringSubmatch(payloadType)
|
||||
if re1 != nil {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == "rtpmap" {
|
||||
re2 := smartRtpmapRegexp.FindStringSubmatch(attr.Value)
|
||||
if re2 != nil {
|
||||
return re2[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return payloadType
|
||||
}
|
||||
|
||||
func getFormatAttribute(attributes []psdp.Attribute, payloadType uint8, key string) string {
|
||||
for _, attr := range attributes {
|
||||
if attr.Key == key {
|
||||
v := strings.TrimSpace(attr.Value)
|
||||
if parts := strings.SplitN(v, " ", 2); len(parts) == 2 {
|
||||
if tmp, err := strconv.ParseUint(parts[0], 10, 8); err == nil && uint8(tmp) == payloadType {
|
||||
return parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getCodecAndClock(rtpMap string) (string, string) {
|
||||
parts2 := strings.SplitN(rtpMap, "/", 2)
|
||||
if len(parts2) != 2 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return strings.ToLower(parts2[0]), parts2[1]
|
||||
}
|
||||
|
||||
func decodeFMTP(enc string) map[string]string {
|
||||
if enc == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make(map[string]string)
|
||||
|
||||
for _, kv := range strings.Split(enc, ";") {
|
||||
kv = strings.Trim(kv, " ")
|
||||
|
||||
if len(kv) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tmp := strings.SplitN(kv, "=", 2)
|
||||
if len(tmp) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
ret[strings.ToLower(tmp[0])] = tmp[1]
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
type unmarshalContext struct {
|
||||
mediaType string
|
||||
payloadType uint8
|
||||
clock string
|
||||
codec string
|
||||
rtpMap string
|
||||
fmtp map[string]string
|
||||
}
|
||||
|
||||
// Format is a media format.
|
||||
// It defines the payload type of RTP packets and how to encode/decode them.
|
||||
type Format interface {
|
||||
unmarshal(ctx *unmarshalContext) error
|
||||
|
||||
// Codec returns the codec name.
|
||||
Codec() string
|
||||
|
||||
// ClockRate returns the clock rate.
|
||||
ClockRate() int
|
||||
|
||||
// PayloadType returns the payload type.
|
||||
PayloadType() uint8
|
||||
|
||||
// RTPMap returns the rtpmap attribute.
|
||||
RTPMap() string
|
||||
|
||||
// FMTP returns the fmtp attribute.
|
||||
FMTP() map[string]string
|
||||
|
||||
// PTSEqualsDTS checks whether PTS is equal to DTS in RTP packets.
|
||||
PTSEqualsDTS(*rtp.Packet) bool
|
||||
}
|
||||
|
||||
// Unmarshal decodes a format from a media description.
|
||||
func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error) {
|
||||
mediaType := md.MediaName.Media
|
||||
payloadTypeStr = replaceSmartPayloadType(payloadTypeStr, md.Attributes)
|
||||
|
||||
tmp, err := strconv.ParseUint(payloadTypeStr, 10, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payloadType := uint8(tmp)
|
||||
|
||||
rtpMap := getFormatAttribute(md.Attributes, payloadType, "rtpmap")
|
||||
fmtp := decodeFMTP(getFormatAttribute(md.Attributes, payloadType, "fmtp"))
|
||||
codec, clock := getCodecAndClock(rtpMap)
|
||||
|
||||
format := func() Format {
|
||||
switch {
|
||||
/*
|
||||
* dynamic payload types
|
||||
**/
|
||||
|
||||
// video
|
||||
|
||||
case codec == "av1" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &AV1{}
|
||||
|
||||
case codec == "vp9" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &VP9{}
|
||||
|
||||
case codec == "vp8" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &VP8{}
|
||||
|
||||
case codec == "h265" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &H265{}
|
||||
|
||||
case codec == "h264" && clock == "90000" && ((payloadType >= 96 && payloadType <= 127) || payloadType == 35):
|
||||
return &H264{}
|
||||
|
||||
case codec == "mp4v-es" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &MPEG4Video{}
|
||||
|
||||
// audio
|
||||
|
||||
case codec == "opus", codec == "multiopus" && payloadType >= 96 && payloadType <= 127:
|
||||
return &Opus{}
|
||||
|
||||
case codec == "vorbis" && payloadType >= 96 && payloadType <= 127:
|
||||
return &Vorbis{}
|
||||
|
||||
case (codec == "mpeg4-generic" || codec == "mp4a-latm") && payloadType >= 96 && payloadType <= 127:
|
||||
return &MPEG4Audio{}
|
||||
|
||||
case codec == "ac3" && payloadType >= 96 && payloadType <= 127:
|
||||
return &AC3{}
|
||||
|
||||
case codec == "speex" && payloadType >= 96 && payloadType <= 127:
|
||||
return &Speex{}
|
||||
|
||||
case (codec == "g726-16" ||
|
||||
codec == "g726-24" ||
|
||||
codec == "g726-32" ||
|
||||
codec == "g726-40" ||
|
||||
codec == "aal2-g726-16" ||
|
||||
codec == "aal2-g726-24" ||
|
||||
codec == "aal2-g726-32" ||
|
||||
codec == "aal2-g726-40") && clock == "8000" && payloadType >= 96 && payloadType <= 127:
|
||||
return &G726{}
|
||||
|
||||
case codec == "pcma", codec == "pcmu" && payloadType >= 96 && payloadType <= 127:
|
||||
return &G711{}
|
||||
|
||||
case codec == "l8", codec == "l16", codec == "l24" && payloadType >= 96 && payloadType <= 127:
|
||||
return &LPCM{}
|
||||
|
||||
/*
|
||||
* static payload types
|
||||
**/
|
||||
|
||||
// video
|
||||
|
||||
case payloadType == 32:
|
||||
return &MPEG1Video{}
|
||||
|
||||
case payloadType == 26:
|
||||
return &MJPEG{}
|
||||
|
||||
case payloadType == 33:
|
||||
return &MPEGTS{}
|
||||
|
||||
// audio
|
||||
|
||||
case payloadType == 14:
|
||||
return &MPEG1Audio{}
|
||||
|
||||
case payloadType == 9:
|
||||
return &G722{}
|
||||
|
||||
case payloadType == 0, payloadType == 8:
|
||||
return &G711{}
|
||||
|
||||
case payloadType == 10, payloadType == 11:
|
||||
return &LPCM{}
|
||||
}
|
||||
|
||||
return &Generic{}
|
||||
}()
|
||||
|
||||
err = format.unmarshal(&unmarshalContext{
|
||||
mediaType: mediaType,
|
||||
payloadType: payloadType,
|
||||
clock: clock,
|
||||
codec: codec,
|
||||
rtpMap: rtpMap,
|
||||
fmtp: fmtp,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return format, nil
|
||||
}
|
||||
134
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g711.go
generated
vendored
Normal file
134
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g711.go
generated
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm"
|
||||
)
|
||||
|
||||
// G711 is the RTP format for the G711 codec, encoded with mu-law or A-law.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3551
|
||||
type G711 struct {
|
||||
PayloadTyp uint8
|
||||
MULaw bool
|
||||
SampleRate int
|
||||
ChannelCount int
|
||||
}
|
||||
|
||||
func (f *G711) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
if ctx.payloadType == 0 {
|
||||
f.MULaw = true
|
||||
f.SampleRate = 8000
|
||||
f.ChannelCount = 1
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.payloadType == 8 {
|
||||
f.MULaw = false
|
||||
f.SampleRate = 8000
|
||||
f.ChannelCount = 1
|
||||
return nil
|
||||
}
|
||||
|
||||
f.MULaw = (ctx.codec == "pcmu")
|
||||
|
||||
tmp := strings.SplitN(ctx.clock, "/", 2)
|
||||
|
||||
tmp1, err := strconv.ParseUint(tmp[0], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SampleRate = int(tmp1)
|
||||
|
||||
if len(tmp) >= 2 {
|
||||
tmp1, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ChannelCount = int(tmp1)
|
||||
} else {
|
||||
f.ChannelCount = 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *G711) Codec() string {
|
||||
return "G711"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *G711) ClockRate() int {
|
||||
return f.SampleRate
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *G711) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *G711) RTPMap() string {
|
||||
ret := ""
|
||||
|
||||
if f.MULaw {
|
||||
ret += "PCMU"
|
||||
} else {
|
||||
ret += "PCMA"
|
||||
}
|
||||
|
||||
ret += "/" + strconv.FormatInt(int64(f.SampleRate), 10)
|
||||
|
||||
if f.ChannelCount != 1 {
|
||||
ret += "/" + strconv.FormatInt(int64(f.ChannelCount), 10)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *G711) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *G711) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *G711) CreateDecoder() (*rtplpcm.Decoder, error) {
|
||||
d := &rtplpcm.Decoder{
|
||||
BitDepth: 8,
|
||||
ChannelCount: f.ChannelCount,
|
||||
}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *G711) CreateEncoder() (*rtplpcm.Encoder, error) {
|
||||
e := &rtplpcm.Encoder{
|
||||
PayloadType: f.PayloadType(),
|
||||
BitDepth: 8,
|
||||
ChannelCount: f.ChannelCount,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
76
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g722.go
generated
vendored
Normal file
76
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g722.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio"
|
||||
)
|
||||
|
||||
// G722 is the RTP format for the G722 codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3551
|
||||
type G722 struct {
|
||||
// in Go, empty structs share the same pointer,
|
||||
// therefore they cannot be used as map keys
|
||||
// or in equality operations. Prevent this.
|
||||
unused int //nolint:unused
|
||||
}
|
||||
|
||||
func (f *G722) unmarshal(_ *unmarshalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *G722) Codec() string {
|
||||
return "G722"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *G722) ClockRate() int {
|
||||
return 8000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *G722) PayloadType() uint8 {
|
||||
return 9
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *G722) RTPMap() string {
|
||||
return "G722/8000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *G722) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *G722) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
||||
d := &rtpsimpleaudio.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *G722) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
||||
e := &rtpsimpleaudio.Encoder{
|
||||
PayloadType: 9,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
73
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g726.go
generated
vendored
Normal file
73
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/g726.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// G726 is the RTP format for the G726 codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3551
|
||||
type G726 struct {
|
||||
PayloadTyp uint8
|
||||
BitRate int
|
||||
BigEndian bool
|
||||
}
|
||||
|
||||
func (f *G726) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
if strings.HasPrefix(ctx.codec, "aal2-") {
|
||||
f.BigEndian = true
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(ctx.codec, "-16"):
|
||||
f.BitRate = 16
|
||||
case strings.HasSuffix(ctx.codec, "-24"):
|
||||
f.BitRate = 24
|
||||
case strings.HasSuffix(ctx.codec, "-32"):
|
||||
f.BitRate = 32
|
||||
default:
|
||||
f.BitRate = 40
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *G726) Codec() string {
|
||||
return "G726"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *G726) ClockRate() int {
|
||||
return 8000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *G726) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *G726) RTPMap() string {
|
||||
codec := ""
|
||||
|
||||
if f.BigEndian {
|
||||
codec += "AAL2-"
|
||||
}
|
||||
|
||||
return codec + "G726-" + strconv.FormatInt(int64(f.BitRate), 10) + "/8000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *G726) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *G726) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
111
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/generic.go
generated
vendored
Normal file
111
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
func findClockRate(payloadType uint8, rtpMap string, isApplication bool) (int, error) {
|
||||
// get clock rate from payload type
|
||||
// https://en.wikipedia.org/wiki/RTP_payload_formats
|
||||
switch payloadType {
|
||||
case 0, 1, 2, 3, 4, 5, 7, 8, 9, 12, 13, 15, 18:
|
||||
return 8000, nil
|
||||
|
||||
case 6:
|
||||
return 16000, nil
|
||||
|
||||
case 10, 11:
|
||||
return 44100, nil
|
||||
|
||||
case 14, 25, 26, 28, 31, 32, 33, 34:
|
||||
return 90000, nil
|
||||
|
||||
case 16:
|
||||
return 11025, nil
|
||||
|
||||
case 17:
|
||||
return 22050, nil
|
||||
}
|
||||
|
||||
// get clock rate from rtpmap
|
||||
// https://tools.ietf.org/html/rfc4566
|
||||
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
|
||||
if rtpMap != "" {
|
||||
if tmp := strings.Split(rtpMap, "/"); len(tmp) >= 2 {
|
||||
v, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
}
|
||||
|
||||
// application format without clock rate.
|
||||
// do not throw an error, but return zero, that disables RTCP sender and receiver reports.
|
||||
if isApplication || rtpMap != "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("clock rate not found")
|
||||
}
|
||||
|
||||
// Generic is a generic RTP format.
|
||||
type Generic struct {
|
||||
PayloadTyp uint8
|
||||
RTPMa string
|
||||
FMT map[string]string
|
||||
|
||||
// clock rate of the format. Filled when calling Init().
|
||||
ClockRat int
|
||||
}
|
||||
|
||||
// Init computes the clock rate of the format. It is mandatory to call it.
|
||||
func (f *Generic) Init() error {
|
||||
var err error
|
||||
f.ClockRat, err = findClockRate(f.PayloadTyp, f.RTPMa, true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *Generic) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
f.RTPMa = ctx.rtpMap
|
||||
f.FMT = ctx.fmtp
|
||||
|
||||
var err error
|
||||
f.ClockRat, err = findClockRate(f.PayloadTyp, f.RTPMa, ctx.mediaType == "application")
|
||||
return err
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *Generic) Codec() string {
|
||||
return "Generic"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *Generic) ClockRate() int {
|
||||
return f.ClockRat
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *Generic) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *Generic) RTPMap() string {
|
||||
return f.RTPMa
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *Generic) FMTP() map[string]string {
|
||||
return f.FMT
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *Generic) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
227
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/h264.go
generated
vendored
Normal file
227
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/h264.go
generated
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph264"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
||||
)
|
||||
|
||||
// H264 is the RTP format for the H264 codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6184
|
||||
type H264 struct {
|
||||
PayloadTyp uint8
|
||||
SPS []byte
|
||||
PPS []byte
|
||||
PacketizationMode int
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *H264) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "sprop-parameter-sets":
|
||||
tmp := strings.Split(val, ",")
|
||||
if len(tmp) >= 2 {
|
||||
sps, err := base64.StdEncoding.DecodeString(tmp[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-parameter-sets (%v)", val)
|
||||
}
|
||||
|
||||
// some cameras ship parameters with Annex-B prefix
|
||||
sps = bytes.TrimPrefix(sps, []byte{0, 0, 0, 1})
|
||||
|
||||
pps, err := base64.StdEncoding.DecodeString(tmp[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-parameter-sets (%v)", val)
|
||||
}
|
||||
|
||||
// some cameras ship parameters with Annex-B prefix
|
||||
pps = bytes.TrimPrefix(pps, []byte{0, 0, 0, 1})
|
||||
|
||||
var spsp h264.SPS
|
||||
err = spsp.Unmarshal(sps)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
f.SPS = sps
|
||||
f.PPS = pps
|
||||
}
|
||||
|
||||
case "packetization-mode":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid packetization-mode (%v)", val)
|
||||
}
|
||||
|
||||
f.PacketizationMode = int(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *H264) Codec() string {
|
||||
return "H264"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *H264) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *H264) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *H264) RTPMap() string {
|
||||
return "H264/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *H264) FMTP() map[string]string {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
|
||||
fmtp := make(map[string]string)
|
||||
|
||||
if f.PacketizationMode != 0 {
|
||||
fmtp["packetization-mode"] = strconv.FormatInt(int64(f.PacketizationMode), 10)
|
||||
}
|
||||
|
||||
var tmp []string
|
||||
if f.SPS != nil {
|
||||
tmp = append(tmp, base64.StdEncoding.EncodeToString(f.SPS))
|
||||
}
|
||||
if f.PPS != nil {
|
||||
tmp = append(tmp, base64.StdEncoding.EncodeToString(f.PPS))
|
||||
}
|
||||
if tmp != nil {
|
||||
fmtp["sprop-parameter-sets"] = strings.Join(tmp, ",")
|
||||
}
|
||||
if len(f.SPS) >= 4 {
|
||||
fmtp["profile-level-id"] = strings.ToUpper(hex.EncodeToString(f.SPS[1:4]))
|
||||
}
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *H264) PTSEqualsDTS(pkt *rtp.Packet) bool {
|
||||
if len(pkt.Payload) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := h264.NALUType(pkt.Payload[0] & 0x1F)
|
||||
|
||||
switch typ {
|
||||
case h264.NALUTypeIDR, h264.NALUTypeSPS, h264.NALUTypePPS:
|
||||
return true
|
||||
|
||||
case 24: // STAP-A
|
||||
payload := pkt.Payload[1:]
|
||||
|
||||
for {
|
||||
if len(payload) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
size := uint16(payload[0])<<8 | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if size == 0 || int(size) > len(payload) {
|
||||
return false
|
||||
}
|
||||
|
||||
var nalu []byte
|
||||
nalu, payload = payload[:size], payload[size:]
|
||||
|
||||
typ = h264.NALUType(nalu[0] & 0x1F)
|
||||
switch typ {
|
||||
case h264.NALUTypeIDR, h264.NALUTypeSPS, h264.NALUTypePPS:
|
||||
return true
|
||||
}
|
||||
|
||||
if len(payload) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case 28: // FU-A
|
||||
if len(pkt.Payload) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
start := pkt.Payload[1] >> 7
|
||||
if start != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := h264.NALUType(pkt.Payload[1] & 0x1F)
|
||||
switch typ {
|
||||
case h264.NALUTypeIDR, h264.NALUTypeSPS, h264.NALUTypePPS:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *H264) CreateDecoder() (*rtph264.Decoder, error) {
|
||||
d := &rtph264.Decoder{
|
||||
PacketizationMode: f.PacketizationMode,
|
||||
}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *H264) CreateEncoder() (*rtph264.Encoder, error) {
|
||||
e := &rtph264.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
PacketizationMode: f.PacketizationMode,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// SafeSetParams sets the codec parameters.
|
||||
func (f *H264) SafeSetParams(sps []byte, pps []byte) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
f.SPS = sps
|
||||
f.PPS = pps
|
||||
}
|
||||
|
||||
// SafeParams returns the codec parameters.
|
||||
func (f *H264) SafeParams() ([]byte, []byte) {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
return f.SPS, f.PPS
|
||||
}
|
||||
240
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/h265.go
generated
vendored
Normal file
240
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/h265.go
generated
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph265"
|
||||
)
|
||||
|
||||
// H265 is the RTP format for the H265 codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7798
|
||||
type H265 struct {
|
||||
PayloadTyp uint8
|
||||
VPS []byte
|
||||
SPS []byte
|
||||
PPS []byte
|
||||
MaxDONDiff int
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *H265) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "sprop-vps":
|
||||
var err error
|
||||
f.VPS, err = base64.StdEncoding.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-vps (%v)", ctx.fmtp)
|
||||
}
|
||||
|
||||
// some cameras ship parameters with Annex-B prefix
|
||||
f.VPS = bytes.TrimPrefix(f.VPS, []byte{0, 0, 0, 1})
|
||||
|
||||
case "sprop-sps":
|
||||
var err error
|
||||
f.SPS, err = base64.StdEncoding.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-sps (%v)", ctx.fmtp)
|
||||
}
|
||||
|
||||
// some cameras ship parameters with Annex-B prefix
|
||||
f.SPS = bytes.TrimPrefix(f.SPS, []byte{0, 0, 0, 1})
|
||||
|
||||
var spsp h265.SPS
|
||||
err = spsp.Unmarshal(f.SPS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid SPS: %w", err)
|
||||
}
|
||||
|
||||
case "sprop-pps":
|
||||
var err error
|
||||
f.PPS, err = base64.StdEncoding.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-pps (%v)", ctx.fmtp)
|
||||
}
|
||||
|
||||
// some cameras ship parameters with Annex-B prefix
|
||||
f.PPS = bytes.TrimPrefix(f.PPS, []byte{0, 0, 0, 1})
|
||||
|
||||
var ppsp h265.PPS
|
||||
err = ppsp.Unmarshal(f.PPS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid PPS: %w", err)
|
||||
}
|
||||
|
||||
case "sprop-max-don-diff":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid sprop-max-don-diff (%v)", ctx.fmtp)
|
||||
}
|
||||
f.MaxDONDiff = int(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *H265) Codec() string {
|
||||
return "H265"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *H265) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *H265) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *H265) RTPMap() string {
|
||||
return "H265/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *H265) FMTP() map[string]string {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
|
||||
fmtp := make(map[string]string)
|
||||
if f.VPS != nil {
|
||||
fmtp["sprop-vps"] = base64.StdEncoding.EncodeToString(f.VPS)
|
||||
}
|
||||
if f.SPS != nil {
|
||||
fmtp["sprop-sps"] = base64.StdEncoding.EncodeToString(f.SPS)
|
||||
}
|
||||
if f.PPS != nil {
|
||||
fmtp["sprop-pps"] = base64.StdEncoding.EncodeToString(f.PPS)
|
||||
}
|
||||
if f.MaxDONDiff != 0 {
|
||||
fmtp["sprop-max-don-diff"] = strconv.FormatInt(int64(f.MaxDONDiff), 10)
|
||||
}
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *H265) PTSEqualsDTS(pkt *rtp.Packet) bool {
|
||||
if len(pkt.Payload) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := h265.NALUType((pkt.Payload[0] >> 1) & 0b111111)
|
||||
|
||||
switch typ {
|
||||
case h265.NALUType_IDR_W_RADL, h265.NALUType_IDR_N_LP, h265.NALUType_CRA_NUT,
|
||||
h265.NALUType_VPS_NUT, h265.NALUType_SPS_NUT, h265.NALUType_PPS_NUT:
|
||||
return true
|
||||
|
||||
case h265.NALUType_AggregationUnit:
|
||||
if len(pkt.Payload) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
payload := pkt.Payload[2:]
|
||||
|
||||
for {
|
||||
size := uint16(payload[0])<<8 | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if size == 0 || int(size) > len(payload) {
|
||||
return false
|
||||
}
|
||||
|
||||
var nalu []byte
|
||||
nalu, payload = payload[:size], payload[size:]
|
||||
|
||||
typ = h265.NALUType((nalu[0] >> 1) & 0b111111)
|
||||
switch typ {
|
||||
case h265.NALUType_IDR_W_RADL, h265.NALUType_IDR_N_LP, h265.NALUType_CRA_NUT,
|
||||
h265.NALUType_VPS_NUT, h265.NALUType_SPS_NUT, h265.NALUType_PPS_NUT:
|
||||
return true
|
||||
}
|
||||
|
||||
if len(payload) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if len(payload) < 2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
case h265.NALUType_FragmentationUnit:
|
||||
if len(pkt.Payload) < 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
start := pkt.Payload[2] >> 7
|
||||
if start != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := h265.NALUType(pkt.Payload[2] & 0b111111)
|
||||
switch typ {
|
||||
case h265.NALUType_IDR_W_RADL, h265.NALUType_IDR_N_LP, h265.NALUType_CRA_NUT,
|
||||
h265.NALUType_VPS_NUT, h265.NALUType_SPS_NUT, h265.NALUType_PPS_NUT:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *H265) CreateDecoder() (*rtph265.Decoder, error) {
|
||||
d := &rtph265.Decoder{
|
||||
MaxDONDiff: f.MaxDONDiff,
|
||||
}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *H265) CreateEncoder() (*rtph265.Encoder, error) {
|
||||
e := &rtph265.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
MaxDONDiff: f.MaxDONDiff,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// SafeSetParams sets the codec parameters.
|
||||
func (f *H265) SafeSetParams(vps []byte, sps []byte, pps []byte) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
f.VPS = vps
|
||||
f.SPS = sps
|
||||
f.PPS = pps
|
||||
}
|
||||
|
||||
// SafeParams returns the codec parameters.
|
||||
func (f *H265) SafeParams() ([]byte, []byte, []byte) {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
return f.VPS, f.SPS, f.PPS
|
||||
}
|
||||
143
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/lpcm.go
generated
vendored
Normal file
143
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/lpcm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm"
|
||||
)
|
||||
|
||||
// LPCM is the RTP format for the LPCM codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3551
|
||||
type LPCM struct {
|
||||
PayloadTyp uint8
|
||||
BitDepth int
|
||||
SampleRate int
|
||||
ChannelCount int
|
||||
}
|
||||
|
||||
func (f *LPCM) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
if ctx.payloadType == 10 {
|
||||
f.BitDepth = 16
|
||||
f.SampleRate = 44100
|
||||
f.ChannelCount = 2
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.payloadType == 11 {
|
||||
f.BitDepth = 16
|
||||
f.SampleRate = 44100
|
||||
f.ChannelCount = 1
|
||||
return nil
|
||||
}
|
||||
|
||||
switch ctx.codec {
|
||||
case "l8":
|
||||
f.BitDepth = 8
|
||||
|
||||
case "l16":
|
||||
f.BitDepth = 16
|
||||
|
||||
case "l24":
|
||||
f.BitDepth = 24
|
||||
}
|
||||
|
||||
tmp := strings.SplitN(ctx.clock, "/", 2)
|
||||
|
||||
tmp1, err := strconv.ParseUint(tmp[0], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SampleRate = int(tmp1)
|
||||
|
||||
if len(tmp) >= 2 {
|
||||
tmp1, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ChannelCount = int(tmp1)
|
||||
} else {
|
||||
f.ChannelCount = 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *LPCM) Codec() string {
|
||||
return "LPCM"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *LPCM) ClockRate() int {
|
||||
return f.SampleRate
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *LPCM) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *LPCM) RTPMap() string {
|
||||
var codec string
|
||||
switch f.BitDepth {
|
||||
case 8:
|
||||
codec = "L8"
|
||||
|
||||
case 16:
|
||||
codec = "L16"
|
||||
|
||||
case 24:
|
||||
codec = "L24"
|
||||
}
|
||||
|
||||
return codec + "/" + strconv.FormatInt(int64(f.SampleRate), 10) +
|
||||
"/" + strconv.FormatInt(int64(f.ChannelCount), 10)
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *LPCM) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *LPCM) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *LPCM) CreateDecoder() (*rtplpcm.Decoder, error) {
|
||||
d := &rtplpcm.Decoder{
|
||||
BitDepth: f.BitDepth,
|
||||
ChannelCount: f.ChannelCount,
|
||||
}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *LPCM) CreateEncoder() (*rtplpcm.Encoder, error) {
|
||||
e := &rtplpcm.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
BitDepth: f.BitDepth,
|
||||
ChannelCount: f.ChannelCount,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mjpeg.go
generated
vendored
Normal file
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mjpeg.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package format //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg"
|
||||
)
|
||||
|
||||
// MJPEG is the RTP format for the Motion-JPEG codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||
type MJPEG struct {
|
||||
// in Go, empty structs share the same pointer,
|
||||
// therefore they cannot be used as map keys
|
||||
// or in equality operations. Prevent this.
|
||||
unused int //nolint:unused
|
||||
}
|
||||
|
||||
func (f *MJPEG) unmarshal(_ *unmarshalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MJPEG) Codec() string {
|
||||
return "M-JPEG"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MJPEG) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MJPEG) PayloadType() uint8 {
|
||||
return 26
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MJPEG) RTPMap() string {
|
||||
return "JPEG/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MJPEG) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MJPEG) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MJPEG) CreateDecoder() (*rtpmjpeg.Decoder, error) {
|
||||
d := &rtpmjpeg.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MJPEG) CreateEncoder() (*rtpmjpeg.Encoder, error) {
|
||||
e := &rtpmjpeg.Encoder{}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg1_audio.go
generated
vendored
Normal file
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg1_audio.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package format //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio"
|
||||
)
|
||||
|
||||
// MPEG1Audio is the RTP format for a MPEG-1/2 Audio codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type MPEG1Audio struct {
|
||||
// in Go, empty structs share the same pointer,
|
||||
// therefore they cannot be used as map keys
|
||||
// or in equality operations. Prevent this.
|
||||
unused int //nolint:unused
|
||||
}
|
||||
|
||||
func (f *MPEG1Audio) unmarshal(_ *unmarshalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MPEG1Audio) Codec() string {
|
||||
return "MPEG-1/2 Audio"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MPEG1Audio) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MPEG1Audio) PayloadType() uint8 {
|
||||
return 14
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MPEG1Audio) RTPMap() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MPEG1Audio) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MPEG1Audio) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MPEG1Audio) CreateDecoder() (*rtpmpeg1audio.Decoder, error) {
|
||||
d := &rtpmpeg1audio.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MPEG1Audio) CreateEncoder() (*rtpmpeg1audio.Encoder, error) {
|
||||
e := &rtpmpeg1audio.Encoder{}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg1_video.go
generated
vendored
Normal file
74
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg1_video.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package format //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video"
|
||||
)
|
||||
|
||||
// MPEG1Video is the RTP format for a MPEG-1/2 Video codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type MPEG1Video struct {
|
||||
// in Go, empty structs share the same pointer,
|
||||
// therefore they cannot be used as map keys
|
||||
// or in equality operations. Prevent this.
|
||||
unused int //nolint:unused
|
||||
}
|
||||
|
||||
func (f *MPEG1Video) unmarshal(_ *unmarshalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MPEG1Video) Codec() string {
|
||||
return "MPEG-1/2 Video"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MPEG1Video) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MPEG1Video) PayloadType() uint8 {
|
||||
return 32
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MPEG1Video) RTPMap() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MPEG1Video) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MPEG1Video) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MPEG1Video) CreateDecoder() (*rtpmpeg1video.Decoder, error) {
|
||||
d := &rtpmpeg1video.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MPEG1Video) CreateEncoder() (*rtpmpeg1video.Encoder, error) {
|
||||
e := &rtpmpeg1video.Encoder{}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
343
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg4_audio.go
generated
vendored
Normal file
343
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg4_audio.go
generated
vendored
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio"
|
||||
)
|
||||
|
||||
// MPEG4Audio is the RTP format for a MPEG-4 Audio codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
|
||||
type MPEG4Audio struct {
|
||||
// payload type of packets.
|
||||
PayloadTyp uint8
|
||||
|
||||
// use LATM format (RFC6416) instead of generic format (RFC3640).
|
||||
LATM bool
|
||||
|
||||
// profile level ID.
|
||||
ProfileLevelID int
|
||||
|
||||
// generic only
|
||||
Config *mpeg4audio.Config
|
||||
SizeLength int
|
||||
IndexLength int
|
||||
IndexDeltaLength int
|
||||
|
||||
// LATM only
|
||||
Bitrate *int
|
||||
CPresent bool
|
||||
StreamMuxConfig *mpeg4audio.StreamMuxConfig
|
||||
SBREnabled *bool
|
||||
}
|
||||
|
||||
func (f *MPEG4Audio) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
f.LATM = (ctx.codec != "mpeg4-generic")
|
||||
|
||||
if !f.LATM {
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "streamtype":
|
||||
if val != "5" { // AudioStream in ISO 14496-1
|
||||
return fmt.Errorf("streamtype of AAC must be 5")
|
||||
}
|
||||
|
||||
case "mode":
|
||||
if strings.ToLower(val) != "aac-hbr" && strings.ToLower(val) != "aac_hbr" {
|
||||
return fmt.Errorf("unsupported AAC mode: %v", val)
|
||||
}
|
||||
|
||||
case "profile-level-id":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid profile-level-id: %v", val)
|
||||
}
|
||||
|
||||
f.ProfileLevelID = int(tmp)
|
||||
|
||||
case "config":
|
||||
enc, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid AAC config: %v", val)
|
||||
}
|
||||
|
||||
f.Config = &mpeg4audio.Config{}
|
||||
err = f.Config.Unmarshal(enc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid AAC config: %v", val)
|
||||
}
|
||||
|
||||
case "sizelength":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil || n > 100 {
|
||||
return fmt.Errorf("invalid AAC SizeLength: %v", val)
|
||||
}
|
||||
f.SizeLength = int(n)
|
||||
|
||||
case "indexlength":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil || n > 100 {
|
||||
return fmt.Errorf("invalid AAC IndexLength: %v", val)
|
||||
}
|
||||
f.IndexLength = int(n)
|
||||
|
||||
case "indexdeltalength":
|
||||
n, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil || n > 100 {
|
||||
return fmt.Errorf("invalid AAC IndexDeltaLength: %v", val)
|
||||
}
|
||||
f.IndexDeltaLength = int(n)
|
||||
}
|
||||
}
|
||||
|
||||
if f.Config == nil {
|
||||
return fmt.Errorf("config is missing")
|
||||
}
|
||||
|
||||
if f.SizeLength == 0 {
|
||||
return fmt.Errorf("sizelength is missing")
|
||||
}
|
||||
} else {
|
||||
// default value set by specification
|
||||
f.ProfileLevelID = 30
|
||||
f.CPresent = true
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "profile-level-id":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid profile-level-id: %v", val)
|
||||
}
|
||||
|
||||
f.ProfileLevelID = int(tmp)
|
||||
|
||||
case "bitrate":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid bitrate: %v", val)
|
||||
}
|
||||
|
||||
v := int(tmp)
|
||||
f.Bitrate = &v
|
||||
|
||||
case "cpresent":
|
||||
f.CPresent = (val == "1")
|
||||
|
||||
case "config":
|
||||
enc, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid AAC config: %v", val)
|
||||
}
|
||||
|
||||
f.StreamMuxConfig = &mpeg4audio.StreamMuxConfig{}
|
||||
err = f.StreamMuxConfig.Unmarshal(enc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid AAC config: %w", err)
|
||||
}
|
||||
|
||||
case "sbr-enabled":
|
||||
v := (val == "1")
|
||||
f.SBREnabled = &v
|
||||
}
|
||||
}
|
||||
|
||||
if f.CPresent {
|
||||
if f.StreamMuxConfig != nil {
|
||||
return fmt.Errorf("config and cpresent can't be used at the same time")
|
||||
}
|
||||
} else {
|
||||
if f.StreamMuxConfig == nil {
|
||||
return fmt.Errorf("config is missing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MPEG4Audio) Codec() string {
|
||||
return "MPEG-4 Audio"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MPEG4Audio) ClockRate() int {
|
||||
if !f.LATM {
|
||||
return f.Config.SampleRate
|
||||
}
|
||||
if f.CPresent {
|
||||
return 16000
|
||||
}
|
||||
return f.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig.SampleRate
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MPEG4Audio) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MPEG4Audio) RTPMap() string {
|
||||
if !f.LATM {
|
||||
sampleRate := f.Config.SampleRate
|
||||
if f.Config.ExtensionSampleRate != 0 {
|
||||
sampleRate = f.Config.ExtensionSampleRate
|
||||
}
|
||||
|
||||
channelCount := f.Config.ChannelCount
|
||||
if f.Config.ExtensionType == mpeg4audio.ObjectTypePS {
|
||||
channelCount = 2
|
||||
}
|
||||
|
||||
return "mpeg4-generic/" + strconv.FormatInt(int64(sampleRate), 10) +
|
||||
"/" + strconv.FormatInt(int64(channelCount), 10)
|
||||
}
|
||||
|
||||
if f.CPresent {
|
||||
return "MP4A-LATM/16000/1"
|
||||
}
|
||||
|
||||
aoc := f.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig
|
||||
|
||||
sampleRate := aoc.SampleRate
|
||||
if aoc.ExtensionSampleRate != 0 {
|
||||
sampleRate = aoc.ExtensionSampleRate
|
||||
}
|
||||
|
||||
channelCount := aoc.ChannelCount
|
||||
if aoc.ExtensionType == mpeg4audio.ObjectTypePS {
|
||||
channelCount = 2
|
||||
}
|
||||
|
||||
return "MP4A-LATM/" + strconv.FormatInt(int64(sampleRate), 10) +
|
||||
"/" + strconv.FormatInt(int64(channelCount), 10)
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MPEG4Audio) FMTP() map[string]string {
|
||||
if !f.LATM {
|
||||
enc, err := f.Config.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
profileLevelID := f.ProfileLevelID
|
||||
if profileLevelID == 0 { // support legacy definition which didn't include profile-level-id
|
||||
profileLevelID = 1
|
||||
}
|
||||
|
||||
fmtp := map[string]string{
|
||||
"streamtype": "5",
|
||||
"mode": "AAC-hbr",
|
||||
"profile-level-id": strconv.FormatInt(int64(profileLevelID), 10),
|
||||
}
|
||||
|
||||
if f.SizeLength > 0 {
|
||||
fmtp["sizelength"] = strconv.FormatInt(int64(f.SizeLength), 10)
|
||||
}
|
||||
|
||||
if f.IndexLength > 0 {
|
||||
fmtp["indexlength"] = strconv.FormatInt(int64(f.IndexLength), 10)
|
||||
}
|
||||
|
||||
if f.IndexDeltaLength > 0 {
|
||||
fmtp["indexdeltalength"] = strconv.FormatInt(int64(f.IndexDeltaLength), 10)
|
||||
}
|
||||
|
||||
fmtp["config"] = hex.EncodeToString(enc)
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
fmtp := map[string]string{
|
||||
"profile-level-id": strconv.FormatInt(int64(f.ProfileLevelID), 10),
|
||||
}
|
||||
|
||||
if f.Bitrate != nil {
|
||||
fmtp["bitrate"] = strconv.FormatInt(int64(*f.Bitrate), 10)
|
||||
}
|
||||
|
||||
if f.CPresent {
|
||||
fmtp["cpresent"] = "1"
|
||||
} else {
|
||||
fmtp["cpresent"] = "0"
|
||||
|
||||
enc, err := f.StreamMuxConfig.Marshal()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmtp["config"] = hex.EncodeToString(enc)
|
||||
fmtp["object"] = strconv.FormatInt(int64(f.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig.Type), 10)
|
||||
}
|
||||
|
||||
if f.SBREnabled != nil {
|
||||
if *f.SBREnabled {
|
||||
fmtp["SBR-enabled"] = "1"
|
||||
} else {
|
||||
fmtp["SBR-enabled"] = "0"
|
||||
}
|
||||
}
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MPEG4Audio) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MPEG4Audio) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
|
||||
d := &rtpmpeg4audio.Decoder{
|
||||
LATM: f.LATM,
|
||||
SizeLength: f.SizeLength,
|
||||
IndexLength: f.IndexLength,
|
||||
IndexDeltaLength: f.IndexDeltaLength,
|
||||
}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MPEG4Audio) CreateEncoder() (*rtpmpeg4audio.Encoder, error) {
|
||||
e := &rtpmpeg4audio.Encoder{
|
||||
LATM: f.LATM,
|
||||
PayloadType: f.PayloadTyp,
|
||||
SizeLength: f.SizeLength,
|
||||
IndexLength: f.IndexLength,
|
||||
IndexDeltaLength: f.IndexDeltaLength,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// GetConfig returns the MPEG-4 Audio configuration.
|
||||
func (f *MPEG4Audio) GetConfig() *mpeg4audio.Config {
|
||||
if !f.LATM {
|
||||
return f.Config
|
||||
}
|
||||
if f.CPresent {
|
||||
return nil
|
||||
}
|
||||
return f.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig
|
||||
}
|
||||
133
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg4_video.go
generated
vendored
Normal file
133
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpeg4_video.go
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4video"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video"
|
||||
)
|
||||
|
||||
// MPEG4Video is the RTP format for a MPEG-4 Video codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.1
|
||||
type MPEG4Video struct {
|
||||
PayloadTyp uint8
|
||||
ProfileLevelID int
|
||||
Config []byte
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *MPEG4Video) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
f.ProfileLevelID = 1 // default value imposed by specification
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
switch key {
|
||||
case "profile-level-id":
|
||||
tmp, err := strconv.ParseUint(val, 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid profile-level-id: %v", val)
|
||||
}
|
||||
|
||||
f.ProfileLevelID = int(tmp)
|
||||
|
||||
case "config":
|
||||
var err error
|
||||
f.Config, err = hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config: %v", val)
|
||||
}
|
||||
|
||||
err = mpeg4video.IsValidConfig(f.Config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MPEG4Video) Codec() string {
|
||||
return "MPEG-4 Video"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MPEG4Video) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MPEG4Video) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MPEG4Video) RTPMap() string {
|
||||
return "MP4V-ES/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MPEG4Video) FMTP() map[string]string {
|
||||
fmtp := map[string]string{
|
||||
"profile-level-id": strconv.FormatInt(int64(f.ProfileLevelID), 10),
|
||||
}
|
||||
|
||||
if f.Config != nil {
|
||||
fmtp["config"] = strings.ToUpper(hex.EncodeToString(f.Config))
|
||||
}
|
||||
|
||||
return fmtp
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MPEG4Video) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MPEG4Video) CreateDecoder() (*rtpmpeg4video.Decoder, error) {
|
||||
d := &rtpmpeg4video.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MPEG4Video) CreateEncoder() (*rtpmpeg4video.Encoder, error) {
|
||||
e := &rtpmpeg4video.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// SafeSetParams sets the codec parameters.
|
||||
func (f *MPEG4Video) SafeSetParams(config []byte) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
f.Config = config
|
||||
}
|
||||
|
||||
// SafeParams returns the codec parameters.
|
||||
func (f *MPEG4Video) SafeParams() []byte {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
return f.Config
|
||||
}
|
||||
48
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpegts.go
generated
vendored
Normal file
48
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/mpegts.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package format //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// MPEGTS is the RTP format for MPEG-TS.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type MPEGTS struct {
|
||||
// in Go, empty structs share the same pointer,
|
||||
// therefore they cannot be used as map keys
|
||||
// or in equality operations. Prevent this.
|
||||
unused int //nolint:unused
|
||||
}
|
||||
|
||||
func (f *MPEGTS) unmarshal(_ *unmarshalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *MPEGTS) Codec() string {
|
||||
return "MPEG-TS"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *MPEGTS) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *MPEGTS) PayloadType() uint8 {
|
||||
return 33
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *MPEGTS) RTPMap() string {
|
||||
return "MP2T/90000"
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *MPEGTS) FMTP() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *MPEGTS) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
199
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/opus.go
generated
vendored
Normal file
199
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/opus.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio"
|
||||
)
|
||||
|
||||
// Opus is the RTP format for the Opus codec.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7587
|
||||
// Specification: https://webrtc-review.googlesource.com/c/src/+/129768
|
||||
type Opus struct {
|
||||
PayloadTyp uint8
|
||||
ChannelCount int
|
||||
|
||||
//
|
||||
// Deprecated: replaced by ChannelCount.
|
||||
IsStereo bool
|
||||
}
|
||||
|
||||
func (f *Opus) unmarshal(ctx *unmarshalContext) error {
|
||||
f.PayloadTyp = ctx.payloadType
|
||||
|
||||
if ctx.codec == "opus" {
|
||||
tmp := strings.SplitN(ctx.clock, "/", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("invalid clock (%v)", ctx.clock)
|
||||
}
|
||||
|
||||
sampleRate, err := strconv.ParseUint(tmp[0], 10, 31)
|
||||
if err != nil || sampleRate != 48000 {
|
||||
return fmt.Errorf("invalid sample rate: '%s", tmp[0])
|
||||
}
|
||||
|
||||
channelCount, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil || channelCount != 2 {
|
||||
return fmt.Errorf("invalid channel count: '%s'", tmp[1])
|
||||
}
|
||||
|
||||
// assume mono
|
||||
f.ChannelCount = 1
|
||||
f.IsStereo = false
|
||||
|
||||
for key, val := range ctx.fmtp {
|
||||
if key == "sprop-stereo" {
|
||||
if val == "1" {
|
||||
f.ChannelCount = 2
|
||||
f.IsStereo = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp := strings.SplitN(ctx.clock, "/", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("invalid clock (%v)", ctx.clock)
|
||||
}
|
||||
|
||||
sampleRate, err := strconv.ParseUint(tmp[0], 10, 31)
|
||||
if err != nil || sampleRate != 48000 {
|
||||
return fmt.Errorf("invalid sample rate: '%s'", tmp[0])
|
||||
}
|
||||
|
||||
channelCount, err := strconv.ParseUint(tmp[1], 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid channel count: '%s'", tmp[1])
|
||||
}
|
||||
|
||||
f.ChannelCount = int(channelCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codec implements Format.
|
||||
func (f *Opus) Codec() string {
|
||||
return "Opus"
|
||||
}
|
||||
|
||||
// ClockRate implements Format.
|
||||
func (f *Opus) ClockRate() int {
|
||||
// RFC7587: the RTP timestamp is incremented with a 48000 Hz
|
||||
// clock rate for all modes of Opus and all sampling rates.
|
||||
return 48000
|
||||
}
|
||||
|
||||
// PayloadType implements Format.
|
||||
func (f *Opus) PayloadType() uint8 {
|
||||
return f.PayloadTyp
|
||||
}
|
||||
|
||||
// RTPMap implements Format.
|
||||
func (f *Opus) RTPMap() string {
|
||||
if f.ChannelCount <= 2 {
|
||||
// RFC7587: The RTP clock rate in "a=rtpmap" MUST be 48000, and the
|
||||
// number of channels MUST be 2.
|
||||
return "opus/48000/2"
|
||||
}
|
||||
|
||||
return "multiopus/48000/" + strconv.FormatUint(uint64(f.ChannelCount), 10)
|
||||
}
|
||||
|
||||
// FMTP implements Format.
|
||||
func (f *Opus) FMTP() map[string]string {
|
||||
if f.ChannelCount <= 2 {
|
||||
return map[string]string{
|
||||
"sprop-stereo": func() string {
|
||||
if f.ChannelCount == 2 || (f.ChannelCount == 0 && f.IsStereo) {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}(),
|
||||
}
|
||||
}
|
||||
|
||||
switch f.ChannelCount {
|
||||
case 3:
|
||||
return map[string]string{
|
||||
"num_streams": "2",
|
||||
"coupled_streams": "1",
|
||||
"channel_mapping": "0,2,1",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
|
||||
case 4:
|
||||
return map[string]string{
|
||||
"num_streams": "2",
|
||||
"coupled_streams": "2",
|
||||
"channel_mapping": "0,1,2,3",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
|
||||
case 5:
|
||||
return map[string]string{
|
||||
"num_streams": "3",
|
||||
"coupled_streams": "2",
|
||||
"channel_mapping": "0,4,1,2,3",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
|
||||
case 6:
|
||||
return map[string]string{
|
||||
"num_streams": "4",
|
||||
"coupled_streams": "2",
|
||||
"channel_mapping": "0,4,1,2,3,5",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
|
||||
case 7:
|
||||
return map[string]string{
|
||||
"num_streams": "4",
|
||||
"coupled_streams": "3",
|
||||
"channel_mapping": "0,4,1,2,3,5,6",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
|
||||
default: // assume 8
|
||||
return map[string]string{
|
||||
"num_streams": "5",
|
||||
"coupled_streams": "3",
|
||||
"channel_mapping": "0,6,1,4,5,2,3,7",
|
||||
"sprop-maxcapturerate": "48000",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PTSEqualsDTS implements Format.
|
||||
func (f *Opus) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
||||
d := &rtpsimpleaudio.Decoder{}
|
||||
|
||||
err := d.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *Opus) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
||||
e := &rtpsimpleaudio.Encoder{
|
||||
PayloadType: f.PayloadTyp,
|
||||
}
|
||||
|
||||
err := e.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
149
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/decoder.go
generated
vendored
Normal file
149
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package rtpac3
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/ac3"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented frame and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a AC-3 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc4184
|
||||
type Decoder struct {
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentsExpected int
|
||||
fragmentNextSeqNum uint16
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes frames from a RTP packet.
|
||||
// It returns the frames and the PTS of the first frame.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 2 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
mbz := pkt.Payload[0] >> 2
|
||||
ft := pkt.Payload[0] & 0b11
|
||||
|
||||
if mbz != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("invalid MBZ: %v", mbz)
|
||||
}
|
||||
|
||||
var frames [][]byte
|
||||
|
||||
switch ft {
|
||||
case 0:
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
|
||||
buf := pkt.Payload[2:]
|
||||
|
||||
for {
|
||||
var syncInfo ac3.SyncInfo
|
||||
err := syncInfo.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := syncInfo.FrameSize()
|
||||
|
||||
if len(buf) < size {
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
frames = append(frames, buf[:size])
|
||||
buf = buf[size:]
|
||||
|
||||
if len(buf) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case 1, 2:
|
||||
d.resetFragments()
|
||||
|
||||
var syncInfo ac3.SyncInfo
|
||||
err := syncInfo.Unmarshal(pkt.Payload[2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := syncInfo.FrameSize()
|
||||
|
||||
le := len(pkt.Payload[2:])
|
||||
d.fragmentsSize = le
|
||||
d.fragmentsExpected = size - le
|
||||
d.fragments = append(d.fragments, pkt.Payload[2:])
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
d.firstPacketReceived = true
|
||||
|
||||
return nil, ErrMorePacketsNeeded
|
||||
|
||||
case 3:
|
||||
if d.fragmentsSize == 0 {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
return nil, fmt.Errorf("received a subsequent fragment without previous fragments")
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
le := len(pkt.Payload[2:])
|
||||
d.fragmentsSize += le
|
||||
d.fragmentsExpected -= le
|
||||
|
||||
if d.fragmentsExpected < 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("fragment is too big")
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[2:])
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if d.fragmentsExpected > 0 {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
frames = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return frames, nil
|
||||
}
|
||||
201
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/encoder.go
generated
vendored
Normal file
201
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
package rtpac3
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/ac3"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a AC-3 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc4184
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes frames into RTP packets.
|
||||
func (e *Encoder) Encode(frames [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
timestamp := uint32(0)
|
||||
|
||||
// split frames into batches
|
||||
for _, frame := range frames {
|
||||
if e.lenAggregated(batch, frame) <= e.PayloadMaxSize {
|
||||
// add to existing batch
|
||||
batch = append(batch, frame)
|
||||
} else {
|
||||
// write current batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
timestamp += uint32(len(batch)) * ac3.SamplesPerFrame
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{frame}
|
||||
}
|
||||
}
|
||||
|
||||
// write last batch
|
||||
pkts, err := e.writeBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
if len(frames) != 1 || e.lenAggregated(frames, nil) < e.PayloadMaxSize {
|
||||
return e.writeAggregated(frames, timestamp)
|
||||
}
|
||||
|
||||
return e.writeFragmented(frames[0], timestamp)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeFragmented(frame []byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
avail := e.PayloadMaxSize - 4
|
||||
le := len(frame)
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
le = avail
|
||||
|
||||
ft := uint8(2)
|
||||
if avail >= (len(frame) * 5 / 8) {
|
||||
ft = 1
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(frame)
|
||||
}
|
||||
|
||||
payload := make([]byte, 2+le)
|
||||
payload[0] = ft
|
||||
payload[1] = uint8(packetCount)
|
||||
|
||||
n := copy(payload[2:], frame)
|
||||
frame = frame[n:]
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: i == (packetCount - 1),
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
ft = 3
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) lenAggregated(frames [][]byte, addFrame []byte) int {
|
||||
n := 2 + len(addFrame)
|
||||
for _, frame := range frames {
|
||||
n += len(frame)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (e *Encoder) writeAggregated(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, e.lenAggregated(frames, nil))
|
||||
|
||||
payload[1] = uint8(len(frames))
|
||||
|
||||
n := 2
|
||||
for _, frame := range frames {
|
||||
n += copy(payload[n:], frame)
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: true,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/rtpac3.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpac3/rtpac3.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpac3 contains a RTP/AC-3 decoder and encoder.
|
||||
package rtpac3
|
||||
202
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/decoder.go
generated
vendored
Normal file
202
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package rtpav1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/av1"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented NALU and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func tuSize(tu [][]byte) int {
|
||||
s := 0
|
||||
for _, obu := range tu {
|
||||
s += len(obu)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Decoder is a RTP/AV1 decoder.
|
||||
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
|
||||
type Decoder struct {
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentNextSeqNum uint16
|
||||
|
||||
// for Decode()
|
||||
frameBuffer [][]byte
|
||||
frameBufferLen int
|
||||
frameBufferSize int
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 2 {
|
||||
return nil, fmt.Errorf("invalid payload size")
|
||||
}
|
||||
|
||||
z := (pkt.Payload[0] & 0b10000000) != 0
|
||||
y := (pkt.Payload[0] & 0b01000000) != 0
|
||||
w := (pkt.Payload[0] >> 4) & 0b11
|
||||
payload := pkt.Payload[1:]
|
||||
var obus [][]byte
|
||||
|
||||
for len(payload) > 0 {
|
||||
var obu []byte
|
||||
|
||||
if w == 0 || byte(len(obus)) < (w-1) {
|
||||
var size av1.LEB128
|
||||
n, err := size.Unmarshal(payload)
|
||||
if err != nil {
|
||||
d.resetFragments()
|
||||
return nil, err
|
||||
}
|
||||
payload = payload[n:]
|
||||
|
||||
if size == 0 || len(payload) < int(size) {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("invalid OBU size")
|
||||
}
|
||||
|
||||
obu, payload = payload[:size], payload[size:]
|
||||
} else {
|
||||
obu, payload = payload, nil
|
||||
}
|
||||
|
||||
obus = append(obus, obu)
|
||||
}
|
||||
|
||||
if w != 0 && len(obus) != int(w) {
|
||||
return nil, fmt.Errorf("invalid W field")
|
||||
}
|
||||
|
||||
// first OBU is continuation of previous one
|
||||
if z {
|
||||
if d.fragmentsSize == 0 {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("received a subsequent fragment without previous fragments")
|
||||
}
|
||||
|
||||
d.firstPacketReceived = true
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(obus[0])
|
||||
|
||||
if d.fragmentsSize > av1.MaxTemporalUnitSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("temporal unit size (%d) is too big, maximum is %d",
|
||||
errSize, av1.MaxTemporalUnitSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, obus[0])
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if len(obus) == 1 && y {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
obus[0] = joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
} else {
|
||||
d.firstPacketReceived = true
|
||||
}
|
||||
|
||||
// last OBU will continue in next packet
|
||||
if y {
|
||||
var obu []byte
|
||||
obu, obus = obus[len(obus)-1], obus[:len(obus)-1]
|
||||
|
||||
d.fragmentsSize = len(obu)
|
||||
d.fragments = append(d.fragments, obu)
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
|
||||
if len(obus) == 0 {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
}
|
||||
|
||||
return obus, nil
|
||||
}
|
||||
|
||||
// Decode decodes a temporal unit from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
obus, err := d.decodeOBUs(pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := len(obus)
|
||||
|
||||
if (d.frameBufferLen + l) > av1.MaxOBUsPerTemporalUnit {
|
||||
errCount := d.frameBufferLen + l
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
return nil, fmt.Errorf("OBU count (%d) exceeds maximum allowed (%d)",
|
||||
errCount, av1.MaxOBUsPerTemporalUnit)
|
||||
}
|
||||
|
||||
addSize := tuSize(obus)
|
||||
|
||||
if (d.frameBufferSize + addSize) > av1.MaxTemporalUnitSize {
|
||||
errSize := d.frameBufferSize + addSize
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
return nil, fmt.Errorf("temporal unit size (%d) is too big, maximum is %d",
|
||||
errSize, av1.MaxOBUsPerTemporalUnit)
|
||||
}
|
||||
|
||||
d.frameBuffer = append(d.frameBuffer, obus...)
|
||||
d.frameBufferLen += l
|
||||
d.frameBufferSize += addSize
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
ret := d.frameBuffer
|
||||
|
||||
// do not reuse frameBuffer to avoid race conditions
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
171
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/encoder.go
generated
vendored
Normal file
171
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
package rtpav1
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/av1"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/AV1 encoder.
|
||||
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes OBUs into RTP packets.
|
||||
func (e *Encoder) Encode(obus [][]byte) ([]*rtp.Packet, error) {
|
||||
var curPacket *rtp.Packet
|
||||
var packets []*rtp.Packet
|
||||
obusInPacket := 0
|
||||
|
||||
createNewPacket := func(z bool) {
|
||||
curPacket = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
},
|
||||
Payload: []byte{0},
|
||||
}
|
||||
e.sequenceNumber++
|
||||
packets = append(packets, curPacket)
|
||||
obusInPacket = 0
|
||||
|
||||
if z {
|
||||
curPacket.Payload[0] |= 1 << 7
|
||||
}
|
||||
}
|
||||
|
||||
finalizeCurPacket := func(y bool) {
|
||||
if y {
|
||||
curPacket.Payload[0] |= 1 << 6
|
||||
}
|
||||
}
|
||||
|
||||
createNewPacket(false)
|
||||
|
||||
maxFragmentedLEBSize := av1.LEB128(e.PayloadMaxSize).MarshalSize()
|
||||
|
||||
for i, obu := range obus {
|
||||
for {
|
||||
avail := e.PayloadMaxSize - len(curPacket.Payload)
|
||||
obuLen := len(obu)
|
||||
omitSize := (i == (len(obus)-1) && obusInPacket < 3)
|
||||
|
||||
var obuLenLEB av1.LEB128
|
||||
var obuLenLEBSize int
|
||||
var needed int
|
||||
|
||||
if omitSize {
|
||||
needed = obuLen
|
||||
} else {
|
||||
obuLenLEB = av1.LEB128(obuLen)
|
||||
obuLenLEBSize = obuLenLEB.MarshalSize()
|
||||
needed = obuLen + obuLenLEBSize
|
||||
}
|
||||
|
||||
if needed <= avail {
|
||||
if omitSize {
|
||||
curPacket.Payload[0] |= byte((obusInPacket + 1) << 4) // W
|
||||
curPacket.Payload = append(curPacket.Payload, obu...)
|
||||
} else {
|
||||
buf := make([]byte, obuLenLEBSize)
|
||||
obuLenLEB.MarshalTo(buf)
|
||||
curPacket.Payload = append(curPacket.Payload, buf...)
|
||||
curPacket.Payload = append(curPacket.Payload, obu...)
|
||||
obusInPacket++
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if omitSize {
|
||||
if avail > 0 {
|
||||
curPacket.Payload[0] |= byte((obusInPacket + 1) << 4) // W
|
||||
curPacket.Payload = append(curPacket.Payload, obu[:avail]...)
|
||||
obu = obu[avail:]
|
||||
}
|
||||
} else {
|
||||
if avail > maxFragmentedLEBSize {
|
||||
fragmentLen := avail - maxFragmentedLEBSize
|
||||
fragmentLenLEB := av1.LEB128(fragmentLen)
|
||||
fragmentLenLEBSize := fragmentLenLEB.MarshalSize()
|
||||
|
||||
buf := make([]byte, fragmentLenLEBSize)
|
||||
fragmentLenLEB.MarshalTo(buf)
|
||||
curPacket.Payload = append(curPacket.Payload, buf...)
|
||||
curPacket.Payload = append(curPacket.Payload, obu[:fragmentLen]...)
|
||||
obu = obu[fragmentLen:]
|
||||
}
|
||||
}
|
||||
|
||||
finalizeCurPacket(true)
|
||||
createNewPacket(true)
|
||||
}
|
||||
}
|
||||
|
||||
finalizeCurPacket(false)
|
||||
|
||||
if av1.IsRandomAccess2(obus) {
|
||||
packets[0].Payload[0] |= 1 << 3
|
||||
}
|
||||
|
||||
packets[len(packets)-1].Marker = true
|
||||
|
||||
return packets, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/rtpav1.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1/rtpav1.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpav1 contains a RTP/AV1 decoder and encoder.
|
||||
package rtpav1
|
||||
306
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/decoder.go
generated
vendored
Normal file
306
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
package rtph264
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented NALU and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func isAllZero(buf []byte) bool {
|
||||
for _, b := range buf {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func auSize(au [][]byte) int {
|
||||
s := 0
|
||||
for _, nalu := range au {
|
||||
s += len(nalu)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Decoder is a RTP/H264 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6184
|
||||
type Decoder struct {
|
||||
// indicates the packetization mode.
|
||||
PacketizationMode int
|
||||
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentNextSeqNum uint16
|
||||
annexBMode bool
|
||||
|
||||
// for Decode()
|
||||
frameBuffer [][]byte
|
||||
frameBufferLen int
|
||||
frameBufferSize int
|
||||
frameBufferTimestamp uint32
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
if d.PacketizationMode >= 2 {
|
||||
return fmt.Errorf("PacketizationMode >= 2 is not supported")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 1 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
typ := h264.NALUType(pkt.Payload[0] & 0x1F)
|
||||
var nalus [][]byte
|
||||
|
||||
switch typ {
|
||||
case h264.NALUTypeFUA:
|
||||
if len(pkt.Payload) < 2 {
|
||||
return nil, fmt.Errorf("invalid FU-A packet (invalid size)")
|
||||
}
|
||||
|
||||
start := pkt.Payload[1] >> 7
|
||||
end := (pkt.Payload[1] >> 6) & 0x01
|
||||
|
||||
if start == 1 {
|
||||
d.resetFragments()
|
||||
|
||||
nri := (pkt.Payload[0] >> 5) & 0x03
|
||||
typ := pkt.Payload[1] & 0x1F
|
||||
d.fragmentsSize = len(pkt.Payload[1:])
|
||||
d.fragments = append(d.fragments, []byte{(nri << 5) | typ}, pkt.Payload[2:])
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
d.firstPacketReceived = true
|
||||
|
||||
// RFC 6184 clearly states:
|
||||
//
|
||||
// A fragmented NAL unit MUST NOT be transmitted in one FU; that is, the
|
||||
// Start bit and End bit MUST NOT both be set to one in the same FU
|
||||
// header.
|
||||
//
|
||||
// However, some vendors camera (e.g. CostarHD) have been observed to nevertheless
|
||||
// emit one fragmented NAL unit for sufficiently small P-frames.
|
||||
if end != 0 {
|
||||
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
break
|
||||
}
|
||||
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
if d.fragmentsSize == 0 {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid FU-A packet (non-starting)")
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(pkt.Payload[2:])
|
||||
|
||||
if d.fragmentsSize > h264.MaxAccessUnitSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big, maximum is %d",
|
||||
errSize, h264.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[2:])
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if end != 1 {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
|
||||
case h264.NALUTypeSTAPA:
|
||||
d.resetFragments()
|
||||
|
||||
payload := pkt.Payload[1:]
|
||||
|
||||
for {
|
||||
if len(payload) < 2 {
|
||||
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
|
||||
}
|
||||
|
||||
size := uint16(payload[0])<<8 | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if size == 0 {
|
||||
// discard padding
|
||||
if isAllZero(payload) {
|
||||
break
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
|
||||
}
|
||||
|
||||
if int(size) > len(payload) {
|
||||
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
|
||||
}
|
||||
|
||||
nalus = append(nalus, payload[:size])
|
||||
payload = payload[size:]
|
||||
|
||||
if len(payload) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nalus == nil {
|
||||
return nil, fmt.Errorf("STAP-A packet doesn't contain any NALU")
|
||||
}
|
||||
|
||||
d.firstPacketReceived = true
|
||||
|
||||
case h264.NALUTypeSTAPB, h264.NALUTypeMTAP16,
|
||||
h264.NALUTypeMTAP24, h264.NALUTypeFUB:
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
return nil, fmt.Errorf("packet type not supported (%v)", typ)
|
||||
|
||||
default:
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
nalus = [][]byte{pkt.Payload}
|
||||
}
|
||||
|
||||
nalus, err := d.removeAnnexB(nalus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nalus, nil
|
||||
}
|
||||
|
||||
// Decode decodes an access unit from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
nalus, err := d.decodeNALUs(pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := len(nalus)
|
||||
|
||||
// support splitting access units by timestamp.
|
||||
// (some cameras do not use the Marker field, like the FLIR M400)
|
||||
if d.frameBuffer != nil && pkt.Timestamp != d.frameBufferTimestamp {
|
||||
ret := d.frameBuffer
|
||||
d.resetFrameBuffer()
|
||||
|
||||
err = d.addToFrameBuffer(nalus, l, pkt.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
err = d.addToFrameBuffer(nalus, l, pkt.Timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
ret := d.frameBuffer
|
||||
d.resetFrameBuffer()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFrameBuffer() {
|
||||
d.frameBuffer = nil // do not reuse frameBuffer to avoid race conditions
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
}
|
||||
|
||||
func (d *Decoder) addToFrameBuffer(nalus [][]byte, l int, ts uint32) error {
|
||||
if (d.frameBufferLen + l) > h264.MaxNALUsPerAccessUnit {
|
||||
errCount := d.frameBufferLen + l
|
||||
d.resetFrameBuffer()
|
||||
return fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
|
||||
errCount, h264.MaxNALUsPerAccessUnit)
|
||||
}
|
||||
|
||||
addSize := auSize(nalus)
|
||||
|
||||
if (d.frameBufferSize + addSize) > h264.MaxAccessUnitSize {
|
||||
errSize := d.frameBufferSize + addSize
|
||||
d.resetFrameBuffer()
|
||||
return fmt.Errorf("access unit size (%d) is too big, maximum is %d",
|
||||
errSize, h264.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.frameBuffer = append(d.frameBuffer, nalus...)
|
||||
d.frameBufferLen += l
|
||||
d.frameBufferSize += addSize
|
||||
d.frameBufferTimestamp = ts
|
||||
return nil
|
||||
}
|
||||
|
||||
// some cameras / servers wrap NALUs into Annex-B
|
||||
func (d *Decoder) removeAnnexB(nalus [][]byte) ([][]byte, error) {
|
||||
if len(nalus) == 1 {
|
||||
nalu := nalus[0]
|
||||
|
||||
if !d.annexBMode && bytes.Contains(nalu, []byte{0x00, 0x00, 0x00, 0x01}) {
|
||||
d.annexBMode = true
|
||||
}
|
||||
|
||||
if d.annexBMode {
|
||||
if !bytes.HasPrefix(nalu, []byte{0x00, 0x00, 0x00, 0x01}) {
|
||||
nalu = append([]byte{0x00, 0x00, 0x00, 0x01}, nalu...)
|
||||
}
|
||||
|
||||
var annexb h264.AnnexB
|
||||
err := annexb.Unmarshal(nalu)
|
||||
return annexb, err
|
||||
}
|
||||
}
|
||||
|
||||
return nalus, nil
|
||||
}
|
||||
248
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/encoder.go
generated
vendored
Normal file
248
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
package rtph264
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func lenAggregated(nalus [][]byte, addNALU []byte) int {
|
||||
n := 1 // header
|
||||
|
||||
for _, nalu := range nalus {
|
||||
n += 2 // size
|
||||
n += len(nalu) // nalu
|
||||
}
|
||||
|
||||
if addNALU != nil {
|
||||
n += 2 // size
|
||||
n += len(addNALU) // nalu
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a RTP/H264 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6184
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
PacketizationMode int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.PacketizationMode >= 2 {
|
||||
return fmt.Errorf("PacketizationMode >= 2 is not supported")
|
||||
}
|
||||
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes an access unit into RTP/H264 packets.
|
||||
func (e *Encoder) Encode(au [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
||||
// split NALUs into batches
|
||||
for _, nalu := range au {
|
||||
if lenAggregated(batch, nalu) <= e.PayloadMaxSize {
|
||||
// add to existing batch
|
||||
batch = append(batch, nalu)
|
||||
} else {
|
||||
// write current batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeBatch(batch, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{nalu}
|
||||
}
|
||||
}
|
||||
|
||||
// write final batch
|
||||
// marker is used to indicate when all NALUs with same PTS have been sent
|
||||
pkts, err := e.writeBatch(batch, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
|
||||
if len(nalus) == 1 {
|
||||
// the NALU fits into a single RTP packet
|
||||
if len(nalus[0]) < e.PayloadMaxSize {
|
||||
return e.writeSingle(nalus[0], marker)
|
||||
}
|
||||
|
||||
// split the NALU into multiple fragmentation packet
|
||||
return e.writeFragmented(nalus[0], marker)
|
||||
}
|
||||
|
||||
return e.writeAggregated(nalus, marker)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeSingle(nalu []byte, marker bool) ([]*rtp.Packet, error) {
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: marker,
|
||||
},
|
||||
Payload: nalu,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeFragmented(nalu []byte, marker bool) ([]*rtp.Packet, error) {
|
||||
// use only FU-A, not FU-B, since we always use non-interleaved mode
|
||||
// (packetization-mode=1)
|
||||
avail := e.PayloadMaxSize - 2
|
||||
le := len(nalu) - 1
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
|
||||
nri := (nalu[0] >> 5) & 0x03
|
||||
typ := nalu[0] & 0x1F
|
||||
nalu = nalu[1:] // remove header
|
||||
le = avail
|
||||
start := uint8(1)
|
||||
end := uint8(0)
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
end = 1
|
||||
le = len(nalu)
|
||||
}
|
||||
|
||||
data := make([]byte, 2+le)
|
||||
data[0] = (nri << 5) | uint8(h264.NALUTypeFUA)
|
||||
data[1] = (start << 7) | (end << 6) | typ
|
||||
copy(data[2:], nalu)
|
||||
nalu = nalu[le:]
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: (i == (packetCount-1) && marker),
|
||||
},
|
||||
Payload: data,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
start = 0
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeAggregated(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, lenAggregated(nalus, nil))
|
||||
|
||||
// header
|
||||
payload[0] = uint8(h264.NALUTypeSTAPA)
|
||||
pos := 1
|
||||
|
||||
for _, nalu := range nalus {
|
||||
// size
|
||||
naluLen := len(nalu)
|
||||
payload[pos] = uint8(naluLen >> 8)
|
||||
payload[pos+1] = uint8(naluLen)
|
||||
pos += 2
|
||||
|
||||
// nalu
|
||||
copy(payload[pos:], nalu)
|
||||
pos += naluLen
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: marker,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/rtph264.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph264/rtph264.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtph264 contains a RTP/H264 decoder and encoder.
|
||||
package rtph264
|
||||
220
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/decoder.go
generated
vendored
Normal file
220
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
package rtph265
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented NALU and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func auSize(au [][]byte) int {
|
||||
s := 0
|
||||
for _, nalu := range au {
|
||||
s += len(nalu)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Decoder is a RTP/H265 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7798
|
||||
type Decoder struct {
|
||||
// indicates that NALUs have an additional field that specifies the decoding order.
|
||||
MaxDONDiff int
|
||||
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentNextSeqNum uint16
|
||||
|
||||
// for Decode()
|
||||
frameBuffer [][]byte
|
||||
frameBufferLen int
|
||||
frameBufferSize int
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
if d.MaxDONDiff != 0 {
|
||||
return fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 2 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
typ := h265.NALUType((pkt.Payload[0] >> 1) & 0b111111)
|
||||
var nalus [][]byte
|
||||
|
||||
switch typ {
|
||||
case h265.NALUType_AggregationUnit:
|
||||
d.resetFragments()
|
||||
|
||||
payload := pkt.Payload[2:]
|
||||
|
||||
for {
|
||||
if len(payload) < 2 {
|
||||
return nil, fmt.Errorf("invalid aggregation unit (invalid size)")
|
||||
}
|
||||
|
||||
size := uint16(payload[0])<<8 | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if size == 0 || int(size) > len(payload) {
|
||||
return nil, fmt.Errorf("invalid aggregation unit (invalid size)")
|
||||
}
|
||||
|
||||
nalus = append(nalus, payload[:size])
|
||||
payload = payload[size:]
|
||||
|
||||
if len(payload) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
d.firstPacketReceived = true
|
||||
|
||||
case h265.NALUType_FragmentationUnit:
|
||||
if len(pkt.Payload) < 3 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
start := pkt.Payload[2] >> 7
|
||||
end := (pkt.Payload[2] >> 6) & 0x01
|
||||
|
||||
if start == 1 {
|
||||
d.resetFragments()
|
||||
|
||||
if end != 0 {
|
||||
return nil, fmt.Errorf("invalid fragmentation unit (can't contain both a start and end bit)")
|
||||
}
|
||||
|
||||
typ := pkt.Payload[2] & 0b111111
|
||||
head := uint16(pkt.Payload[0]&0b10000001)<<8 | uint16(typ)<<9 | uint16(pkt.Payload[1])
|
||||
d.fragmentsSize = len(pkt.Payload[1:])
|
||||
d.fragments = append(d.fragments, []byte{byte(head >> 8), byte(head)}, pkt.Payload[3:])
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
d.firstPacketReceived = true
|
||||
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
if d.fragmentsSize == 0 {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid fragmentation unit (non-starting)")
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(pkt.Payload[3:])
|
||||
|
||||
if d.fragmentsSize > h265.MaxAccessUnitSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("NALU size (%d) is too big, maximum is %d",
|
||||
errSize, h265.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[3:])
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if end != 1 {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
|
||||
case h265.NALUType_PACI:
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("PACI packets are not supported (yet)")
|
||||
|
||||
default:
|
||||
d.resetFragments()
|
||||
nalus = [][]byte{pkt.Payload}
|
||||
}
|
||||
|
||||
return nalus, nil
|
||||
}
|
||||
|
||||
// Decode decodes an access unit from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
nalus, err := d.decodeNALUs(pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := len(nalus)
|
||||
|
||||
if (d.frameBufferLen + l) > h265.MaxNALUsPerAccessUnit {
|
||||
errCount := d.frameBufferLen + l
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
return nil, fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
|
||||
errCount, h265.MaxNALUsPerAccessUnit)
|
||||
}
|
||||
|
||||
addSize := auSize(nalus)
|
||||
|
||||
if (d.frameBufferSize + addSize) > h265.MaxAccessUnitSize {
|
||||
errSize := d.frameBufferSize + addSize
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
|
||||
errSize, h265.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.frameBuffer = append(d.frameBuffer, nalus...)
|
||||
d.frameBufferLen += l
|
||||
d.frameBufferSize += addSize
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
ret := d.frameBuffer
|
||||
|
||||
// do not reuse frameBuffer to avoid race conditions
|
||||
d.frameBuffer = nil
|
||||
d.frameBufferLen = 0
|
||||
d.frameBufferSize = 0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
247
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/encoder.go
generated
vendored
Normal file
247
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
package rtph265
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a RTP/H265 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7798
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
// indicates that NALUs have an additional field that specifies the decoding order.
|
||||
MaxDONDiff int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.MaxDONDiff != 0 {
|
||||
return fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
|
||||
}
|
||||
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes an access unit into RTP/H265 packets.
|
||||
func (e *Encoder) Encode(au [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
||||
// split NALUs into batches
|
||||
for _, nalu := range au {
|
||||
if e.lenAggregationUnit(batch, nalu) <= e.PayloadMaxSize {
|
||||
// add to existing batch
|
||||
batch = append(batch, nalu)
|
||||
} else {
|
||||
// write batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeBatch(batch, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{nalu}
|
||||
}
|
||||
}
|
||||
|
||||
// write final batch
|
||||
// marker is used to indicate that the entire access unit has been sent
|
||||
pkts, err := e.writeBatch(batch, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
|
||||
if len(nalus) == 1 {
|
||||
// the NALU fits into a single RTP packet
|
||||
if len(nalus[0]) < e.PayloadMaxSize {
|
||||
return e.writeSingle(nalus[0], marker)
|
||||
}
|
||||
|
||||
// split the NALU into multiple fragmentation packet
|
||||
return e.writeFragmentationUnits(nalus[0], marker)
|
||||
}
|
||||
|
||||
return e.writeAggregationUnit(nalus, marker)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeSingle(nalu []byte, marker bool) ([]*rtp.Packet, error) {
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: marker,
|
||||
},
|
||||
Payload: nalu,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeFragmentationUnits(nalu []byte, marker bool) ([]*rtp.Packet, error) {
|
||||
avail := e.PayloadMaxSize - 3
|
||||
le := len(nalu) - 2
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
|
||||
head := nalu[:2]
|
||||
nalu = nalu[2:]
|
||||
le = avail
|
||||
start := uint8(1)
|
||||
end := uint8(0)
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(nalu)
|
||||
end = 1
|
||||
}
|
||||
|
||||
data := make([]byte, 3+le)
|
||||
data[0] = head[0]&0b10000001 | 49<<1
|
||||
data[1] = head[1]
|
||||
data[2] = (start << 7) | (end << 6) | (head[0]>>1)&0b111111
|
||||
copy(data[3:], nalu)
|
||||
nalu = nalu[le:]
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: (i == (packetCount-1) && marker),
|
||||
},
|
||||
Payload: data,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
start = 0
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) lenAggregationUnit(nalus [][]byte, addNALU []byte) int {
|
||||
ret := 2 // header
|
||||
|
||||
for _, nalu := range nalus {
|
||||
ret += 2 // size
|
||||
ret += len(nalu) // nalu
|
||||
}
|
||||
|
||||
if addNALU != nil {
|
||||
ret += 2 // size
|
||||
ret += len(addNALU) // nalu
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e *Encoder) writeAggregationUnit(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, e.lenAggregationUnit(nalus, nil))
|
||||
|
||||
// header
|
||||
h := uint16(48) << 9
|
||||
payload[0] = byte(h >> 8)
|
||||
payload[1] = byte(h)
|
||||
pos := 2
|
||||
|
||||
for _, nalu := range nalus {
|
||||
// size
|
||||
naluLen := len(nalu)
|
||||
payload[pos] = uint8(naluLen >> 8)
|
||||
payload[pos+1] = uint8(naluLen)
|
||||
pos += 2
|
||||
|
||||
// nalu
|
||||
copy(payload[pos:], nalu)
|
||||
pos += naluLen
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: marker,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/rtph265.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtph265/rtph265.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtph265 contains a RTP/H265 decoder and encoder.
|
||||
package rtph265
|
||||
33
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/decoder.go
generated
vendored
Normal file
33
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package rtplpcm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// Decoder is a RTP/LPCM decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||
type Decoder struct {
|
||||
BitDepth int
|
||||
ChannelCount int
|
||||
|
||||
sampleSize int
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
d.sampleSize = d.BitDepth * d.ChannelCount / 8
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode decodes audio samples from a RTP packet.
|
||||
// It returns audio samples and PTS of the first sample.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
plen := len(pkt.Payload)
|
||||
if (plen % d.sampleSize) != 0 {
|
||||
return nil, fmt.Errorf("received payload of wrong size")
|
||||
}
|
||||
|
||||
return pkt.Payload, nil
|
||||
}
|
||||
124
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/encoder.go
generated
vendored
Normal file
124
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package rtplpcm
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/LPCM encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// bit depth.
|
||||
BitDepth int
|
||||
|
||||
// channel count.
|
||||
ChannelCount int
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
sampleSize int
|
||||
maxPayloadSize int
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
e.sampleSize = e.BitDepth * e.ChannelCount / 8
|
||||
e.maxPayloadSize = (e.PayloadMaxSize / e.sampleSize) * e.sampleSize
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) packetCount(slen int) int {
|
||||
n := (slen / e.maxPayloadSize)
|
||||
if (slen % e.maxPayloadSize) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encode encodes audio samples into RTP packets.
|
||||
func (e *Encoder) Encode(samples []byte) ([]*rtp.Packet, error) {
|
||||
slen := len(samples)
|
||||
if (slen % e.sampleSize) != 0 {
|
||||
return nil, fmt.Errorf("invalid samples")
|
||||
}
|
||||
|
||||
packetCount := e.packetCount(slen)
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
pos := 0
|
||||
payloadSize := e.maxPayloadSize
|
||||
timestamp := uint32(0)
|
||||
|
||||
for i := range ret {
|
||||
if payloadSize > len(samples[pos:]) {
|
||||
payloadSize = len(samples[pos:])
|
||||
}
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: false,
|
||||
},
|
||||
Payload: samples[pos : pos+payloadSize],
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
pos += payloadSize
|
||||
timestamp += uint32(payloadSize / e.sampleSize)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/rtplpcm.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm/rtplpcm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtplpcm contains a RTP/LPCM decoder and encoder.
|
||||
package rtplpcm
|
||||
304
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/decoder.go
generated
vendored
Normal file
304
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package rtpmjpeg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/jpeg"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// fragment of an image and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
var lumDcCodeLens = []byte{
|
||||
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
var lumDcSymbols = []byte{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
}
|
||||
|
||||
var lumAcCodelens = []byte{
|
||||
0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d,
|
||||
}
|
||||
|
||||
var lumAcSymbols = []byte{ //nolint:dupl
|
||||
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa,
|
||||
}
|
||||
|
||||
var chmDcCodelens = []byte{
|
||||
0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
var chmDcSymbols = []byte{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
}
|
||||
|
||||
var chmAcCodelens = []byte{
|
||||
0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77,
|
||||
}
|
||||
|
||||
var chmAcSymbols = []byte{ //nolint:dupl
|
||||
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa,
|
||||
}
|
||||
|
||||
var lumaQuantizers = []int{
|
||||
0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
|
||||
0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
|
||||
0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
|
||||
0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
|
||||
0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
|
||||
0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
|
||||
0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
|
||||
0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
|
||||
}
|
||||
|
||||
var chromaQuantizers = []int{
|
||||
0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
|
||||
0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
|
||||
}
|
||||
|
||||
func makeQuantizationTables(q uint8) [][]byte {
|
||||
var scale int
|
||||
if q < 50 {
|
||||
scale = 5000 / int(q)
|
||||
} else {
|
||||
scale = 200 - 2*int(q)
|
||||
}
|
||||
|
||||
tables := make([][]byte, 2)
|
||||
|
||||
tables[0] = make([]byte, 64)
|
||||
|
||||
for i := 0; i < 64; i++ {
|
||||
v := (lumaQuantizers[i]*scale + 50) / 100
|
||||
if v > 255 {
|
||||
v = 255
|
||||
} else if v == 0 {
|
||||
v = 1
|
||||
}
|
||||
tables[0][i] = byte(v)
|
||||
}
|
||||
|
||||
tables[1] = make([]byte, 64)
|
||||
|
||||
for i := 0; i < 64; i++ {
|
||||
v := (chromaQuantizers[i]*scale + 50) / 100
|
||||
if v > 255 {
|
||||
v = 255
|
||||
} else if v == 0 {
|
||||
v = 1
|
||||
}
|
||||
tables[1][i] = byte(v)
|
||||
}
|
||||
|
||||
return tables
|
||||
}
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/M-JPEG decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||
type Decoder struct {
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
firstJpegHeader *headerJPEG
|
||||
quantizationTables [][]byte
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes an image from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
byts := pkt.Payload
|
||||
|
||||
var jh headerJPEG
|
||||
n, err := jh.unmarshal(byts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byts = byts[n:]
|
||||
|
||||
if jh.Width > maxDimension {
|
||||
return nil, fmt.Errorf("width of %d is not supported", jh.Width)
|
||||
}
|
||||
|
||||
if jh.Height > maxDimension {
|
||||
return nil, fmt.Errorf("height of %d is not supported", jh.Height)
|
||||
}
|
||||
|
||||
if jh.FragmentOffset == 0 {
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
|
||||
if jh.Quantization >= 128 {
|
||||
var hqt headerQuantizationTable
|
||||
n, err := hqt.unmarshal(byts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.quantizationTables = hqt.Tables
|
||||
byts = byts[n:]
|
||||
} else {
|
||||
d.quantizationTables = makeQuantizationTables(jh.Quantization)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, byts)
|
||||
d.fragmentsSize = len(byts)
|
||||
d.firstJpegHeader = &jh
|
||||
} else {
|
||||
if int(jh.FragmentOffset) != d.fragmentsSize {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("received wrong fragment")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(byts)
|
||||
d.fragments = append(d.fragments, byts)
|
||||
}
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
if d.fragmentsSize < 2 {
|
||||
return nil, fmt.Errorf("invalid data")
|
||||
}
|
||||
|
||||
data := joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
|
||||
var buf []byte
|
||||
|
||||
buf = jpeg.StartOfImage{}.Marshal(buf)
|
||||
|
||||
var dqt jpeg.DefineQuantizationTable
|
||||
id := uint8(0)
|
||||
for _, table := range d.quantizationTables {
|
||||
dqt.Tables = append(dqt.Tables, jpeg.QuantizationTable{
|
||||
ID: id,
|
||||
Data: table,
|
||||
})
|
||||
id++
|
||||
}
|
||||
buf = dqt.Marshal(buf)
|
||||
|
||||
buf = jpeg.StartOfFrame1{
|
||||
Type: d.firstJpegHeader.Type,
|
||||
Width: d.firstJpegHeader.Width,
|
||||
Height: d.firstJpegHeader.Height,
|
||||
QuantizationTableCount: id,
|
||||
}.Marshal(buf)
|
||||
|
||||
buf = jpeg.DefineHuffmanTable{
|
||||
Codes: lumDcCodeLens,
|
||||
Symbols: lumDcSymbols,
|
||||
TableNumber: 0,
|
||||
TableClass: 0,
|
||||
}.Marshal(buf)
|
||||
|
||||
buf = jpeg.DefineHuffmanTable{
|
||||
Codes: lumAcCodelens,
|
||||
Symbols: lumAcSymbols,
|
||||
TableNumber: 0,
|
||||
TableClass: 1,
|
||||
}.Marshal(buf)
|
||||
|
||||
buf = jpeg.DefineHuffmanTable{
|
||||
Codes: chmDcCodelens,
|
||||
Symbols: chmDcSymbols,
|
||||
TableNumber: 1,
|
||||
TableClass: 0,
|
||||
}.Marshal(buf)
|
||||
|
||||
buf = jpeg.DefineHuffmanTable{
|
||||
Codes: chmAcCodelens,
|
||||
Symbols: chmAcSymbols,
|
||||
TableNumber: 1,
|
||||
TableClass: 1,
|
||||
}.Marshal(buf)
|
||||
|
||||
buf = jpeg.StartOfScan{}.Marshal(buf)
|
||||
|
||||
buf = append(buf, data...)
|
||||
|
||||
if data[len(data)-2] != 0xFF || data[len(data)-1] != jpeg.MarkerEndOfImage {
|
||||
buf = append(buf, []byte{0xFF, jpeg.MarkerEndOfImage}...)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
279
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/encoder.go
generated
vendored
Normal file
279
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
package rtpmjpeg
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/jpeg"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/M-JPEG encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes an image into RTP/M-JPEG packets.
|
||||
func (e *Encoder) Encode(image []byte) ([]*rtp.Packet, error) {
|
||||
l := len(image)
|
||||
if l < 2 || image[0] != 0xFF || image[1] != jpeg.MarkerStartOfImage {
|
||||
return nil, fmt.Errorf("SOI not found")
|
||||
}
|
||||
|
||||
image = image[2:]
|
||||
var sof *jpeg.StartOfFrame1
|
||||
var dri *jpeg.DefineRestartInterval
|
||||
quantizationTables := make(map[uint8][]byte)
|
||||
var data []byte
|
||||
|
||||
outer:
|
||||
for {
|
||||
if len(image) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
h0, h1 := image[0], image[1]
|
||||
image = image[2:]
|
||||
|
||||
if h0 != 0xFF {
|
||||
return nil, fmt.Errorf("invalid image")
|
||||
}
|
||||
|
||||
switch h1 {
|
||||
case 0xE0, 0xE1, 0xE2, // JFIF
|
||||
jpeg.MarkerDefineHuffmanTable,
|
||||
jpeg.MarkerComment:
|
||||
mlen := int(image[0])<<8 | int(image[1])
|
||||
if len(image) < mlen {
|
||||
return nil, fmt.Errorf("image is too short")
|
||||
}
|
||||
image = image[mlen:]
|
||||
|
||||
case jpeg.MarkerDefineQuantizationTable:
|
||||
mlen := int(image[0])<<8 | int(image[1])
|
||||
if len(image) < mlen {
|
||||
return nil, fmt.Errorf("image is too short")
|
||||
}
|
||||
|
||||
var dqt jpeg.DefineQuantizationTable
|
||||
err := dqt.Unmarshal(image[2:mlen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image = image[mlen:]
|
||||
|
||||
for _, t := range dqt.Tables {
|
||||
quantizationTables[t.ID] = t.Data
|
||||
}
|
||||
|
||||
case jpeg.MarkerDefineRestartInterval:
|
||||
mlen := int(image[0])<<8 | int(image[1])
|
||||
if len(image) < mlen {
|
||||
return nil, fmt.Errorf("image is too short")
|
||||
}
|
||||
|
||||
dri = &jpeg.DefineRestartInterval{}
|
||||
err := dri.Unmarshal(image[2:mlen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image = image[mlen:]
|
||||
|
||||
case jpeg.MarkerStartOfFrame1:
|
||||
mlen := int(image[0])<<8 | int(image[1])
|
||||
if len(image) < mlen {
|
||||
return nil, fmt.Errorf("image is too short")
|
||||
}
|
||||
|
||||
sof = &jpeg.StartOfFrame1{}
|
||||
err := sof.Unmarshal(image[2:mlen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image = image[mlen:]
|
||||
|
||||
if sof.Width > maxDimension {
|
||||
return nil, fmt.Errorf("an image with width of %d can't be sent with RTSP", sof.Width)
|
||||
}
|
||||
|
||||
if sof.Height > maxDimension {
|
||||
return nil, fmt.Errorf("an image with height of %d can't be sent with RTSP", sof.Height)
|
||||
}
|
||||
|
||||
if (sof.Width % 8) != 0 {
|
||||
return nil, fmt.Errorf("width must be multiple of 8")
|
||||
}
|
||||
|
||||
if (sof.Height % 8) != 0 {
|
||||
return nil, fmt.Errorf("height must be multiple of 8")
|
||||
}
|
||||
|
||||
case jpeg.MarkerStartOfScan:
|
||||
mlen := int(image[0])<<8 | int(image[1])
|
||||
if len(image) < mlen {
|
||||
return nil, fmt.Errorf("image is too short")
|
||||
}
|
||||
|
||||
var sos jpeg.StartOfScan
|
||||
err := sos.Unmarshal(image[2:mlen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image = image[mlen:]
|
||||
|
||||
data = image
|
||||
break outer
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown marker: 0x%.2x", h1)
|
||||
}
|
||||
}
|
||||
|
||||
if sof == nil {
|
||||
return nil, fmt.Errorf("SOF not found")
|
||||
}
|
||||
|
||||
if sof.Type > 63 {
|
||||
return nil, fmt.Errorf("JPEG type %d is not supported", sof.Type)
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("image data not found")
|
||||
}
|
||||
|
||||
jh := headerJPEG{
|
||||
TypeSpecific: 0,
|
||||
Type: sof.Type,
|
||||
Quantization: 255,
|
||||
Width: sof.Width,
|
||||
Height: sof.Height,
|
||||
}
|
||||
|
||||
if dri != nil {
|
||||
jh.Type += 64
|
||||
}
|
||||
|
||||
first := true
|
||||
offset := 0
|
||||
var ret []*rtp.Packet
|
||||
|
||||
for {
|
||||
var buf []byte
|
||||
|
||||
jh.FragmentOffset = uint32(offset)
|
||||
buf = jh.marshal(buf)
|
||||
|
||||
if dri != nil {
|
||||
buf = headerRestartMarker{
|
||||
Interval: dri.Interval,
|
||||
Count: 0xFFFF,
|
||||
}.marshal(buf)
|
||||
}
|
||||
|
||||
if first {
|
||||
first = false
|
||||
|
||||
qth := headerQuantizationTable{}
|
||||
|
||||
// gather and sort tables IDs
|
||||
ids := make([]uint8, len(quantizationTables))
|
||||
i := 0
|
||||
for id := range quantizationTables {
|
||||
ids[i] = id
|
||||
i++
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool {
|
||||
return ids[i] < ids[j]
|
||||
})
|
||||
|
||||
// add tables sorted by ID
|
||||
for _, id := range ids {
|
||||
qth.Tables = append(qth.Tables, quantizationTables[id])
|
||||
}
|
||||
|
||||
buf = qth.marshal(buf)
|
||||
}
|
||||
|
||||
remaining := e.PayloadMaxSize - len(buf)
|
||||
ldata := len(data)
|
||||
if remaining > ldata {
|
||||
remaining = ldata
|
||||
}
|
||||
|
||||
buf = append(buf, data[:remaining]...)
|
||||
data = data[remaining:]
|
||||
offset += remaining
|
||||
|
||||
ret = append(ret, &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 26,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: len(data) == 0,
|
||||
},
|
||||
Payload: buf,
|
||||
})
|
||||
e.sequenceNumber++
|
||||
|
||||
if len(data) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
49
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_jpeg.go
generated
vendored
Normal file
49
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_jpeg.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package rtpmjpeg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type headerJPEG struct {
|
||||
TypeSpecific uint8
|
||||
FragmentOffset uint32
|
||||
Type uint8
|
||||
Quantization uint8
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
func (h *headerJPEG) unmarshal(byts []byte) (int, error) {
|
||||
if len(byts) < 8 {
|
||||
return 0, fmt.Errorf("buffer is too short")
|
||||
}
|
||||
|
||||
h.TypeSpecific = byts[0]
|
||||
h.FragmentOffset = uint32(byts[1])<<16 | uint32(byts[2])<<8 | uint32(byts[3])
|
||||
|
||||
h.Type = byts[4]
|
||||
if h.Type > 63 {
|
||||
return 0, fmt.Errorf("type %d is not supported", h.Type)
|
||||
}
|
||||
|
||||
h.Quantization = byts[5]
|
||||
if h.Quantization == 0 ||
|
||||
(h.Quantization > 99 && h.Quantization < 127) {
|
||||
return 0, fmt.Errorf("quantization %d is invalid", h.Quantization)
|
||||
}
|
||||
|
||||
h.Width = int(byts[6]) * 8
|
||||
h.Height = int(byts[7]) * 8
|
||||
|
||||
return 8, nil
|
||||
}
|
||||
|
||||
func (h headerJPEG) marshal(byts []byte) []byte {
|
||||
byts = append(byts, h.TypeSpecific)
|
||||
byts = append(byts, []byte{byte(h.FragmentOffset >> 16), byte(h.FragmentOffset >> 8), byte(h.FragmentOffset)}...)
|
||||
byts = append(byts, h.Type)
|
||||
byts = append(byts, h.Quantization)
|
||||
byts = append(byts, byte(h.Width/8))
|
||||
byts = append(byts, byte(h.Height/8))
|
||||
return byts
|
||||
}
|
||||
59
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_quantization_table.go
generated
vendored
Normal file
59
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_quantization_table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package rtpmjpeg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type headerQuantizationTable struct {
|
||||
MBZ uint8
|
||||
Precision uint8
|
||||
Tables [][]byte
|
||||
}
|
||||
|
||||
func (h *headerQuantizationTable) unmarshal(byts []byte) (int, error) {
|
||||
if len(byts) < 4 {
|
||||
return 0, fmt.Errorf("buffer is too short")
|
||||
}
|
||||
|
||||
h.MBZ = byts[0]
|
||||
h.Precision = byts[1]
|
||||
if h.Precision != 0 {
|
||||
return 0, fmt.Errorf("precision %d is not supported", h.Precision)
|
||||
}
|
||||
|
||||
length := int(byts[2])<<8 | int(byts[3])
|
||||
switch length {
|
||||
case 64, 128:
|
||||
default:
|
||||
return 0, fmt.Errorf("table length %d is not supported", length)
|
||||
}
|
||||
|
||||
if (len(byts) - 4) < length {
|
||||
return 0, fmt.Errorf("buffer is too short")
|
||||
}
|
||||
|
||||
tableCount := length / 64
|
||||
h.Tables = make([][]byte, tableCount)
|
||||
n := 0
|
||||
|
||||
for i := 0; i < tableCount; i++ {
|
||||
h.Tables[i] = byts[4+n : 4+64+n]
|
||||
n += 64
|
||||
}
|
||||
|
||||
return 4 + length, nil
|
||||
}
|
||||
|
||||
func (h headerQuantizationTable) marshal(byts []byte) []byte {
|
||||
byts = append(byts, h.MBZ)
|
||||
byts = append(byts, h.Precision)
|
||||
|
||||
l := len(h.Tables) * 64
|
||||
byts = append(byts, []byte{byte(l >> 8), byte(l)}...)
|
||||
|
||||
for i := 0; i < len(h.Tables); i++ {
|
||||
byts = append(byts, h.Tables[i]...)
|
||||
}
|
||||
|
||||
return byts
|
||||
}
|
||||
26
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_restart_marker.go
generated
vendored
Normal file
26
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/header_restart_marker.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package rtpmjpeg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type headerRestartMarker struct {
|
||||
Interval uint16
|
||||
Count uint16
|
||||
}
|
||||
|
||||
func (h *headerRestartMarker) unmarshal(byts []byte) (int, error) {
|
||||
if len(byts) < 4 {
|
||||
return 0, fmt.Errorf("buffer is too short")
|
||||
}
|
||||
|
||||
h.Interval = uint16(byts[0])<<8 | uint16(byts[1])
|
||||
h.Count = uint16(byts[2])<<8 | uint16(byts[3])
|
||||
return 4, nil
|
||||
}
|
||||
|
||||
func (h headerRestartMarker) marshal(byts []byte) []byte {
|
||||
byts = append(byts, []byte{byte(h.Interval >> 8), byte(h.Interval)}...)
|
||||
byts = append(byts, []byte{byte(h.Count >> 8), byte(h.Count)}...)
|
||||
return byts
|
||||
}
|
||||
6
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/rtpmjpeg.go
generated
vendored
Normal file
6
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmjpeg/rtpmjpeg.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Package rtpmjpeg contains a RTP/M-JPEG decoder and encoder.
|
||||
package rtpmjpeg
|
||||
|
||||
const (
|
||||
maxDimension = 2040
|
||||
)
|
||||
127
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/decoder.go
generated
vendored
Normal file
127
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package rtpmpeg1audio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg1audio"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented frame and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/MPEG-1/2 Audio decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Decoder struct {
|
||||
firstPacketReceived bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentsExpected int
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes frames from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 5 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
mbz := uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1])
|
||||
if mbz != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("invalid MBZ: %v", mbz)
|
||||
}
|
||||
|
||||
offset := uint16(pkt.Payload[2])<<8 | uint16(pkt.Payload[3])
|
||||
|
||||
var frames [][]byte
|
||||
|
||||
if offset == 0 {
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
|
||||
buf := pkt.Payload[4:]
|
||||
for {
|
||||
var h mpeg1audio.FrameHeader
|
||||
err := h.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fl := h.FrameLen()
|
||||
bl := len(buf)
|
||||
if bl >= fl {
|
||||
frames = append(frames, buf[:fl])
|
||||
buf = buf[fl:]
|
||||
if len(buf) == 0 {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if len(frames) != 0 {
|
||||
return nil, fmt.Errorf("invalid packet")
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, buf)
|
||||
d.fragmentsSize = bl
|
||||
d.fragmentsExpected = fl - bl
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if int(offset) != d.fragmentsSize {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("unexpected offset %v, expected %v", offset, d.fragmentsSize)
|
||||
}
|
||||
|
||||
bl := len(pkt.Payload[4:])
|
||||
d.fragmentsSize += bl
|
||||
d.fragmentsExpected -= bl
|
||||
|
||||
if d.fragmentsExpected < 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("fragment is too big")
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[4:])
|
||||
|
||||
if d.fragmentsExpected > 0 {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
frames = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return frames, nil
|
||||
}
|
||||
196
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/encoder.go
generated
vendored
Normal file
196
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
package rtpmpeg1audio
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg1audio"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func lenAggregated(frames [][]byte, frame []byte) int {
|
||||
n := 4 + len(frame)
|
||||
for _, fr := range frames {
|
||||
n += len(fr)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG-1/2 Audio encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes frames into RTP packets.
|
||||
func (e *Encoder) Encode(frames [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
timestamp := uint32(0)
|
||||
|
||||
for _, frame := range frames {
|
||||
if lenAggregated(batch, frame) <= e.PayloadMaxSize {
|
||||
batch = append(batch, frame)
|
||||
} else {
|
||||
// write current batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
for _, frame := range batch {
|
||||
var h mpeg1audio.FrameHeader
|
||||
err := h.Unmarshal(frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timestamp += uint32(h.SampleCount())
|
||||
}
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{frame}
|
||||
}
|
||||
}
|
||||
|
||||
// write last batch
|
||||
pkts, err := e.writeBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
if len(frames) != 1 || lenAggregated(frames, nil) < e.PayloadMaxSize {
|
||||
return e.writeAggregated(frames, timestamp)
|
||||
}
|
||||
|
||||
return e.writeFragmented(frames[0], timestamp)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeFragmented(frame []byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
avail := e.PayloadMaxSize - 4
|
||||
le := len(frame)
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
pos := 0
|
||||
le = avail
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(frame) - pos
|
||||
}
|
||||
|
||||
payload := make([]byte, 4+le)
|
||||
payload[2] = byte(pos >> 8)
|
||||
payload[3] = byte(pos)
|
||||
|
||||
pos += copy(payload[4:], frame[pos:])
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 14,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: true,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeAggregated(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, lenAggregated(frames, nil))
|
||||
|
||||
n := 4
|
||||
for _, frame := range frames {
|
||||
n += copy(payload[n:], frame)
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 14,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: true,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/rtpmpeg1audio.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio/rtpmpeg1audio.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpmpeg1audio contains a RTP/MPEG-1/2 Audio decoder and encoder.
|
||||
package rtpmpeg1audio
|
||||
163
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/decoder.go
generated
vendored
Normal file
163
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
package rtpmpeg1video
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
maxFrameSize = 1 * 1024 * 1024
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented frame and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/MPEG-1/2 Video decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Decoder struct {
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentNextSeqNum uint16
|
||||
|
||||
sliceBuffer [][]byte
|
||||
sliceBufferSize int
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeSlice(pkt *rtp.Packet) ([]byte, error) {
|
||||
if len(pkt.Payload) < 4 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
mbz := pkt.Payload[0] >> 3
|
||||
if mbz != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("invalid MBZ: %v", mbz)
|
||||
}
|
||||
|
||||
t := (pkt.Payload[0] >> 2) & 0x01
|
||||
if t != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("MPEG-2 video-specific header extension is not supported yet")
|
||||
}
|
||||
|
||||
an := pkt.Payload[2] >> 7
|
||||
if an != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("AN not supported yet")
|
||||
}
|
||||
|
||||
n := (pkt.Payload[2] >> 6) & 0x01
|
||||
if n != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("N not supported yet")
|
||||
}
|
||||
|
||||
b := (pkt.Payload[2] >> 4) & 0x01
|
||||
e := (pkt.Payload[2] >> 3) & 0x01
|
||||
|
||||
switch {
|
||||
case b == 1 && e == 1:
|
||||
return pkt.Payload[4:], nil
|
||||
|
||||
case b == 1:
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragments = append(d.fragments, pkt.Payload[4:])
|
||||
d.fragmentsSize = len(pkt.Payload[4:])
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
return nil, ErrMorePacketsNeeded
|
||||
|
||||
case e == 1:
|
||||
if d.fragmentsSize == 0 {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[4:])
|
||||
d.fragmentsSize += len(pkt.Payload[4:])
|
||||
|
||||
slice := joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
return slice, nil
|
||||
|
||||
default:
|
||||
if d.fragmentsSize == 0 {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload[4:])
|
||||
d.fragmentsSize += len(pkt.Payload[4:])
|
||||
d.fragmentNextSeqNum++
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
}
|
||||
|
||||
// Decode decodes frames from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
slice, err := d.decodeSlice(pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addSize := len(slice)
|
||||
|
||||
if (d.sliceBufferSize + addSize) > maxFrameSize {
|
||||
errSize := d.sliceBufferSize + addSize
|
||||
d.sliceBuffer = nil
|
||||
d.sliceBufferSize = 0
|
||||
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d",
|
||||
errSize, maxFrameSize)
|
||||
}
|
||||
|
||||
d.sliceBuffer = append(d.sliceBuffer, slice)
|
||||
d.sliceBufferSize += addSize
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
ret := joinFragments(d.sliceBuffer, d.sliceBufferSize)
|
||||
|
||||
// do not reuse sliceBuffer to avoid race conditions
|
||||
d.sliceBuffer = nil
|
||||
d.sliceBufferSize = 0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
239
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/encoder.go
generated
vendored
Normal file
239
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package rtpmpeg1video
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func lenAggregated(slices [][]byte, slice []byte) int {
|
||||
n := 4 + len(slice)
|
||||
for _, fr := range slices {
|
||||
n += len(fr)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG-1/2 Video encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes frames into RTP packets.
|
||||
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
||||
var temporalReference uint16
|
||||
beginOfSequence := uint8(0)
|
||||
var frameType uint8
|
||||
|
||||
for {
|
||||
var slice []byte
|
||||
end := bytes.Index(frame[4:], []byte{0, 0, 1})
|
||||
if end >= 0 {
|
||||
slice, frame = frame[:end+4], frame[end+4:]
|
||||
} else {
|
||||
slice, frame = frame, nil
|
||||
}
|
||||
|
||||
if lenAggregated(batch, slice) <= e.PayloadMaxSize {
|
||||
batch = append(batch, slice)
|
||||
} else {
|
||||
// write current batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeBatch(batch,
|
||||
temporalReference,
|
||||
beginOfSequence,
|
||||
frameType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
beginOfSequence = 0
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{slice}
|
||||
}
|
||||
|
||||
switch slice[3] {
|
||||
case 0:
|
||||
temporalReference = uint16(slice[4])<<2 | uint16(slice[5])>>6
|
||||
frameType = (slice[5] >> 3) & 0b111
|
||||
|
||||
case 0xB8:
|
||||
beginOfSequence = 1
|
||||
}
|
||||
|
||||
if frame == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// write last batch
|
||||
pkts, err := e.writeBatch(batch,
|
||||
temporalReference,
|
||||
beginOfSequence,
|
||||
frameType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
rets[len(rets)-1].Marker = true
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(
|
||||
slices [][]byte,
|
||||
temporalReference uint16,
|
||||
beginOfSequence uint8,
|
||||
frameType uint8,
|
||||
) ([]*rtp.Packet, error) {
|
||||
if len(slices) != 1 || lenAggregated(slices, nil) < e.PayloadMaxSize {
|
||||
return e.writeAggregated(slices, temporalReference, beginOfSequence, frameType)
|
||||
}
|
||||
|
||||
return e.writeFragmented(slices[0], temporalReference, beginOfSequence, frameType)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeFragmented(
|
||||
slice []byte,
|
||||
temporalReference uint16,
|
||||
beginOfSequence uint8,
|
||||
frameType uint8,
|
||||
) ([]*rtp.Packet, error) {
|
||||
avail := e.PayloadMaxSize - 4
|
||||
le := len(slice)
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
le = avail
|
||||
start := uint8(1)
|
||||
end := uint8(0)
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(slice)
|
||||
end = 1
|
||||
}
|
||||
|
||||
payload := make([]byte, 4+le)
|
||||
payload[0] = byte(temporalReference >> 8)
|
||||
payload[1] = byte(temporalReference)
|
||||
payload[2] = beginOfSequence<<5 | start<<4 | end<<3 | frameType
|
||||
copy(payload[4:], slice)
|
||||
slice = slice[le:]
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 32,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
start = 0
|
||||
beginOfSequence = 0
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeAggregated(
|
||||
slices [][]byte,
|
||||
temporalReference uint16,
|
||||
beginOfSequence uint8,
|
||||
frameType uint8,
|
||||
) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, lenAggregated(slices, nil))
|
||||
|
||||
payload[0] = byte(temporalReference >> 8)
|
||||
payload[1] = byte(temporalReference)
|
||||
payload[2] = beginOfSequence<<5 | 1<<4 | 1<<3 | frameType
|
||||
|
||||
n := 4
|
||||
for _, slice := range slices {
|
||||
n += copy(payload[n:], slice)
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 32,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/rtpmpeg1video.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1video/rtpmpeg1video.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpmpeg1video contains a RTP/MPEG-1/2 Video decoder and encoder.
|
||||
package rtpmpeg1video
|
||||
62
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder.go
generated
vendored
Normal file
62
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/MPEG-4 Audio decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
|
||||
type Decoder struct {
|
||||
// use RFC6416 (LATM) instead of RFC3640 (generic).
|
||||
LATM bool
|
||||
|
||||
// Generic-only
|
||||
// The number of bits in which the AU-size field is encoded in the AU-header.
|
||||
SizeLength int
|
||||
// The number of bits in which the AU-Index is encoded in the first AU-header.
|
||||
IndexLength int
|
||||
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
|
||||
IndexDeltaLength int
|
||||
|
||||
firstAUParsed bool
|
||||
adtsMode bool
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentsExpected int
|
||||
fragmentNextSeqNum uint16
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes AUs from a RTP packet.
|
||||
// It returns the AUs and the PTS of the first AU.
|
||||
// The PTS of subsequent AUs can be calculated by adding time.Second*mpeg4audio.SamplesPerAccessUnit/clockRate.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if !d.LATM {
|
||||
return d.decodeGeneric(pkt)
|
||||
}
|
||||
return d.decodeLATM(pkt)
|
||||
}
|
||||
201
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder_generic.go
generated
vendored
Normal file
201
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder_generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/bits"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
func (d *Decoder) decodeGeneric(pkt *rtp.Packet) ([][]byte, error) {
|
||||
if len(pkt.Payload) < 2 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
// AU-headers-length (16 bits)
|
||||
headersLen := int(uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1]))
|
||||
if headersLen == 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("invalid AU-headers-length")
|
||||
}
|
||||
payload := pkt.Payload[2:]
|
||||
|
||||
// AU-headers
|
||||
dataLens, err := d.readAUHeaders(payload, headersLen)
|
||||
if err != nil {
|
||||
d.resetFragments()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pos := (headersLen / 8)
|
||||
if (headersLen % 8) != 0 {
|
||||
pos++
|
||||
}
|
||||
payload = payload[pos:]
|
||||
|
||||
var aus [][]byte
|
||||
|
||||
if d.fragmentsSize == 0 {
|
||||
d.resetFragments()
|
||||
|
||||
if pkt.Marker {
|
||||
// AUs
|
||||
aus = make([][]byte, len(dataLens))
|
||||
for i, dataLen := range dataLens {
|
||||
if len(payload) < int(dataLen) {
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
aus[i] = payload[:dataLen]
|
||||
payload = payload[dataLen:]
|
||||
}
|
||||
} else {
|
||||
if len(dataLens) != 1 {
|
||||
return nil, fmt.Errorf("a fragmented packet can only contain one AU")
|
||||
}
|
||||
|
||||
if len(payload) < int(dataLens[0]) {
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
d.fragmentsSize = int(dataLens[0])
|
||||
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
} else {
|
||||
// we are decoding a fragmented AU
|
||||
if len(dataLens) != 1 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("a fragmented packet can only contain one AU")
|
||||
}
|
||||
|
||||
if len(payload) < int(dataLens[0]) {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += int(dataLens[0])
|
||||
|
||||
if d.fragmentsSize > mpeg4audio.MaxAccessUnitSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
|
||||
errSize, mpeg4audio.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
aus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return d.removeADTS(aus)
|
||||
}
|
||||
|
||||
func (d *Decoder) readAUHeaders(buf []byte, headersLen int) ([]uint64, error) {
|
||||
firstRead := false
|
||||
|
||||
count := 0
|
||||
for i := 0; i < headersLen; {
|
||||
if i == 0 {
|
||||
i += d.SizeLength
|
||||
i += d.IndexLength
|
||||
} else {
|
||||
i += d.SizeLength
|
||||
i += d.IndexDeltaLength
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
dataLens := make([]uint64, count)
|
||||
|
||||
pos := 0
|
||||
i := 0
|
||||
|
||||
for headersLen > 0 {
|
||||
dataLen, err := bits.ReadBits(buf, &pos, d.SizeLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headersLen -= d.SizeLength
|
||||
|
||||
if !firstRead {
|
||||
firstRead = true
|
||||
if d.IndexLength > 0 {
|
||||
auIndex, err := bits.ReadBits(buf, &pos, d.IndexLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headersLen -= d.IndexLength
|
||||
|
||||
if auIndex != 0 {
|
||||
return nil, fmt.Errorf("AU-index different than zero is not supported")
|
||||
}
|
||||
}
|
||||
} else if d.IndexDeltaLength > 0 {
|
||||
auIndexDelta, err := bits.ReadBits(buf, &pos, d.IndexDeltaLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headersLen -= d.IndexDeltaLength
|
||||
|
||||
if auIndexDelta != 0 {
|
||||
return nil, fmt.Errorf("AU-index-delta different than zero is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
dataLens[i] = dataLen
|
||||
i++
|
||||
}
|
||||
|
||||
return dataLens, nil
|
||||
}
|
||||
|
||||
// some cameras wrap AUs into ADTS
|
||||
func (d *Decoder) removeADTS(aus [][]byte) ([][]byte, error) {
|
||||
if !d.firstAUParsed {
|
||||
d.firstAUParsed = true
|
||||
|
||||
if len(aus) == 1 && len(aus[0]) >= 2 {
|
||||
if aus[0][0] == 0xFF && (aus[0][1]&0xF0) == 0xF0 {
|
||||
var pkts mpeg4audio.ADTSPackets
|
||||
err := pkts.Unmarshal(aus[0])
|
||||
if err == nil && len(pkts) == 1 {
|
||||
d.adtsMode = true
|
||||
aus[0] = pkts[0].AU
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if d.adtsMode {
|
||||
if len(aus) != 1 {
|
||||
return nil, fmt.Errorf("multiple AUs in ADTS mode are not supported")
|
||||
}
|
||||
|
||||
var pkts mpeg4audio.ADTSPackets
|
||||
err := pkts.Unmarshal(aus[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode ADTS: %w", err)
|
||||
}
|
||||
|
||||
if len(pkts) != 1 {
|
||||
return nil, fmt.Errorf("multiple ADTS packets are not supported")
|
||||
}
|
||||
|
||||
aus[0] = pkts[0].AU
|
||||
}
|
||||
|
||||
return aus, nil
|
||||
}
|
||||
63
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder_latm.go
generated
vendored
Normal file
63
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/decoder_latm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
func (d *Decoder) decodeLATM(pkt *rtp.Packet) ([][]byte, error) {
|
||||
var au []byte
|
||||
buf := pkt.Payload
|
||||
|
||||
if d.fragmentsSize == 0 {
|
||||
pl, n, err := payloadLengthInfoDecode(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf = buf[n:]
|
||||
bl := len(buf)
|
||||
|
||||
if pl <= bl {
|
||||
au = buf[:pl]
|
||||
// there could be other data, due to otherDataPresent. Ignore it.
|
||||
} else {
|
||||
if pl > mpeg4audio.MaxAccessUnitSize {
|
||||
errSize := pl
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
|
||||
errSize, mpeg4audio.MaxAccessUnitSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, buf)
|
||||
d.fragmentsSize = pl
|
||||
d.fragmentsExpected = pl - bl
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
} else {
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
bl := len(buf)
|
||||
|
||||
if d.fragmentsExpected > bl {
|
||||
d.fragments = append(d.fragments, buf)
|
||||
d.fragmentsExpected -= bl
|
||||
d.fragmentNextSeqNum++
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, buf[:d.fragmentsExpected])
|
||||
// there could be other data, due to otherDataPresent. Ignore it.
|
||||
|
||||
au = joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return [][]byte{au}, nil
|
||||
}
|
||||
88
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder.go
generated
vendored
Normal file
88
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG-4 audio encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// use RFC6416 (LATM) instead of RFC3640 (generic).
|
||||
LATM bool
|
||||
|
||||
// The number of bits in which the AU-size field is encoded in the AU-header.
|
||||
SizeLength int
|
||||
|
||||
// The number of bits in which the AU-Index is encoded in the first AU-header.
|
||||
IndexLength int
|
||||
|
||||
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
|
||||
IndexDeltaLength int
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes AUs into RTP packets.
|
||||
func (e *Encoder) Encode(aus [][]byte) ([]*rtp.Packet, error) {
|
||||
if !e.LATM {
|
||||
return e.encodeGeneric(aus)
|
||||
}
|
||||
return e.encodeLATM(aus)
|
||||
}
|
||||
196
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder_generic.go
generated
vendored
Normal file
196
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder_generic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/bits"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
)
|
||||
|
||||
func packetCountGeneric(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeGeneric(aus [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
timestamp := uint32(0)
|
||||
|
||||
// split AUs into batches
|
||||
for _, au := range aus {
|
||||
if e.lenGenericAggregated(batch, au) <= e.PayloadMaxSize {
|
||||
// add to existing batch
|
||||
batch = append(batch, au)
|
||||
} else {
|
||||
// write current batch
|
||||
if batch != nil {
|
||||
pkts, err := e.writeGenericBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
timestamp += uint32(len(batch)) * mpeg4audio.SamplesPerAccessUnit
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{au}
|
||||
}
|
||||
}
|
||||
|
||||
// write last batch
|
||||
pkts, err := e.writeGenericBatch(batch, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkts...)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeGenericBatch(aus [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
if len(aus) != 1 || e.lenGenericAggregated(aus, nil) < e.PayloadMaxSize {
|
||||
return e.writeGenericAggregated(aus, timestamp)
|
||||
}
|
||||
|
||||
return e.writeGenericFragmented(aus[0], timestamp)
|
||||
}
|
||||
|
||||
func (e *Encoder) writeGenericFragmented(au []byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
auHeadersLen := e.SizeLength + e.IndexLength
|
||||
auHeadersLenBytes := auHeadersLen / 8
|
||||
if (auHeadersLen % 8) != 0 {
|
||||
auHeadersLenBytes++
|
||||
}
|
||||
|
||||
avail := e.PayloadMaxSize - 2 - auHeadersLenBytes
|
||||
le := len(au)
|
||||
packetCount := packetCountGeneric(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
le = avail
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(au)
|
||||
}
|
||||
|
||||
payload := make([]byte, 2+auHeadersLenBytes+le)
|
||||
|
||||
// AU-headers-length
|
||||
payload[0] = byte(auHeadersLen >> 8)
|
||||
payload[1] = byte(auHeadersLen)
|
||||
|
||||
// AU-headers
|
||||
pos := 0
|
||||
bits.WriteBitsUnsafe(payload[2:], &pos, uint64(le), e.SizeLength)
|
||||
bits.WriteBitsUnsafe(payload[2:], &pos, 0, e.IndexLength)
|
||||
|
||||
// AU
|
||||
copy(payload[2+auHeadersLenBytes:], au)
|
||||
au = au[le:]
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: (i == packetCount-1),
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) lenGenericAggregated(aus [][]byte, addAU []byte) int {
|
||||
n := 2 // AU-headers-length
|
||||
|
||||
// AU-headers
|
||||
auHeadersLen := 0
|
||||
i := 0
|
||||
for range aus {
|
||||
if i == 0 {
|
||||
auHeadersLen += e.SizeLength + e.IndexLength
|
||||
} else {
|
||||
auHeadersLen += e.SizeLength + e.IndexDeltaLength
|
||||
}
|
||||
i++
|
||||
}
|
||||
if addAU != nil {
|
||||
if i == 0 {
|
||||
auHeadersLen += e.SizeLength + e.IndexLength
|
||||
} else {
|
||||
auHeadersLen += e.SizeLength + e.IndexDeltaLength
|
||||
}
|
||||
}
|
||||
n += auHeadersLen / 8
|
||||
if (auHeadersLen % 8) != 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
// AU
|
||||
for _, au := range aus {
|
||||
n += len(au)
|
||||
}
|
||||
n += len(addAU)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (e *Encoder) writeGenericAggregated(aus [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
payload := make([]byte, e.lenGenericAggregated(aus, nil))
|
||||
|
||||
// AU-headers
|
||||
written := 0
|
||||
pos := 0
|
||||
for i, au := range aus {
|
||||
bits.WriteBitsUnsafe(payload[2:], &pos, uint64(len(au)), e.SizeLength)
|
||||
written += e.SizeLength
|
||||
if i == 0 {
|
||||
bits.WriteBitsUnsafe(payload[2:], &pos, 0, e.IndexLength)
|
||||
written += e.IndexLength
|
||||
} else {
|
||||
bits.WriteBitsUnsafe(payload[2:], &pos, 0, e.IndexDeltaLength)
|
||||
written += e.IndexDeltaLength
|
||||
}
|
||||
}
|
||||
pos = 2 + (written / 8)
|
||||
if (written % 8) != 0 {
|
||||
pos++
|
||||
}
|
||||
|
||||
// AU-headers-length
|
||||
payload[0] = byte(written >> 8)
|
||||
payload[1] = byte(written)
|
||||
|
||||
// AUs
|
||||
for _, au := range aus {
|
||||
auLen := copy(payload[pos:], au)
|
||||
pos += auLen
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: true,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return []*rtp.Packet{pkt}, nil
|
||||
}
|
||||
76
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder_latm.go
generated
vendored
Normal file
76
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/encoder_latm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
|
||||
)
|
||||
|
||||
func (e *Encoder) packetCountLATM(auLen int, plil int) int {
|
||||
totalLen := plil + auLen
|
||||
n := totalLen / e.PayloadMaxSize
|
||||
if (totalLen % e.PayloadMaxSize) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeLATM(aus [][]byte) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
|
||||
for i, au := range aus {
|
||||
timestamp := uint32(i) * mpeg4audio.SamplesPerAccessUnit
|
||||
|
||||
add, err := e.encodeLATMSingle(au, timestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, add...)
|
||||
}
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeLATMSingle(au []byte, timestamp uint32) ([]*rtp.Packet, error) {
|
||||
auLen := len(au)
|
||||
plil := payloadLengthInfoEncodeSize(auLen)
|
||||
packetCount := e.packetCountLATM(auLen, plil)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
le := e.PayloadMaxSize - plil
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(au)
|
||||
}
|
||||
|
||||
var payload []byte
|
||||
|
||||
if i == 0 {
|
||||
payload = make([]byte, plil+le)
|
||||
payloadLengthInfoEncode(plil, auLen, payload)
|
||||
copy(payload[plil:], au[:le])
|
||||
au = au[le:]
|
||||
le = e.PayloadMaxSize
|
||||
} else {
|
||||
payload = au[:le]
|
||||
au = au[le:]
|
||||
}
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: timestamp,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: (i == packetCount-1),
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
38
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/payload_length_info.go
generated
vendored
Normal file
38
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/payload_length_info.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package rtpmpeg4audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func payloadLengthInfoDecode(buf []byte) (int, int, error) {
|
||||
lb := len(buf)
|
||||
l := 0
|
||||
n := 0
|
||||
|
||||
for {
|
||||
if (lb - n) == 0 {
|
||||
return 0, 0, fmt.Errorf("not enough bytes")
|
||||
}
|
||||
|
||||
b := buf[n]
|
||||
n++
|
||||
l += int(b)
|
||||
|
||||
if b != 255 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return l, n, nil
|
||||
}
|
||||
|
||||
func payloadLengthInfoEncodeSize(auLen int) int {
|
||||
return auLen/255 + 1
|
||||
}
|
||||
|
||||
func payloadLengthInfoEncode(plil int, auLen int, buf []byte) {
|
||||
for i := 0; i < (plil - 1); i++ {
|
||||
buf[i] = 255
|
||||
}
|
||||
buf[plil-1] = byte(auLen % 255)
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/rtpmpeg4audio.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio/rtpmpeg4audio.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpmpeg4audio contains a RTP/MPEG-4 Audio decoder and encoder.
|
||||
package rtpmpeg4audio
|
||||
82
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/decoder.go
generated
vendored
Normal file
82
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package rtpmpeg4video
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4video"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/MPEG-4 Video decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
|
||||
type Decoder struct {
|
||||
fragments [][]byte
|
||||
fragmentsSize int
|
||||
fragmentNextSeqNum uint16
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes a frame from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
var frame []byte
|
||||
|
||||
if d.fragmentsSize == 0 {
|
||||
if pkt.Marker {
|
||||
frame = pkt.Payload
|
||||
} else {
|
||||
d.fragmentsSize = len(pkt.Payload)
|
||||
d.fragments = append(d.fragments, pkt.Payload)
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
} else {
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(pkt.Payload)
|
||||
|
||||
if d.fragmentsSize > mpeg4video.MaxFrameSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d",
|
||||
errSize, mpeg4video.MaxFrameSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, pkt.Payload)
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
frame = joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
108
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/encoder.go
generated
vendored
Normal file
108
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package rtpmpeg4video
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
func packetCount(avail, le int) int {
|
||||
n := le / avail
|
||||
if (le % avail) != 0 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG-4 Video encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes a frame into RTP packets.
|
||||
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
|
||||
avail := e.PayloadMaxSize
|
||||
le := len(frame)
|
||||
packetCount := packetCount(avail, le)
|
||||
|
||||
ret := make([]*rtp.Packet, packetCount)
|
||||
pos := 0
|
||||
le = avail
|
||||
|
||||
for i := range ret {
|
||||
if i == (packetCount - 1) {
|
||||
le = len(frame[pos:])
|
||||
}
|
||||
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: (i == packetCount-1),
|
||||
},
|
||||
Payload: frame[pos : pos+le],
|
||||
}
|
||||
|
||||
pos += le
|
||||
e.sequenceNumber++
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/rtpmpeg4video.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video/rtpmpeg4video.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpmpeg4video contains a RTP/MPEG-4 Video decoder and encoder.
|
||||
package rtpmpeg4video
|
||||
18
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/decoder.go
generated
vendored
Normal file
18
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package rtpsimpleaudio
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// Decoder is a RTP/simple audio decoder.
|
||||
type Decoder struct{}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode decodes an audio frame from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
return pkt.Payload, nil
|
||||
}
|
||||
89
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/encoder.go
generated
vendored
Normal file
89
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package rtpsimpleaudio
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/simple audio encoder.
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes an audio frame into a RTP packet.
|
||||
func (e *Encoder) Encode(frame []byte) (*rtp.Packet, error) {
|
||||
if len(frame) > e.PayloadMaxSize {
|
||||
return nil, fmt.Errorf("frame is too big")
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: false,
|
||||
},
|
||||
Payload: frame,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/rtpsimpleaudio.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio/rtpsimpleaudio.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpsimpleaudio contains a RTP decoder and encoder for audio codecs that fit in a single packet.
|
||||
package rtpsimpleaudio
|
||||
113
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/decoder.go
generated
vendored
Normal file
113
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package rtpvp8
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/vp8"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
)
|
||||
|
||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// ErrNonStartingPacketAndNoPrevious is returned when we received a non-starting
|
||||
// packet of a fragmented frame and we didn't received anything before.
|
||||
// It's normal to receive this when decoding a stream that has been already
|
||||
// running for some time.
|
||||
var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
func joinFragments(fragments [][]byte, size int) []byte {
|
||||
ret := make([]byte, size)
|
||||
n := 0
|
||||
for _, p := range fragments {
|
||||
n += copy(ret[n:], p)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decoder is a RTP/VP8 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
||||
type Decoder struct {
|
||||
firstPacketReceived bool
|
||||
fragmentsSize int
|
||||
fragments [][]byte
|
||||
fragmentNextSeqNum uint16
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) resetFragments() {
|
||||
d.fragments = d.fragments[:0]
|
||||
d.fragmentsSize = 0
|
||||
}
|
||||
|
||||
// Decode decodes a VP8 frame from a RTP packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
|
||||
var vpkt codecs.VP8Packet
|
||||
_, err := vpkt.Unmarshal(pkt.Payload)
|
||||
if err != nil {
|
||||
d.resetFragments()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vpkt.PID != 0 {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("packets containing single partitions are not supported")
|
||||
}
|
||||
|
||||
var frame []byte
|
||||
|
||||
if vpkt.S == 1 {
|
||||
d.resetFragments()
|
||||
d.firstPacketReceived = true
|
||||
|
||||
if !pkt.Marker {
|
||||
d.fragmentsSize = len(vpkt.Payload)
|
||||
d.fragments = append(d.fragments, vpkt.Payload)
|
||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
frame = vpkt.Payload
|
||||
} else {
|
||||
if d.fragmentsSize == 0 {
|
||||
if !d.firstPacketReceived {
|
||||
return nil, ErrNonStartingPacketAndNoPrevious
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("received a non-starting fragment")
|
||||
}
|
||||
|
||||
if pkt.SequenceNumber != d.fragmentNextSeqNum {
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("discarding frame since a RTP packet is missing")
|
||||
}
|
||||
|
||||
d.fragmentsSize += len(vpkt.Payload)
|
||||
|
||||
if d.fragmentsSize > vp8.MaxFrameSize {
|
||||
errSize := d.fragmentsSize
|
||||
d.resetFragments()
|
||||
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d",
|
||||
errSize, vp8.MaxFrameSize)
|
||||
}
|
||||
|
||||
d.fragments = append(d.fragments, vpkt.Payload)
|
||||
d.fragmentNextSeqNum++
|
||||
|
||||
if !pkt.Marker {
|
||||
return nil, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
frame = joinFragments(d.fragments, d.fragmentsSize)
|
||||
d.resetFragments()
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
98
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/encoder.go
generated
vendored
Normal file
98
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package rtpvp8
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
defaultPayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
)
|
||||
|
||||
func randUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
|
||||
}
|
||||
|
||||
// Encoder is a RTP/VP8 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
vp codecs.VP8Payloader
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() error {
|
||||
if e.SSRC == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v, err := randUint32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v2 := uint16(v)
|
||||
e.InitialSequenceNumber = &v2
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes a VP8 frame into RTP/VP8 packets.
|
||||
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
|
||||
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
|
||||
if payloads == nil {
|
||||
return nil, fmt.Errorf("payloader failed")
|
||||
}
|
||||
|
||||
plen := len(payloads)
|
||||
ret := make([]*rtp.Packet, plen)
|
||||
|
||||
for i, payload := range payloads {
|
||||
ret[i] = &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: e.PayloadType,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
SSRC: *e.SSRC,
|
||||
Marker: i == (plen - 1),
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/rtpvp8.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8/rtpvp8.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package rtpvp8 contains a RTP/VP8 decoder and encoder.
|
||||
package rtpvp8
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user