Simple IP Camera

This article demonstrates how to create a simple Windows Froms application in C#, which is able to reach password protected IP Cameras or RTSP streams. The application is also able to show status messages about camera states and error messages.

To use this example, you need to have Ozeki VoIP SIP SDK installed, and a reference to ozeki VOIPSDK.dll should be added to your visual studio project. It is also recommended to read about mediahandlers before you begin to study this article.

What is an IP Camera? Where is it needed?

An Internet protocol camera, or IP camera, is a type of digital video camera commonly employed for surveillance, and which can send and receive data via a computer network and the Internet.

IP Cameras and CCTV cameras can be used for many different purposes, for example; Crime prevention and security purposes, industrial processes, traffic monitoring, transport safety, control of retail, criminal use and much more.

How to use IP Cameras in C# using Ozeki VoIP SIP SDK?

With Ozeki VoIP SIP SDK, IP Cameras can be accessed easily by the following ways:

  • IP address or HTTP URI: by providing the camera's IP address, you can reach the camera with all of its available streams, and the extension will provide you the availability to use PTZ commands (if your camera supports it). You might also need to provide the port number.
    For example: 192.168.115.171:8080 or http://192.168.115.171:8080
  • RTSP URI: an RTSP uri represents a single camera stream.
    For example: rtsp://192.168.115.171/12

This means, you have 3 ways to provide the adress, when using the IPCamera class's GetCamera() method:

		IPCamera myIPCamera = IPCamera.GetCamera("address", "username", "password");

How to get notified about the camera's state?

While the IPCamera instance tries to gain acces to the IP Camera device and tries to start streaming, it enters into different states. To get notified about these, you have to be subscribed for the instance's CameraStateChanged event.
The camera's states can be the followings:

  • Disconnected: the instance is not connected to the device.
  • Connecting: the instance is trying to connect to the device.
  • Connected: the instance has successfully reached the camera. In this state you have access to the camera streams.
  • StreamConnecting: the instance is trying to connect to one of the camera's streams, using RTSP packets. Please note that, these streams can be password protected, so you might need to provide the correct username and password at the IPCamera instance's initialization, in order to successfully authenticate.
  • Streaming: the instance is receiving the media data from the camera device within RTP packets.

How to get notified when an error occurrs?

The IPCamera instance provides an event, called CameraErrorOccured for the purpose to notify the subscribers when an error occurrs.
The camera can notify the subscribes about the following erros:

  • NoEndPoint: there is no IP camera listening on the give address.
  • ConnectionFailure: occurrs, when the connection could not been established.
  • AuthenticationFailure: occurrs, when the endpoint has been reached, but the username and/or password was incorrect.
  • PTZError: occurrs, when there is error while camera movement. This type of error is using the "Details" property of the error object to clarify the reason of the error.

How to work with the camera streams?

To connect to a camera, the IPCamera class provides two methods:

  • Connect(): tries to connect to the camera device. If succeeds, the camera instance enters to "Connected" state, and the camera device's public streams become available to be listened.
  • Start(): if the camera instance is not connected, than calls the Connect() method first. After successfully connected, automatically selects a stream to receive the data from; by default, it tries to select the SubStream, which is dedicated for media data translation (which usually means lower resolution, lower framerate etc.).
    This method can be also called while the camera is in "Streaming" state, with the optional parameters:
    • IPCameraStream: if this parameter is not "null", the camera tries to switch to the selected stream.
    • ListenedMedia: if this parameter is not "null", the camera tries to start to receive the selected media(s).
    Please note that, both of the parameters can be set with the same method call.
    If the method is being called with no parameters, by default it will connect to the SubStream (see above), and starts to listen both of the media channels (audio and video).

To finish the receiving, there are two methods, provided by the IPCamera class:

  • StopListening(): stops receiving the media data, the camera instance steps to "Connected" state. At this point the received camera informations are still available to be used.
  • Disconnect(): stops receiving the media data, the camera instance steps to "Disconnected" state.

How to implement simple IP Camera Application using Ozeki VoIP SIP SDK

The following steps will guide you through the creation of a very simple IP Camera Application;

