2017-08-03 46 views
0

我使用UDP套接字将图像从客户端传输到服务器。对于编码和解码,我使用OpenCV。有时我会得到错误的解码图像,因为丢失了一个或一些数据包(只发送了头文件,请查看我的终端屏幕了解一些信息)。我必须将jpeg的质量降低到30,以减少错误的解码图像比例。如何使用条件代码忽略丢失某些数据包的帧(不进行解码工作),或者在imshow函数中不显示错误的解码图像。如何修复或忽略通过套接字流式传输的错误解码图像

这里的错误解码图像:

sample

终端跟踪屏幕:

image

我的客户代码:

#include "PracticalSocket.h" 
#include <iostream> 
#include <cstdlib> 

#include "cv.hpp" 
#include "config.h" 

#include "logger.h" // For trace 
using namespace ModernCppCI; 

using namespace cv; 
using namespace std; 

int main(int argc, char * argv[]) { 

Logger log{__func__}; 

if ((argc < 4) || (argc > 4)) { // Test for correct number of arguments 
    log.error("Usage: {} <Server> <Server Port>\n <RTSP link>", argv[0]); 
    exit(1); 
} 

string servAddress = argv[1]; // First arg: server address 
unsigned short servPort = Socket::resolveService(argv[2], "udp"); 

try { 
    UDPSocket sock; 
    int jpegqual = ENCODE_QUALITY; // It's 30 

    Mat frame, send; 
    vector <uchar> encoded; 
    //VideoCapture cap("rtsp://admin:[email protected].234/Streaming/Channels/1?tcp"); // Grab the camera 
    VideoCapture cap(argv[3]); 
    if (!cap.isOpened()) { 
     log.error("OpenCV failed to open camera"); 
     exit(1); 
    } 

    clock_t last_cycle = clock(); 
    unsigned char pressed_key; 
    while (1) { 
     vector <int> compression_params; 
     cap >> send; 
     if(send.empty())continue; 
     // JPEG encoding 
     compression_params.push_back(CV_IMWRITE_JPEG_QUALITY); 
     compression_params.push_back(jpegqual); 
     imencode(".jpg", send, encoded, compression_params); 


     imshow("send", send); 

     int total_pack = 1 + (encoded.size() - 1)/PACK_SIZE; // PACK_SIZE is 4096 

     int ibuf[1]; 
     ibuf[0] = total_pack; 
     sock.sendTo(ibuf, sizeof(int), servAddress, servPort); 

     for (int i = 0; i < total_pack; i++) 
      sock.sendTo(& encoded[i * PACK_SIZE], PACK_SIZE, servAddress, servPort); 

     pressed_key = waitKey(1); 

     if(pressed_key == ' ') 
      pressed_key = waitKey(0); 

     if(pressed_key == 'q') 
      break; 
     clock_t next_cycle = clock(); 
     double duration = (next_cycle - last_cycle)/(double) CLOCKS_PER_SEC; 
     log.info(" FPS: {}, kbps: {}, Processing time: {}ms" , (1/duration), (PACK_SIZE * total_pack/duration/1024 * 8), 1000*duration); 

     last_cycle = next_cycle; 
    } 
    // Destructor closes the socket 

} catch (SocketException & e) { 
    log.error(e.what()); 
    exit(1); 
} 

return 0; 
} 

Server代码

#include "PracticalSocket.h" 
#include <iostream> 
#include <cstdlib> 

#include "cv.hpp" 
#include "config.h" 
#include "logger.h" // For trace 

using namespace ModernCppCI; 
using namespace cv; 


