2012-01-10 27 views
1

我试图创建一个应用程序,允许我通过局域网使用特定的多播地址多播网络摄像头馈送,并使用sendto()发送帧缓冲区。我试图构建的应用程序与本网站上的应用程序几乎相同 http://nashruddin.com/Streaming_OpenCV_Videos_Over_the_Network 并且使用相同的体系结构。 而不是我使用SOCK_DGRAM的TCP套接字。问题是,当我从不同的线程使用sendto()函数时,它往往会失败,即它返回-1并且errno被设置为90(EMSGSIZE),这基本上意味着形成的数据包太大而无法通过网络。 但即使我尝试发送一个简单的字符串(如“hello”)到相同的多播地址,也会发生这种情况。这似乎工作正常,如果应用程序是一个单线程。也就是说我只是捕捉图像并将它全部在同一个线程中多播。这是代码:通过来自不同线程的UDP进行多播

#include <netinet/in.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <pthread.h> 
#include "cv.h" 
#include "highgui.h" 

#define PORT 12345 
#define GROUP "225.0.0.37" 
CvCapture* capture; 
IplImage* img0; 
IplImage* img1; 
int   is_data_ready = 0; 
int   serversock, clientsock; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

void* streamServer(void* arg); 
void quit(char* msg, int retval); 

int main(int argc, char** argv) 
{ 
     pthread_t  thread_s; 
int   key; 

if (argc == 2) { 
    capture = cvCaptureFromFile(argv[1]); 
} else { 
    capture = cvCaptureFromCAM(0); 
} 

if (!capture) { 
    quit("cvCapture failed", 1); 
} 

img0 = cvQueryFrame(capture); 
img1 = cvCreateImage(cvGetSize(img0), IPL_DEPTH_8U, 1); 

cvZero(img1); 
cvNamedWindow("stream_server", CV_WINDOW_AUTOSIZE); 

/* print the width and height of the frame, needed by the client */ 
fprintf(stdout, "width: %d\nheight: %d\n\n", img0->width, img0->height); 
fprintf(stdout, "Press 'q' to quit.\n\n"); 

/* run the streaming server as a separate thread */ 
if (pthread_create(&thread_s, NULL, streamServer, NULL)) { 
    quit("pthread_create failed.", 1); 
} 

while(key != 'q') { 
    /* get a frame from camera */ 
    img0 = cvQueryFrame(capture); 
    if (!img0) break; 

    img0->origin = 0; 
    cvFlip(img0, img0, -1); 

    /** 
    * convert to grayscale 
    * note that the grayscaled image is the image to be sent to the client 
    * so we enclose it with pthread_mutex_lock to make it thread safe 
    */ 
    pthread_mutex_lock(&mutex); 
    cvCvtColor(img0, img1, CV_BGR2GRAY); 
    is_data_ready = 1; 
    pthread_mutex_unlock(&mutex); 

    /* also display the video here on server */ 
    cvShowImage("stream_server", img0); 
    key = cvWaitKey(30); 
} 

/* user has pressed 'q', terminate the streaming server */ 
if (pthread_cancel(thread_s)) { 
    quit("pthread_cancel failed.", 1); 
} 

/* free memory */ 
cvDestroyWindow("stream_server"); 
quit(NULL, 0); 
} 