example graphical user interface for ip application
Figure 1 - Example Graphical User Interface for the simple IPCamera application

  1. First of all, you need to create a Windows Forms Application, which is using .NET Framework 4.0, and you have to add reference to the VOIPSDK.dll file. You can find a detailed guide about these first steps of the project's creation.
  2. Create a simple GUI with three TextBoxes, two Buttons, and two other objects which can be used to display the status and error messages (for example another two TextBoxes or two Labels).
    For example, as Figure 1 shows above.
  3. You have to declare some global objects:
    • IPCamera _ipCamera: represents the IPCamera device.
    • VideoViewerWF _videoViewer: a video player, which will be added to the Form's controls.
    • DrawingImageProvider _imageProvider: provides playable data for the VideoViewerWF object.
    • Speaker _speaker: represents a speaker device, which will play the received audio data.
    • MediaConnector _connector: will connect the previously mentioned other mediahandlers.
    After their creation, you have to define and initilize them when the form loads:
    		    _imageProvider = new DrawingImageProvider();
                _videoViewer = new VideoViewerWF();
                _speaker = Speaker.GetDefaultDevice();
    
                _connector = new MediaConnector();
    
    			// Since the VideoViewerWF does not appear within the designer view, it
    			// is being set up programmatically
                _videoViewer.Location = new Point(10, 10);
                _videoViewer.Size = new Size(500, 400);
                _videoViewer.BackColor = Color.Black;
                _videoViewer.TabStop = false;
    
                Controls.Add(_videoViewer);
    		
  4. Create a method, which initializes the camera instance, and connects the mediahandlers:
    			void InitializeCamera(string address, string userName, string password)
    	        {
    	            // Gets the camera, which can be reached by the address,
    	            // and requires authentication.
    	            _ipCamera = IPCamera.GetCamera(address, userName, password);
    	            _ipCamera.CameraStateChanged += _ipCamera_CameraStateChanged;
    	            _ipCamera.CameraErrorOccured += _ipCamera_CameraErrorOccured;
    	            _speaker.Start();
    	
    	            _connector.Connect(_ipCamera.AudioChannel, _speaker);
    	            _connector.Connect(_ipCamera.VideoChannel, _imageProvider);
    	
    	            _videoViewer.SetImageProvider(_imageProvider);
    	        }
    		
    As you can see, the camera's AudioChannel is being connected to the speaker, and the VideoChannel is being prepared by the ImageProvider instancefor the VideoViewerWF object.
    As you can see, the example also subscribes to the two previously mentioned events, which will inform it about state changes and error occurrence.
  5. Implement the buttons' Click event:
    • Start button: if the address of the camera is not null, it calls the InitializeCamera() and the Start() methods of the VideoViewerWF and IPCamera objects:
      					if (!String.IsNullOrEmpty(tb_CameraAddress.Text))
      		            {
      		                InitializeCamera(tb_CameraAddress.Text, tb_UserName.Text, tb_Password.Text);
      		
      		                _videoViewer.Start();
      		                _ipCamera.Start();
      		            }
      				
    • Disconnect button: if there is an IPCamera instance, it unsubscribes from the events, disconnects the IPCamera, and stops the VideoViewerWF object.
      if (_ipCamera == null)
      return;
      
      WireDownEvents();
      
      _ipCamera.Disconnect();
      _videoViewer.Stop();
      
      The WireDownEvents() method is used for the purpose to unsubscribe from the events, since the application will no longer need them:
      void WireDownEvents()
      {
          if (_ipCamera == null)
              return;
      
          _ipCamera.CameraStateChanged -= _ipCamera_CameraStateChanged;
          _ipCamera.CameraErrorOccured -= _ipCamera_CameraErrorOccured;
      }
      
  6. You have to implement the methods, called by the events you are subscribed to. To safely modify the GUI elements, you can write a helper method, which solves thread blocking:
            void InvokeGuiThread(Action action)
            	{
    	            Invoke(action);
            	}
    		
    You have to write two methods, since the application is subscribed to two events:
    • CameraStateChanged: when the camera's state changes, you can display the actual state as one of the TextBoxes' Text field (for example):
      				void _ipCamera_CameraStateChanged(object sender, CameraState e)
              		{
      			        void _ipCamera_CameraStateChanged(object sender, CameraState e)
      			        {
      			            InvokeGuiThread(() => { tb_Status.Text = e.Item.ToString(); });
      			        }
      		        }
      				
    • CameraErrorOccured: when error occurrs, the error can be displayed as one of the TextBoxes' Text field (for example):
      		        void _ipCamera_CameraErrorOccured(object sender, CameraError e)
              		{
      		            InvokeGuiThread(() =>
      	            	{
      		                if (e.Details == null)
      	                	{
      		                    tb_Error.Text = e.Error.ToString();
      	                	}
      	                	else
      	                	{
      		                    tb_Error.Text = e.Details;
      	                	}
      	            	});
                  	}
      				
      As you can see, this method checks if there is "Details" information about the error, and displays that.
      For example: the "PTZError" could mean that there was an error while trying to move, zoom, setting a preset, moving to preset etc. By checking the "Details" property, you can get more information about the error.