int main(int argc, char * argv[]) { 

Logger log{__func__}; 

if (argc != 2) { // Test for correct number of parameters 
    log.error("Usage: {} <Server Port>", argv[0]); 
    exit(1); 
} 

unsigned short servPort = atoi(argv[1]); // First arg: Server port 

try { 
    UDPSocket sock(servPort); 

    char buffer[BUF_LEN]; // Buffer for echo string 
    int recvMsgSize; // Size of received message 
    string sourceAddress; // Address of datagram source 
    unsigned short sourcePort; // Port of datagram source 

    clock_t last_cycle = clock(); 
    unsigned char pressed_key; 
    while (1) { 
     // Block until receive message from a client 
     do { 
      recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort); // BUF_LEN is 65540 
     } while (recvMsgSize > sizeof(int)); 
     int total_pack = ((int *) buffer)[0]; 

     log.info("expecting length of packs: {}", total_pack); 
     char * longbuf = new char[PACK_SIZE * total_pack]; 
     for (int i = 0; i < total_pack; i++) { 
      recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort); 
      if (recvMsgSize != PACK_SIZE) { 
       log.error("Received unexpected size pack: {}", recvMsgSize); 
       continue; 
      } 
      memcpy(& longbuf[i * PACK_SIZE], buffer, PACK_SIZE); // Copy PACK_SIZE bytes from buffer to longbuf 
     } 

     log.info("Received packet from {}:{}", sourceAddress, sourcePort); 
     Logger::level(LogLevel::trace); 
     log.trace("longbuf size: {}", ((int *) &longbuf)[0]); 

     Mat rawData = Mat(1, PACK_SIZE * total_pack, CV_8UC1, longbuf); 
     Mat frame = imdecode(rawData, CV_LOAD_IMAGE_COLOR); 
     if (frame.empty()) { 
      log.error("Decode failure!"); 
      continue; 
     } 
     imshow("recv", frame); 
     pressed_key = waitKey(1); 

     if(pressed_key == ' ') 
      pressed_key = waitKey(0); 

     if(pressed_key == 'q') 
      break; 

     free(longbuf); 

     clock_t next_cycle = clock(); 
     double duration = (next_cycle - last_cycle)/(double) CLOCKS_PER_SEC; 
     log.info(" FPS: {} , kbps: {} , Processing time: {}", (1/duration), (PACK_SIZE * total_pack/duration/1024 * 8), (next_cycle - last_cycle)); 

     last_cycle = next_cycle; 
    } 
} catch (SocketException & e) { 
    log.error(e.what()); 
    exit(1); 
    } 

return 0; 
} 
+0

我想了解你的问题。当你检测到一个意想不到的大小数据包时,你想跳过整个帧;基本上,当你检查解码失败并继续时,你会想检查帧是否有意想不到的大小数据包并继续? – Basya

回答

0

我试图理解你的问题,您应该不能混用C++和C分配(新的和免费的)。当你检测到一个意想不到的大小数据包时,你想跳过整个帧;基本上,当你检查解码失败并继续时,你会想检查帧是否有意想不到的大小数据包并继续?或者之前,跳过解码尝试以及....

如果这是你正在尝试做的,你可以做这样的事情:

1.增加在while循环水平的标志:

