QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ImageProtocolManager.cc
Go to the documentation of this file.
3
4#include <cstring>
5
6QGC_LOGGING_CATEGORY(ImageProtocolManagerLog, "MAVLink.ImageProtocolManager")
7
9 : QObject(parent)
10{
11 // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this;
12}
13
15{
16 // qCDebug(ImageProtocolManagerLog) << Q_FUNC_INFO << this;
17}
18
19bool ImageProtocolManager::requestImage(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message)
20{
21 // Check if there is already an image transmission going on
22 if (_imageHandshake.packets != 0) {
23 return false;
24 }
25
26 constexpr mavlink_data_transmission_handshake_t data = {
27 0, 0, 0, 0,
28 MAVLINK_DATA_STREAM_IMG_JPEG,
29 0,
30 50
31 };
32 (void) mavlink_msg_data_transmission_handshake_encode_chan(system_id, component_id, chan, &message, &data);
33
34 return true;
35}
36
37void ImageProtocolManager::cancelRequest(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message)
38{
39 constexpr mavlink_data_transmission_handshake_t data{};
40 (void) mavlink_msg_data_transmission_handshake_encode_chan(system_id, component_id, chan, &message, &data);
41}
42
44{
45 switch (message.msgid) {
46 case MAVLINK_MSG_ID_DATA_TRANSMISSION_HANDSHAKE:
47 {
48 if (_imageHandshake.packets > 0) {
49 qCWarning(ImageProtocolManagerLog) << "DATA_TRANSMISSION_HANDSHAKE: Previous image transmission incomplete.";
50 }
51 _imageBytes.clear();
52 mavlink_msg_data_transmission_handshake_decode(&message, &_imageHandshake);
53 qCDebug(ImageProtocolManagerLog) << QStringLiteral("DATA_TRANSMISSION_HANDSHAKE: type(%1) width(%2) height (%3)").arg(_imageHandshake.type).arg(_imageHandshake.width).arg(_imageHandshake.height);
54
55 // Validate handshake fields to prevent out-of-bounds writes on subsequent ENCAPSULATED_DATA
56 static constexpr uint32_t kMaxImageSize = 1u * 1024u * 1024u; // 1 MB upper bound (optical flow images are typically small grayscale frames)
57 if (_imageHandshake.size == 0 || _imageHandshake.payload == 0 || _imageHandshake.packets == 0) {
58 qCWarning(ImageProtocolManagerLog) << "DATA_TRANSMISSION_HANDSHAKE: Invalid field(s) - size:" << _imageHandshake.size
59 << "payload:" << _imageHandshake.payload << "packets:" << _imageHandshake.packets;
60 _imageHandshake = {};
61 break;
62 }
63 if (_imageHandshake.size > kMaxImageSize) {
64 qCWarning(ImageProtocolManagerLog) << "DATA_TRANSMISSION_HANDSHAKE: Image size exceeds limit. size:" << _imageHandshake.size;
65 _imageHandshake = {};
66 break;
67 }
68 if (_imageHandshake.payload > sizeof(mavlink_encapsulated_data_t::data)) {
69 qCWarning(ImageProtocolManagerLog) << "DATA_TRANSMISSION_HANDSHAKE: payload exceeds ENCAPSULATED_DATA data field size. payload:" << _imageHandshake.payload;
70 _imageHandshake = {};
71 break;
72 }
73
74 _imageBytes.resize(_imageHandshake.size, '\0');
75 break;
76 }
77 case MAVLINK_MSG_ID_ENCAPSULATED_DATA:
78 {
79 if (_imageHandshake.packets == 0) {
80 qCWarning(ImageProtocolManagerLog) << "ENCAPSULATED_DATA: received with no prior DATA_TRANSMISSION_HANDSHAKE.";
81 break;
82 }
83
84 mavlink_encapsulated_data_t encapsulatedData;
85 mavlink_msg_encapsulated_data_decode(&message, &encapsulatedData);
86
87 const uint32_t bytePosition = static_cast<uint32_t>(encapsulatedData.seqnr) * _imageHandshake.payload;
88 if (bytePosition >= _imageHandshake.size) {
89 qCWarning(ImageProtocolManagerLog) << "ENCAPSULATED_DATA: seqnr is past end of image size. seqnr:" << encapsulatedData.seqnr << "_imageHandshake.size:" << _imageHandshake.size;
90 break;
91 }
92
93 // Clamp the number of bytes to copy so we never write past the declared image size
94 const uint32_t bytesRemaining = _imageHandshake.size - bytePosition;
95 const uint32_t bytesToCopy = qMin(static_cast<uint32_t>(_imageHandshake.payload), bytesRemaining);
96
97 (void) memcpy(_imageBytes.data() + bytePosition, encapsulatedData.data, bytesToCopy);
98
99 // We use the packets field to track completion
100 _imageHandshake.packets--;
101 if (_imageHandshake.packets == 0) {
102 // We have all the packets
103 emit imageReady(_getImage());
104
105 _flowImageIndex++;
106 emit flowImageIndexChanged(_flowImageIndex);
107 }
108 break;
109 }
110 default:
111 break;
112 }
113}
114
115QImage ImageProtocolManager::_getImage()
116{
117 QImage image;
118
119 if (_imageBytes.isEmpty()) {
120 qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Called when no image available";
121 return image;
122 }
123
124 if (_imageHandshake.packets > 0) {
125 qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Called when image is imcomplete. _imageHandshake.packets:" << _imageHandshake.packets;
126 return image;
127 }
128
129 switch (_imageHandshake.type) {
130 case MAVLINK_DATA_STREAM_IMG_RAW8U:
131 case MAVLINK_DATA_STREAM_IMG_RAW32U:
132 {
133 // Construct PGM header
134 const QString header = QStringLiteral("P5\n%1 %2\n255\n").arg(_imageHandshake.width).arg(_imageHandshake.height);
135
136 QByteArray tempImage(header.toStdString().c_str(), header.length());
137 (void) tempImage.append(_imageBytes);
138
139 if (!image.loadFromData(tempImage, "PGM")) {
140 qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "IMG_RAW8U QImage::loadFromData failed";
141 }
142 break;
143 }
144 case MAVLINK_DATA_STREAM_IMG_BMP:
145 case MAVLINK_DATA_STREAM_IMG_JPEG:
146 case MAVLINK_DATA_STREAM_IMG_PGM:
147 case MAVLINK_DATA_STREAM_IMG_PNG:
148 if (!image.loadFromData(_imageBytes)) {
149 qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Known header QImage::loadFromData failed";
150 }
151 break;
152
153 default:
154 qCWarning(ImageProtocolManagerLog) << Q_FUNC_INFO << "Unsupported image type:" << _imageHandshake.type;
155 break;
156 }
157
158 return image;
159}
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool requestImage(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message)
void cancelRequest(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t &message)
void mavlinkMessageReceived(const mavlink_message_t &message)
void flowImageIndexChanged(uint32_t index)
void imageReady(const QImage &image)