2020-08-08 23:14:32 +08:00
|
|
|
package rtspv2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"crypto/md5"
|
2021-11-13 15:34:17 +08:00
|
|
|
"crypto/tls"
|
2020-08-08 23:14:32 +08:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"html"
|
|
|
|
"io"
|
|
|
|
"log"
|
2022-03-10 00:31:51 +08:00
|
|
|
"math"
|
2020-08-08 23:14:32 +08:00
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/deepch/vdk/av"
|
2021-01-08 12:23:06 +08:00
|
|
|
"github.com/deepch/vdk/codec"
|
|
|
|
"github.com/deepch/vdk/codec/aacparser"
|
2020-08-08 23:14:32 +08:00
|
|
|
"github.com/deepch/vdk/codec/h264parser"
|
2021-02-03 11:10:03 +08:00
|
|
|
"github.com/deepch/vdk/codec/h265parser"
|
2020-08-08 23:14:32 +08:00
|
|
|
"github.com/deepch/vdk/format/rtsp/sdp"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
SignalStreamRTPStop = iota
|
|
|
|
SignalCodecUpdate
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
VIDEO = "video"
|
|
|
|
AUDIO = "audio"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-08-05 01:32:31 +08:00
|
|
|
RTPHeaderSize = 12
|
|
|
|
RTCPSenderReport = 200
|
|
|
|
RTCPReceiverReport = 201
|
2020-08-08 23:14:32 +08:00
|
|
|
)
|
2022-08-05 01:32:31 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
const (
|
|
|
|
DESCRIBE = "DESCRIBE"
|
|
|
|
OPTIONS = "OPTIONS"
|
|
|
|
PLAY = "PLAY"
|
|
|
|
SETUP = "SETUP"
|
2021-02-18 18:56:44 +08:00
|
|
|
TEARDOWN = "TEARDOWN"
|
2020-08-08 23:14:32 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type RTSPClient struct {
|
2021-01-06 22:58:38 +08:00
|
|
|
control string
|
|
|
|
seq int
|
|
|
|
session string
|
|
|
|
realm string
|
|
|
|
nonce string
|
|
|
|
username string
|
|
|
|
password string
|
|
|
|
startVideoTS int64
|
|
|
|
startAudioTS int64
|
|
|
|
videoID int
|
|
|
|
audioID int
|
2021-01-08 12:23:06 +08:00
|
|
|
videoIDX int8
|
|
|
|
audioIDX int8
|
2021-01-06 22:58:38 +08:00
|
|
|
mediaSDP []sdp.Media
|
|
|
|
SDPRaw []byte
|
|
|
|
conn net.Conn
|
|
|
|
connRW *bufio.ReadWriter
|
|
|
|
pURL *url.URL
|
|
|
|
headers map[string]string
|
|
|
|
Signals chan int
|
|
|
|
OutgoingProxyQueue chan *[]byte
|
|
|
|
OutgoingPacketQueue chan *av.Packet
|
|
|
|
clientDigest bool
|
|
|
|
clientBasic bool
|
|
|
|
fuStarted bool
|
|
|
|
options RTSPClientOptions
|
|
|
|
BufferRtpPacket *bytes.Buffer
|
2021-02-03 11:10:03 +08:00
|
|
|
vps []byte
|
2021-01-06 22:58:38 +08:00
|
|
|
sps []byte
|
|
|
|
pps []byte
|
|
|
|
CodecData []av.CodecData
|
2021-01-09 22:24:48 +08:00
|
|
|
AudioTimeLine time.Duration
|
2021-01-09 12:37:19 +08:00
|
|
|
AudioTimeScale int64
|
2021-01-09 22:24:48 +08:00
|
|
|
audioCodec av.CodecType
|
2021-02-03 11:10:03 +08:00
|
|
|
videoCodec av.CodecType
|
2021-01-09 22:24:48 +08:00
|
|
|
PreAudioTS int64
|
|
|
|
PreVideoTS int64
|
2021-02-03 09:07:15 +08:00
|
|
|
PreSequenceNumber int
|
2021-05-09 04:07:59 +08:00
|
|
|
FPS int
|
2021-08-29 18:34:06 +08:00
|
|
|
WaitCodec bool
|
2022-02-11 04:50:25 +08:00
|
|
|
chTMP int
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type RTSPClientOptions struct {
|
2022-02-08 20:14:06 +08:00
|
|
|
Debug bool
|
|
|
|
URL string
|
|
|
|
DialTimeout time.Duration
|
|
|
|
ReadWriteTimeout time.Duration
|
|
|
|
DisableAudio bool
|
|
|
|
OutgoingProxy bool
|
|
|
|
InsecureSkipVerify bool
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func Dial(options RTSPClientOptions) (*RTSPClient, error) {
|
|
|
|
client := &RTSPClient{
|
2021-01-06 22:58:38 +08:00
|
|
|
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{}),
|
2021-01-09 12:37:19 +08:00
|
|
|
videoID: -1,
|
|
|
|
audioID: -2,
|
|
|
|
videoIDX: -1,
|
|
|
|
audioIDX: -2,
|
2021-01-06 22:58:38 +08:00
|
|
|
options: options,
|
2021-01-09 12:37:19 +08:00
|
|
|
AudioTimeScale: 8000,
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2022-02-08 20:14:06 +08:00
|
|
|
client.headers["User-Agent"] = "Lavf58.76.100"
|
2020-08-08 23:14:32 +08:00
|
|
|
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
|
|
|
|
}
|
2021-11-13 15:34:17 +08:00
|
|
|
if client.pURL.Scheme == "rtsps" {
|
2022-02-08 20:14:06 +08:00
|
|
|
tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: options.InsecureSkipVerify, ServerName: client.pURL.Hostname()})
|
2021-11-13 15:34:17 +08:00
|
|
|
err = tlsConn.Handshake()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
conn = tlsConn
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
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) {
|
2023-08-25 19:35:34 +08:00
|
|
|
//TODO check it
|
|
|
|
if strings.Contains(string(client.SDPRaw), "LaunchDigital") {
|
|
|
|
client.chTMP += 2
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
continue
|
|
|
|
}
|
2022-02-11 04:50:25 +08:00
|
|
|
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)
|
2020-08-08 23:14:32 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if i2.AVType == VIDEO {
|
2021-02-09 09:27:04 +08:00
|
|
|
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{})
|
2021-08-29 18:34:06 +08:00
|
|
|
client.WaitCodec = true
|
2021-02-03 11:10:03 +08:00
|
|
|
}
|
2021-05-09 04:07:59 +08:00
|
|
|
client.FPS = i2.FPS
|
2021-02-09 09:27:04 +08:00
|
|
|
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{})
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2021-02-09 09:27:04 +08:00
|
|
|
client.videoCodec = av.H265
|
2022-02-08 20:14:06 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
} else {
|
|
|
|
client.Println("SDP Video Codec Type Not Supported", i2.Type)
|
|
|
|
}
|
2021-02-09 09:27:04 +08:00
|
|
|
client.videoIDX = int8(len(client.CodecData) - 1)
|
2022-02-11 04:50:25 +08:00
|
|
|
client.videoID = client.chTMP
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
if i2.AVType == AUDIO {
|
2022-02-11 04:50:25 +08:00
|
|
|
client.audioID = client.chTMP
|
2021-01-08 12:23:06 +08:00
|
|
|
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")
|
|
|
|
}
|
2021-01-09 12:37:19 +08:00
|
|
|
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)
|
2021-01-08 12:23:06 +08:00
|
|
|
case av.PCM_MULAW:
|
|
|
|
CodecData = codec.NewPCMMulawCodecData()
|
|
|
|
case av.PCM_ALAW:
|
2021-01-09 12:37:19 +08:00
|
|
|
CodecData = codec.NewPCMAlawCodecData()
|
2021-01-08 12:23:06 +08:00
|
|
|
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)
|
2021-01-09 22:24:48 +08:00
|
|
|
client.audioCodec = CodecData.Type()
|
2021-01-09 12:37:19 +08:00
|
|
|
if i2.TimeScale != 0 {
|
|
|
|
client.AudioTimeScale = int64(i2.TimeScale)
|
|
|
|
}
|
2021-01-08 12:23:06 +08:00
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2022-02-11 04:50:25 +08:00
|
|
|
client.chTMP += 2
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2021-09-30 07:10:08 +08:00
|
|
|
//test := map[string]string{"Scale": "1.000000", "Speed": "1.000000", "Range": "clock=20210929T210000Z-20210929T211000Z"}
|
2020-08-08 23:14:32 +08:00
|
|
|
err = client.request(PLAY, nil, client.control, false, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
go client.startStream()
|
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
2020-09-04 09:09:55 +08:00
|
|
|
func (client *RTSPClient) ControlTrack(track string) string {
|
|
|
|
if strings.Contains(track, "rtsp://") {
|
|
|
|
return track
|
|
|
|
}
|
2021-02-14 22:56:19 +08:00
|
|
|
if !strings.HasSuffix(client.control, "/") {
|
|
|
|
track = "/" + track
|
|
|
|
}
|
2020-09-04 09:09:55 +08:00
|
|
|
return client.control + track
|
|
|
|
}
|
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
func (client *RTSPClient) startStream() {
|
|
|
|
defer func() {
|
|
|
|
client.Signals <- SignalStreamRTPStop
|
|
|
|
}()
|
|
|
|
timer := time.Now()
|
|
|
|
oneb := make([]byte, 1)
|
|
|
|
header := make([]byte, 4)
|
|
|
|
var fixed bool
|
|
|
|
for {
|
|
|
|
err := client.conn.SetDeadline(time.Now().Add(client.options.ReadWriteTimeout))
|
|
|
|
if err != nil {
|
|
|
|
client.Println("RTSP Client RTP SetDeadline", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if int(time.Now().Sub(timer).Seconds()) > 25 {
|
|
|
|
err := client.request(OPTIONS, map[string]string{"Require": "implicit-play"}, client.control, false, true)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("RTSP Client RTP keep-alive", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
timer = time.Now()
|
|
|
|
}
|
|
|
|
if !fixed {
|
2021-01-09 22:24:48 +08:00
|
|
|
nb, err := io.ReadFull(client.connRW, header)
|
2020-08-08 23:14:32 +08:00
|
|
|
if err != nil || nb != 4 {
|
|
|
|
client.Println("RTSP Client RTP Read Header", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fixed = false
|
|
|
|
switch header[0] {
|
|
|
|
case 0x24:
|
|
|
|
length := int32(binary.BigEndian.Uint16(header[2:]))
|
|
|
|
if length > 65535 || length < 12 {
|
|
|
|
client.Println("RTSP Client RTP Incorrect Packet Size")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
content := make([]byte, length+4)
|
|
|
|
content[0] = header[0]
|
|
|
|
content[1] = header[1]
|
|
|
|
content[2] = header[2]
|
|
|
|
content[3] = header[3]
|
2021-01-09 22:24:48 +08:00
|
|
|
n, rerr := io.ReadFull(client.connRW, content[4:length+4])
|
2020-08-08 23:14:32 +08:00
|
|
|
if rerr != nil || n != int(length) {
|
|
|
|
client.Println("RTSP Client RTP ReadFull", err)
|
|
|
|
return
|
|
|
|
}
|
2022-02-08 20:14:06 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
//atomic.AddInt64(&client.Bitrate, int64(length+4))
|
2021-01-06 22:58:38 +08:00
|
|
|
if client.options.OutgoingProxy {
|
|
|
|
if len(client.OutgoingProxyQueue) < 2000 {
|
|
|
|
client.OutgoingProxyQueue <- &content
|
|
|
|
} else {
|
|
|
|
client.Println("RTSP Client OutgoingProxy Chanel Full")
|
|
|
|
return
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
pkt, got := client.RTPDemuxer(&content)
|
|
|
|
if !got {
|
|
|
|
continue
|
|
|
|
}
|
2022-02-08 20:14:06 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
for _, i2 := range pkt {
|
2021-01-06 22:58:38 +08:00
|
|
|
if len(client.OutgoingPacketQueue) > 2000 {
|
2020-08-08 23:14:32 +08:00
|
|
|
client.Println("RTSP Client OutgoingPacket Chanel Full")
|
|
|
|
return
|
|
|
|
}
|
2021-01-06 22:58:38 +08:00
|
|
|
client.OutgoingPacketQueue <- i2
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
case 0x52:
|
|
|
|
var responseTmp []byte
|
|
|
|
for {
|
2021-01-09 22:24:48 +08:00
|
|
|
n, rerr := io.ReadFull(client.connRW, oneb)
|
2020-08-08 23:14:32 +08:00
|
|
|
if rerr != nil || n != 1 {
|
|
|
|
client.Println("RTSP Client RTP Read Keep-Alive Header", rerr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
responseTmp = append(responseTmp, oneb...)
|
|
|
|
if (len(responseTmp) > 4 && bytes.Compare(responseTmp[len(responseTmp)-4:], []byte("\r\n\r\n")) == 0) || len(responseTmp) > 768 {
|
|
|
|
if strings.Contains(string(responseTmp), "Content-Length:") {
|
|
|
|
si, err := strconv.Atoi(stringInBetween(string(responseTmp), "Content-Length: ", "\r\n"))
|
|
|
|
if err != nil {
|
|
|
|
client.Println("RTSP Client RTP Read Keep-Alive Content-Length", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cont := make([]byte, si)
|
2021-01-09 22:24:48 +08:00
|
|
|
_, err = io.ReadFull(client.connRW, cont)
|
2020-08-08 23:14:32 +08:00
|
|
|
if err != nil {
|
|
|
|
client.Println("RTSP Client RTP Read Keep-Alive ReadFull", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
client.Println("RTSP Client RTP Read DeSync")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) request(method string, customHeaders map[string]string, uri string, one bool, nores bool) (err error) {
|
|
|
|
err = client.conn.SetDeadline(time.Now().Add(client.options.ReadWriteTimeout))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client.seq++
|
|
|
|
builder := bytes.Buffer{}
|
|
|
|
builder.WriteString(fmt.Sprintf("%s %s RTSP/1.0\r\n", method, uri))
|
|
|
|
builder.WriteString(fmt.Sprintf("CSeq: %d\r\n", client.seq))
|
|
|
|
if client.clientDigest {
|
|
|
|
builder.WriteString(fmt.Sprintf("Authorization: %s\r\n", client.createDigest(method, uri)))
|
|
|
|
}
|
|
|
|
if customHeaders != nil {
|
|
|
|
for k, v := range customHeaders {
|
|
|
|
builder.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range client.headers {
|
|
|
|
builder.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
|
|
|
|
}
|
|
|
|
builder.WriteString(fmt.Sprintf("\r\n"))
|
|
|
|
client.Println(builder.String())
|
|
|
|
s := builder.String()
|
|
|
|
_, err = client.connRW.WriteString(s)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = client.connRW.Flush()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
builder.Reset()
|
|
|
|
if !nores {
|
|
|
|
var isPrefix bool
|
|
|
|
var line []byte
|
|
|
|
var contentLen int
|
|
|
|
res := make(map[string]string)
|
|
|
|
for {
|
|
|
|
line, isPrefix, err = client.connRW.ReadLine()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if strings.Contains(string(line), "RTSP/1.0") && (!strings.Contains(string(line), "200") && !strings.Contains(string(line), "401")) {
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
err = errors.New("Camera send status" + string(line))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
builder.Write(line)
|
|
|
|
if !isPrefix {
|
|
|
|
builder.WriteString("\r\n")
|
|
|
|
}
|
|
|
|
if len(line) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
splits := strings.SplitN(string(line), ":", 2)
|
|
|
|
if len(splits) == 2 {
|
|
|
|
if splits[0] == "Content-length" {
|
|
|
|
splits[0] = "Content-Length"
|
|
|
|
}
|
|
|
|
res[splits[0]] = splits[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if val, ok := res["WWW-Authenticate"]; ok {
|
|
|
|
if strings.Contains(val, "Digest") {
|
|
|
|
client.realm = stringInBetween(val, "realm=\"", "\"")
|
|
|
|
client.nonce = stringInBetween(val, "nonce=\"", "\"")
|
|
|
|
client.clientDigest = true
|
|
|
|
} else if strings.Contains(val, "Basic") {
|
|
|
|
client.headers["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(client.username+":"+client.password))
|
|
|
|
client.clientBasic = true
|
|
|
|
}
|
|
|
|
if !one {
|
|
|
|
err = client.request(method, customHeaders, uri, true, false)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = errors.New("RTSP Client Unauthorized 401")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if val, ok := res["Session"]; ok {
|
|
|
|
splits2 := strings.Split(val, ";")
|
|
|
|
client.session = strings.TrimSpace(splits2[0])
|
|
|
|
client.headers["Session"] = strings.TrimSpace(splits2[0])
|
|
|
|
}
|
|
|
|
if val, ok := res["Content-Base"]; ok {
|
|
|
|
client.control = strings.TrimSpace(val)
|
|
|
|
}
|
|
|
|
if val, ok := res["RTP-Info"]; ok {
|
|
|
|
splits := strings.Split(val, ",")
|
|
|
|
for _, v := range splits {
|
|
|
|
splits2 := strings.Split(v, ";")
|
|
|
|
for _, vs := range splits2 {
|
|
|
|
if strings.Contains(vs, "rtptime") {
|
|
|
|
splits3 := strings.Split(vs, "=")
|
|
|
|
if len(splits3) == 2 {
|
|
|
|
if client.startVideoTS == 0 {
|
|
|
|
ts, _ := strconv.Atoi(strings.TrimSpace(splits3[1]))
|
|
|
|
client.startVideoTS = int64(ts)
|
|
|
|
} else {
|
|
|
|
ts, _ := strconv.Atoi(strings.TrimSpace(splits3[1]))
|
|
|
|
client.startAudioTS = int64(ts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if method == DESCRIBE {
|
|
|
|
if val, ok := res["Content-Length"]; ok {
|
|
|
|
contentLen, err = strconv.Atoi(strings.TrimSpace(val))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client.SDPRaw = make([]byte, contentLen)
|
|
|
|
_, err = io.ReadFull(client.connRW, client.SDPRaw)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
builder.Write(client.SDPRaw)
|
|
|
|
_, client.mediaSDP = sdp.Parse(string(client.SDPRaw))
|
|
|
|
}
|
|
|
|
}
|
2022-02-11 04:50:25 +08:00
|
|
|
if method == SETUP {
|
|
|
|
//deep := stringInBetween(builder.String(), "interleaved=", ";")
|
|
|
|
if val, ok := res["Transport"]; ok {
|
|
|
|
splits2 := strings.Split(val, ";")
|
|
|
|
for _, vs := range splits2 {
|
|
|
|
if strings.Contains(vs, "interleaved") {
|
|
|
|
splits3 := strings.Split(vs, "=")
|
|
|
|
if len(splits3) == 2 {
|
|
|
|
splits4 := strings.Split(splits3[1], "-")
|
|
|
|
if len(splits4) == 2 {
|
|
|
|
if val, err := strconv.Atoi(splits4[0]); err == nil {
|
|
|
|
client.chTMP = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
client.Println(builder.String())
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) Close() {
|
|
|
|
if client.conn != nil {
|
2021-02-18 18:56:44 +08:00
|
|
|
client.conn.SetDeadline(time.Now().Add(time.Second))
|
|
|
|
client.request(TEARDOWN, nil, client.control, false, true)
|
2020-08-08 23:14:32 +08:00
|
|
|
err := client.conn.Close()
|
|
|
|
client.Println("RTSP Client Close", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) parseURL(rawURL string) error {
|
|
|
|
l, err := url.Parse(rawURL)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
username := l.User.Username()
|
|
|
|
password, _ := l.User.Password()
|
|
|
|
l.User = nil
|
|
|
|
if l.Port() == "" {
|
|
|
|
l.Host = fmt.Sprintf("%s:%s", l.Host, "554")
|
|
|
|
}
|
2021-11-13 15:34:17 +08:00
|
|
|
if l.Scheme != "rtsp" && l.Scheme != "rtsps" {
|
2020-08-08 23:14:32 +08:00
|
|
|
l.Scheme = "rtsp"
|
|
|
|
}
|
|
|
|
client.pURL = l
|
|
|
|
client.username = username
|
|
|
|
client.password = password
|
|
|
|
client.control = l.String()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) createDigest(method string, uri string) string {
|
|
|
|
md5UserRealmPwd := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", client.username, client.realm, client.password))))
|
|
|
|
md5MethodURL := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s", method, uri))))
|
|
|
|
response := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", md5UserRealmPwd, client.nonce, md5MethodURL))))
|
|
|
|
Authorization := fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", client.username, client.realm, client.nonce, uri, response)
|
|
|
|
return Authorization
|
|
|
|
}
|
|
|
|
|
|
|
|
func stringInBetween(str string, start string, end string) (result string) {
|
|
|
|
s := strings.Index(str, start)
|
|
|
|
if s == -1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
str = str[s+len(start):]
|
|
|
|
e := strings.Index(str, end)
|
|
|
|
if e == -1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
str = str[:e]
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
|
2022-03-17 23:20:49 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
content := *payloadRAW
|
|
|
|
firstByte := content[4]
|
|
|
|
padding := (firstByte>>5)&1 == 1
|
|
|
|
extension := (firstByte>>4)&1 == 1
|
|
|
|
CSRCCnt := int(firstByte & 0x0f)
|
2021-02-03 09:07:15 +08:00
|
|
|
SequenceNumber := int(binary.BigEndian.Uint16(content[6:8]))
|
2022-03-17 23:20:49 +08:00
|
|
|
timestamp := int64(binary.BigEndian.Uint32(content[8:16]))
|
|
|
|
|
2022-08-05 01:32:31 +08:00
|
|
|
if isRTCPPacket(content) {
|
|
|
|
client.Println("skipping RTCP packet")
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
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
|
2024-01-08 22:35:54 +08:00
|
|
|
if len(content) < end {
|
|
|
|
return nil, false
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
switch int(content[1]) {
|
|
|
|
case client.videoID:
|
2021-01-09 22:24:48 +08:00
|
|
|
if client.PreVideoTS == 0 {
|
|
|
|
client.PreVideoTS = timestamp
|
|
|
|
}
|
2022-03-10 00:31:51 +08:00
|
|
|
if timestamp-client.PreVideoTS < 0 {
|
|
|
|
if math.MaxUint32-client.PreVideoTS < 90*100 { //100 ms
|
2022-03-10 00:34:30 +08:00
|
|
|
client.PreVideoTS = 0
|
|
|
|
client.PreVideoTS -= (math.MaxUint32 - client.PreVideoTS)
|
2022-03-10 00:31:51 +08:00
|
|
|
} else {
|
|
|
|
client.PreVideoTS = 0
|
|
|
|
}
|
|
|
|
}
|
2021-02-03 09:07:15 +08:00
|
|
|
if client.PreSequenceNumber != 0 && SequenceNumber-client.PreSequenceNumber != 1 {
|
|
|
|
client.Println("drop packet", SequenceNumber-1)
|
|
|
|
}
|
|
|
|
client.PreSequenceNumber = SequenceNumber
|
2020-08-08 23:14:32 +08:00
|
|
|
if client.BufferRtpPacket.Len() > 4048576 {
|
|
|
|
client.Println("Big Buffer Flush")
|
|
|
|
client.BufferRtpPacket.Truncate(0)
|
|
|
|
client.BufferRtpPacket.Reset()
|
|
|
|
}
|
|
|
|
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
|
2021-04-10 15:46:45 +08:00
|
|
|
if len(nalRaw) == 0 || len(nalRaw[0]) == 0 {
|
|
|
|
return nil, false
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
var retmap []*av.Packet
|
|
|
|
for _, nal := range nalRaw {
|
2021-02-03 11:10:03 +08:00
|
|
|
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,
|
2021-02-09 09:27:04 +08:00
|
|
|
IsKeyFrame: false,
|
2021-02-03 11:10:03 +08:00
|
|
|
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
|
2021-02-09 09:27:04 +08:00
|
|
|
naluType := nal[2] & 0x3f
|
2021-02-03 11:10:03 +08:00
|
|
|
if se == 2 {
|
|
|
|
client.BufferRtpPacket.Truncate(0)
|
|
|
|
client.BufferRtpPacket.Reset()
|
2021-02-09 09:27:04 +08:00
|
|
|
client.BufferRtpPacket.Write([]byte{(nal[0] & 0x81) | (naluType << 1), nal[1]})
|
2021-02-03 11:10:03 +08:00
|
|
|
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:])
|
2020-08-08 23:14:32 +08:00
|
|
|
retmap = append(retmap, &av.Packet{
|
2021-02-09 09:27:04 +08:00
|
|
|
Data: append(binSize(client.BufferRtpPacket.Len()), client.BufferRtpPacket.Bytes()...),
|
2020-08-08 23:14:32 +08:00
|
|
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
2021-01-08 12:23:06 +08:00
|
|
|
Idx: client.videoIDX,
|
2021-02-03 11:10:03 +08:00
|
|
|
IsKeyFrame: naluType == h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
|
|
|
|
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
|
2020-08-08 23:14:32 +08:00
|
|
|
Time: time.Duration(timestamp/90) * time.Millisecond,
|
|
|
|
})
|
2021-02-03 11:10:03 +08:00
|
|
|
} else {
|
|
|
|
client.BufferRtpPacket.Write(nal[3:])
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
default:
|
2021-09-30 07:10:08 +08:00
|
|
|
//client.Println("Unsupported Nal", naluType)
|
2021-02-03 11:10:03 +08:00
|
|
|
}
|
2021-02-09 09:27:04 +08:00
|
|
|
|
2021-02-03 11:10:03 +08:00
|
|
|
} 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:
|
2022-03-14 04:51:51 +08:00
|
|
|
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:]
|
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
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
|
2021-04-10 15:46:45 +08:00
|
|
|
if naluTypef == 7 || naluTypef == 9 {
|
2021-02-03 11:10:03 +08:00
|
|
|
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:
|
2024-01-08 22:35:54 +08:00
|
|
|
//client.Println("Unsupported NAL Type", naluType)
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(retmap) > 0 {
|
2021-01-09 22:24:48 +08:00
|
|
|
client.PreVideoTS = timestamp
|
2020-08-08 23:14:32 +08:00
|
|
|
return retmap, true
|
|
|
|
}
|
|
|
|
case client.audioID:
|
2021-01-09 22:24:48 +08:00
|
|
|
if client.PreAudioTS == 0 {
|
|
|
|
client.PreAudioTS = timestamp
|
|
|
|
}
|
2021-01-08 12:23:06 +08:00
|
|
|
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
|
|
|
|
var retmap []*av.Packet
|
|
|
|
for _, nal := range nalRaw {
|
2021-01-09 22:24:48 +08:00
|
|
|
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
|
2021-01-12 08:58:07 +08:00
|
|
|
retmap = append(retmap, &av.Packet{
|
2021-01-15 23:26:23 +08:00
|
|
|
Data: nal,
|
2021-01-12 08:58:07 +08:00
|
|
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
|
|
|
Duration: duration,
|
|
|
|
Idx: client.audioIDX,
|
|
|
|
IsKeyFrame: false,
|
|
|
|
Time: client.AudioTimeLine,
|
|
|
|
})
|
2021-01-09 22:24:48 +08:00
|
|
|
case av.PCM_ALAW:
|
|
|
|
duration = time.Duration(len(nal)) * time.Second / time.Duration(client.AudioTimeScale)
|
|
|
|
client.AudioTimeLine += duration
|
2021-01-12 08:58:07 +08:00
|
|
|
retmap = append(retmap, &av.Packet{
|
2021-01-15 23:26:23 +08:00
|
|
|
Data: nal,
|
2021-01-12 08:58:07 +08:00
|
|
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
|
|
|
Duration: duration,
|
|
|
|
Idx: client.audioIDX,
|
|
|
|
IsKeyFrame: false,
|
|
|
|
Time: client.AudioTimeLine,
|
|
|
|
})
|
2021-01-09 22:24:48 +08:00
|
|
|
case av.OPUS:
|
|
|
|
duration = time.Duration(20) * time.Millisecond
|
|
|
|
client.AudioTimeLine += duration
|
2021-01-12 08:58:07 +08:00
|
|
|
retmap = append(retmap, &av.Packet{
|
2021-01-15 23:26:23 +08:00
|
|
|
Data: nal,
|
2021-01-12 08:58:07 +08:00
|
|
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
|
|
|
Duration: duration,
|
|
|
|
Idx: client.audioIDX,
|
|
|
|
IsKeyFrame: false,
|
|
|
|
Time: client.AudioTimeLine,
|
|
|
|
})
|
2021-01-09 22:24:48 +08:00
|
|
|
case av.AAC:
|
2021-01-12 08:58:07 +08:00
|
|
|
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:]
|
|
|
|
}
|
2021-09-15 16:01:00 +08:00
|
|
|
duration = time.Duration((float32(1024)/float32(client.AudioTimeScale))*1000*1000*1000) * time.Nanosecond
|
2021-01-12 08:58:07 +08:00
|
|
|
client.AudioTimeLine += duration
|
|
|
|
retmap = append(retmap, &av.Packet{
|
2021-01-15 23:26:23 +08:00
|
|
|
Data: frame,
|
2021-01-12 08:58:07 +08:00
|
|
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
|
|
|
Duration: duration,
|
|
|
|
Idx: client.audioIDX,
|
|
|
|
IsKeyFrame: false,
|
|
|
|
Time: client.AudioTimeLine,
|
|
|
|
})
|
2021-01-09 22:24:48 +08:00
|
|
|
}
|
2021-01-09 22:24:48 +08:00
|
|
|
}
|
2021-01-08 12:23:06 +08:00
|
|
|
}
|
|
|
|
if len(retmap) > 0 {
|
2021-01-09 22:24:48 +08:00
|
|
|
client.PreAudioTS = timestamp
|
2021-01-08 12:23:06 +08:00
|
|
|
return retmap, true
|
|
|
|
}
|
2020-08-08 23:14:32 +08:00
|
|
|
default:
|
2021-02-18 18:56:44 +08:00
|
|
|
//client.Println("Unsuported Intervaled data packet", int(content[1]), content[offset:end])
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) CodecUpdateSPS(val []byte) {
|
2021-02-03 11:10:03 +08:00
|
|
|
if client.videoCodec != av.H264 && client.videoCodec != av.H265 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if bytes.Compare(val, client.sps) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client.sps = val
|
|
|
|
if (client.videoCodec == av.H264 && len(client.pps) == 0) || (client.videoCodec == av.H265 && (len(client.vps) == 0 || len(client.pps) == 0)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var codecData av.VideoCodecData
|
|
|
|
var err error
|
|
|
|
switch client.videoCodec {
|
|
|
|
case av.H264:
|
2021-02-18 18:56:44 +08:00
|
|
|
client.Println("Codec Update SPS", val)
|
2021-02-03 11:10:03 +08:00
|
|
|
codecData, err = h264parser.NewCodecDataFromSPSAndPPS(val, client.pps)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("Parse Codec Data Error", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case av.H265:
|
|
|
|
codecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(client.vps, val, client.pps)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("Parse Codec Data Error", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(client.CodecData) > 0 {
|
|
|
|
for i, i2 := range client.CodecData {
|
|
|
|
if i2.Type().IsVideo() {
|
|
|
|
client.CodecData[i] = codecData
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
} else {
|
|
|
|
client.CodecData = append(client.CodecData, codecData)
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
client.Signals <- SignalCodecUpdate
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) CodecUpdatePPS(val []byte) {
|
2021-02-03 11:10:03 +08:00
|
|
|
if client.videoCodec != av.H264 && client.videoCodec != av.H265 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if bytes.Compare(val, client.pps) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client.pps = val
|
|
|
|
if (client.videoCodec == av.H264 && len(client.sps) == 0) || (client.videoCodec == av.H265 && (len(client.vps) == 0 || len(client.sps) == 0)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var codecData av.VideoCodecData
|
|
|
|
var err error
|
|
|
|
switch client.videoCodec {
|
|
|
|
case av.H264:
|
2021-02-18 18:56:44 +08:00
|
|
|
client.Println("Codec Update PPS", val)
|
2021-02-03 11:10:03 +08:00
|
|
|
codecData, err = h264parser.NewCodecDataFromSPSAndPPS(client.sps, val)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("Parse Codec Data Error", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case av.H265:
|
|
|
|
codecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(client.vps, client.sps, val)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("Parse Codec Data Error", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(client.CodecData) > 0 {
|
|
|
|
for i, i2 := range client.CodecData {
|
|
|
|
if i2.Type().IsVideo() {
|
|
|
|
client.CodecData[i] = codecData
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
client.CodecData = append(client.CodecData, codecData)
|
|
|
|
}
|
|
|
|
client.Signals <- SignalCodecUpdate
|
|
|
|
}
|
|
|
|
|
|
|
|
func (client *RTSPClient) CodecUpdateVPS(val []byte) {
|
|
|
|
if client.videoCodec != av.H265 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if bytes.Compare(val, client.vps) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client.vps = val
|
|
|
|
if len(client.sps) == 0 || len(client.pps) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
codecData, err := h265parser.NewCodecDataFromVPSAndSPSAndPPS(val, client.sps, client.pps)
|
|
|
|
if err != nil {
|
|
|
|
client.Println("Parse Codec Data Error", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(client.CodecData) > 0 {
|
|
|
|
for i, i2 := range client.CodecData {
|
|
|
|
if i2.Type().IsVideo() {
|
|
|
|
client.CodecData[i] = codecData
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-03 11:10:03 +08:00
|
|
|
} else {
|
|
|
|
client.CodecData = append(client.CodecData, codecData)
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
2022-03-17 23:21:39 +08:00
|
|
|
|
2021-02-03 11:10:03 +08:00
|
|
|
client.Signals <- SignalCodecUpdate
|
2022-03-17 23:21:39 +08:00
|
|
|
|
2020-08-08 23:14:32 +08:00
|
|
|
}
|
|
|
|
|
2023-08-25 19:35:34 +08:00
|
|
|
// Println mini logging functions
|
2020-08-08 23:14:32 +08:00
|
|
|
func (client *RTSPClient) Println(v ...interface{}) {
|
|
|
|
if client.options.Debug {
|
|
|
|
log.Println(v)
|
|
|
|
}
|
|
|
|
}
|
2021-05-23 18:37:05 +08:00
|
|
|
|
2023-08-25 19:35:34 +08:00
|
|
|
// binSize
|
2020-08-08 23:14:32 +08:00
|
|
|
func binSize(val int) []byte {
|
|
|
|
buf := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(buf, uint32(val))
|
|
|
|
return buf
|
|
|
|
}
|
2022-08-05 01:32:31 +08:00
|
|
|
|
|
|
|
func isRTCPPacket(content []byte) bool {
|
|
|
|
rtcpPacketType := content[5]
|
|
|
|
return rtcpPacketType == RTCPSenderReport || rtcpPacketType == RTCPReceiverReport
|
|
|
|
}
|