Writing RTSP Client to view live video

Topics: Question
Dec 31, 2014 at 2:56 AM
I'm writing an application to view a live video feed from my security camera system. However my code get an exception error "Cannot Start Playing, No Tracks Setup" when it gets to the StartPlaying method. Any help would be much appreciated. Here is my code:

RtspClient oRTSPClient = null;
    Thread oThread = null;

    public frmMain()
    {
        InitializeComponent();

        string sURL = "rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0";
        InitializeVideoFeed(sURL);
    }



    void InitializeVideoFeed(string sURL)
    {
        oRTSPClient = new RtspClient(sURL, RtspClient.ClientProtocolType.Tcp, Media.Rtsp.RtspMessage.MaximumLength *4);
        oRTSPClient.Credential = new System.Net.NetworkCredential("admin", "Notmypassword");
        oRTSPClient.AuthenticationScheme = System.Net.AuthenticationSchemes.Basic;
        oRTSPClient.OnConnect += client_OnConnect;

        oThread = new Thread(oRTSPClient.Connect);
        oRTSPClient.OnDisconnect += client_OnDisconnect;
        oRTSPClient.OnStop += client_OnStop;
        oRTSPClient.OnResponse += client_OnResponse;

        oThread.Start();
    }

    private void client_OnResponse(RtspClient sender, RtspMessage request, RtspMessage response)
    {
        //throw new NotImplementedException();
    }

    private void client_OnStop(RtspClient sender, object args)
    {
        //throw new NotImplementedException();
    }

    private void client_OnDisconnect(RtspClient sender, object args)
    {
        //throw new NotImplementedException();
    }

    void client_OnConnect(RtspClient sender, object args)
    {
        if (oRTSPClient.Connected)
        {
            try
            {
                if (!sender.Playing)
                {
                    sender.SocketReadTimeout = sender.SocketWriteTimeout = 30000;
                    sender.OnPlay += sender_OnPlay;
                    //sender.StartPlaying(null, Media.Sdp.MediaType.video);
                    sender.StartPlaying(null,null, Media.Sdp.MediaType.video); //GET ERROR HERE                        
                }
            }
            catch (Exception ex)
            {
                return;
            }
        }
    }



    void sender_OnPlay(RtspClient sender, object args)
    {
        try
        {
            if (sender.Playing)
            {
                sender.Client.FrameChangedEventsEnabled = true;
                sender.Client.RtpFrameChanged += Client_RtpFrameChanged;
            }
        }
        catch (Exception ex)
        { }

    }

    private void Client_RtpFrameChanged(object sender, Media.Rtp.RtpFrame frame)
    {
        //var ff = (new RFC2435Stream.RFC2435Frame(frame));
        //var bitmap = (Bitmap)ff.ToImage();

        var ff = (new Media.Rtsp.Server.MediaTypes.RFC2435Media.RFC2435Frame (frame));
        var bitmap = (Bitmap)ff.ToImage();
        picSecurityCamera.Image = bitmap;
        picSecurityCamera.Refresh();

    }
}
Coordinator
Dec 31, 2014 at 4:53 AM
Edited Dec 31, 2014 at 6:05 AM
Hello and thanks for trying out the library!

1) Good example code, especially observing the state before taking action!

The only thing I would ask is why the buffer size needs to be so large, 16384 bytes will have no benefit unless the packets are guaranteed larger than 1500. This which would require your MTU/MSS to be configured which they should not be unless specifically decided to be so.

The Socket is configured with a very large receive buffer anyway which is more important unless your are using blocking sockets.

2) When the frame is changed you will want to check if it is complete, and you can simply use the new frame like so:
if(frame.IsComplete) {
    using(var ff = (new Media.Rtsp.Server.MediaTypes.RFC2435Media.RFC2435Frame (frame))) { 
         picSecurityCamera.Image = ff.ToImage(); 
         picSecurityCamera.Invalidate();
    }
}
Now, on to why the example doesn't work, this is a little bit harder to diagnose.

