@@ -247,6 +247,146 @@ func Dial(options RTSPClientOptions) (*RTSPClient, error) {
247247 return client , nil
248248}
249249
250+ func ReplayDial (options RTSPClientOptions , startTime string ) (* RTSPClient , error ) {
251+ client := & RTSPClient {
252+ headers : make (map [string ]string ),
253+ Signals : make (chan int , 100 ),
254+ OutgoingProxyQueue : make (chan * []byte , 3000 ),
255+ OutgoingPacketQueue : make (chan * av.Packet , 3000 ),
256+ BufferRtpPacket : bytes .NewBuffer ([]byte {}),
257+ videoID : - 1 ,
258+ audioID : - 2 ,
259+ videoIDX : - 1 ,
260+ audioIDX : - 2 ,
261+ options : options ,
262+ AudioTimeScale : 8000 ,
263+ }
264+ client .headers ["User-Agent" ] = "Lavf58.76.100"
265+ err := client .parseURL (html .UnescapeString (client .options .URL ))
266+ if err != nil {
267+ return nil , err
268+ }
269+ conn , err := net .DialTimeout ("tcp" , client .pURL .Host , client .options .DialTimeout )
270+ if err != nil {
271+ return nil , err
272+ }
273+ err = conn .SetDeadline (time .Now ().Add (client .options .ReadWriteTimeout ))
274+ if err != nil {
275+ return nil , err
276+ }
277+ if client .pURL .Scheme == "rtsps" {
278+ tlsConn := tls .Client (conn , & tls.Config {InsecureSkipVerify : options .InsecureSkipVerify , ServerName : client .pURL .Hostname ()})
279+ err = tlsConn .Handshake ()
280+ if err != nil {
281+ return nil , err
282+ }
283+ conn = tlsConn
284+ }
285+ client .conn = conn
286+ client .connRW = bufio .NewReadWriter (bufio .NewReader (conn ), bufio .NewWriter (conn ))
287+ err = client .request (OPTIONS , nil , client .pURL .String (), false , false )
288+ if err != nil {
289+ return nil , err
290+ }
291+ err = client .request (DESCRIBE , map [string ]string {"Accept" : "application/sdp" }, client .pURL .String (), false , false )
292+ if err != nil {
293+ return nil , err
294+ }
295+ for _ , i2 := range client .mediaSDP {
296+ if (i2 .AVType != VIDEO && i2 .AVType != AUDIO ) || (client .options .DisableAudio && i2 .AVType == AUDIO ) {
297+ //TODO check it
298+ if strings .Contains (string (client .SDPRaw ), "LaunchDigital" ) {
299+ client .chTMP += 2
300+ }
301+ continue
302+ }
303+ // err = client.request(SETUP, map[string]string{"Require": "onvif-replay", "Transport": "RTP/UDP"}, client.ControlTrack(i2.Control), false, false)
304+ 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 )
305+ if err != nil {
306+ return nil , err
307+ }
308+ if i2 .AVType == VIDEO {
309+ if i2 .Type == av .H264 {
310+ if len (i2 .SpropParameterSets ) > 1 {
311+ if codecData , err := h264parser .NewCodecDataFromSPSAndPPS (i2 .SpropParameterSets [0 ], i2 .SpropParameterSets [1 ]); err == nil {
312+ client .sps = i2 .SpropParameterSets [0 ]
313+ client .pps = i2 .SpropParameterSets [1 ]
314+ client .CodecData = append (client .CodecData , codecData )
315+ }
316+ } else {
317+ client .CodecData = append (client .CodecData , h264parser.CodecData {})
318+ client .WaitCodec = true
319+ }
320+ client .FPS = i2 .FPS
321+ client .videoCodec = av .H264
322+ } else if i2 .Type == av .H265 {
323+ if len (i2 .SpropVPS ) > 1 && len (i2 .SpropSPS ) > 1 && len (i2 .SpropPPS ) > 1 {
324+ if codecData , err := h265parser .NewCodecDataFromVPSAndSPSAndPPS (i2 .SpropVPS , i2 .SpropSPS , i2 .SpropPPS ); err == nil {
325+ client .vps = i2 .SpropVPS
326+ client .sps = i2 .SpropSPS
327+ client .pps = i2 .SpropPPS
328+ client .CodecData = append (client .CodecData , codecData )
329+ }
330+ } else {
331+ client .CodecData = append (client .CodecData , h265parser.CodecData {})
332+ }
333+ client .videoCodec = av .H265
334+
335+ } else {
336+ client .Println ("SDP Video Codec Type Not Supported" , i2 .Type )
337+ }
338+ client .videoIDX = int8 (len (client .CodecData ) - 1 )
339+ client .videoID = client .chTMP
340+ }
341+ if i2 .AVType == AUDIO {
342+ client .audioID = client .chTMP
343+ var CodecData av.AudioCodecData
344+ switch i2 .Type {
345+ case av .AAC :
346+ CodecData , err = aacparser .NewCodecDataFromMPEG4AudioConfigBytes (i2 .Config )
347+ if err == nil {
348+ client .Println ("Audio AAC bad config" )
349+ }
350+ case av .OPUS :
351+ var cl av.ChannelLayout
352+ switch i2 .ChannelCount {
353+ case 1 :
354+ cl = av .CH_MONO
355+ case 2 :
356+ cl = av .CH_STEREO
357+ default :
358+ cl = av .CH_MONO
359+ }
360+ CodecData = codec .NewOpusCodecData (i2 .TimeScale , cl )
361+ case av .PCM_MULAW :
362+ CodecData = codec .NewPCMMulawCodecData ()
363+ case av .PCM_ALAW :
364+ CodecData = codec .NewPCMAlawCodecData ()
365+ case av .PCM :
366+ CodecData = codec .NewPCMCodecData ()
367+ default :
368+ client .Println ("Audio Codec" , i2 .Type , "not supported" )
369+ }
370+ if CodecData != nil {
371+ client .CodecData = append (client .CodecData , CodecData )
372+ client .audioIDX = int8 (len (client .CodecData ) - 1 )
373+ client .audioCodec = CodecData .Type ()
374+ if i2 .TimeScale != 0 {
375+ client .AudioTimeScale = int64 (i2 .TimeScale )
376+ }
377+ }
378+ }
379+ client .chTMP += 2
380+ }
381+ test := map [string ]string {"Require" : "onvif-replay" , "Scale" : "1.000000" , "Speed" : "1.000000" , "Range" : "clock=" + startTime + "-" }
382+ err = client .request (PLAY , test , client .control , false , false )
383+ if err != nil {
384+ return nil , err
385+ }
386+ go client .startStream ()
387+ return client , nil
388+ }
389+
250390func (client * RTSPClient ) ControlTrack (track string ) string {
251391 if strings .Contains (track , "rtsp://" ) {
252392 return track
0 commit comments