vdk/format/rtspv2/client.go

828 lines
23 KiB
Go
Raw Permalink Normal View History

2020-08-08 23:14:32 +08:00
package rtspv2
import (
"bufio"
"bytes"
"crypto/md5"
"crypto/tls"
2020-08-08 23:14:32 +08:00
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"html"
"io"
"log"
"net"
"net/url"
"strconv"
"strings"
"time"
"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"
2020-08-08 23:14:32 +08:00
)
const (
SignalStreamRTPStop = iota
SignalCodecUpdate
)
const (
VIDEO = "video"
AUDIO = "audio"
)
const (
RTPHeaderSize = 12
RTCPSenderReport = 200
RTCPReceiverReport = 201
2020-08-08 23:14:32 +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
2024-02-11 00:20:37 +08:00
timestamp int64
sequenceNumber int
end int
offset 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
}
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()})
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
}
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
}
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")
}
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) 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
}
func isRTCPPacket(content []byte) bool {
rtcpPacketType := content[5]
return rtcpPacketType == RTCPSenderReport || rtcpPacketType == RTCPReceiverReport
}