Simple IP Camera application in C#

Please note that, in order to work with the following code, you have to:

  • add reference to VOIPSDK.dll
  • create a Windows Forms application with a GUI and rename the objects as they are used in the source code
using System;
using System.Drawing;
using System.Windows.Forms;
using Ozeki.Media.MediaHandlers;
using Ozeki.Media.MediaHandlers.Video;
using Ozeki.Media.Video.Controls;
using Ozeki.VoIP.Media.MediaHandlers.IPCamera;

namespace IPCamera_Simple
{
    public partial class IPCameraForm : Form
    {
        // IPCamera declaration.
        IPCamera _ipCamera;

        // Required for image displaying.
        DrawingImageProvider _imageProvider;
        VideoViewerWF _videoViewer;

        // Required for playing the audio.
        Speaker _speaker;

        // Required for connecting the mediahandlers.
        MediaConnector _connector;

        public IPCameraForm()
        {
            InitializeComponent();
        }

        void IPCameraForm_Load(object sender, EventArgs e)
        {
            _imageProvider = new DrawingImageProvider();
            _videoViewer = new VideoViewerWF();
            _speaker = Speaker.GetDefaultDevice();

            _connector = new MediaConnector();

            SetVideoViewer();
        }

        // Sets the videoviewer's params.
        void SetVideoViewer()
        {
            // Since the VideoViewerWF does not appear within the designer view, it
            // is being set up programmatically
            _videoViewer.Location = new Point(10, 10);
            _videoViewer.Size = new Size(500, 400);
            _videoViewer.BackColor = Color.Black;
            _videoViewer.TabStop = false;

            Controls.Add(_videoViewer);
        }

        // Initializes the camera, connects the mediahandlers.
        void InitializeCamera(string address, string userName, string password)
        {
            if (_ipCamera == null)
            {
                // Gets the camera, which can be reached by the address, and requires authentication.
                _ipCamera = IPCamera.GetCamera(address, userName, password);
                _ipCamera.CameraStateChanged += _ipCamera_CameraStateChanged;
                _ipCamera.CameraErrorOccured += _ipCamera_CameraErrorOccured;
                _speaker.Start();

                // Connects the channels to the correct receivers.
                _connector.Connect(_ipCamera.AudioChannel, _speaker);
                _connector.Connect(_ipCamera.VideoChannel, _imageProvider);

                _videoViewer.SetImageProvider(_imageProvider);
            }
        }

        // Unsubscribes from the events.
        void WireDownEvents()
        {
            if (_ipCamera == null)
                return;

            _ipCamera.CameraStateChanged -= _ipCamera_CameraStateChanged;
            _ipCamera.CameraErrorOccured -= _ipCamera_CameraErrorOccured;
        }

        // This method is being called, when the camera's state has been changed.
        void _ipCamera_CameraStateChanged(object sender, CameraState e)
        {
            InvokeGuiThread(() => { tb_Status.Text = e.Item.ToString(); });
        }

        // This method is being called, when an error occurrs.
        void _ipCamera_CameraErrorOccured(object sender, CameraError e)
        {
            InvokeGuiThread(() =>
            {
                if (e.Details == null)
                {
                    tb_Error.Text = e.Error.ToString();
                }
                else
                {
                    tb_Error.Text = e.Details;
                }
            });
        }

        // Tries to connect to the device and starts to receive data from the stream.
        void btn_Start_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(tb_CameraAddress.Text))
            {
                InitializeCamera(tb_CameraAddress.Text, tb_UserName.Text, tb_Password.Text);

                // Starts the VideoViewerWF and the IPCamera object
                _videoViewer.Start();
                _ipCamera.Start();
            }
        }

        // Disconnects from the IPCamera
        void btn_Disconnect_Click(object sender, EventArgs e)
        {
            if (_ipCamera == null)
                return;

            WireDownEvents();

            // The camera disconnects and the videoviewer stops.
            _ipCamera.Disconnect();
            _videoViewer.Stop();
        }

        // This helper method solves thread blockings.
        void InvokeGuiThread(Action action)
        {
            Invoke(action);
        }

    }
}

You can find more functions and opportunities provided by Ozeki VoIP SIP SDK at the article, which explains how to continue to develop this application to a more advanced one.


Related Pages