while (1) { 
bool goodFrame = true; // start out optimistic! 

// Block until receive message from a client 

2.Change旗当你发现了错误的数据包:

 if (recvMsgSize != PACK_SIZE) { 
      log.error("Received unexpected size pack: {}", recvMsgSize); 
      goodFrame = false; 
      continue; 
     } 

3.检查标志,并跳过帧的解码及用途:

log.trace("longbuf size: {}", ((int *) &longbuf)[0]); 

    if (!goodFrame) { 
     // you probably do not need to log an error, as you did it above when you detected the bad packet. 
     continue; 
    } 

    Mat rawData = Mat(1, PACK_SIZE * total_pack, CV_8UC1, longbuf); 

4.You也可能要跳过复制此帧数据包的其余部分,因为框架不会还是使用:

 if (goodFrame) 
      memcpy(& longbuf[i * PACK_SIZE], buffer, PACK_SIZE); // Copy PACK_SIZE bytes from buffer to longbuf 

,使得得到完整的while循环看起来像这样:

while (1) { 
    bool goodFrame = true; // start out optimistic! 

    // Block until receive message from a client 
    do { 
     recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort); // BUF_LEN is 65540 
    } while (recvMsgSize > sizeof(int)); 
    int total_pack = ((int *) buffer)[0]; 

    log.info("expecting length of packs: {}", total_pack); 
    char * longbuf = new char[PACK_SIZE * total_pack]; 
    for (int i = 0; i < total_pack; i++) { 
     recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort); 
     if (recvMsgSize != PACK_SIZE) { 
      log.error("Received unexpected size pack: {}", recvMsgSize); 
      goodFrame = false; 
      continue; 
     } 
    if (goodFrame) 
      memcpy(& longbuf[i * PACK_SIZE], buffer, PACK_SIZE); // Copy PACK_SIZE bytes from buffer to longbuf 
    } 

    log.info("Received packet from {}:{}", sourceAddress, sourcePort); 
    Logger::level(LogLevel::trace); 
    log.trace("longbuf size: {}", ((int *) &longbuf)[0]); 

    if (!goodFrame) { 
     // you probably do not need to log an error, as you did it above when you detected the bad packet. 
     continue; 
    } 

    Mat rawData = Mat(1, PACK_SIZE * total_pack, CV_8UC1, longbuf); 
    Mat frame = imdecode(rawData, CV_LOAD_IMAGE_COLOR); 
    if (frame.empty()) { 
     log.error("Decode failure!"); 
     continue; 
    } 
    imshow("recv", frame); 
    pressed_key = waitKey(1); 

    if(pressed_key == ' ') 
     pressed_key = waitKey(0); 

    if(pressed_key == 'q') 
     break; 

    free(longbuf); 

    clock_t next_cycle = clock(); 
    double duration = (next_cycle - last_cycle)/(double) CLOCKS_PER_SEC; 
    log.info(" FPS: {} , kbps: {} , Processing time: {}", (1/duration), (PACK_SIZE * total_pack/duration/1024 * 8), (next_cycle - last_cycle)); 

    last_cycle = next_cycle; 
} 

如果我误解了你的问题,请澄清你的问题,我希望能够帮助更多。

+0

感谢您的全力帮助 –

+0

非常欢迎。帮助解决了您的问题,还是需要更多帮助?如果我们的答案之一解决了您的问题,或者帮助解决了您的问题,请立即将其标记为/或将其标记为解决方案。如果我们尚未回答您的问题,请说明您仍然需要哪些帮助,哪些不起作用等,我们会尽力为您提供更多帮助。 – Basya

0

如果我正确得到你的问题,你的协议是:

  1. 发送抬头(IBUF),其中包含预期的数据分组数n。
  2. 发送N个数据分组

和什么发生在服务器侧是:

  1. 接收头
  2. 接收N-1的数据包(一个丢失)
  3. 接收下一个报头作为数据包并丢弃当前帧。
  4. 等待整个帧为新的标题,因此2帧丢失。

您在这里错过的东西是标头和数据包之间的区别。您已经使用的最简单的方法是检查数据包的大小。知道你可以决定如何处理当前数据包 - 它是从新帧开始(所以以前消失)还是新数据。有了这个,你可以开始阅读新的框架,只有当数据包丢失时才会丢失。

此片段显示了它的一个想法:

int total_pack = 0; 
    int counter = 0; 
    char * longbuf = nullptr; 
    while (1) { 
     recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort); // BUF_LEN is 65540 

     if (recvMsgSize == sizeof(int)) { // header 
      total_pack = ((int *)buffer)[0]; 
      counter = 0; // reset frame counter 
      log.info("expecting length of packs: {}", total_pack); 
      if (longbuf) delete[] longbuf; 
      longbuf = new char[PACK_SIZE * total_pack]; 
     } 
     else if (recvMsgSize == PACK_SIZE){ // if we know size of incoming frame 
      if (total_pack > 0) { // skip it if we dont know header yet 
       memcpy(&longbuf[counter * PACK_SIZE], buffer, PACK_SIZE); // Copy PACK_SIZE bytes from buffer to longbuf 
       counter++; 
       if (counter == total_pack) { 
        total_pack = 0; // clear header 
        break; // whole frame received 
       } 
      } 
     } 
     else 
      log.error("Received unexpected size pack: {}", recvMsgSize); 
    } 

而且,数据分组应当包含小头与它的全部缓冲器位置(帧号将是有用的太),因为UDP数据包不说是按发送顺序接收。 (他们可能会转移)。

Can you mix free and constructor in C++?

+0

感谢您的帮助 –