Compare commits

..

53 Commits

Author SHA1 Message Date
kunmeng
0e65c9d1cc 将包名改为 git.r-2.top/kunmeng/vdk
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
2025-01-24 14:45:45 +08:00
kunmeng
bcb44378a5 解析 RTP 包时,将 RTP 的扩展部分解析出来
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (go) (push) Waiting to run
2025-01-24 11:46:13 +08:00
Andrey Semochkin
dc22c36964 testing 2024-12-18 14:00:58 +03:00
Andrey Semochkin
7bb8737cc8 Merge remote-tracking branch 'origin/master' 2024-12-18 10:46:09 +03:00
Andrey Semochkin
5438b5fbf7 testing 2024-12-18 10:46:02 +03:00
Andrey Semochkin
2979d3f55d
Merge pull request #111 from chaymankala/master
Added Dial function for Replay stream
2024-10-27 11:03:40 +03:00
Chaitanya Mankala
3c1eaad780
[update] added Dial function for Repla stream on ONVIF 2024-10-24 18:46:25 +05:30
deepch
b6b1f4a437 testing 2024-04-30 17:06:05 +03:00
deepch
f6346a05aa testing 2024-04-29 13:11:11 +03:00
Andrey Semochkin
413b746b2f add range header 2024-03-05 14:52:49 +03:00
Andrey Semochkin
c6f36a9a89 style 2024-02-11 00:05:40 +07:00
Andrey Semochkin
230b6c6ad1 style 2024-02-10 23:50:56 +07:00
Andrey Semochkin
fd7ecfeaad style 2024-02-10 23:20:37 +07:00
Andrey Semochkin
4b060bc442 fix dig 2024-02-08 01:00:12 +07:00
Andrey Semochkin
9f075c6682 fix hostShort / hostLong bug 2024-02-07 20:44:30 +07:00
Andrey Semochkin
0e06666006 fix listTag bug 2024-02-07 20:33:12 +07:00
deepch
b84c19f719 testing 2024-02-02 02:48:16 +03:00
deepch
7a563b07e3 testing 2024-01-29 12:46:06 +03:00
deepch
a743575ac9 testing 2024-01-22 16:07:30 +03:00
deepch
e4035e4407 testing 2024-01-22 11:36:06 +03:00
deepch
4df258b899 testing 2024-01-10 18:02:17 +03:00
deepch
385d9cc93c testing 2024-01-08 18:08:19 +03:00
deepch
06a7f70701 Merge remote-tracking branch 'origin/master' 2024-01-08 17:36:09 +03:00
deepch
975d994507 testing 2024-01-08 17:35:54 +03:00
Andrey Semochkin
eaab841cfb
Update mp4io.go 2023-11-20 10:00:15 +03:00
deepch
9d21d056dd test 2023-08-25 14:35:34 +03:00
deepch
5a989a5c40 test 2023-05-25 13:08:57 +03:00
deepch
0b08aa7224 test 2023-05-25 13:07:13 +03:00
deepch
9e244f72bf test 2023-05-25 12:35:12 +03:00
deepch
8d4be0821c test 2023-05-25 12:00:15 +03:00
deepch
8958197548 fix ip camera bug 2023-04-02 17:58:56 +03:00
deepch
def88cb695 test transcode slow 2023-03-22 22:01:39 +03:00
deepch
175afae1c1 test transcode slow 2023-03-22 21:59:06 +03:00
deepch
c923e0010b fix rtmp handshake flashphoner 2023-03-13 19:26:29 +03:00
deepch
cc3f215a23 mod update 2023-03-12 00:32:08 +03:00
deepch
f567925cf9 [RTMP] client / server full support h265 video codec 2022-10-25 11:24:20 +03:00
deepch
472775f07c [RTMP] client / server full support h265 video codec 2022-10-25 11:16:23 +03:00
deepch
b31edccea5 test h265 2022-09-22 11:15:59 +03:00
deepch
a027ba9c94 Merge remote-tracking branch 'origin/master' 2022-09-22 11:14:04 +03:00
deepch
57a79ff9e2 test h265 2022-09-22 11:13:57 +03:00
Andrey Semochkin
8679fe28f1
Merge pull request #67 from idcpj/master
fix parse sdp  sprop-parameter-sets
2022-09-13 08:50:29 +03:00
陈鹏杰
e8d8603d2c
fix parse sdp sprop-parameter-sets
when sprop-parameter-sets  value is "sprop-parameter-sets=,xxxxxx",the first param is emprty
2022-09-13 09:20:14 +08:00
deepch
61aa8e4f72 fix h264 mp4 bug 2022-08-04 21:11:38 +03:00
Andrey Semochkin
030575e585
Merge pull request #65 from osery/osery/ingore-rtcp
Skip RTCP packets when these are multiplexed on the same port.
2022-08-04 21:09:44 +03:00
Ondrej Sery
24c185b51e Skip RTCP packets when these are multiplexed on the same port.
Whithout this change, the packet is miss interpretted as a valid one and
breaks packet sequence as well as duration computation from prev.
packet timestamp.

Following description here https://www.rfc-editor.org/rfc/rfc5761#section-4.
The second header byte is the best way to discriminate RTP data stream
from RTCP packets. This change only focuses on the common scenario of
Senders and Receiver report packets. Other RTCP packets are more
ambiguous as they potentially conflict with payload types.
2022-08-04 19:32:31 +02:00
deepch
31d128e853 fix ContinuityCounter bug 2022-07-16 16:02:21 +03:00
deepch
37a9a8619c test drop rtc 2022-07-04 17:01:23 +03:00
deepch
417f833371 mjpeg 2022-06-14 15:32:10 +03:00
deepch
b2320900f5 Merge remote-tracking branch 'origin/master' 2022-06-05 22:28:49 +03:00
deepch
898c5e5d57 fps 2022-06-05 22:26:12 +03:00
deepch
4db7d612fc codec Equal 2022-05-27 03:47:09 +03:00
deepch
2be4196875 go mod update 2022-05-27 01:29:09 +03:00
deepch
7d87dbe2dd test annex 2022-04-27 23:59:59 +03:00
90 changed files with 2083 additions and 795 deletions

View File

@ -43,7 +43,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -68,4 +68,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v1

View File

@ -121,6 +121,7 @@ var (
VP8 = MakeVideoCodecType(avCodecTypeMagic + 4)
VP9 = MakeVideoCodecType(avCodecTypeMagic + 5)
AV1 = MakeVideoCodecType(avCodecTypeMagic + 6)
MJPEG = MakeVideoCodecType(avCodecTypeMagic + 7)
AAC = MakeAudioCodecType(avCodecTypeMagic + 1)
PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2)
PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3)
@ -254,6 +255,8 @@ type Packet struct {
Time time.Duration // packet decode time
Duration time.Duration //packet duration
Data []byte // packet data
Extension bool
Extensions []byte
}
// Raw audio frame.

View File

@ -5,10 +5,10 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/av/pktque"
"github.com/deepch/vdk/av/transcode"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/av/pktque"
"git.r-2.top/kunmeng/vdk/av/transcode"
)
var Debug bool

View File

@ -9,7 +9,9 @@ import (
"path"
"strings"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
)
type HandlerDemuxer struct {
@ -310,3 +312,31 @@ func CopyFile(dst av.Muxer, src av.Demuxer) (err error) {
}
return
}
func Equal(c1 []av.CodecData, c2 []av.CodecData) bool {
if len(c1) != len(c2) {
return false
}
for i, codec := range c1 {
if codec.Type() != c2[i].Type() {
return false
}
switch codec.Type() {
case av.H264:
if eq := bytes.Compare(
codec.(h264parser.CodecData).AVCDecoderConfRecordBytes(),
c2[i].(h264parser.CodecData).AVCDecoderConfRecordBytes(),
); eq != 0 {
return false
}
case av.AAC:
if eq := bytes.Compare(
codec.(aacparser.CodecData).MPEG4AudioConfigBytes(),
c2[i].(aacparser.CodecData).MPEG4AudioConfigBytes(),
); eq != 0 {
return false
}
}
}
return true
}

View File