/** 
* This is the streaming server, run as a separate thread 
* This function waits for a client to connect, and send the grayscaled images 
*/ 
void* streamServer(void* arg) 
{ 
struct sockaddr_in server; 

/* make this thread cancellable using pthread_cancel() */ 
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 

/* open socket */ 
if ((serversock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 
    quit("socket() failed", 1); 
} 
memset(&server,0,sizeof(server)); 
server.sin_family = AF_INET; 
server.sin_port = htons(PORT); 
server.sin_addr.s_addr = inet_addr(GROUP); 
int opt = 1; 
//if(setsockopt(serversock,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(int))==-1){ 
    // quit("setsockopt failed",0); 
//} 
// /* setup server's IP and port */ 
// memset(&server, 0, sizeof(server)); 
// server.sin_family = AF_INET; 
// server.sin_port = htons(PORT); 
// server.sin_addr.s_addr = INADDR_ANY; 
// 
// /* bind the socket */ 
// if (bind(serversock, (const void*)&server, sizeof(server)) == -1) { 
//  quit("bind() failed", 1); 
// } 
// 
// /* wait for connection */ 
// if (listen(serversock, 10) == -1) { 
//  quit("listen() failed.", 1); 
// } 
// 
// /* accept a client */ 
// if ((clientsock = accept(serversock, NULL, NULL)) == -1) { 
//  quit("accept() failed", 1); 
// } 

/* the size of the data to be sent */ 
int imgsize = img1->imageSize; 
int bytes=0, i; 

/* start sending images */ 
while(1) 
{ 
    /* send the grayscaled frame, thread safe */ 
    pthread_mutex_lock(&mutex); 
    if (is_data_ready) { 
//   bytes = send(clientsock, img1->imageData, imgsize, 0); 
     is_data_ready = 0; 
     if((bytes = sendto(serversock,img1->imageData,imgsize,0,(struct sockaddr*)&server,sizeof(server)))==-1){ 
      quit("sendto FAILED",1); 
     } 
    } 
    pthread_mutex_unlock(&mutex); 

//  /* if something went wrong, restart the connection */ 
//  if (bytes != imgsize) { 
//   fprintf(stderr, "Connection closed.\n"); 
//   close(clientsock); 
// 
//   if ((clientsock = accept(serversock, NULL, NULL)) == -1) { 
//    quit("accept() failed", 1); 
//   } 
//  } 

    /* have we terminated yet? */ 
    pthread_testcancel(); 

    /* no, take a rest for a while */ 
    usleep(1000); 
} 
} 

/** 
    * this function provides a way to exit nicely from the system 
    */ 
void quit(char* msg, int retval) 
{ 
    if (retval == 0) { 
    fprintf(stdout, (msg == NULL ? "" : msg)); 
    fprintf(stdout, "\n"); 
} else { 
    fprintf(stderr, (msg == NULL ? "" : msg)); 
    fprintf(stderr, "\n"); 
} 

if (clientsock) close(clientsock); 
if (serversock) close(serversock); 
if (capture) cvReleaseCapture(&capture); 
if (img1) cvReleaseImage(&img1); 

pthread_mutex_destroy(&mutex); 

    exit(retval); 
} 

回答

0

你是否绝对确信你不会发送大于65500字节的UDP限制?根据我的经验,您甚至不应发送大于1500字节的以太网数据包限制,以保持最佳的UDP可靠性。

我认为你现在正在尝试以流的形式发送更多的数据。 UDP不是流协议,你不能用它取代TCP。但是,当然可以使用UDP在多播上发送视频流,但是您需要一些UDP协议,以处理UDP的消息大小限制。在现实世界中,在UDP之上的RTP协议被用于这种类型的任务。

+0

我敢肯定这是不是因为UDP的硬限制,因为当我使用单线程方法,这已经奏效。当我尝试在代码的那部分代替图像传递一个简单的字符串(比如“hello”)时,对'sendto'的调用甚至失败。 – 2012-01-11 04:37:36

3

sendto()调用中,您参考imgsize,它被初始化为img1->imageSize。 但我没有看到img1->imageSize的设置,看起来imgsize从不更新。

所以首先检查imgsize值传递给sendto()是正确的。 然后检查它是否不太大:

UDP/IP数据报的硬件负载限制为65,507字节。但是,IPv4网络不需要支持超过548字节的有效负载。 (576是最小的IPv4 MTU大小,少于28字节的UDP/IP开销)。大多数网络的MTU为1500,给你一个1472字节的标称有效载荷。

大多数网络都允许您通过将数据报分解为IP分段(接收操作系统必须重新组合)来超过MTU。这对您的应用程序是不可见的:recvfrom()或者获得整个重组数据包,或者它什么都没有。但由于丢失任何碎片会导致整个数据包丢失,导致碎片的可能性增加。另外,一些路由器和操作系统具有模糊的安全规则,这将阻塞某些UDP模式或特定大小的片段。

最后,即使有碎片,任何给定的网络也可能实施最大的数据报大小,并且这通常远小于65507字节。

由于您正在处理特定的网络,因此您需要进行试验以确定您可以稳定走多远。

UDP/IP at Wikipedia

IPv4 at Wikipedia

+0

Hello @SethNoble,当我执行'img1 = cvCreate(cvGetSize(img0),IPL_DEPTH_8U,1)'时'img1-> imageSize'被设置,它基本上为img1结构体分配了一些与img0尺寸相同的内存。对'sendto'的调用往往会失败,即使我做了这样的事情(在我使用'sendto'的代码完全相同的部分)'sendto(serversock,“hello”,sizeof(“hello”),0 ,(struct sockaddr *)&server,sizeof(server))'。当我在任何一种情况下使用单线程方法时都没有问题。 – 2012-01-11 04:35:32

+0

完整性检查:在'socket()'之后,打印出'serversock'的值。然后在'sendto()'处,再次打印出'serversock'的值,以确保它没有改变。对于'server'中的值也是如此。如果某个内存在某处超出,那可能会导致你所看到的内容。 – 2012-01-11 05:19:26

+0

还有其他要考虑的事情:'usleep(1000)'只会睡1毫秒。这意味着该互斥量正在被调查至每秒1000。取决于还在发生什么,这可能会造成资源争夺(在内核或其他地方挨饿)。最好使用一个条件变量,或者至少一个更大的等待时间。我不知道这是否能解决您的问题,但这可能会有所帮助。 – 2012-01-11 15:00:05