3) If the tracks are not being setup then its possibly something with the media description, can you use Wireshark to capture the first few seconds of the conversation between a player such as MPlayer or VLC and the camera?

If not can you capture the result of the 'OnResponse' event when the 'DESCRIBE' occurs and post that?

From those things I will be able to tell if there is something wrong with the logic in the case of this camera, it will ensure that the camera is working correctly and narrow down if communication in total or just the SETUP is going wrong.

From there I should be able to give a more intelligent response which outlines what needs to be done by you or me to fix the problem, if there is something incompatible with the library for whatever reason it surely will be fixed so long as it standards complaint.

Just out of curiosity, what kind of camera is it?

Take care and have a happy New Year!
Dec 31, 2014 at 5:01 AM
Here is the network capture from VLC to the DVR:

OPTIONS rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)

RTSP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="device"
Server: Dahua Rtsp Server
Content-Length: 0
CSeq: 2

DESCRIBE rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Accept: application/sdp

RTSP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="device"
Server: Dahua Rtsp Server
Content-Length: 0
CSeq: 3

OPTIONS rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 4
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)

RTSP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="device"
Server: Dahua Rtsp Server
Content-Length: 0
CSeq: 4

OPTIONS rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 5
Authorization: Basic YWRtaW46QzByNWEx
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)

RTSP/1.0 200 OK
Content-Length: 0
CSeq: 5

DESCRIBE rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 6
Authorization: Basic YWRtaW46QzByNWEx
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Accept: application/sdp

RTSP/1.0 200 OK
Content-Length: 327
Content-Type: application/sdp
CSeq: 6
User-Agent: DH RTSP Server
Content-Base: rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0/
Cache-Control: must-revalidate
x-Accept-Retransmit: our-retransmit
x-Accept-Dynamic-Rate: 1

v=0
o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.0.20
s=RTSP Session of ZheJiang Dahua Technology CO.,LTD.
c=IN IP4 0.0.0.0
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=control:trackID=0
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=6742e0;sprop-parameter-sets=Z0IAH5WoPA9k,aM48gA==;packetization-mode=1
SETUP rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0/trackID=0 RTSP/1.0
CSeq: 7
Authorization: Basic YWRtaW46QzByNWEx
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Transport: RTP/AVP;unicast;client_port=60994-60995

RTSP/1.0 200 OK
Content-Length: 0
CSeq: 7
Server: DH Server/1.1
User-Agent: DH RTSP Server
Session: 1866596842
Transport: RTP/AVP;unicast;client_port=60994-60995;server_port=20016;ssrc=6F4201EA

PLAY rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0/ RTSP/1.0
CSeq: 8
Authorization: Basic YWRtaW46QzByNWEx
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Session: 1866596842
Range: npt=0.000-

RTSP/1.0 200 OK
Content-Length: 0
CSeq: 8
Range: npt=now-
Session: 1866596842

TEARDOWN rtsp://192.168.0.20:554/cam/realmonitor?channel=1&subtype=0/ RTSP/1.0
CSeq: 9
Authorization: Basic YWRtaW46QzByNWEx
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Session: 1866596842

END OF CAPTURE



And here is the capture from my code to the DVR:

OPTIONS rtsp://192.168.0.20/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
Content-Encoding: utf-8
User-Agent: ASTI RTP Client
Authorization: Basic YWRtaW46QzByNWEx
Blocksize: 16384
CSeq: 1

RTSP/1.0 200 OK
Content-Length: 0
CSeq: 1

DESCRIBE rtsp://192.168.0.20/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
Content-Encoding: utf-8
Accept: application/sdp
User-Agent: ASTI RTP Client
Authorization: Basic YWRtaW46QzByNWEx
Blocksize: 16384
CSeq: 2

RTSP/1.0 200 OK
Content-Length: 327
Content-Type: application/sdp
CSeq: 2
User-Agent: DH RTSP Server
Content-Base: rtsp://192.168.0.20/cam/realmonitor?channel=1&subtype=0/
Cache-Control: must-revalidate
x-Accept-Retransmit: our-retransmit
x-Accept-Dynamic-Rate: 1