@ -1,7 +1,7 @@
package pktque
import (
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type Buf struct {

View File

@ -4,7 +4,7 @@ package pktque
import (
"time"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type Filter interface {
@ -36,7 +36,7 @@ type FilterDemuxer struct {
audioidx int
}
func (self FilterDemuxer) ReadPacket() (pkt av.Packet, err error) {
func (self *FilterDemuxer) ReadPacket() (pkt av.Packet, err error) {
if self.streams == nil {
if self.streams, err = self.Demuxer.Streams(); err != nil {
return
@ -170,6 +170,20 @@ func (self *AVSync) check(i int) (start time.Duration, end time.Duration, correc
return
}
type CalcDuration struct {
LastTime map[int8]time.Duration
}
func (self *CalcDuration) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
if tmp, ok := self.LastTime[pkt.Idx]; ok && tmp != 0 {
pkt.Duration = pkt.Time - self.LastTime[pkt.Idx]
} else if pkt.Time < 100*time.Millisecond {
pkt.Duration = pkt.Time
}
self.LastTime[pkt.Idx] = pkt.Time
return
}
// Make packets reading speed as same as walltime, effect like ffmpeg -re option.
type Walltime struct {
firsttime time.Time

View File

@ -6,8 +6,8 @@ import (
"sync"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/pktque"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/pktque"
)
// time

View File

@ -5,8 +5,8 @@ import (
"fmt"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/pktque"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/pktque"
)
var Debug bool

View File

@ -17,9 +17,9 @@ import (
"time"
"unsafe"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
)
const debug = false

View File

@ -20,8 +20,8 @@ import (
"runtime"
"unsafe"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
)
type VideoDecoder struct {

View File

@ -6,8 +6,8 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/utils/bits"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/utils/bits"
)
// copied from libavcodec/mpeg4audio.h

View File

@ -3,8 +3,8 @@ package codec
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/fake"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/fake"
)
type OpusCodecData struct {

View File

@ -1,7 +1,7 @@
package fake
import (
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type CodecData struct {

View File

@ -5,10 +5,11 @@ import (
"fmt"
"math"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/utils/bits"
"github.com/deepch/vdk/utils/bits/pio"
"time"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/utils/bits"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const (
@ -664,13 +665,21 @@ func (self CodecData) AVCDecoderConfRecordBytes() []byte {
}
func (self CodecData) SPS() []byte {
if len(self.RecordInfo.SPS) > 0 {
return self.RecordInfo.SPS[0]
}
return []byte{0}
}
func (self CodecData) PPS() []byte {
if len(self.RecordInfo.PPS) > 0 {
return self.RecordInfo.PPS[0]
}
return []byte{0}
}
func (self CodecData) Width() int {
return int(self.SPSInfo.Width)
}

View File

@ -4,10 +4,11 @@ import (
"bytes"
"errors"
"fmt"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/utils/bits"
"github.com/deepch/vdk/utils/bits/pio"
"time"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/utils/bits"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type SPSInfo struct {
@ -462,7 +463,8 @@ func (self CodecData) Resolution() string {
}
func (self CodecData) Tag() string {
return fmt.Sprintf("hvc1.%02X%02X%02X", self.RecordInfo.AVCProfileIndication, self.RecordInfo.ProfileCompatibility, self.RecordInfo.AVCLevelIndication)
//return fmt.Sprintf("hvc1.%02X%02X%02X", self.RecordInfo.AVCProfileIndication, self.RecordInfo.ProfileCompatibility, self.RecordInfo.AVCLevelIndication)
return "hev1.1.6.L120.90"
}
func (self CodecData) Bandwidth() string {
@ -529,7 +531,7 @@ type AVCDecoderConfRecord struct {
var ErrDecconfInvalid = fmt.Errorf("h265parser: AVCDecoderConfRecord invalid")
func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
if len(b) < 7 {
if len(b) < 30 {
err = ErrDecconfInvalid
return
}
@ -537,8 +539,36 @@ func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
self.ProfileCompatibility = b[2]
self.AVCLevelIndication = b[3]
self.LengthSizeMinusOne = b[4] & 0x03
spscount := int(b[5] & 0x1f)
n += 6
vpscount := int(b[25] & 0x1f)
n += 26
for i := 0; i < vpscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
vpslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+vpslen {
err = ErrDecconfInvalid
return
}
self.VPS = append(self.VPS, b[n:n+vpslen])
n += vpslen
}
if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
n++
n++
spscount := int(b[n])
n++
for i := 0; i < spscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
@ -555,10 +585,8 @@ func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
n += spslen
}
if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
n++
n++
ppscount := int(b[n])
n++
@ -578,25 +606,6 @@ func (self *AVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
self.PPS = append(self.PPS, b[n:n+ppslen])
n += ppslen
}
vpscount := int(b[n])
n++
for i := 0; i < vpscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
vpslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+vpslen {
err = ErrDecconfInvalid
return
}
self.VPS = append(self.VPS, b[n:n+vpslen])
n += vpslen
}
return
}

10
codec/mjpeg/parser.go Normal file
View File

@ -0,0 +1,10 @@
package mjpeg
import "git.r-2.top/kunmeng/vdk/av"
type CodecData struct {
}
func (d CodecData) Type() av.CodecType {
return av.MJPEG
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"time"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type CodecData struct {

25
example/test/main.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"git.r-2.top/kunmeng/vdk/format/ts"
"log"
"os"
)
func main() {
f, _ := os.Open("edb9708f29b24ba9b175808d6b9df9c6541e25766d4a40209a8f903948b72f3f.ts")
m := ts.NewDemuxer(f)
var i int
for {
p, err := m.ReadPacket()
if err != nil {
return
}
if p.IsKeyFrame {
i = 0
}
log.Println(i, p.Time, p.Data[4:10], len(p.Data))
i++
}
}

View File

@ -0,0 +1,71 @@
package main
import (
"context"
"encoding/binary"
"git.r-2.top/kunmeng/vdk/format/rtspv2"
"git.r-2.top/kunmeng/vdk/format/ts"
"log"
"os/exec"
"time"
)
func main() {
RTSPClient, err := rtspv2.Dial(rtspv2.RTSPClientOptions{URL: "rtsp://192.168.211.210:8554", DisableAudio: true, DialTimeout: 3 * time.Second, ReadWriteTimeout: 5 * time.Second, Debug: false, OutgoingProxy: false})
if err != nil {
panic(err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cmd := exec.CommandContext(ctx, "ffmpeg", "-flags", "low_delay", "-analyzeduration", "1", "-fflags", "-nobuffer", "-probesize", "1024k", "-f", "mpegts", "-i", "-", "-vcodec", "libx264", "-preset", "ultrafast", "-bf", "0", "-f", "mpegts", "-max_muxing_queue_size", "400", "-pes_payload_size", "0", "pipe:1")
inPipe, _ := cmd.StdinPipe()
outPipe, _ := cmd.StdoutPipe()
//cmd.Stderr = os.Stderr
mux := ts.NewMuxer(inPipe)
demuxer := ts.NewDemuxer(outPipe)
codec := RTSPClient.CodecData
mux.WriteHeader(codec)
go func() {
imNewCodec, err := demuxer.Streams()
log.Println("new codec data", imNewCodec, err)
//for i, data := range imNewCodec {
// log.Println(i, data)
//}
for {
demuxer.ReadPacket()
//pkt, err := demuxer.ReadPacket()
//if err != nil {
// log.Panic(err)
//}
//log.Println("im new pkt ===>", pkt.Idx, pkt.Time)
}
}()
cmd.Start()
var start bool
for {
select {
case signals := <-RTSPClient.Signals:
switch signals {
case rtspv2.SignalCodecUpdate:
//?
case rtspv2.SignalStreamRTPStop:
return
}
case packetAV := <-RTSPClient.OutgoingPacketQueue:
//log.Println(packetAV.Extensions)
t := time.UnixMilli(int64(binary.LittleEndian.Uint32(packetAV.Extensions[20:24]))*1000 + int64(binary.LittleEndian.Uint32(packetAV.Extensions[24:28]))/1000)
log.Println(t.Format("2006-01-02 15:04:05.000"))
//println(binary.LittleEndian.Uint32(packetAV.Extensions[24:28]))
//println(binary.LittleEndian.Uint32(packetAV.Extensions[20:24]))
if packetAV.IsKeyFrame {
start = true
}
if !start {
continue
}
if err = mux.WritePacket(*packetAV); err != nil {
return
}
}
}
}

View File

@ -6,9 +6,9 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
)
type Muxer struct {

View File

@ -12,9 +12,9 @@ import (
"strings"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec"
"github.com/deepch/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
)
const (

View File

@ -5,14 +5,15 @@ import (
"fmt"
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/codec"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/fake"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/flv/flvio"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/codec"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/fake"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"git.r-2.top/kunmeng/vdk/format/flv/flvio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
var MaxProbePacketCount = 20
@ -28,6 +29,8 @@ func NewMetadataByStreams(streams []av.CodecData) (metadata flvio.AMFMap, err er
switch typ {
case av.H264:
metadata["videocodecid"] = flvio.VIDEO_H264
case av.H265:
metadata["videocodecid"] = flvio.VIDEO_H265
default:
err = fmt.Errorf("flv: metadata: unsupported video codecType=%v", stream.Type())
@ -87,11 +90,13 @@ func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
switch tag.AVCPacketType {
case flvio.AVC_SEQHDR:
if !self.GotVideo {
var stream h264parser.CodecData
var stream av.CodecData
if stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil {
if stream, err = h265parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: h264 seqhdr invalid")
return
}
}
self.VideoStreamIdx = len(self.Streams)
self.Streams = append(self.Streams, stream)
self.GotVideo = true
@ -222,7 +227,17 @@ func CodecDataToTag(stream av.CodecData) (_tag flvio.Tag, ok bool, err error) {
}
ok = true
_tag = tag
case av.H265:
h265c := stream.(h265parser.CodecData)
tag := flvio.Tag{
Type: flvio.TAG_VIDEO,
AVCPacketType: flvio.AVC_SEQHDR,
CodecID: flvio.VIDEO_H265,
Data: h265c.AVCDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
ok = true
_tag = tag
case av.NELLYMOSER:
case av.SPEEX:
@ -272,7 +287,19 @@ func PacketToTag(pkt av.Packet, stream av.CodecData) (tag flvio.Tag, timestamp i
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.H265:
tag = flvio.Tag{
Type: flvio.TAG_VIDEO,
AVCPacketType: flvio.AVC_NALU,
CodecID: flvio.VIDEO_H265,
Data: pkt.Data,
CompositionTime: flvio.TimeToTs(pkt.CompositionTime),
}
if pkt.IsKeyFrame {
tag.FrameType = flvio.FRAME_KEY
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.AAC:
tag = flvio.Tag{
Type: flvio.TAG_AUDIO,
@ -336,7 +363,7 @@ func NewMuxer(w io.Writer) *Muxer {
return NewMuxerWriteFlusher(bufio.NewWriterSize(w, pio.RecommendBufioSize))
}
var CodecTypes = []av.CodecType{av.H264, av.AAC, av.SPEEX}
var CodecTypes = []av.CodecType{av.H264, av.AAC, av.SPEEX, av.H265}
func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {
var flags uint8

View File

@ -6,7 +6,7 @@ import (
"strings"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type AMF0ParseError struct {

View File

@ -5,8 +5,8 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
func TsToTime(ts int32) time.Duration {
@ -59,6 +59,7 @@ const (
FRAME_INTER = 2
VIDEO_H264 = 7
VIDEO_H265 = 12
)
type Tag struct {

View File

@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type DecoderConfigDescriptor struct {

View File

@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type StreamDescriptor struct {

View File

@ -6,7 +6,7 @@ import (
"os"
"strings"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type Tag uint32

View File

@ -1,6 +1,6 @@
package fmp4io
import "github.com/deepch/vdk/utils/bits/pio"
import "git.r-2.top/kunmeng/vdk/utils/bits/pio"
const AVC1 = Tag(0x61766331)

View File

@ -1,6 +1,6 @@
package fmp4io
import "github.com/deepch/vdk/utils/bits/pio"
import "git.r-2.top/kunmeng/vdk/utils/bits/pio"
const MVEX = Tag(0x6d766578)

View File

@ -1,6 +1,6 @@
package fmp4io
import "github.com/deepch/vdk/utils/bits/pio"
import "git.r-2.top/kunmeng/vdk/utils/bits/pio"
const FTYP = Tag(0x66747970)

View File

@ -3,7 +3,7 @@ package fmp4io
import (
"fmt"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MOOF = Tag(0x6d6f6f66)

View File

@ -4,7 +4,7 @@ import (
"math"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
func GetTime32(b []byte) (t time.Time) {

View File

@ -3,7 +3,7 @@ package fmp4io
import (
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MDIA = Tag(0x6d646961)

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MOOV = Tag(0x6d6f6f76)

View File

@ -1,8 +1,8 @@
package fmp4io
import (
"github.com/deepch/vdk/format/fmp4/esio"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/format/fmp4/esio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MP4A = Tag(0x6d703461)

View File

@ -1,7 +1,7 @@
package fmp4io
import (
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const (

View File

@ -3,7 +3,7 @@ package fmp4io
import (
"bytes"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const DREF = Tag(0x64726566)

View File

@ -3,7 +3,7 @@ package fmp4io
import (
"fmt"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const STBL = Tag(0x7374626c)

View File

@ -1,6 +1,6 @@
package fmp4io
import "github.com/deepch/vdk/utils/bits/pio"
import "git.r-2.top/kunmeng/vdk/utils/bits/pio"
const SIDX = Tag(0x73696478)

View File

@ -3,7 +3,7 @@ package fragment
import (
"time"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type Fragment struct {

View File

@ -6,9 +6,9 @@ import (
"sync"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/fmp4/fmp4io"
"github.com/deepch/vdk/format/fmp4/fragment"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/fmp4/fmp4io"
"git.r-2.top/kunmeng/vdk/format/fmp4/fragment"
)
var (

View File

@ -1,11 +1,11 @@
package fmp4
import (
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/fmp4/fmp4io"
"github.com/deepch/vdk/format/fmp4/fragment"
"github.com/deepch/vdk/format/fmp4/timescale"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/fmp4/fmp4io"
"git.r-2.top/kunmeng/vdk/format/fmp4/fragment"
"git.r-2.top/kunmeng/vdk/format/fmp4/timescale"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type fragmentWithData struct {

View File

@ -3,12 +3,12 @@ package fmp4
import (
"fmt"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/codec/opusparser"
"github.com/deepch/vdk/format/fmp4/esio"
"github.com/deepch/vdk/format/fmp4/fmp4io"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/opusparser"
"git.r-2.top/kunmeng/vdk/format/fmp4/esio"
"git.r-2.top/kunmeng/vdk/format/fmp4/fmp4io"
)
// Track creates a TRAK atom for this stream

View File

@ -3,10 +3,10 @@ package fmp4
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/fmp4/fmp4io"
"github.com/deepch/vdk/format/fmp4/fragment"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/fmp4/fmp4io"
"git.r-2.top/kunmeng/vdk/format/fmp4/fragment"
)
// TrackFragmenter writes a single audio or video stream as a series of CMAF (fMP4) fragments

View File

@ -1,13 +1,13 @@
package format
import (
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/format/aac"
"github.com/deepch/vdk/format/flv"
"github.com/deepch/vdk/format/mp4"
"github.com/deepch/vdk/format/rtmp"
"github.com/deepch/vdk/format/rtsp"
"github.com/deepch/vdk/format/ts"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/format/aac"
"git.r-2.top/kunmeng/vdk/format/flv"
"git.r-2.top/kunmeng/vdk/format/mp4"
"git.r-2.top/kunmeng/vdk/format/rtmp"
"git.r-2.top/kunmeng/vdk/format/rtsp"
"git.r-2.top/kunmeng/vdk/format/ts"
)
func RegisterAll() {

View File

@ -6,9 +6,9 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mkv/mkvio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/mkv/mkvio"
)
type Demuxer struct {

View File

@ -3,8 +3,8 @@ package mkv
import (
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
)
var CodecTypes = []av.CodecType{av.H264, av.AAC}

View File

@ -3,7 +3,7 @@ package mkv
import (
"time"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type Stream struct {

View File

@ -6,10 +6,10 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
)
type Demuxer struct {

View File

@ -3,8 +3,8 @@ package mp4
import (
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
)
var CodecTypes = []av.CodecType{av.H264, av.AAC}

View File

@ -1,9 +1,10 @@
package mp4io
import (
"errors"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MOOF = Tag(0x6d6f6f66)
@ -32,6 +33,7 @@ func (self HV1Desc) Tag() Tag {
}
// const HVC1 = Tag(0x68766331)
//
// func (self HVC1Desc) Tag() Tag {
// return HVC1
// }
@ -304,6 +306,10 @@ func (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("trak", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -313,6 +319,10 @@ func (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -601,6 +611,10 @@ func (self *Track) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -940,6 +954,10 @@ func (self *Media) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1173,6 +1191,10 @@ func (self *MediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1255,6 +1277,10 @@ func (self *DataInfo) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1817,6 +1843,10 @@ func (self *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1982,6 +2012,10 @@ func (self *MP4ADesc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2291,6 +2325,10 @@ func (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2411,6 +2449,10 @@ func (self *HV1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2954,6 +2996,10 @@ func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("traf", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -2963,6 +3009,10 @@ func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -3127,6 +3177,10 @@ func (self *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -3197,6 +3251,10 @@ func (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("trex", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -3206,6 +3264,10 @@ func (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}

View File

@ -966,7 +966,7 @@ func genatoms(filename, outfilename string) {
&ast.GenDecl{
Tok: token.IMPORT,
Specs: []ast.Spec{
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.r-2.top/kunmeng/vdk/utils/bits/pio"`}},
},
},
&ast.GenDecl{

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type ParseError struct {
@ -387,6 +387,10 @@ func ReadFileAtoms(r io.ReadSeeker) (atoms []Atom, err error) {
return
}
size := pio.U32BE(taghdr[0:])
if size > 5242880 {
err = parseErr("len", 5242880, err)
return
}
tag := Tag(pio.U32BE(taghdr[4:]))
var atom Atom

View File

@ -3,14 +3,14 @@ package mp4
import (
"bufio"
"fmt"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
)
type Muxer struct {
@ -18,6 +18,7 @@ type Muxer struct {
bufw *bufio.Writer
wpos int64
streams []*Stream
NegativeTsMakeZero bool
}
func NewMuxer(w io.WriteSeeker) *Muxer {
@ -120,7 +121,7 @@ func (self *Stream) fillTrackAtom() (err error) {
self.trackAtom.Header.TrackWidth = float64(width)
self.trackAtom.Header.TrackHeight = float64(height)
} else if self.Type() == av.H265 {
codec := self.CodecData.(h264parser.CodecData)
codec := self.CodecData.(h265parser.CodecData)
width, height := codec.Width(), codec.Height()
self.sample.SampleDesc.HV1Desc = &mp4io.HV1Desc{
DataRefIdx: 1,
@ -204,9 +205,13 @@ func (self *Muxer) WritePacket(pkt av.Packet) (err error) {
func (self *Stream) writePacket(pkt av.Packet, rawdur time.Duration) (err error) {
if rawdur < 0 {
if self.muxer.NegativeTsMakeZero {
rawdur = 0
} else {
err = fmt.Errorf("mp4: stream#%d time=%v < lasttime=%v", pkt.Idx, pkt.Time, self.lastpkt.Time)
return
}
}
if _, err = self.muxer.bufw.Write(pkt.Data); err != nil {
return

View File

@ -3,8 +3,8 @@ package mp4
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
)
type Stream struct {

View File

@ -1,6 +1,6 @@
package mp4f
import "github.com/deepch/vdk/format/mp4/mp4io"
import "git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
type FDummy struct {
Data []byte

View File

@ -1,8 +1,8 @@
package mp4fio
import (
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
func (self MovieFrag) Tag() mp4io.Tag {

View File

@ -966,7 +966,7 @@ func genatoms(filename, outfilename string) {
&ast.GenDecl{
Tok: token.IMPORT,
Specs: []ast.Spec{
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.r-2.top/kunmeng/vdk/utils/bits/pio"`}},
},
},
&ast.GenDecl{

View File

@ -1,8 +1,8 @@
package mp4fio
import (
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type ElemStreamDesc struct {

View File

@ -3,17 +3,18 @@ package mp4f
import (
"bufio"
"fmt"
"log"
"os"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/codec/h265parser"
"github.com/deepch/vdk/format/fmp4/fmp4io"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/format/mp4f/mp4fio"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"git.r-2.top/kunmeng/vdk/format/fmp4/fmp4io"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/format/mp4f/mp4fio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type Muxer struct {
@ -202,7 +203,8 @@ func (self *Stream) fillTrackAtom() (err error) {
self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{
Flags: 0x000001,
}
self.codecString = fmt.Sprintf("hvc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
//self.codecString = fmt.Sprintf("hvc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
self.codecString = "hev1.1.6.L120.90"
} else if self.Type() == av.AAC {
codec := self.CodecData.(aacparser.CodecData)
@ -237,14 +239,15 @@ func (self *Muxer) WriteTrailer() (err error) {
return
}
func (element *Muxer) WriteHeader(streams []av.CodecData) (err error) {
func (element *Muxer) WriteHeader(streams []av.CodecData) error {
element.streams = []*Stream{}
for _, stream := range streams {
if err = element.newStream(stream); err != nil {
return
if err := element.newStream(stream); err != nil {
log.Println("WriteHeader", err)
}
}
return
return nil
}
func (element *Muxer) GetInit(streams []av.CodecData) (string, []byte) {
@ -284,6 +287,9 @@ func (element *Muxer) GetInit(streams []av.CodecData) (string, []byte) {
}
func (element *Muxer) WritePacket(pkt av.Packet, GOP bool) (bool, []byte, error) {
if pkt.Idx+1 > int8(len(element.streams)) {
return false, nil, nil
}
stream := element.streams[pkt.Idx]
if GOP {
ts := time.Duration(0)

View File

@ -3,10 +3,10 @@ package mp4f
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/mp4"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/format/mp4f/mp4fio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/mp4"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/format/mp4f/mp4fio"
)
type Stream struct {

View File

@ -6,10 +6,10 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
)
type Demuxer struct {

View File

@ -3,8 +3,8 @@ package mp4
import (
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
)
var CodecTypes = []av.CodecType{av.H264, av.AAC}

View File

@ -1,9 +1,10 @@
package mp4io
import (
"errors"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const MOOF = Tag(0x6d6f6f66)
@ -32,6 +33,7 @@ func (self HV1Desc) Tag() Tag {
}
// const HVC1 = Tag(0x68766331)
//
// func (self HVC1Desc) Tag() Tag {
// return HVC1
// }
@ -304,6 +306,10 @@ func (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("trak", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -313,6 +319,10 @@ func (self *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -601,6 +611,10 @@ func (self *Track) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -940,6 +954,10 @@ func (self *Media) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1173,6 +1191,10 @@ func (self *MediaInfo) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1255,6 +1277,10 @@ func (self *DataInfo) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1817,6 +1843,10 @@ func (self *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -1982,6 +2012,10 @@ func (self *MP4ADesc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2291,6 +2325,10 @@ func (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2411,6 +2449,10 @@ func (self *HV1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -2954,6 +2996,10 @@ func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("traf", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -2963,6 +3009,10 @@ func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -3127,6 +3177,10 @@ func (self *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
@ -3197,6 +3251,10 @@ func (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("trex", n+offset, err)
return
}
if len(self.Tracks) > 100 {
err = errors.New("too many tracks")
return
}
self.Tracks = append(self.Tracks, atom)
}
default:
@ -3206,6 +3264,10 @@ func (self *MovieExtend) Unmarshal(b []byte, offset int) (n int, err error) {
err = parseErr("", n+offset, err)
return
}
if len(self.Unknowns) > 100 {
err = errors.New("too many unknowns")
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}

View File

@ -966,7 +966,7 @@ func genatoms(filename, outfilename string) {
&ast.GenDecl{
Tok: token.IMPORT,
Specs: []ast.Spec{
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/deepch/vdk/utils/bits/pio"`}},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.r-2.top/kunmeng/vdk/utils/bits/pio"`}},
},
},
&ast.GenDecl{

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type ParseError struct {
@ -387,6 +387,12 @@ func ReadFileAtoms(r io.ReadSeeker) (atoms []Atom, err error) {
return
}
size := pio.U32BE(taghdr[0:])
if size == 0 {
err = fmt.Errorf("bad hdr size")
return
}
tag := Tag(pio.U32BE(taghdr[4:]))
var atom Atom

View File

@ -6,11 +6,11 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type Muxer struct {
@ -18,6 +18,7 @@ type Muxer struct {
bufw *bufio.Writer
wpos int64
streams []*Stream
NegativeTsMakeZero bool
}
func NewMuxer(w io.WriteSeeker) *Muxer {
@ -181,9 +182,13 @@ func (self *Muxer) WritePacket(pkt av.Packet) (err error) {
func (self *Stream) writePacket(pkt av.Packet, rawdur time.Duration) (err error) {
if rawdur < 0 {
if self.muxer.NegativeTsMakeZero {
rawdur = 0
} else {
err = fmt.Errorf("mp4: stream#%d time=%v < lasttime=%v", pkt.Idx, pkt.Time, self.lastpkt.Time)
return
}
}
if _, err = self.muxer.bufw.Write(pkt.Data); err != nil {
return

View File

@ -3,8 +3,8 @@ package mp4
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/mp4/mp4io"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/mp4/mp4io"
)
type Stream struct {

77
format/mse/muxer.go Normal file
View File

@ -0,0 +1,77 @@
package mse
import (
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/mp4f"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"net"
"net/http"
)
var Debug bool
type Muxer struct {
m *mp4f.Muxer
r *http.Request
w http.ResponseWriter
conn net.Conn
}
func NewMuxer(r *http.Request, w http.ResponseWriter) (*Muxer, error) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
return nil, err
}
go func() {
defer func() {
conn.Close()
}()
for {
if _, _, err = wsutil.NextReader(conn, ws.StateServerSide); err != nil {
return
}
}
}()
return &Muxer{
conn: conn,
m: mp4f.NewMuxer(nil),
r: r,
w: w,
}, nil
}
func (m *Muxer) WriteHeader(streams []av.CodecData) (err error) {
if err = m.m.WriteHeader(streams); err != nil {
return
}
meta, fist := m.m.GetInit(streams)
if err = wsutil.WriteServerText(m.conn, []byte(meta)); err != nil {
return
}
if err = wsutil.WriteServerBinary(m.conn, fist); err != nil {
return
}
return
}
func (m *Muxer) WritePacket(pkt av.Packet) (err error) {
gotFrame, buffer, err := m.m.WritePacket(pkt, false)
if err != nil {
return
}
if gotFrame {
if err = wsutil.WriteServerBinary(m.conn, buffer); err != nil {
return
}
}
return
}
func (m *Muxer) WriteTrailer() (err error) {
return m.conn.Close()
}

View File

@ -1,59 +1,383 @@
package nvr
import (
"log"
"bytes"
"encoding/binary"
"encoding/gob"
"errors"
"fmt"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/mp4"
"github.com/google/uuid"
"github.com/shirou/gopsutil/v3/disk"
"os"
"path/filepath"
"strings"
"time"
)
"github.com/deepch/vdk/av"
var MIME = []byte{11, 22, 111, 222, 11, 22, 111, 222}
var listTag = []string{"{server_id}", "{host_name}", "{host_name_short}", "{host_name_long}",
"{stream_name}", "{channel_name}", "{stream_id}", "{channel_id}",
"{start_year}", "{start_month}", "{start_day}", "{start_hour}", "{start_minute}", "{start_second}",
"{start_millisecond}", "{start_unix_second}", "{start_unix_millisecond}", "{start_time}", "{start_pts}",
"{end_year}", "{end_month}", "{end_day}", "{end_hour}", "{end_minute}", "{end_second}",
"{end_millisecond}", "{end_unix_millisecond}", "{end_unix_second}", "{end_time}", "{end_pts}", "{duration_second}", "{duration_millisecond}"}
const (
MP4 = "mp4"
NVR = "nvr"
)
type Muxer struct {
name string
muxer *mp4.Muxer
format string
limit int
d *os.File
m *os.File
dur time.Duration
h int
gof *Gof
patch string
mpoint []string
start, end time.Time
pstart, pend time.Duration
started bool
file *os.File
codec []av.CodecData
buffer []*av.Packet
bufferDur time.Duration
seqDur time.Duration
serverID, streamName, channelName, streamID, channelID, hostLong, hostShort string
handleFileChange func(bool, string, string, int64, time.Time, time.Time, time.Duration)
}
//NewMuxer func
func NewMuxer(codec []av.CodecData, name, patch string, seqDur time.Duration) *Muxer {
return &Muxer{
codec: codec,
name: name,
type Gof struct {
Streams []av.CodecData
Packet []av.Packet
}
type Data struct {
Time int64
Start int64
Dur int64
}
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
func init() {
gob.RegisterName("nvr.Gof", Gof{})
gob.RegisterName("h264parser.CodecData", h264parser.CodecData{})
gob.RegisterName("aacparser.CodecData", aacparser.CodecData{})
}
func NewMuxer(serverID, streamName, channelName, streamID, channelID string, mpoint []string, patch, format string, limit int, c func(bool, string, string, int64, time.Time, time.Time, time.Duration)) (m *Muxer, err error) {
hostLong, _ := os.Hostname()
var hostShort string
if p, _, ok := strings.Cut(hostLong, "."); ok {
hostShort = p
}
m = &Muxer{
mpoint: mpoint,
patch: patch,
seqDur: seqDur,
h: -1,
gof: &Gof{},
format: format,
limit: limit,
serverID: serverID,
streamName: streamName,
channelName: channelName,
streamID: streamID,
channelID: channelID,
hostLong: hostLong,
hostShort: hostShort,
handleFileChange: c,
}
}
//WritePacket func
func (obj *Muxer) CodecUpdate(val []av.CodecData) {
obj.codec = val
}
//WritePacket func
func (obj *Muxer) WritePacket(pkt *av.Packet) (err error) {
if !obj.started && pkt.IsKeyFrame {
obj.started = true
}
if obj.started {
if pkt.IsKeyFrame && obj.bufferDur >= obj.seqDur {
log.Println("write to drive", len(obj.buffer), obj.bufferDur)
obj.buffer = nil
obj.bufferDur = 0
}
obj.buffer = append(obj.buffer, pkt)
if pkt.Idx == 0 {
obj.bufferDur += pkt.Duration
}
}
return nil
}
//Close func
func (obj *Muxer) Close() {
return
}
func (m *Muxer) WriteHeader(streams []av.CodecData) (err error) {
m.gof.Streams = streams
if m.format == MP4 {
return m.OpenMP4()
}
return
}
func (m *Muxer) WritePacket(pkt av.Packet) (err error) {
if len(m.gof.Streams) == 0 {
return
}
if !m.started && pkt.IsKeyFrame {
m.started = true
}
if m.started {
switch m.format {
case MP4:
return m.writePacketMP4(pkt)
case NVR:
return m.writePacketNVR(pkt)
}
}
return
}
func (m *Muxer) writePacketMP4(pkt av.Packet) (err error) {
if pkt.IsKeyFrame && m.dur > time.Duration(m.limit)*time.Second {
m.pstart = pkt.Time
if err = m.OpenMP4(); err != nil {
return
}
m.dur = 0
}
m.dur += pkt.Duration
m.pend = pkt.Time
return m.muxer.WritePacket(pkt)
}
func (m *Muxer) writePacketNVR(pkt av.Packet) (err error) {
if pkt.IsKeyFrame {
if len(m.gof.Packet) > 0 {
if err = m.writeGop(); err != nil {
return
}
}
m.gof.Packet, m.dur = nil, 0
}
if pkt.Idx == 0 {
m.dur += pkt.Duration
}
m.gof.Packet = append(m.gof.Packet, pkt)
return
}
func (m *Muxer) writeGop() (err error) {
t := time.Now().UTC()
if m.h != t.Hour() {
if err = m.OpenNVR(); err != nil {
return
}
}
f := Data{
Time: t.UnixNano(),
Dur: m.dur.Milliseconds(),
}
if f.Start, err = m.d.Seek(0, 2); err != nil {
return
}
enc := gob.NewEncoder(m.d)
if err = enc.Encode(m.gof); err != nil {
return
}
buf := bytes.NewBuffer([]byte{})
if err = binary.Write(buf, binary.LittleEndian, f); err != nil {
return
}
if _, err = buf.Write(MIME); err != nil {
return
}
_, err = m.m.Write(buf.Bytes())
return
}
func (m *Muxer) OpenNVR() (err error) {
m.WriteTrailer()
t := time.Now().UTC()
if err = os.MkdirAll(fmt.Sprintf("%s/%s", m.patch, t.Format("2006/01/02")), 0755); err != nil {
return
}
if m.d, err = os.OpenFile(fmt.Sprintf("%s/%s/%d.d", m.patch, t.Format("2006/01/02"), t.Hour()), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660); err != nil {
return
}
if m.m, err = os.OpenFile(fmt.Sprintf("%s/%s/%d.m", m.patch, t.Format("2006/01/02"), t.Hour()), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660); err != nil {
return
}
m.h = t.Hour()
return
}
func (m *Muxer) OpenMP4() (err error) {
m.WriteTrailer()
m.start = time.Now().UTC()
d, err := m.filePatch()
if err != nil {
return
}
if err = os.MkdirAll(filepath.Dir(d), 0755); err != nil {
return
}
name := filepath.Join(filepath.Dir(d), fmt.Sprintf("tmp_%s_%d.mp4", uuid.New(), time.Now().Unix()))
if m.d, err = os.Create(name); err != nil {
return
}
m.handleFileChange(
true,
m.Codecs(),
name,
0,
m.start,
m.end,
m.dur,
)
m.muxer = mp4.NewMuxer(m.d)
m.muxer.NegativeTsMakeZero = true
if err = m.muxer.WriteHeader(m.gof.Streams); err != nil {
return
}
return
}
func (m *Muxer) filePatch() (string, error) {
var (
mu = float64(100)
ui = -1
)
for i, i2 := range m.mpoint {
//if m, err := mountinfo.Mounted(i2); err == nil && m {
if d, err := disk.Usage(i2); err == nil {
if d.UsedPercent < mu {
ui = i
mu = d.UsedPercent
}
}
// }
}
if ui == -1 {
return "", errors.New("not mount ready")
}
ts := filepath.Join(m.mpoint[ui], m.patch)
m.end = time.Now().UTC()
for _, s := range listTag {
switch s {
case "{server_id}":
ts = strings.Replace(ts, "{server_id}", m.serverID, -1)
case "{host_name}":
ts = strings.Replace(ts, "{host_name}", m.hostLong, -1)
case "{host_name_short}":
ts = strings.Replace(ts, "{host_name_short}", m.hostShort, -1)
case "{host_name_long}":
ts = strings.Replace(ts, "{host_name_long}", m.hostLong, -1)
case "{stream_name}":
ts = strings.Replace(ts, "{stream_name}", m.streamName, -1)
case "{channel_name}":
ts = strings.Replace(ts, "{channel_name}", m.channelName, -1)
case "{stream_id}":
ts = strings.Replace(ts, "{stream_id}", m.streamID, -1)
case "{channel_id}":
ts = strings.Replace(ts, "{channel_id}", m.channelID, -1)
case "{start_year}":
ts = strings.Replace(ts, "{start_year}", fmt.Sprintf("%d", m.start.Year()), -1)
case "{start_month}":
ts = strings.Replace(ts, "{start_month}", fmt.Sprintf("%02d", int(m.start.Month())), -1)
case "{start_day}":
ts = strings.Replace(ts, "{start_day}", fmt.Sprintf("%02d", m.start.Day()), -1)
case "{start_hour}":
ts = strings.Replace(ts, "{start_hour}", fmt.Sprintf("%02d", m.start.Hour()), -1)
case "{start_minute}":
ts = strings.Replace(ts, "{start_minute}", fmt.Sprintf("%02d", m.start.Minute()), -1)
case "{start_second}":
ts = strings.Replace(ts, "{start_second}", fmt.Sprintf("%02d", m.start.Second()), -1)
case "{start_millisecond}":
ts = strings.Replace(ts, "{start_millisecond}", fmt.Sprintf("%d", m.start.Nanosecond()/1000/1000), -1)
case "{start_unix_millisecond}":
ts = strings.Replace(ts, "{start_unix_millisecond}", fmt.Sprintf("%d", m.start.UnixMilli()), -1)
case "{start_unix_second}":
ts = strings.Replace(ts, "{start_unix_second}", fmt.Sprintf("%d", m.start.Unix()), -1)
case "{start_time}":
ts = strings.Replace(ts, "{start_time}", fmt.Sprintf("%s", m.start.Format("2006-01-02T15:04:05-0700")), -1)
case "{start_pts}":
ts = strings.Replace(ts, "{start_pts}", fmt.Sprintf("%d", m.pstart.Milliseconds()), -1)
case "{end_year}":
ts = strings.Replace(ts, "{end_year}", fmt.Sprintf("%d", m.end.Year()), -1)
case "{end_month}":
ts = strings.Replace(ts, "{end_month}", fmt.Sprintf("%02d", int(m.end.Month())), -1)
case "{end_day}":
ts = strings.Replace(ts, "{end_day}", fmt.Sprintf("%02d", m.end.Day()), -1)
case "{end_hour}":
ts = strings.Replace(ts, "{end_hour}", fmt.Sprintf("%02d", m.end.Hour()), -1)
case "{end_minute}":
ts = strings.Replace(ts, "{end_minute}", fmt.Sprintf("%02d", m.end.Minute()), -1)
case "{end_second}":
ts = strings.Replace(ts, "{end_second}", fmt.Sprintf("%02d", m.end.Second()), -1)
case "{end_millisecond}":
ts = strings.Replace(ts, "{end_millisecond}", fmt.Sprintf("%d", m.end.Nanosecond()/1000/1000), -1)
case "{end_unix_millisecond}":
ts = strings.Replace(ts, "{end_unix_millisecond}", fmt.Sprintf("%d", m.end.UnixMilli()), -1)
case "{end_unix_second}":
ts = strings.Replace(ts, "{end_unix_second}", fmt.Sprintf("%d", m.end.Unix()), -1)
case "{end_time}":
ts = strings.Replace(ts, "{end_time}", fmt.Sprintf("%s", m.end.Format("2006-01-02T15:04:05-0700")), -1)
case "{end_pts}":
ts = strings.Replace(ts, "{end_pts}", fmt.Sprintf("%d", m.pend.Milliseconds()), -1)
case "{duration_second}":
ts = strings.Replace(ts, "{duration_second}", fmt.Sprintf("%f", m.dur.Seconds()), -1)
case "{duration_millisecond}":
ts = strings.Replace(ts, "{duration_millisecond}", fmt.Sprintf("%d", m.dur.Milliseconds()), -1)
}
}
return ts, nil
}
func (m *Muxer) Codecs() string {
var codecs []string
for _, stream := range m.gof.Streams {
codecs = append(codecs, strings.ToLower(stream.Type().String()))
}
return strings.Join(codecs, ",")
}
func (m *Muxer) WriteTrailer() (err error) {
if m.muxer != nil {
m.muxer.WriteTrailer()
}
if m.m != nil {
err = m.m.Close()
}
if m.d != nil {
if m.format == MP4 {
p, err := m.filePatch()
if err != nil {
return err
}
if err = os.Rename(m.d.Name(), filepath.Join(filepath.Dir(m.d.Name()), filepath.Base(p))); err != nil {
return err
}
var size int64
if fi, err := m.d.Stat(); err == nil {
size = fi.Size()
}
m.handleFileChange(
false,
m.Codecs(),
filepath.Join(filepath.Dir(m.d.Name()), filepath.Base(p)),
size,
m.start,
m.end,
m.dur,
)
}
err = m.d.Close()
}
return
}

8
format/nvr/streams.go Normal file
View File

@ -0,0 +1,8 @@
package nvr
import "git.r-2.top/kunmeng/vdk/av"
type Stream struct {
codec av.CodecData
idx int
}

View File

@ -4,11 +4,11 @@ import (
"bytes"
"os"
"github.com/deepch/vdk/codec/h265parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"github.com/deepch/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
var startCode = []byte{0, 0, 0, 1}

View File

@ -14,11 +14,11 @@ import (
"strings"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/format/flv"
"github.com/deepch/vdk/format/flv/flvio"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/format/flv"
"git.r-2.top/kunmeng/vdk/format/flv/flvio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
var Debug bool
@ -258,8 +258,11 @@ func (self *Conn) RxBytes() uint64 {
}
func (self *Conn) Close() (err error) {
if self.netconn != nil {
return self.netconn.Close()
}
return
}
func (self *Conn) pollCommand() (err error) {
for {
@ -1623,20 +1626,15 @@ func (self *Conn) handshakeServer() (err error) {
clitime := pio.U32BE(C1[0:4])
srvtime := clitime
srvver := uint32(0x0d0e0a0d)
cliver := pio.U32BE(C1[4:8])
if cliver != 0 {
var ok bool
var digest []byte
if ok, digest = hsParse1(C1, hsClientPartialKey, hsServerFullKey); !ok {
err = fmt.Errorf("rtmp: handshake server: C1 invalid")
return
}
if ok, digest = hsParse1(C1, hsClientPartialKey, hsServerFullKey); ok {
hsCreate01(S0S1, srvtime, srvver, hsServerPartialKey)
hsCreate2(S2, digest)
} else {
copy(S1, C1)
copy(S2, C2)
copy(S1, C2)
copy(S2, C1)
}
if _, err = self.bufw.Write(S0S1S2); err != nil {

View File

@ -9,7 +9,6 @@ import (
"encoding/hex"
"fmt"
"io"
//"log"
"net"
"net/textproto"
"net/url"
@ -17,13 +16,13 @@ import (
"strings"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/codec"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/rtsp/sdp"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/codec"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/rtsp/sdp"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
var ErrCodecDataChange = fmt.Errorf("rtsp: codec data change, please call HandleCodecDataChange()")
@ -43,6 +42,7 @@ const (
type Client struct {
DebugRtsp bool
DebugRtp bool
DisableAudio bool
Headers []string
SkipErrRtpBlock bool
@ -147,7 +147,12 @@ func (self *Client) probe() (err error) {
}
func (self *Client) prepare(stage int) (err error) {
var waitIdle int
for self.stage < stage {
waitIdle++
if waitIdle > 20 {
return fmt.Errorf("codec not ready")
}
switch self.stage {
case 0:
if err = self.Options(); err != nil {
@ -695,7 +700,9 @@ func (self *Client) Describe() (streams []sdp.Media, err error) {
self.streams = []*Stream{}
for _, media := range medias {
stream := &Stream{Sdp: media, client: self}
stream.makeCodecData()
if err = stream.makeCodecData(); err != nil && DebugRtsp {
fmt.Println("rtsp: makeCodecData error", err)
}
self.streams = append(self.streams, stream)
streams = append(streams, media)
}
@ -767,7 +774,7 @@ func (self *Stream) timeScale() int {
func (self *Stream) makeCodecData() (err error) {
media := self.Sdp
if media.PayloadType >= 96 && media.PayloadType <= 127 {
if (media.PayloadType >= 96 && media.PayloadType <= 127) || media.Type == av.H264 || media.Type == av.AAC {
switch media.Type {
case av.H264:
for _, nalu := range media.SpropParameterSets {
@ -1070,12 +1077,44 @@ func (self *Stream) handleRtpPacket(packet []byte) (err error) {
err = fmt.Errorf("rtp: packet too short")
return
}
payloadOffset := 12 + int(packet[0]&0xf)*4
timestamp := binary.BigEndian.Uint32(packet[4:8])
/*
Test offset
*/
Padding := (packet[0]>>5)&1 == 1
Extension := (packet[0]>>4)&1 == 1
CSRCCnt := int(packet[0] & 0x0f)
RTPHeaderSize := 12
payloadOffset := RTPHeaderSize
end := len(packet)
if end-payloadOffset >= 4*CSRCCnt {
payloadOffset += 4 * CSRCCnt
}
if Extension && end-payloadOffset >= 4 {
extLen := 4 * int(binary.BigEndian.Uint16(packet[payloadOffset+2:]))
payloadOffset += 4
if end-payloadOffset >= extLen {
payloadOffset += extLen
}
}
if Padding && end-payloadOffset > 0 {
paddingLen := int(packet[end-1])
if end-payloadOffset >= paddingLen {
end -= paddingLen
}
}
if payloadOffset > len(packet) {
err = fmt.Errorf("rtp: packet too short")
return
}
timestamp := binary.BigEndian.Uint32(packet[4:8])
payload := packet[payloadOffset:]
/*
@ -1152,6 +1191,7 @@ func (self *Client) Play() (err error) {
Method: "PLAY",
Uri: self.requestUri,
}
req.Header = append(req.Header, "Range: npt=0.000-")
req.Header = append(req.Header, "Session: "+self.session)
if err = self.WriteRequest(req); err != nil {
return

View File

@ -8,7 +8,7 @@ import (
"strconv"
"strings"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
type Session struct {
@ -160,6 +160,9 @@ func Parse(content string) (sess Session, medias []Media) {
case "sprop-parameter-sets":
fields := strings.Split(val, ",")
for _, field := range fields {
if field == "" {
continue
}
val, _ := base64.StdEncoding.DecodeString(field)
media.SpropParameterSets = append(media.SpropParameterSets, val)
}

1
format/rtsp/server.go Normal file
View File

@ -0,0 +1 @@
package rtsp

View File

@ -3,8 +3,8 @@ package rtsp
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/rtsp/sdp"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/rtsp/sdp"
)
type Stream struct {

View File

@ -12,19 +12,18 @@ import (
"html"
"io"
"log"
"math"
"net"
"net/url"
"strconv"
"strings"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/codec/h265parser"
"github.com/deepch/vdk/format/rtsp/sdp"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"git.r-2.top/kunmeng/vdk/format/rtsp/sdp"
)
const (
@ -39,7 +38,10 @@ const (
const (
RTPHeaderSize = 12
RTCPSenderReport = 200
RTCPReceiverReport = 201
)
const (
DESCRIBE = "DESCRIBE"
OPTIONS = "OPTIONS"
@ -90,6 +92,10 @@ type RTSPClient struct {
FPS int
WaitCodec bool
chTMP int
timestamp int64
sequenceNumber int
end int
offset int
}
type RTSPClientOptions struct {
@ -147,9 +153,12 @@ func Dial(options RTSPClientOptions) (*RTSPClient, error) {
if err != nil {
return nil, err
}
for _, i2 := range client.mediaSDP {
if (i2.AVType != VIDEO && i2.AVType != AUDIO) || (client.options.DisableAudio && i2.AVType == AUDIO) {
//TODO check it
if strings.Contains(string(client.SDPRaw), "LaunchDigital") {
client.chTMP += 2
}
continue
}
err = client.request(SETUP, map[string]string{"Transport": "RTP/AVP/TCP;unicast;interleaved=" + strconv.Itoa(client.chTMP) + "-" + strconv.Itoa(client.chTMP+1)}, client.ControlTrack(i2.Control), false, false)
@ -238,6 +247,146 @@ func Dial(options RTSPClientOptions) (*RTSPClient, error) {
return client, nil
}
func ReplayDial(options RTSPClientOptions, startTime string) (*RTSPClient, error) {
client := &RTSPClient{
headers: make(map[string]string),
Signals: make(chan int, 100),
OutgoingProxyQueue: make(chan *[]byte, 3000),
OutgoingPacketQueue: make(chan *av.Packet, 3000),
BufferRtpPacket: bytes.NewBuffer([]byte{}),
videoID: -1,
audioID: -2,
videoIDX: -1,
audioIDX: -2,
options: options,
AudioTimeScale: 8000,
}
client.headers["User-Agent"] = "Lavf58.76.100"
err := client.parseURL(html.UnescapeString(client.options.URL))
if err != nil {
return nil, err
}
conn, err := net.DialTimeout("tcp", client.pURL.Host, client.options.DialTimeout)
if err != nil {
return nil, err
}
err = conn.SetDeadline(time.Now().Add(client.options.ReadWriteTimeout))
if err != nil {
return nil, err
}
if client.pURL.Scheme == "rtsps" {
tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: options.InsecureSkipVerify, ServerName: client.pURL.Hostname()})
err = tlsConn.Handshake()
if err != nil {
return nil, err
}
conn = tlsConn
}
client.conn = conn
client.connRW = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
err = client.request(OPTIONS, nil, client.pURL.String(), false, false)
if err != nil {
return nil, err
}
err = client.request(DESCRIBE, map[string]string{"Accept": "application/sdp"}, client.pURL.String(), false, false)
if err != nil {
return nil, err
}
for _, i2 := range client.mediaSDP {
if (i2.AVType != VIDEO && i2.AVType != AUDIO) || (client.options.DisableAudio && i2.AVType == AUDIO) {
//TODO check it
if strings.Contains(string(client.SDPRaw), "LaunchDigital") {
client.chTMP += 2
}
continue
}
// err = client.request(SETUP, map[string]string{"Require": "onvif-replay", "Transport": "RTP/UDP"}, client.ControlTrack(i2.Control), false, false)
err = client.request(SETUP, map[string]string{"Require": "onvif-replay", "Transport": "RTP/AVP/TCP;unicast;interleaved=" + strconv.Itoa(client.chTMP) + "-" + strconv.Itoa(client.chTMP+1)}, client.ControlTrack(i2.Control), false, false)
if err != nil {
return nil, err
}
if i2.AVType == VIDEO {
if i2.Type == av.H264 {
if len(i2.SpropParameterSets) > 1 {
if codecData, err := h264parser.NewCodecDataFromSPSAndPPS(i2.SpropParameterSets[0], i2.SpropParameterSets[1]); err == nil {
client.sps = i2.SpropParameterSets[0]
client.pps = i2.SpropParameterSets[1]
client.CodecData = append(client.CodecData, codecData)
}
} else {
client.CodecData = append(client.CodecData, h264parser.CodecData{})
client.WaitCodec = true
}
client.FPS = i2.FPS
client.videoCodec = av.H264
} else if i2.Type == av.H265 {
if len(i2.SpropVPS) > 1 && len(i2.SpropSPS) > 1 && len(i2.SpropPPS) > 1 {
if codecData, err := h265parser.NewCodecDataFromVPSAndSPSAndPPS(i2.SpropVPS, i2.SpropSPS, i2.SpropPPS); err == nil {
client.vps = i2.SpropVPS
client.sps = i2.SpropSPS
client.pps = i2.SpropPPS
client.CodecData = append(client.CodecData, codecData)
}
} else {
client.CodecData = append(client.CodecData, h265parser.CodecData{})
}
client.videoCodec = av.H265
} else {
client.Println("SDP Video Codec Type Not Supported", i2.Type)
}
client.videoIDX = int8(len(client.CodecData) - 1)
client.videoID = client.chTMP
}
if i2.AVType == AUDIO {
client.audioID = client.chTMP
var CodecData av.AudioCodecData
switch i2.Type {
case av.AAC:
CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(i2.Config)
if err == nil {
client.Println("Audio AAC bad config")
}
case av.OPUS:
var cl av.ChannelLayout
switch i2.ChannelCount {
case 1:
cl = av.CH_MONO
case 2:
cl = av.CH_STEREO
default:
cl = av.CH_MONO
}
CodecData = codec.NewOpusCodecData(i2.TimeScale, cl)
case av.PCM_MULAW:
CodecData = codec.NewPCMMulawCodecData()
case av.PCM_ALAW:
CodecData = codec.NewPCMAlawCodecData()
case av.PCM:
CodecData = codec.NewPCMCodecData()
default:
client.Println("Audio Codec", i2.Type, "not supported")
}
if CodecData != nil {
client.CodecData = append(client.CodecData, CodecData)
client.audioIDX = int8(len(client.CodecData) - 1)
client.audioCodec = CodecData.Type()
if i2.TimeScale != 0 {
client.AudioTimeScale = int64(i2.TimeScale)
}
}
}
client.chTMP += 2
}
test := map[string]string{"Require": "onvif-replay", "Scale": "1.000000", "Speed": "1.000000", "Range": "clock=" + startTime + "-"}
err = client.request(PLAY, test, client.control, false, false)
if err != nil {
return nil, err
}
go client.startStream()
return client, nil
}
func (client *RTSPClient) ControlTrack(track string) string {
if strings.Contains(track, "rtsp://") {
return track
@ -548,285 +697,6 @@ func stringInBetween(str string, start string, end string) (result string) {
return str
}
func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
content := *payloadRAW
firstByte := content[4]
padding := (firstByte>>5)&1 == 1
extension := (firstByte>>4)&1 == 1
CSRCCnt := int(firstByte & 0x0f)
SequenceNumber := int(binary.BigEndian.Uint16(content[6:8]))
timestamp := int64(binary.BigEndian.Uint32(content[8:16]))
offset := RTPHeaderSize
end := len(content)
if end-offset >= 4*CSRCCnt {
offset += 4 * CSRCCnt
}
if extension && len(content) < 4+offset+2+2 {
return nil, false
}
if extension && end-offset >= 4 {
extLen := 4 * int(binary.BigEndian.Uint16(content[4+offset+2:]))
offset += 4
if end-offset >= extLen {
offset += extLen
}
}
if padding && end-offset > 0 {
paddingLen := int(content[end-1])
if end-offset >= paddingLen {
end -= paddingLen
}
}
offset += 4
switch int(content[1]) {
case client.videoID:
if client.PreVideoTS == 0 {
client.PreVideoTS = timestamp
}
if timestamp-client.PreVideoTS < 0 {
if math.MaxUint32-client.PreVideoTS < 90*100 { //100 ms
client.PreVideoTS = 0
client.PreVideoTS -= (math.MaxUint32 - client.PreVideoTS)
} else {
client.PreVideoTS = 0
}
}
if client.PreSequenceNumber != 0 && SequenceNumber-client.PreSequenceNumber != 1 {
client.Println("drop packet", SequenceNumber-1)
}
client.PreSequenceNumber = SequenceNumber
if client.BufferRtpPacket.Len() > 4048576 {
client.Println("Big Buffer Flush")
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
}
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
if len(nalRaw) == 0 || len(nalRaw[0]) == 0 {
return nil, false
}
var retmap []*av.Packet
for _, nal := range nalRaw {
if client.videoCodec == av.H265 {
naluType := (nal[0] >> 1) & 0x3f
switch naluType {
case h265parser.NAL_UNIT_CODED_SLICE_TRAIL_R:
retmap = append(retmap, &av.Packet{
Data: append(binSize(len(nal)), nal...),
CompositionTime: time.Duration(1) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: false,
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
Time: time.Duration(timestamp/90) * time.Millisecond,
})
case h265parser.NAL_UNIT_VPS:
client.CodecUpdateVPS(nal)
case h265parser.NAL_UNIT_SPS:
client.CodecUpdateSPS(nal)
case h265parser.NAL_UNIT_PPS:
client.CodecUpdatePPS(nal)
case h265parser.NAL_UNIT_UNSPECIFIED_49:
se := nal[2] >> 6
naluType := nal[2] & 0x3f
if se == 2 {
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write([]byte{(nal[0] & 0x81) | (naluType << 1), nal[1]})
r := make([]byte, 2)
r[1] = nal[1]
r[0] = (nal[0] & 0x81) | (naluType << 1)
client.BufferRtpPacket.Write(nal[3:])
} else if se == 1 {
client.BufferRtpPacket.Write(nal[3:])
retmap = append(retmap, &av.Packet{
Data: append(binSize(client.BufferRtpPacket.Len()), client.BufferRtpPacket.Bytes()...),
CompositionTime: time.Duration(1) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: naluType == h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
Time: time.Duration(timestamp/90) * time.Millisecond,
})
} else {
client.BufferRtpPacket.Write(nal[3:])
}
default:
//client.Println("Unsupported Nal", naluType)
}
} else if client.videoCodec == av.H264 {
naluType := nal[0] & 0x1f
switch {
case naluType >= 1 && naluType <= 5:
retmap = append(retmap, &av.Packet{
Data: append(binSize(len(nal)), nal...),
CompositionTime: time.Duration(1) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: naluType == 5,
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
Time: time.Duration(timestamp/90) * time.Millisecond,
})
case naluType == 7:
client.CodecUpdateSPS(nal)
case naluType == 8:
client.CodecUpdatePPS(nal)
case naluType == 24:
packet := nal[1:]
for len(packet) >= 2 {
size := int(packet[0])<<8 | int(packet[1])
if size+2 > len(packet) {
break
}
naluTypefs := packet[2] & 0x1f
switch {
case naluTypefs >= 1 && naluTypefs <= 5:
retmap = append(retmap, &av.Packet{
Data: append(binSize(len(packet[2:size+2])), packet[2:size+2]...),
CompositionTime: time.Duration(1) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: naluType == 5,
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
Time: time.Duration(timestamp/90) * time.Millisecond,
})
case naluTypefs == 7:
client.CodecUpdateSPS(packet[2 : size+2])
case naluTypefs == 8:
client.CodecUpdatePPS(packet[2 : size+2])
}
packet = packet[size+2:]
}
case naluType == 28:
fuIndicator := content[offset]
fuHeader := content[offset+1]
isStart := fuHeader&0x80 != 0
isEnd := fuHeader&0x40 != 0
if isStart {
client.fuStarted = true
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write([]byte{fuIndicator&0xe0 | fuHeader&0x1f})
}
if client.fuStarted {
client.BufferRtpPacket.Write(content[offset+2 : end])
if isEnd {
client.fuStarted = false
naluTypef := client.BufferRtpPacket.Bytes()[0] & 0x1f
if naluTypef == 7 || naluTypef == 9 {
bufered, _ := h264parser.SplitNALUs(append([]byte{0, 0, 0, 1}, client.BufferRtpPacket.Bytes()...))
for _, v := range bufered {
naluTypefs := v[0] & 0x1f
switch {
case naluTypefs == 5:
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write(v)
naluTypef = 5
case naluTypefs == 7:
client.CodecUpdateSPS(v)
case naluTypefs == 8:
client.CodecUpdatePPS(v)
}
}
}
retmap = append(retmap, &av.Packet{
Data: append(binSize(client.BufferRtpPacket.Len()), client.BufferRtpPacket.Bytes()...),
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: naluTypef == 5,
Time: time.Duration(timestamp/90) * time.Millisecond,
})
}
}
default:
client.Println("Unsupported NAL Type", naluType)
}
}
}
if len(retmap) > 0 {
client.PreVideoTS = timestamp
return retmap, true
}
case client.audioID:
if client.PreAudioTS == 0 {
client.PreAudioTS = timestamp
}
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
var retmap []*av.Packet
for _, nal := range nalRaw {
var duration time.Duration
switch client.audioCodec {
case av.PCM_MULAW:
duration = time.Duration(len(nal)) * time.Second / time.Duration(client.AudioTimeScale)
client.AudioTimeLine += duration
retmap = append(retmap, &av.Packet{
Data: nal,
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: duration,
Idx: client.audioIDX,
IsKeyFrame: false,
Time: client.AudioTimeLine,
})
case av.PCM_ALAW:
duration = time.Duration(len(nal)) * time.Second / time.Duration(client.AudioTimeScale)
client.AudioTimeLine += duration
retmap = append(retmap, &av.Packet{
Data: nal,
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: duration,
Idx: client.audioIDX,
IsKeyFrame: false,
Time: client.AudioTimeLine,
})
case av.OPUS:
duration = time.Duration(20) * time.Millisecond
client.AudioTimeLine += duration
retmap = append(retmap, &av.Packet{
Data: nal,
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: duration,
Idx: client.audioIDX,
IsKeyFrame: false,
Time: client.AudioTimeLine,
})
case av.AAC:
auHeadersLength := uint16(0) | (uint16(nal[0]) << 8) | uint16(nal[1])
auHeadersCount := auHeadersLength >> 4
framesPayloadOffset := 2 + int(auHeadersCount)<<1
auHeaders := nal[2:framesPayloadOffset]
framesPayload := nal[framesPayloadOffset:]
for i := 0; i < int(auHeadersCount); i++ {
auHeader := uint16(0) | (uint16(auHeaders[0]) << 8) | uint16(auHeaders[1])
frameSize := auHeader >> 3
frame := framesPayload[:frameSize]
auHeaders = auHeaders[2:]
framesPayload = framesPayload[frameSize:]
if _, _, _, _, err := aacparser.ParseADTSHeader(frame); err == nil {
frame = frame[7:]
}
duration = time.Duration((float32(1024)/float32(client.AudioTimeScale))*1000*1000*1000) * time.Nanosecond
client.AudioTimeLine += duration
retmap = append(retmap, &av.Packet{
Data: frame,
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: duration,
Idx: client.audioIDX,
IsKeyFrame: false,
Time: client.AudioTimeLine,
})
}
}
}
if len(retmap) > 0 {
client.PreAudioTS = timestamp
return retmap, true
}
default:
//client.Println("Unsuported Intervaled data packet", int(content[1]), content[offset:end])
}
return nil, false
}
func (client *RTSPClient) CodecUpdateSPS(val []byte) {
if client.videoCodec != av.H264 && client.videoCodec != av.H265 {
return
@ -950,3 +820,8 @@ func binSize(val int) []byte {
binary.BigEndian.PutUint32(buf, uint32(val))
return buf
}
func isRTCPPacket(content []byte) bool {
rtcpPacketType := content[5]
return rtcpPacketType == RTCPSenderReport || rtcpPacketType == RTCPReceiverReport
}

290
format/rtspv2/demuxer.go Normal file
View File

@ -0,0 +1,290 @@
package rtspv2
import (
"encoding/binary"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"math"
"time"
)
const (
TimeBaseFactor = 90
TimeDelay = 1
)
func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
content := *payloadRAW
firstByte := content[4]
padding := (firstByte>>5)&1 == 1
extension := (firstByte>>4)&1 == 1
CSRCCnt := int(firstByte & 0x0f)
client.sequenceNumber = int(binary.BigEndian.Uint16(content[6:8]))
client.timestamp = int64(binary.BigEndian.Uint32(content[8:16]))
if isRTCPPacket(content) {
client.Println("skipping RTCP packet")
return nil, false
}
client.offset = RTPHeaderSize
client.end = len(content)
if client.end-client.offset >= 4*CSRCCnt {
client.offset += 4 * CSRCCnt
}
extensionStart := 0
extensionEnd := 0
if extension && len(content) < 4+client.offset+2+2 {
return nil, false
}
if extension && client.end-client.offset >= 4 {
extLen := 4 * int(binary.BigEndian.Uint16(content[4+client.offset+2:]))
client.offset += 4
if client.end-client.offset >= extLen {
extensionStart = client.offset
extensionEnd = client.offset + extLen
client.offset += extLen
}
}
if padding && client.end-client.offset > 0 {
paddingLen := int(content[client.end-1])
if client.end-client.offset >= paddingLen {
client.end -= paddingLen
}
}
client.offset += 4
if len(content) < client.end {
return nil, false
}
switch int(content[1]) {
case client.videoID:
pck, ok := client.handleVideo(content)
for _, p := range pck {
p.Extension = extension
p.Extensions = content[extensionStart+4 : extensionEnd+4]
}
return pck, ok
case client.audioID:
pck, ok := client.handleAudio(content)
for _, p := range pck {
p.Extension = extension
p.Extensions = content[extensionStart+4 : extensionEnd+4]
}
return pck, ok
}
return nil, false
}
func (client *RTSPClient) handleVideo(content []byte) ([]*av.Packet, bool) {
if client.PreVideoTS == 0 {
client.PreVideoTS = client.timestamp
}
if client.timestamp-client.PreVideoTS < 0 {
if math.MaxUint32-client.PreVideoTS < 90*100 { //100 ms
client.PreVideoTS = 0
client.PreVideoTS -= (math.MaxUint32 - client.PreVideoTS)
} else {
client.PreVideoTS = 0
}
}
if client.PreSequenceNumber != 0 && client.sequenceNumber-client.PreSequenceNumber != 1 {
client.Println("drop packet", client.sequenceNumber-1)
}
client.PreSequenceNumber = client.sequenceNumber
if client.BufferRtpPacket.Len() > 4048576 {
client.Println("Big Buffer Flush")
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
}
nalRaw, _ := h264parser.SplitNALUs(content[client.offset:client.end])
if len(nalRaw) == 0 || len(nalRaw[0]) == 0 {
return nil, false
}
var retmap []*av.Packet
for _, nal := range nalRaw {
if client.videoCodec == av.H265 {
retmap = client.handleH265Payload(nal, retmap)
} else if client.videoCodec == av.H264 {
retmap = client.handleH264Payload(content, nal, retmap)
}
}
if len(retmap) > 0 {
client.PreVideoTS = client.timestamp
return retmap, true
}
return nil, false
}
func (client *RTSPClient) handleH264Payload(content, nal []byte, retmap []*av.Packet) []*av.Packet {
naluType := nal[0] & 0x1f
switch {
case naluType >= 1 && naluType <= 5:
retmap = client.appendVideoPacket(retmap, nal, naluType == 5)
case naluType == h264parser.NALU_SPS:
client.CodecUpdateSPS(nal)
case naluType == h264parser.NALU_PPS:
client.CodecUpdatePPS(nal)
case naluType == 24:
packet := nal[1:]
for len(packet) >= 2 {
size := int(packet[0])<<8 | int(packet[1])
if size+2 > len(packet) {
break
}
naluTypefs := packet[2] & 0x1f
switch {
case naluTypefs >= 1 && naluTypefs <= 5:
retmap = client.appendVideoPacket(retmap, packet[2:size+2], naluTypefs == 5)
case naluTypefs == h264parser.NALU_SPS:
client.CodecUpdateSPS(packet[2 : size+2])
case naluTypefs == h264parser.NALU_PPS:
client.CodecUpdatePPS(packet[2 : size+2])
}
packet = packet[size+2:]
}
case naluType == 28:
fuIndicator := content[client.offset]
fuHeader := content[client.offset+1]
isStart := fuHeader&0x80 != 0
isEnd := fuHeader&0x40 != 0
if isStart {
client.fuStarted = true
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write([]byte{fuIndicator&0xe0 | fuHeader&0x1f})
}
if client.fuStarted {
client.BufferRtpPacket.Write(content[client.offset+2 : client.end])
if isEnd {
client.fuStarted = false
naluTypef := client.BufferRtpPacket.Bytes()[0] & 0x1f
if naluTypef == 7 || naluTypef == 9 {
bufered, _ := h264parser.SplitNALUs(append([]byte{0, 0, 0, 1}, client.BufferRtpPacket.Bytes()...))
for _, v := range bufered {
naluTypefs := v[0] & 0x1f
switch {
case naluTypefs == 5:
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write(v)
naluTypef = 5
case naluTypefs == h264parser.NALU_SPS:
client.CodecUpdateSPS(v)
case naluTypefs == h264parser.NALU_PPS:
client.CodecUpdatePPS(v)
}
}
}
retmap = client.appendVideoPacket(retmap, client.BufferRtpPacket.Bytes(), naluTypef == 5)
}
}
default:
//client.Println("Unsupported NAL Type", naluType)
}
return retmap
}
func (client *RTSPClient) handleH265Payload(nal []byte, retmap []*av.Packet) []*av.Packet {
naluType := (nal[0] >> 1) & 0x3f
switch naluType {
case h265parser.NAL_UNIT_CODED_SLICE_TRAIL_R:
retmap = client.appendVideoPacket(retmap, nal, false)
case h265parser.NAL_UNIT_VPS:
client.CodecUpdateVPS(nal)
case h265parser.NAL_UNIT_SPS:
client.CodecUpdateSPS(nal)
case h265parser.NAL_UNIT_PPS:
client.CodecUpdatePPS(nal)
case h265parser.NAL_UNIT_UNSPECIFIED_49:
se := nal[2] >> 6
naluType := nal[2] & 0x3f
switch se {
case 2:
client.BufferRtpPacket.Truncate(0)
client.BufferRtpPacket.Reset()
client.BufferRtpPacket.Write([]byte{(nal[0] & 0x81) | (naluType << 1), nal[1]})
r := make([]byte, 2)
r[1] = nal[1]
r[0] = (nal[0] & 0x81) | (naluType << 1)
client.BufferRtpPacket.Write(nal[3:])
case 1:
client.BufferRtpPacket.Write(nal[3:])
retmap = client.appendVideoPacket(retmap, client.BufferRtpPacket.Bytes(), naluType == h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL)
default:
client.BufferRtpPacket.Write(nal[3:])
}
default:
//client.Println("Unsupported Nal", naluType)
}
return retmap
}
func (client *RTSPClient) handleAudio(content []byte) ([]*av.Packet, bool) {
if client.PreAudioTS == 0 {
client.PreAudioTS = client.timestamp
}
nalRaw, _ := h264parser.SplitNALUs(content[client.offset:client.end])
var retmap []*av.Packet
for _, nal := range nalRaw {
var duration time.Duration
switch client.audioCodec {
case av.PCM_MULAW, av.PCM_ALAW:
duration = time.Duration(len(nal)) * time.Second / time.Duration(client.AudioTimeScale)
retmap = client.appendAudioPacket(retmap, nal, duration)
case av.OPUS:
duration = time.Duration(20) * time.Millisecond
retmap = client.appendAudioPacket(retmap, nal, duration)
case av.AAC:
auHeadersLength := uint16(0) | (uint16(nal[0]) << 8) | uint16(nal[1])
auHeadersCount := auHeadersLength >> 4
framesPayloadOffset := 2 + int(auHeadersCount)<<1
auHeaders := nal[2:framesPayloadOffset]
framesPayload := nal[framesPayloadOffset:]
for i := 0; i < int(auHeadersCount); i++ {
auHeader := uint16(0) | (uint16(auHeaders[0]) << 8) | uint16(auHeaders[1])
frameSize := auHeader >> 3
frame := framesPayload[:frameSize]
auHeaders = auHeaders[2:]
framesPayload = framesPayload[frameSize:]
if _, _, _, _, err := aacparser.ParseADTSHeader(frame); err == nil {
frame = frame[7:]
}
duration = time.Duration((float32(1024)/float32(client.AudioTimeScale))*1000*1000*1000) * time.Nanosecond
retmap = client.appendAudioPacket(retmap, frame, duration)
}
}
}
if len(retmap) > 0 {
client.PreAudioTS = client.timestamp
return retmap, true
}
return nil, false
}
func (client *RTSPClient) appendAudioPacket(retmap []*av.Packet, nal []byte, duration time.Duration) []*av.Packet {
client.AudioTimeLine += duration
return append(retmap, &av.Packet{
Data: nal,
CompositionTime: time.Duration(1) * time.Millisecond,
Duration: duration,
Idx: client.audioIDX,
IsKeyFrame: false,
Time: client.AudioTimeLine,
})
}
func (client *RTSPClient) appendVideoPacket(retmap []*av.Packet, nal []byte, isKeyFrame bool) []*av.Packet {
return append(retmap, &av.Packet{
Data: append(binSize(len(nal)), nal...),
CompositionTime: time.Duration(TimeDelay) * time.Millisecond,
Idx: client.videoIDX,
IsKeyFrame: isKeyFrame,
Duration: time.Duration(float32(client.timestamp-client.PreVideoTS)/TimeBaseFactor) * time.Millisecond,
Time: time.Duration(client.timestamp/TimeBaseFactor) * time.Millisecond,
})
}

View File

@ -25,6 +25,7 @@ type ProxyConn struct {
cseq int
session string
protocol int
in int
}
type Proxy struct {
@ -40,7 +41,6 @@ func NewProxyConn(netconn net.Conn) *ProxyConn {
conn.writebuf = make([]byte, 4096)
conn.readbuf = make([]byte, 4096)
conn.session = uuid.New().String()
conn.cseq = 1
return conn
}
@ -156,6 +156,7 @@ func (self *ProxyConn) prepare() error {
return errors.New("no fist cmd")
}
cseq := strings.TrimSpace(stringInBetween(string(self.readbuf[:n]), "CSeq:", "\r\n"))
switch fistStringsSlice[0] {
case OPTIONS:
@ -165,45 +166,46 @@ func (self *ProxyConn) prepare() error {
if self.URL, err = url.Parse(fistStringsSlice[1]); err != nil {
return err
}
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nPublic: OPTIONS, DESCRIBE, SETUP, PLAY\r\nSession: " + self.session + "\r\nCSeq: " + strconv.Itoa(self.cseq) + "\r\n\r\n"))
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nPublic: OPTIONS, DESCRIBE, SETUP, PLAY\r\nSession: " + self.session + "\r\nCSeq: " + cseq + "\r\n\r\n"))
if err != nil {
return err
}
self.options = true
case SETUP:
if strings.Contains(string(self.readbuf[:n]), "RTP/AVP/UDP") {
_, err := self.netconn.Write([]byte("RTSP/1.0 461 Unsupported transport\r\nCSeq: " + strconv.Itoa(self.cseq) + "\r\nSession: " + self.session + "\r\n\r\n"))
_, err := self.netconn.Write([]byte("RTSP/1.0 461 Unsupported transport\r\nCSeq: " + cseq + "\r\nSession: " + self.session + "\r\n\r\n"))
if err != nil {
return err
}
return nil
}
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nCSeq: " + strconv.Itoa(self.cseq) + "\r\nSession: " + self.session + "\r\nTransport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n\r\n"))
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nCSeq: " + cseq + "\r\nSession: " + self.session + "\r\nTransport: RTP/AVP/TCP;unicast;interleaved=" + strconv.Itoa(self.in) + "-" + strconv.Itoa(self.in+1) + "\r\n\r\n"))
if err != nil {
return err
}
self.in = self.in + 2
case DESCRIBE:
buf := "RTSP/1.0 200 OK\r\nContent-Type: application/sdp\r\nSession: " + self.session + "\r\nContent-Length: " + strconv.Itoa(len(self.sdp)) + "\r\nCSeq: " + strconv.Itoa(self.cseq) + "\r\n\r\n"
buf := "RTSP/1.0 200 OK\r\nContent-Type: application/sdp\r\nSession: " + self.session + "\r\nContent-Length: " + strconv.Itoa(len(self.sdp)) + "\r\nCSeq: " + cseq + "\r\n\r\n"
_, err := self.netconn.Write([]byte(buf + string(self.sdp)))
if err != nil {
return err
}
case PLAY:
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nSession: " + self.session + ";timeout=60\r\nCSeq: " + strconv.Itoa(self.cseq) + "\r\n\r\n"))
_, err := self.netconn.Write([]byte("RTSP/1.0 200 OK\r\nSession: " + self.session + ";timeout=60\r\nCSeq: " + cseq + "\r\n\r\n"))
if err != nil {
return err
}
self.playing = true
case TEARDOWN:
self.netconn.Close()
return errors.New("exit")
default:
return errors.New("metod not found")
return errors.New("metod not found " + fistStringsSlice[0])
}
return nil

View File

@ -7,7 +7,7 @@ import (
"net/url"
"time"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
)
const (
@ -30,7 +30,6 @@ const (
LocalCache int = 3
)
//
const (
StreamTypeH264 = 0x1b
StreamTypeH265 = 0x24

View File

@ -6,11 +6,12 @@ import (
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/ts/tsio"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/mjpeg"
"git.r-2.top/kunmeng/vdk/format/ts/tsio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
type Demuxer struct {
@ -22,7 +23,7 @@ type Demuxer struct {
pmt *tsio.PMT
streams []*Stream
tshdr []byte
AnnexB bool
stage int
}
@ -118,6 +119,8 @@ func (self *Demuxer) initPMT(payload []byte) (err error) {
self.streams = append(self.streams, stream)
case tsio.ElementaryStreamTypeAdtsAAC:
self.streams = append(self.streams, stream)
case tsio.ElementaryStreamTypeAlignmentDescriptor:
self.streams = append(self.streams, stream)
}
}
return
@ -187,7 +190,7 @@ func (self *Demuxer) readTSPacket() (err error) {
return
}
func (self *Stream) addPacket(payload []byte, timedelta time.Duration) {
func (self *Stream) addPacket(payload []byte, timedelta time.Duration, fixed time.Duration) {
dts := self.dts
pts := self.pts
@ -199,6 +202,8 @@ func (self *Stream) addPacket(payload []byte, timedelta time.Duration) {
if self.pt > 0 {
dur = dts + timedelta - self.pt
} else {
dur = fixed
}
self.pt = dts + timedelta
@ -227,8 +232,16 @@ func (self *Stream) payloadEnd() (n int, err error) {
return
}
self.data = nil
switch self.streamType {
case tsio.ElementaryStreamTypeAlignmentDescriptor:
if self.CodecData == nil {
self.CodecData = mjpeg.CodecData{}
}
b := make([]byte, 4+len(payload))
pio.PutU32BE(b[0:4], uint32(len(payload)))
copy(b[4:], payload)
self.addPacket(b, time.Duration(0), 0)
n++
case tsio.ElementaryStreamTypeAdtsAAC:
var config aacparser.MPEG4AudioConfig
@ -243,7 +256,7 @@ func (self *Stream) payloadEnd() (n int, err error) {
return
}
}
self.addPacket(payload[hdrlen:framelen], delta)
self.addPacket(payload[hdrlen:framelen], delta, time.Duration(samples)*time.Second/time.Duration(config.SampleRate))
n++
delta += time.Duration(samples) * time.Second / time.Duration(config.SampleRate)
payload = payload[framelen:]
@ -252,24 +265,43 @@ func (self *Stream) payloadEnd() (n int, err error) {
case tsio.ElementaryStreamTypeH264:
nalus, _ := h264parser.SplitNALUs(payload)
var sps, pps []byte
for _, nalu := range nalus {
if len(nalu) > 0 {
naltype := nalu[0] & 0x1f
switch {
case naltype == 7:
sps = nalu
info, err := h264parser.ParseSPS(sps)
if err == nil {
self.fps = info.FPS
}
case naltype == 8:
pps = nalu
case h264parser.IsDataNALU(nalu):
// raw nalu to avcc
if !self.demuxer.AnnexB {
b := make([]byte, 4+len(nalu))
pio.PutU32BE(b[0:4], uint32(len(nalu)))
copy(b[4:], nalu)
self.addPacket(b, time.Duration(0))
fps := self.fps
if self.fps == 0 {
fps = 25
}
self.addPacket(b, time.Duration(0), (1000*time.Millisecond)/time.Duration(fps))
n++
}
}
}
}
if self.demuxer.AnnexB {
b := make([]byte, 4+len(payload))
pio.PutU32BE(b[0:4], uint32(len(payload)))
copy(b[4:], payload)
self.addPacket(b, time.Duration(0), 0)
n++
}
if self.CodecData == nil && len(sps) > 0 && len(pps) > 0 {
if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil {

View File

@ -3,8 +3,8 @@ package ts
import (
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/av/avutil"
)
func Handler(h *avutil.RegisterHandler) {

View File

@ -2,16 +2,17 @@ package ts
import (
"fmt"
"git.r-2.top/kunmeng/vdk/codec/h265parser"
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/ts/tsio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/aacparser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/format/ts/tsio"
)
var CodecTypes = []av.CodecType{av.H264, av.AAC}
var CodecTypes = []av.CodecType{av.H264, av.H265, av.AAC}
type Muxer struct {
w io.Writer
@ -67,11 +68,16 @@ func (self *Muxer) newStream(idx int, codec av.CodecData) (err error) {
return
}
func (self *Muxer) writePaddingTSPackets(tsw *tsio.TSWriter) (err error) {
for tsw.ContinuityCounter&0xf != 0x0 {
if err = tsw.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil {
func (self *Muxer) writePaddingTSPackets(streamW *Stream) (err error) {
for streamW.tsw.ContinuityCounter&0xf != 0x0 {
header := tsio.TSHeader{
PID: uint(streamW.pid),
ContinuityCounter: streamW.tsw.ContinuityCounter,
}
if _, err = tsio.WriteTSHeader(self.w, header, 0); err != nil {
return
}
streamW.tsw.ContinuityCounter++
}
return
}
@ -79,7 +85,7 @@ func (self *Muxer) writePaddingTSPackets(tsw *tsio.TSWriter) (err error) {
func (self *Muxer) WriteTrailer() (err error) {
if self.PaddingToMakeCounterCont {
for _, stream := range self.streams {
if err = self.writePaddingTSPackets(stream.tsw); err != nil {
if err = self.writePaddingTSPackets(stream); err != nil {
return
}
}
@ -118,6 +124,11 @@ func (self *Muxer) WritePATPMT() (err error) {
StreamType: tsio.ElementaryStreamTypeH264,
ElementaryPID: stream.pid,
})
case av.H265:
elemStreams = append(elemStreams, tsio.ElementaryStreamInfo{
StreamType: tsio.ElementaryStreamTypeH265,
ElementaryPID: stream.pid,
})
}
}
@ -205,6 +216,36 @@ func (self *Muxer) WritePacket(pkt av.Packet) (err error) {
n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdH264, -1, pkt.Time+pkt.CompositionTime, pkt.Time)
datav[0] = self.peshdr[:n]
if err = stream.tsw.WritePackets(self.w, datav, pkt.Time, pkt.IsKeyFrame, false); err != nil {
return
}
case av.H265:
codec := stream.CodecData.(h265parser.CodecData)
nalus := self.nalus[:0]
if pkt.IsKeyFrame {
nalus = append(nalus, codec.SPS())
nalus = append(nalus, codec.PPS())
nalus = append(nalus, codec.VPS())
}
pktnalus, _ := h265parser.SplitNALUs(pkt.Data)
for _, nalu := range pktnalus {
nalus = append(nalus, nalu)
}
datav := self.datav[:1]
for i, nalu := range nalus {
if i == 0 {
datav = append(datav, h265parser.AUDBytes)
} else {
datav = append(datav, h265parser.StartCodeBytes)
}
datav = append(datav, nalu)
}
n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdH264, -1, pkt.Time+pkt.CompositionTime, pkt.Time)
datav[0] = self.peshdr[:n]
if err = stream.tsw.WritePackets(self.w, datav, pkt.Time, pkt.IsKeyFrame, false); err != nil {
return
}

View File

@ -3,8 +3,8 @@ package ts
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/ts/tsio"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/format/ts/tsio"
)
type Stream struct {
@ -19,7 +19,7 @@ type Stream struct {
tsw *tsio.TSWriter
idx int
fps uint
iskeyframe bool
pts, dts, pt time.Duration
data []byte

View File

@ -5,7 +5,7 @@ import (
"io"
"time"
"github.com/deepch/vdk/utils/bits/pio"
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
)
const (
@ -35,6 +35,8 @@ var ErrParsePAT = fmt.Errorf("invalid PAT")
const (
ElementaryStreamTypeH264 = 0x1B
ElementaryStreamTypeAdtsAAC = 0x0F
ElementaryStreamTypeAlignmentDescriptor = 0x06
ElementaryStreamTypeH265 = 0x24
)
type PATEntry struct {

View File

@ -10,8 +10,8 @@ import (
"github.com/pion/webrtc/v2"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/av"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"github.com/pion/webrtc/v2/pkg/media"
)

View File

@ -7,9 +7,9 @@ import (
"log"
"time"
"github.com/deepch/vdk/codec/h264parser"
"git.r-2.top/kunmeng/vdk/codec/h264parser"
"github.com/deepch/vdk/av"
"git.r-2.top/kunmeng/vdk/av"
"github.com/pion/interceptor"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media"
@ -65,6 +65,10 @@ func (element *Muxer) NewPeerConnection(configuration webrtc.Configuration) (*we
Credential: element.Options.ICECredential,
CredentialType: webrtc.ICECredentialTypePassword,
})
} else {
configuration.ICEServers = append(configuration.ICEServers, webrtc.ICEServer{
URLs: []string{"stun:stun.l.google.com:19302"},
})
}
m := &webrtc.MediaEngine{}
if err := m.RegisterDefaultCodecs(); err != nil {
@ -118,13 +122,22 @@ func (element *Muxer) WriteHeader(streams []av.CodecData, sdp64 string) (string,
if i2.Type().IsVideo() {
if i2.Type() == av.H264 {
track, err = webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{
MimeType: "video/h264",
}, "pion-rtsp-video", "pion-rtsp-video")
MimeType: webrtc.MimeTypeH264,
}, "pion-rtsp-video", "pion-video")
if err != nil {
return "", err
}
if _, err = peerConnection.AddTrack(track); err != nil {
if rtpSender, err := peerConnection.AddTrack(track); err != nil {
return "", err
} else {
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
}
}
} else if i2.Type().IsAudio() {
@ -148,8 +161,17 @@ func (element *Muxer) WriteHeader(streams []av.CodecData, sdp64 string) (string,
if err != nil {
return "", err
}
if _, err = peerConnection.AddTrack(track); err != nil {
if rtpSender, err := peerConnection.AddTrack(track); err != nil {
return "", err
} else {
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
}
}
element.streams[int8(i)] = &Stream{track: track, codec: i2}
@ -225,8 +247,7 @@ func (element *Muxer) WritePacket(pkt av.Packet) (err error) {
if naltype == 5 {
codec := tmp.codec.(h264parser.CodecData)
err = tmp.track.WriteSample(media.Sample{Data: append([]byte{0, 0, 0, 1}, bytes.Join([][]byte{codec.SPS(), codec.PPS(), nalu}, []byte{0, 0, 0, 1})...), Duration: pkt.Duration})
} else if naltype == 1 {
} else {
err = tmp.track.WriteSample(media.Sample{Data: append([]byte{0, 0, 0, 1}, nalu...), Duration: pkt.Duration})
}
if err != nil {

55
go.mod
View File

@ -1,11 +1,56 @@
module github.com/deepch/vdk
module git.r-2.top/kunmeng/vdk
go 1.15
go 1.18
require (
github.com/gobwas/ws v1.3.1
github.com/google/uuid v1.3.0
github.com/pion/interceptor v0.1.8
github.com/pion/interceptor v0.1.17
github.com/pion/webrtc/v2 v2.2.26
github.com/pion/webrtc/v3 v3.1.24
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c // indirect
github.com/pion/webrtc/v3 v3.2.12
github.com/shirou/gopsutil/v3 v3.24.5
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect
github.com/lucas-clemente/quic-go v0.31.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.4 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.2 // indirect
github.com/onsi/ginkgo/v2 v2.9.0 // indirect
github.com/pion/datachannel v1.5.5 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/ice v0.7.18 // indirect
github.com/pion/ice/v2 v2.3.9 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.7 // indirect
github.com/pion/quic v0.1.4 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.10 // indirect
github.com/pion/rtp v1.7.13 // indirect
github.com/pion/sctp v1.8.7 // indirect
github.com/pion/sdp/v2 v2.4.0 // indirect
github.com/pion/sdp/v3 v3.0.6 // indirect
github.com/pion/srtp v1.5.2 // indirect
github.com/pion/srtp/v2 v2.0.15 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport v0.14.1 // indirect
github.com/pion/transport/v2 v2.2.1 // indirect
github.com/pion/turn/v2 v2.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/tools v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

447
go.sum
View File

@ -1,197 +1,301 @@
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU=
github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc=
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4=
github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-18 v0.1.4 h1:ogomB+lWV3Vmwiu6RTwDVTMGx+9j7SEi98e8QB35Its=
github.com/marten-seemann/qtls-go1-18 v0.1.4/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/marten-seemann/qtls-go1-19 v0.1.2 h1:ZevAEqKXH0bZmoOBPiqX2h5rhQ7cbZi+X+rlq2JUbCE=
github.com/marten-seemann/qtls-go1-19 v0.1.2/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.1 h1:foqVmeWDD6yYpK+Yz3fHyNIxFYNxswxqNFjSKe+vI54=
github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ=
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
github.com/pion/dtls/v2 v2.0.1/go.mod h1:uMQkz2W0cSqY00xav7WByQ4Hb+18xeQh2oH2fRezr5U=
github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I=
github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8=
github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho=
github.com/pion/dtls/v2 v2.0.13/go.mod h1:OaE7eTM+ppaUhJ99OTO4aHl9uY6vPrT1gPY27uNTxRY=
github.com/pion/dtls/v2 v2.1.0 h1:g6gtKVNLp6URDkv9OijFJl16kqGHzVzZG+Fa4A38GTY=
github.com/pion/dtls/v2 v2.1.0/go.mod h1:qG3gA7ZPZemBqpEFqRKyURYdKEwFZQCGb7gv9T3ON3Y=
github.com/pion/dtls/v2 v2.1.2/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus=
github.com/pion/dtls/v2 v2.1.3 h1:3UF7udADqous+M2R5Uo2q/YaP4EzUoWKdfX2oscCUio=
github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/ice v0.7.18 h1:KbAWlzWRUdX9SmehBh3gYpIFsirjhSQsCw6K2MjYMK0=
github.com/pion/ice v0.7.18/go.mod h1:+Bvnm3nYC6Nnp7VV6glUkuOfToB/AtMRZpOU8ihuf4c=
github.com/pion/ice/v2 v2.1.7 h1:FjgDfUNrVYTxQabJrkBX6ld12tvYbgzHenqPh3PJF6E=
github.com/pion/ice/v2 v2.1.7/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0=
github.com/pion/ice/v2 v2.1.12 h1:ZDBuZz+fEI7iDifZCYFVzI4p0Foy0YhdSSZ87ZtRcRE=
github.com/pion/ice/v2 v2.1.12/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU=
github.com/pion/ice/v2 v2.1.18 h1:mDzd+iPKJmU30p4Kb+RPjK9olORLqJmQdiTUnVba50g=
github.com/pion/ice/v2 v2.1.18/go.mod h1:9jDr0iIUg8P6+0Jq8QJ/eFSkX3JnsPd293TjCdkfpTs=
github.com/pion/ice/v2 v2.2.1 h1:R3MeuJZpU1ty3diPqpD5OxaxcZ15eprAc+EtUiSoFxg=
github.com/pion/ice/v2 v2.2.1/go.mod h1:Op8jlPtjeiycsXh93Cs4jK82C9j/kh7vef6ztIOvtIQ=
github.com/pion/interceptor v0.0.12 h1:eC1iVneBIAQJEfaNAfDqAncJWhMDAnaXPRCJsltdokE=
github.com/pion/interceptor v0.0.12/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4=
github.com/pion/interceptor v0.1.0 h1:SlXKaDlEvSl7cr4j8fJykzVz4UdH+7UDtcvx+u01wLU=
github.com/pion/interceptor v0.1.0/go.mod h1:j5NIl3tJJPB3u8+Z2Xz8MZs/VV6rc+If9mXEKNuFmEM=
github.com/pion/interceptor v0.1.6 h1:ZTXN9fApUDmFqifG64g+ar57XY7vlnXUs7/0DjHVtLo=
github.com/pion/interceptor v0.1.6/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U=
github.com/pion/interceptor v0.1.7/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U=
github.com/pion/interceptor v0.1.8 h1:5K27KMw8enTB1jVDFrjadK8sZjI5JbPJ91OVfiih5fE=
github.com/pion/interceptor v0.1.8/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U=
github.com/pion/ice/v2 v2.3.9 h1:7yZpHf3PhPxJGT4JkMj1Y8Rl5cQ6fB709iz99aeMd/U=
github.com/pion/ice/v2 v2.3.9/go.mod h1:lT3kv5uUIlHfXHU/ZRD7uKD/ufM202+eTa3C/umgGf4=
github.com/pion/interceptor v0.1.17 h1:prJtgwFh/gB8zMqGZoOgJPHivOwVAp61i2aG61Du/1w=
github.com/pion/interceptor v0.1.17/go.mod h1:SY8kpmfVBvrbUzvj2bsXz7OJt5JvmVNZ+4Kjq7FcwrI=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
github.com/pion/quic v0.1.1 h1:D951FV+TOqI9A0rTF7tHx0Loooqz+nyzjEyj8o3PuMA=
github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U=
github.com/pion/mdns v0.0.7/go.mod h1:4iP2UbeFhLI/vWju/bw6ZfwjJzk0z8DNValjGxR/dD8=
github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k=
github.com/pion/quic v0.1.4 h1:bNz9sCJjlM3GqMdq7Fne57FiWfdyiJ++yHVbuqeoD3Y=
github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk=
github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
github.com/pion/rtcp v1.2.8 h1:Cys8X6r0xxU65ESTmXkqr8eU1Q1Wx+lNkoZCUH4zD7E=
github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U=
github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc=
github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I=
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.6.5 h1:o2cZf8OascA5HF/b0PAbTxRKvOWxTQxWYt7SlToxFGI=
github.com/pion/rtp v1.6.5/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.7.2 h1:HCDKDCixh7PVjkQTsqHAbk1lg+bx059EHxcnyl42dYs=
github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA=
github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA=
github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY=
github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
github.com/pion/sctp v1.8.2 h1:yBBCIrUMJ4yFICL3RIvR4eh/H2BTTvlligmSTy+3kiA=
github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0=
github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw=
github.com/pion/sctp v1.8.7/go.mod h1:g1Ul+ARqZq5JEmoFy87Q/4CePtKnTJ1QCL9dBBdN6AU=
github.com/pion/sdp/v2 v2.4.0 h1:luUtaETR5x2KNNpvEMv/r4Y+/kzImzbz4Lm1z8eQNQI=
github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E=
github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8=
github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
github.com/pion/srtp v1.5.1 h1:9Q3jAfslYZBt+C69SI/ZcONJh9049JUHZWYRRf5KEKw=
github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw=
github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA=
github.com/pion/srtp/v2 v2.0.2 h1:664iGzVmaY7KYS5M0gleY0DscRo9ReDfTxQrq4UgGoU=
github.com/pion/srtp/v2 v2.0.2/go.mod h1:VEyLv4CuxrwGY8cxM+Ng3bmVy8ckz/1t6A0q/msKOw0=
github.com/pion/srtp/v2 v2.0.5 h1:ks3wcTvIUE/GHndO3FAvROQ9opy0uLELpwHJaQ1yqhQ=
github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A=
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
github.com/pion/srtp v1.5.2 h1:25DmvH+fqKZDqvX64vTwnycVwL9ooJxHF/gkX16bDBY=
github.com/pion/srtp v1.5.2/go.mod h1:NiBff/MSxUwMUwx/fRNyD/xGE+dVvf8BOCeXhjCXZ9U=
github.com/pion/srtp/v2 v2.0.15 h1:+tqRtXGsGwHC0G0IUIAzRmdkHvriF79IHVfZGfHrQoA=
github.com/pion/srtp/v2 v2.0.15/go.mod h1:b/pQOlDrbB0HEH5EUAQXzSYxikFbNcNuKmF8tM0hCtw=
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw=
github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A=
github.com/pion/transport v0.13.0 h1:KWTA5ZrQogizzYwPEciGtHPLwpAjE91FgXnyu+Hv2uY=
github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g=
github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40=
github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI=
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
github.com/pion/transport/v2 v2.2.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
github.com/pion/turn/v2 v2.0.6 h1:AsXjSPR6Im15DMTB39NlfdTY9BQfieANPBjdg/aVNwY=
github.com/pion/turn/v2 v2.0.6/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw=
github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw=
github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw=
github.com/pion/turn/v2 v2.1.2 h1:wj0cAoGKltaZ790XEGW9HwoUewqjliwmhtxCuB2ApyM=
github.com/pion/turn/v2 v2.1.2/go.mod h1:1kjnPkBcex3dhCU2Am+AAmxDcGhLX3WnMfmkNpvSTQU=
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o=
github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M=
github.com/pion/webrtc/v2 v2.2.26 h1:01hWE26pL3LgqfxvQ1fr6O4ZtyRFFJmQEZK39pHWfFc=
github.com/pion/webrtc/v2 v2.2.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc=
github.com/pion/webrtc/v3 v3.0.29 h1:pVs6mYjbbYvC8pMsztayEz35DnUEFLPswsicGXaQjxo=
github.com/pion/webrtc/v3 v3.0.29/go.mod h1:XFQeLYBf++bWWA0sJqh6zF1ouWluosxwTOMOoTZGaD0=
github.com/pion/webrtc/v3 v3.1.1 h1:UA4qU+zoAAlUkl0J7ek/YnV3e0orvE8V667imvmnAoo=
github.com/pion/webrtc/v3 v3.1.1/go.mod h1:t51XSam1k56eYLuO1Ubxjs3pDBfGYxkGBFhYf55Mn/s=
github.com/pion/webrtc/v3 v3.1.17 h1:6V4Yf5wnvJZKs86401EcpsKmB5Px5pfF1ICXdPIRsC0=
github.com/pion/webrtc/v3 v3.1.17/go.mod h1:kHunUx6HPCbCvGy/HdWQNwtT9LJ2XMS/sBmLwB1A4rs=
github.com/pion/webrtc/v3 v3.1.24 h1:s9PuwisrgHe1FTqfwK4p3T7rXtAHaUNhycbdMjADT28=
github.com/pion/webrtc/v3 v3.1.24/go.mod h1:mO/yv7fBN3Lp7YNlnYcTj1jtpvNvssJG+7eh6itZ4xM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pion/webrtc/v3 v3.2.12 h1:pVqz5NdtTqyhKIhMcXR8bPp709kCf9blyAhDjoVRLvA=
github.com/pion/webrtc/v3 v3.2.12/go.mod h1:/Oz6K95CGWaN+3No+Z0NYvgOPOr3aY8UyTlMm/dec3A=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c h1:Nu1I2o5Lk5nvn2RChPOJ1MzTXKZoHiy665a5aANHssQ=
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c/go.mod h1:Mtv+LuzkLR5r7fwBlvqI/N/5d4nOaH/7fpgQFXwxnFk=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw=
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -201,82 +305,159 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210420210106-798c2154c571 h1:Q6Bg8xzKzpFPU4Oi1sBnBTHBwlMsLeEXpu4hYBY8rAg=
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe h1:WdX7u8s3yOigWAhHEaDl8r9G+4XwFQEQFtBMYyN+kXQ=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=