1

I am trying to implement a UDP server with C++ to receive frames from the client. I am using this question as reference. Only difference is that I have a Java Client that sends the frames via udp:

Java client that sends image via UDP:

byte[] buffer = GetImageByte(FrameData,384,288,false); //GetImageByte is a function that returns a raw frame
DatagramSocket clientSocket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, IPAddress, 9999);
clientSocket.send(packet); 

C++ server that receives udp image:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    char buffer[1024];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    int height = 384;
    int width= 288;


    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(9999);


    Mat img = Mat::zeros( height,width, CV_8UC3);
    int imgSize = img.total()*img.elemSize();
    uchar sockData[imgSize];
    int bytes = 0; 
    for (int i = 0; i < imgSize; i += bytes) {
        if ((bytes = recv(sockfd, sockData +i, imgSize  - i, 0)) == -1)  {
            break;
        }
        
    }
    int ptr=0;
    for (int i = 0;  i < img.rows; i++) {
        for (int j = 0; j < img.cols; j++) {                                     
            img.at<cv::Vec3b>(i,j) = cv::Vec3b(sockData[ptr+ 0],sockData[ptr+1],sockData[ptr+2]);
            ptr=ptr+3;
    }
}


    Mat image(384,288,CV_8UC3,*buffer);
    close(newsockfd);
    close(sockfd);
    
    
    imshow( "Server", image);  
    waitKey(0);
    return 0; 
}

What I am trying to do is simply receive a frame and show it using OpenCV. The problem is that the code compiles successfully but no errors or any helpful message is shown in the console whatsoever. I tested sending the image to a UDP server in Python it works. I'd like to know how can I receive a frame via UDP using C++ and show it on screen using OpenCv?

xnok
  • 299
  • 2
  • 6
  • 35

1 Answers1

2

After a brief consultation with OpenCV reference, the above appears to be working with 384x288 three channel image, which adds up to 324kb worth of data.

The Java client appears to be attempting to construct a single datagram packet, of this size, then send it all at once.

The chances of being able to send a single UDP 324kb packet, anywhere, are slim to none. The maximum size of a single UDP socket is unspecified and depends on the underlying network socket; 508 bytes is the generally accepted maximum size, with modern network protocols.

This is implied by your C++ code, which uses a loop to attempt to read the entire frame (which is also somewhat naive, for the reasons mentioned shortly, but let's set this aside for now). You will need to add appropriate logic to the sending code to break it up.

Once you do this you will run into the next problem: UDP gives you absolutely no guarantees, whatsoever, that any UDP packet you send will be received. UDP, by definition, lacks any kind of retransmission logic, and is not a guaranteed delivery transport layer. This is explained in every textbook that focuses on network programming.

Common media streaming platforms achieve more or less reliable streaming over UDP through a combination of sophisticated rate limiting, detecting and adapting to the available bandwidth, and using complicated logic that deals with occasional dropped packets; minimizing the impact of dropped packets with some level of redundancy (a single dropped frame in a Youtube stream will be hardly noticed, as well as a slight, temporary, degradation in the audio stream). The chances of successfully receiving every one of the packets that adds up to the 324kb image, when the Java sender simply blasts them out, as fast as it can, are not very promising.

So, you have lot of work cut out for you, here, inventing and implementing your own versions of all of these underlying algorithms, for implementing some kind of a throttled streaming mechanism on top of UDP. It will be frankly easier to switch to TCP, get it working with TCP, then attempt to implement all of this on top of UDP after the fact.

One final note:

    uchar sockData[imgSize];

This is not valid C++ either. Variable length arrays are non-standard C++, and are only a non-standard C++ extension that your compiler supports. Additionally, this will eat the aforementioned 324kb out of your stack, all in one gulp.

Although modern C++ implementations will typically have so much stack space available: getting into a habit of doing this will eventually lead to tears. Don't do this, use std::vector.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thanks for the input, however UDP is used because real-time is required for my case. Additionally, it is more important to send the image than to have guarantees. I understand I maybe attempting to read the image the wrong way, but i still need the server to read the UDP image. – xnok Jul 03 '22 at 04:51
  • Well, then, you'll just have to do all the work that's outlined here: the sender must correctly break up the image into individual packets and send them individually and implement an adaptive rate limiting algorithm on both the sender and the receiver. With the current code the receiver has no clue whatsoever if a packet gets lost. We're not talking about one or two extra lines of code for this, but a small novel. And it doesn't matter how important it is to send an image, if there's no guarantee if it'll ever get received. There goes the image's importance out the window. – Sam Varshavchik Jul 03 '22 at 12:18
  • A similar approach is used here: https://github.com/chenxiaoqino/udp-image-streaming/blob/master/Server.cpp. Although the only difference is that before the image is sent a byte informing ahead the file size is sent and the server reads that amount of bytes from the socket – xnok Jul 03 '22 at 19:51
  • 1
    ... aaaaand that code is an excellent way to leak a crapton of memory in the event of lost packets. Not surprising -- typical quality of random code found on the Intertubes. Just because something's found on github doesn't mean it's a marvel of software engineering. There is no substitute for learning any skill (not just this one) properly, through a textbook-guided, organized curriculum. Attempting to learn something from Google will always end in tears. At least that pile of bugs used `new` instead of `malloc`. Still, `std::vector` would've prevented that leak... So close...! – Sam Varshavchik Jul 03 '22 at 22:18
  • I keep encountering a popular belief that any complicated task can be done in C++ by writing just a few lines of code, or maybe call a C++ library function. This is rarely the case. The C++ library provides only a collection of basic algorithms and containers. Any logic that's more complicated than a "Hello World" requires work. A lot of it. A tailored media streaming algorithm based on an unreliable and non-guaranteed transport (UDP), needs more than just a few lines of code to implement. It's unlikely that there are secret shortcuts around this, somewhere, that a SO bounty will uncover. – Sam Varshavchik Jul 11 '22 at 14:25
  • i am trying to get the snippet working, as it comes from an accepted answer – xnok Jul 11 '22 at 17:48
  • The C++ code is very close to what I am trying to do really. The Java is a fully functional code as it works for a java-java UDP and java-python UDP transmission, as I've tested it. The code above was taken from an accepted answer on how to receive the data and convert it to image, that is all I am trying to do really – xnok Jul 11 '22 at 19:41
  • Well, whoever wrote it deserves a Nobel prize, for figuring out how to reliably send and receive a single 324kb UDP packet. – Sam Varshavchik Jul 11 '22 at 20:46