v=0
o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.0.20
s=RTSP Session of ZheJiang Dahua Technology CO.,LTD.
c=IN IP4 0.0.0.0
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=control:trackID=0
a=rtpmap:96 H264/90000

a=fmtp:96 profile-level-id=6742e0;sprop-parameter-sets=Z0IAH5WoPA9k,aM48gA==;packetization-mode=1



The Security DVR system is a Q-See QC588.
Coordinator
Dec 31, 2014 at 6:17 AM
Edited Dec 31, 2014 at 6:50 AM
Okay well first off I would like to let you know that even when you get the DVR to connect and begin streaming you will probably need something else to get the bitmaps to view because RFC2435 is for JPEG and that SDP describes H.264.

You can use any decoder you like, the only one I know of in purely managed code is csCodec at the current time.

You can find a thread in which others have used it here: https://net7mma.codeplex.com/discussions/569279

This is going to take a bit of work on your part if you would like to continue debugging, I would appreciate it though as it may fix something in the library.

Can you step thogh StartPlaying and specifically set a break-point on foreach (Sdp.MediaDescription md in SessionDescription.MediaDescriptions).

When it gets there can you take a look and tell me what happens next?

if (mediaType.HasValue && md.MediaType != mediaType) continue; should be skipped because nothing is passed.

if (Client != null && Client.GetContextForMediaDescription(md) != null && !hasContext) should then branch and cause hasContext to equal true.

Since there is only a single media then next place you should go into is RtspMessage setup = SendSetup(md)

Then off to if (hasContext) using (RtspMessage play = SendPlay(Location, start ?? StartTime, end ?? EndTime)) which should start the events for RtpPackets from occurring shortly thereafter.

I like to think I have kept enough comments and used common sense naming in enough places that you should be able to tell what is going wrong very easily from there, if not I can try to access the camera and give it a shot.

The fact you get the exception means that it's running to that point but not obtaining any context which is weird but it should be easy to find out whats wrong from there.

Let me know what you find!
Dec 31, 2014 at 5:05 PM
It Exceptions (InvalidOperationException) at using (RtspMessage setup = SendSetup(md)) with the message "Server does not support SETUP".
Coordinator
Dec 31, 2014 at 5:20 PM
Edited Dec 31, 2014 at 5:22 PM
Yes but do you see why?

The server doesn't indicate which methods it supports in the options response.

There is a force option though which allows you to bypass these checks.

The only way to really proceed is to probe methods to ensure they get responses and then if you do add the method to m_Supported.

I suppose that I could add logic for this into the client but its not standard so I will have to see what ramifications it has on the overall design.

Thanks for bringing this up.

As a work around you can make it work by calling Play manually with the MediaDescription passing force when the Connect event is raised.

I may just add logic that probes at least once before throwing the exception.

This is achieved by looking at m_RecievedBytes and m_SentBytes on the rtsp client and if 0 assuming force is true because nothing has been sent or received.

Let me know if you need anything else.
Marked as answer by juliusfriedman on 12/31/2014 at 9:20 AM
Dec 31, 2014 at 5:45 PM
Where do I set “force” in the MediaDescription?

Sent from Windows Mail

Coordinator
Dec 31, 2014 at 5:58 PM
This is not in the media description, its in the API.

And actually looking force isn't there for PLAY because a server is required to support it.

I have just release 110702 which is just for you.

Let me know if you have any issues with it.

Thanks again for bringing this up!

Coordinator
Dec 31, 2014 at 5:59 PM
This is not in the media description, its in the API.

And actually looking at the souce force isn't there for PLAY because a server is required to support it.

I have just release 110702 which is just for you.

Let me know if you have any issues with it.

Thanks again for bringing this up and please keep me updated if you notice any other issues!
Marked as answer by juliusfriedman on 12/31/2014 at 9:59 AM