FreeRDP
Loading...
Searching...
No Matches
rdpgfx_codec.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25#include <winpr/stream.h>
26#include <freerdp/log.h>
27#include <freerdp/utils/profiler.h>
28
29#include "rdpgfx_common.h"
30
31#include "rdpgfx_codec.h"
32
38static UINT rdpgfx_read_h264_metablock(WINPR_ATTR_UNUSED RDPGFX_PLUGIN* gfx, wStream* s,
40{
41 RECTANGLE_16* regionRect = nullptr;
42 RDPGFX_H264_QUANT_QUALITY* quantQualityVal = nullptr;
43 UINT error = ERROR_INVALID_DATA;
44 meta->regionRects = nullptr;
45 meta->quantQualityVals = nullptr;
46
47 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 4))
48 goto error_out;
49
50 Stream_Read_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */
51
52 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, meta->numRegionRects, 8ull))
53 goto error_out;
54
55 meta->regionRects = (RECTANGLE_16*)calloc(meta->numRegionRects, sizeof(RECTANGLE_16));
56
57 if (!meta->regionRects)
58 {
59 WLog_Print(gfx->base.log, WLOG_ERROR, "malloc failed!");
60 error = CHANNEL_RC_NO_MEMORY;
61 goto error_out;
62 }
63
64 meta->quantQualityVals =
65 (RDPGFX_H264_QUANT_QUALITY*)calloc(meta->numRegionRects, sizeof(RDPGFX_H264_QUANT_QUALITY));
66
67 if (!meta->quantQualityVals)
68 {
69 WLog_Print(gfx->base.log, WLOG_ERROR, "malloc failed!");
70 error = CHANNEL_RC_NO_MEMORY;
71 goto error_out;
72 }
73
74 WLog_Print(gfx->base.log, WLOG_TRACE, "H264_METABLOCK: numRegionRects: %" PRIu32 "",
75 meta->numRegionRects);
76
77 for (UINT32 index = 0; index < meta->numRegionRects; index++)
78 {
79 regionRect = &(meta->regionRects[index]);
80
81 if ((error = rdpgfx_read_rect16(gfx->base.log, s, regionRect)))
82 {
83 WLog_Print(gfx->base.log, WLOG_ERROR,
84 "rdpgfx_read_rect16 failed with error %" PRIu32 "!", error);
85 goto error_out;
86 }
87
88 WLog_Print(gfx->base.log, WLOG_TRACE,
89 "regionRects[%" PRIu32 "]: left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16
90 " bottom: %" PRIu16 "",
91 index, regionRect->left, regionRect->top, regionRect->right, regionRect->bottom);
92 }
93
94 if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, meta->numRegionRects, 2ull))
95 {
96 error = ERROR_INVALID_DATA;
97 goto error_out;
98 }
99
100 for (UINT32 index = 0; index < meta->numRegionRects; index++)
101 {
102 quantQualityVal = &(meta->quantQualityVals[index]);
103 Stream_Read_UINT8(s, quantQualityVal->qpVal); /* qpVal (1 byte) */
104 Stream_Read_UINT8(s, quantQualityVal->qualityVal); /* qualityVal (1 byte) */
105 quantQualityVal->qp = quantQualityVal->qpVal & 0x3F;
106 quantQualityVal->r = (quantQualityVal->qpVal >> 6) & 1;
107 quantQualityVal->p = (quantQualityVal->qpVal >> 7) & 1;
108 WLog_Print(gfx->base.log, WLOG_TRACE,
109 "quantQualityVals[%" PRIu32 "]: qp: %" PRIu8 " r: %" PRIu8 " p: %" PRIu8
110 " qualityVal: %" PRIu8 "",
111 index, quantQualityVal->qp, quantQualityVal->r, quantQualityVal->p,
112 quantQualityVal->qualityVal);
113 }
114
115 return CHANNEL_RC_OK;
116error_out:
117 free_h264_metablock(meta);
118 return error;
119}
120
121WINPR_ATTR_NODISCARD
122static BOOL checkSurfaceCommand(const RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
123{
124 switch (cmd->codecId)
125 {
126 case RDPGFX_CODECID_UNCOMPRESSED:
127 case RDPGFX_CODECID_CAVIDEO:
128 case RDPGFX_CODECID_CLEARCODEC:
129 case RDPGFX_CODECID_PLANAR:
130 case RDPGFX_CODECID_AVC420:
131 case RDPGFX_CODECID_ALPHA:
132 case RDPGFX_CODECID_AVC444:
133 case RDPGFX_CODECID_AVC444v2:
134 case RDPGFX_CODECID_CAPROGRESSIVE:
135 case RDPGFX_CODECID_CAPROGRESSIVE_V2:
136 return TRUE;
137#if defined(WITH_GFX_AV1)
138 case RDPGFX_CODECID_AV1:
139 if (gfx->ConnectionCaps.version != RDPGFX_CAPVERSION_FRDP_1)
140 {
141 WLog_Print(gfx->base.log, WLOG_ERROR,
142 "RDPGFX_SURFACE_COMMAND::codecId %" PRIu32
143 " only supported with %s but connection uses %s [0x%08" PRIx32 "]",
144 cmd->codecId, rdpgfx_caps_version_str(RDPGFX_CAPVERSION_FRDP_1),
145 rdpgfx_caps_version_str(gfx->ConnectionCaps.version),
146 gfx->ConnectionCaps.version);
147 return FALSE;
148 }
149 return TRUE;
150#endif
151 default:
152 WLog_Print(gfx->base.log, WLOG_ERROR,
153 "Unknown RDPGFX_SURFACE_COMMAND::codecId %" PRIu32, cmd->codecId);
154 return FALSE;
155 }
156}
157
158static UINT logSurfaceCommand(RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
159{
160 WINPR_ASSERT(gfx);
161
162 WLog_Print(gfx->base.log, WLOG_DEBUG, "Got GFX %s",
163 rdpgfx_get_codec_id_string(WINPR_ASSERTING_INT_CAST(UINT16, cmd->codecId)));
164
165 RdpgfxClientContext* context = gfx->context;
166 if (!context)
167 return CHANNEL_RC_OK;
168
169 if (!checkSurfaceCommand(gfx, cmd))
170 return CHANNEL_RC_NULL_DATA;
171
172 const UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->SurfaceCommand, context, cmd);
173
174 if (error)
175 WLog_Print(gfx->base.log, WLOG_ERROR,
176 "context->SurfaceCommand failed with error %" PRIu32 "", error);
177 return error;
178}
179
185static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
186{
187 RDPGFX_AVC420_BITMAP_STREAM h264 = WINPR_C_ARRAY_INIT;
188 wStream* s = Stream_New(cmd->data, cmd->length);
189
190 if (!s)
191 {
192 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
193 return CHANNEL_RC_NO_MEMORY;
194 }
195
196 UINT error = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta));
197 if (error != CHANNEL_RC_OK)
198 {
199 Stream_Free(s, FALSE);
200 WLog_Print(gfx->base.log, WLOG_ERROR,
201 "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
202 return error;
203 }
204
205 h264.data = Stream_Pointer(s);
206 h264.length = (UINT32)Stream_GetRemainingLength(s);
207 Stream_Free(s, FALSE);
208 cmd->extra = (void*)&h264;
209
210 error = logSurfaceCommand(gfx, cmd);
211
212 free_h264_metablock(&h264.meta);
213 cmd->extra = nullptr;
214 return error;
215}
216
222static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
223{
224 UINT error = CHANNEL_RC_OK;
225 UINT32 tmp = 0;
226 size_t pos1 = 0;
227 size_t pos2 = 0;
228
229 RDPGFX_AVC444_BITMAP_STREAM h264 = WINPR_C_ARRAY_INIT;
230 wStream* s = Stream_New(cmd->data, cmd->length);
231
232 if (!s)
233 {
234 WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
235 return CHANNEL_RC_NO_MEMORY;
236 }
237
238 if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 4))
239 {
240 error = ERROR_INVALID_DATA;
241 goto fail;
242 }
243
244 Stream_Read_UINT32(s, tmp);
245 h264.cbAvc420EncodedBitstream1 = tmp & 0x3FFFFFFFUL;
246 h264.LC = (tmp >> 30UL) & 0x03UL;
247
248 if (h264.LC == 0x03)
249 {
250 error = ERROR_INVALID_DATA;
251 goto fail;
252 }
253
254 pos1 = Stream_GetPosition(s);
255
256 if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[0].meta))))
257 {
258 WLog_Print(gfx->base.log, WLOG_ERROR,
259 "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
260 goto fail;
261 }
262
263 pos2 = Stream_GetPosition(s);
264 h264.bitstream[0].data = Stream_Pointer(s);
265
266 if (h264.LC == 0)
267 {
268 const size_t bitstreamLen = 1ULL * h264.cbAvc420EncodedBitstream1 - pos2 + pos1;
269
270 if ((bitstreamLen > UINT32_MAX) ||
271 !Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, bitstreamLen))
272 {
273 error = ERROR_INVALID_DATA;
274 goto fail;
275 }
276
277 h264.bitstream[0].length = (UINT32)bitstreamLen;
278 Stream_Seek(s, bitstreamLen);
279
280 if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[1].meta))))
281 {
282 WLog_Print(gfx->base.log, WLOG_ERROR,
283 "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
284 goto fail;
285 }
286
287 h264.bitstream[1].data = Stream_Pointer(s);
288
289 const size_t len = Stream_GetRemainingLength(s);
290 if (len > UINT32_MAX)
291 goto fail;
292 h264.bitstream[1].length = (UINT32)len;
293 }
294 else
295 {
296 const size_t len = Stream_GetRemainingLength(s);
297 if (len > UINT32_MAX)
298 goto fail;
299 h264.bitstream[0].length = (UINT32)len;
300 }
301
302 cmd->extra = (void*)&h264;
303
304 error = logSurfaceCommand(gfx, cmd);
305
306fail:
307 Stream_Free(s, FALSE);
308 free_h264_metablock(&h264.bitstream[0].meta);
309 free_h264_metablock(&h264.bitstream[1].meta);
310 cmd->extra = nullptr;
311 return error;
312}
313
319UINT rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
320{
321 UINT error = CHANNEL_RC_OK;
322 PROFILER_ENTER(context->SurfaceProfiler)
323
324 switch (cmd->codecId)
325 {
326#if defined(WITH_GFX_AV1)
327 case RDPGFX_CODECID_AV1:
328#endif
329 case RDPGFX_CODECID_AVC420:
330 if ((error = rdpgfx_decode_AVC420(gfx, cmd)))
331 WLog_Print(gfx->base.log, WLOG_ERROR,
332 "rdpgfx_decode_AVC420 failed with error %" PRIu32 "", error);
333
334 break;
335
336 case RDPGFX_CODECID_AVC444:
337 case RDPGFX_CODECID_AVC444v2:
338 if ((error = rdpgfx_decode_AVC444(gfx, cmd)))
339 WLog_Print(gfx->base.log, WLOG_ERROR,
340 "rdpgfx_decode_AVC444 failed with error %" PRIu32 "", error);
341
342 break;
343
344 default:
345 error = logSurfaceCommand(gfx, cmd);
346 break;
347 }
348
349 PROFILER_EXIT(context->SurfaceProfiler)
350 return error;
351}