Wiris Ethernet Stream SDK Programming Guide

Wiris Ethernet Stream SDK Programming Guide

This SDK is intended to stream video from WIRIS device and to control it over Ethernet connection. WIRIS camera runs TCP/IP server, which can be controlled using simple text commands. Please refer to the SDKs manual for description of communication protocol.

In this guide, we will create sample application that can communicate with camera using text commands. We will use Boost ASIO library (https://www.boost.org/) for that purpose, hovewer, you can use other library that supports network programming.

1. Create new C++ project in your IDE, link the boost ASIO library. In case you are using Visual studio, add boost include files to Additional Include Directories and binaries to Additional Dependencies in Project Properties.

2. Create new class Network Client, which will implement sending commands.

Figure 1 NetworkClient Class
Figure 1 NetworkClient Class

Copy code for the NetworkClient from pastebin: https://pastebin.com/fbrmu37b

3. Now we are ready to use NetworkClient to communicate with WIRIS device. In main source file, create function for sending commands trough NetworkClient, it will also print response.

Figure 2. Function to send messages using NetworkClient
Figure 2. Function to send messages using NetworkClient

4. Now we can test the communication with commands. Verify, that you have the license code used to activate Ethernet mode. You can activate the ethernet mode using native camera firmware, or using commands(Figure 2)

Figure 3 Sending commands to enable ethernet mode and obtain information about camera
Figure 3 Sending commands to enable ethernet mode and obtain information about camera
Figure 4. Sending commands for zoom control, setting palettes, thermal parameters and stream capture
Figure 4. Sending commands for zoom control, setting palettes, thermal parameters and stream capture
5.  After running this sample code, you can check all WIRIS responses and make sure everything works correctly by reading program standard output(the console output). For example in Ethernet Stream SDK GUI the application workflow is as following:

1. First step is to call connect method, with IP address of WIRIS as passed argument


bool ControllerCore::connect(const QString& ip)
{
    //network client class takes care of TCP layer of application
    //it takes wiris ip and communication port(2240) as constructor parameters
    //from now on it is fairly easy to call method send on network client instance with any command as parameter
    //network client should automatically disconnect from WIRIS in its destructor
    if(this->_networkClient == nullptr || !*this->_networkClient || !_connected)
        _networkClient = std::shared_ptr(new NetworkClient(ip.toStdString(), this->_endpointPort.toStdString()));

    if (*_networkClient)
    {
        std::istringstream iss;
        //Sending test message
        if(!send("HIWS\n", iss) || !isPositive(iss.str()))
            return false;

        //Setting delimiter to null character
        send("SDLM NULL\n");

        //checking camera type
        fetchBasicInfo();
        if(_cameraType != _vals.wirisProType && _cameraType != _vals.wirisSecurityType)
            return false;

        //we are now connected, so we can save passed IP as valid application state
        _endpointIp = ip;

        //we can even save it to local storage so app could remember last used ip on startup
        setLastIp(_endpointIp);
    }

    _connected = *_networkClient;
    emit connectedChanged(_connected, 10);

    return _connected;

}
2. activate method – if isActivated method returns false(SDK was never activated previously), it is required to activate it by calling activate method, with your activation code as passed argument

bool ControllerCore::activate(const QString & actStr)
{
    if (!isActivated())
    {
        std::istringstream iss;
        if(send("ACTV " + actStr, iss) && isPositive(iss.str()))
            return true;
    }
    else return true;

    return false;
}

3. At last, setup method needs to be called to enable ethernet mode, which causes WIRIS to start RTSP server and to stop transmitting to standard HDMI output (note that this is purely application constraint, commands would work properly even with ethernet mode off, but it is not recommended), it also then fetches current camera states to the application

bool ControllerCore::setup()
{
    //checking whether connect function was called
    if (*_networkClient) {
        if (!this->isActivated())
            return false;

        //Starting Ethernet Mode
        send("SETH TRUE");
        send("GETH");

4. After that application is ready to use any command…

bool ControllerCore::changeStorage(const QString & storage){
    if (storage == _currentStorage)
        return true;

    std::istringstream iss;
    std::string cache;

    if (storage == "SSD" || storage == "SD_CARD" || storage == "FLASH_DRIVE")
    {
        if (send("SILC " + storage, iss) && isPositive(iss.str()))
        {
            _currentStorage = storage;
            return true;
        }
    }

    return false;
}

Full project: https://github.com/SoftwareWorkswell/EthernetStreamSDKGUI When it comes to video streaming any library that is capable of receiving RTSP stream can be used, however we highly recommend using GStreamer which should be the fastest solution. Have a look at Ethernet Stream SDK GUI source code: https://github.com/SoftwareWorkswell/EthernetStreamSDKGUI/blob/master/ethGui/src/thermalthread.cpp (ThermalThread::run function)
  • for this kind of solution we used OpenCV 4.1.1 with GStreamer support (first install GStreamer runtime + GStreamer devel and then build OpenCV using CMake)

void ThermalThread::run()
{
#ifdef _WIN32
    const std::string addr(("rtspsrc location=rtsp://" + this->_ssrc.toStdString() + ":8554/thermal latency=250 ! rtph264depay ! avdec_h264 ! videoconvert ! appsink"));
#else
    const std::string addr("rtspsrc location=rtsp://" + this->_ssrc.toStdString() + ":8554/thermal latency=300 caps = \"application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, payload=(int)96\" ! rtph264depay ! decodebin ! videoconvert ! appsink");
#endif
    cv::VideoCapture cap(addr, cv::CAP_GSTREAMER);
    if(!cap.isOpened())
    {
        qDebug() << "visible: Input error";
        *_stream = false;
        return;
    }

    cv::Mat frame;
    while(*_stream)
    {
        cap >> frame;
        if (frame.empty())
        {
            qDebug() << "thermal: empty frame occurance, stopping stream!";
            break;
        }

        cv::cvtColor(frame, frame,cv::ColorConversionCodes::COLOR_BGR2RGB);
        zoomImage(frame);
        streamFrame = new QImage(reinterpret_cast(frame.data), frame.cols, frame.rows, static_cast(frame.step), QImage::Format_RGB888);
        drawExtremes();
        emit imageSourceChanged();
    }
    cap.release();
    *_stream = false;
}