diff --git a/format/rtspv2/client.go b/format/rtspv2/client.go index 397f1e7..d6d4816 100644 --- a/format/rtspv2/client.go +++ b/format/rtspv2/client.go @@ -247,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