我使用UDP套接字将图像从客户端流式传输到服务器。对于编码和解码,我使用OpenCV。有时我得到一个错误的解码图像,因为一个或一些数据包丢失(只有标题被发送,请参阅我的终端屏幕获取一些信息)。我必须将jpeg的质量降低到只有30才能降低错误的解码图像比率。如何使用条件代码忽略某些数据包丢失的帧(不进行解码工作),或者在imshow函数中没有显示错误的解码图像。
这里是错误的解码图像:
终端跟踪屏幕:
我的客户代码:
#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:centic.vn@10.49.34.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;
}
服务器代码
#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 :(得分:0)
如果我的问题正确,您的协议是:
在服务器端发生的事情是:
你在这里想到的是标题和数据包之间的区别。您已经使用的最简单方法是检查数据包的大小。知道你可以确定如何处理当前数据包 - 它是新帧的开始(所以之前已经消失)或新数据。有了这个,你就可以开始阅读新的框架,只有当数据包丢失时才会丢失。
此代码段显示了它的概念:
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包不是按发送顺序接收的。 (他们可能会改变)。
你不应该混合C ++和C分配(新的和免费的) Can you mix free and constructor in C++?
答案 1 :(得分:0)
我想了解你的问题。当您检测到意外大小的数据包时,您想要跳过整个帧;基本上,当您检查解码失败并继续时,您需要检查帧是否有意外大小的数据包并继续?或者之前,跳过解码尝试......
如果你正在尝试这样做,你可以这样做:
1.在while循环的级别添加一个标志:
while (1) {
bool goodFrame = true; // start out optimistic!
// Block until receive message from a client
2.检测到错误数据包时更改标志:
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.您可能还想跳过复制此帧中的其余数据包,因为无论如何都不会使用该帧:
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;
}
如果我误解了您的问题,请澄清您的要求,我希望能够提供更多帮助。