Files
flyLink/skylink_qt_station/src/GroundReceiver.h

248 lines
6.8 KiB
C
Raw Normal View History

2026-01-19 17:08:49 +08:00
/**
* @file GroundReceiver.h
* @brief SkyLink Qt - UDP
*
* 线
* 1. UDP 广
* 2.
* 3.
* 4. JPEG
*
2026-01-20 10:41:22 +08:00
* @author flowzl
2026-01-19 17:08:49 +08:00
* @date 2026-01-19
*/
#pragma once
#include <QObject>
#include <QUdpSocket>
#include <QThread>
#include <QImage>
#include <QMap>
2026-01-21 13:44:29 +08:00
#include <QDebug>
#include <QString>
2026-01-19 17:08:49 +08:00
#include <map>
#include <vector>
#include <memory>
#include <cstdint>
#include <mutex>
2026-01-21 13:44:29 +08:00
// OpenCV for YUYV conversion
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
2026-01-19 17:08:49 +08:00
#include "protocol.h"
/**
* @struct FrameBuffer
* @brief
*/
struct FrameBuffer {
uint32_t frame_id;
uint16_t total_chunks;
std::map<uint16_t, std::vector<uint8_t>> chunks; // chunk_index -> 数据
double timestamp;
double lat, lon, alt;
uint64_t receive_time_ms;
2026-02-02 17:06:20 +08:00
uint32_t photo_number = 0; // 照片编号从reserve字段提取
2026-01-19 17:08:49 +08:00
/**
* @brief
*/
bool is_complete() const {
if (total_chunks == 0) return false;
for (uint16_t i = 0; i < total_chunks; i++) {
if (chunks.find(i) == chunks.end()) {
return false;
}
}
return true;
}
/**
* @brief
2026-01-21 13:44:29 +08:00
* @return vector
2026-01-19 17:08:49 +08:00
*/
std::vector<uint8_t> merge() const {
std::vector<uint8_t> result;
2026-01-21 13:44:29 +08:00
// 预分配空间以提高性能
size_t estimated_size = chunks.size() * 500; // 估算每个分片约500字节
result.reserve(estimated_size);
// 调试:检查分片顺序
QString chunk_indices;
for (const auto& pair : chunks) {
chunk_indices += QString::number(pair.first) + " ";
}
qDebug() << "" << frame_id << "合并前,分片索引:" << chunk_indices;
2026-01-19 17:08:49 +08:00
for (uint16_t i = 0; i < total_chunks; i++) {
auto it = chunks.find(i);
if (it != chunks.end()) {
2026-01-21 13:44:29 +08:00
// 调试:如果是第一个分片,打印前几个字节
if (i == 0 && !it->second.empty()) {
QString hex_preview;
for (size_t j = 0; j < std::min((size_t)8, it->second.size()); j++) {
hex_preview += QString::number(it->second[j], 16).rightJustified(2, '0') + " ";
}
qDebug() << "" << frame_id << "合并时分片0前8字节:" << hex_preview;
}
2026-01-19 17:08:49 +08:00
result.insert(result.end(), it->second.begin(), it->second.end());
2026-01-21 13:44:29 +08:00
} else {
// 如果缺少分片返回空vector表示不完整
qWarning() << "" << frame_id << "合并时缺少分片" << i;
return std::vector<uint8_t>();
2026-01-19 17:08:49 +08:00
}
}
return result;
}
};
/**
* @class GroundReceiver
* @brief UDP
*/
class GroundReceiver : public QObject {
Q_OBJECT
public:
/**
* @brief
* @param parent
* @param port UDP ( 9999)
*/
explicit GroundReceiver(QObject* parent = nullptr, uint16_t port = 9999);
/**
* @brief
*/
~GroundReceiver();
/**
* @brief
*/
void start();
/**
* @brief
*/
void stop();
/**
* @brief
*/
bool is_running() const;
signals:
/**
* @brief
* @param img QImage
* @param lat
* @param lon
* @param alt
2026-02-02 17:06:20 +08:00
* @param photo_number
2026-01-19 17:08:49 +08:00
*/
2026-02-02 17:06:20 +08:00
void frameDecoded(const QImage& img, double lat, double lon, double alt, uint32_t photo_number);
2026-01-19 17:08:49 +08:00
/**
* @brief
* @param fps
* @param kbs KB
*/
void statsUpdated(int fps, int kbs);
/**
* @brief
* @param error_msg
*/
void error(const QString& error_msg);
private slots:
/**
* @brief UDP
*/
void on_udp_read_ready();
/**
* @brief UDP
*/
void on_udp_error();
/**
* @brief
*/
void on_cleanup_timeout();
private:
// ========================================================================
// UDP 套接字和网络
// ========================================================================
uint16_t listen_port_;
std::unique_ptr<QUdpSocket> udp_socket_;
QThread worker_thread_;
// ========================================================================
// 帧缓冲区管理
// ========================================================================
std::map<uint32_t, FrameBuffer> frame_buffers_; // frame_id -> FrameBuffer
std::mutex frame_buffers_mutex_;
uint32_t last_complete_frame_id_;
// ========================================================================
// 统计信息
// ========================================================================
struct {
uint32_t total_packets = 0; ///< 总接收包数
uint32_t total_bytes = 0; ///< 总接收字节数
uint32_t total_frames = 0; ///< 完整帧数
uint32_t dropped_frames = 0; ///< 丢弃的帧数
uint32_t frames_this_second = 0; ///< 本秒帧数
uint32_t bytes_this_second = 0; ///< 本秒字节数
uint64_t last_stats_time = 0; ///< 上次统计时刻
} stats_;
std::mutex stats_mutex_;
// ========================================================================
// 私有方法
// ========================================================================
/**
* @brief UDP
*/
void process_packet(const uint8_t* data, size_t len);
/**
* @brief
*/
void handle_complete_frame(const FrameBuffer& frame);
/**
* @brief JPEG QImage
*/
QImage decode_jpeg(const std::vector<uint8_t>& data);
2026-01-21 13:44:29 +08:00
/**
* @brief YUYV QImage
* @param yuyv_data YUYV格式的原始数据
* @param width
* @param height
* @return QImage
*/
QImage decode_yuyv(const std::vector<uint8_t>& yuyv_data, int width, int height);
2026-01-19 17:08:49 +08:00
/**
* @brief
*/
void cleanup_timeout_frames();
/**
* @brief
*/
void update_stats(uint32_t packet_size);
};