2017-08-03 49 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

感謝您的幫助 –