Coverage Report

Created: 2026-04-12 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/rdpgfx/client/rdpgfx_codec.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Graphics Pipeline Extension
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2015 Thincast Technologies GmbH
7
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
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
33
/**
34
 * Function description
35
 *
36
 * @return 0 on success, otherwise a Win32 error code
37
 */
38
static UINT rdpgfx_read_h264_metablock(WINPR_ATTR_UNUSED RDPGFX_PLUGIN* gfx, wStream* s,
39
                                       RDPGFX_H264_METABLOCK* meta)
40
0
{
41
0
  RECTANGLE_16* regionRect = nullptr;
42
0
  RDPGFX_H264_QUANT_QUALITY* quantQualityVal = nullptr;
43
0
  UINT error = ERROR_INVALID_DATA;
44
0
  meta->regionRects = nullptr;
45
0
  meta->quantQualityVals = nullptr;
46
47
0
  if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 4))
48
0
    goto error_out;
49
50
0
  Stream_Read_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */
51
52
0
  if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, meta->numRegionRects, 8ull))
53
0
    goto error_out;
54
55
0
  meta->regionRects = (RECTANGLE_16*)calloc(meta->numRegionRects, sizeof(RECTANGLE_16));
56
57
0
  if (!meta->regionRects)
58
0
  {
59
0
    WLog_Print(gfx->base.log, WLOG_ERROR, "malloc failed!");
60
0
    error = CHANNEL_RC_NO_MEMORY;
61
0
    goto error_out;
62
0
  }
63
64
0
  meta->quantQualityVals =
65
0
      (RDPGFX_H264_QUANT_QUALITY*)calloc(meta->numRegionRects, sizeof(RDPGFX_H264_QUANT_QUALITY));
66
67
0
  if (!meta->quantQualityVals)
68
0
  {
69
0
    WLog_Print(gfx->base.log, WLOG_ERROR, "malloc failed!");
70
0
    error = CHANNEL_RC_NO_MEMORY;
71
0
    goto error_out;
72
0
  }
73
74
0
  WLog_Print(gfx->base.log, WLOG_TRACE, "H264_METABLOCK: numRegionRects: %" PRIu32 "",
75
0
             meta->numRegionRects);
76
77
0
  for (UINT32 index = 0; index < meta->numRegionRects; index++)
78
0
  {
79
0
    regionRect = &(meta->regionRects[index]);
80
81
0
    if ((error = rdpgfx_read_rect16(gfx->base.log, s, regionRect)))
82
0
    {
83
0
      WLog_Print(gfx->base.log, WLOG_ERROR,
84
0
                 "rdpgfx_read_rect16 failed with error %" PRIu32 "!", error);
85
0
      goto error_out;
86
0
    }
87
88
0
    WLog_Print(gfx->base.log, WLOG_TRACE,
89
0
               "regionRects[%" PRIu32 "]: left: %" PRIu16 " top: %" PRIu16 " right: %" PRIu16
90
0
               " bottom: %" PRIu16 "",
91
0
               index, regionRect->left, regionRect->top, regionRect->right, regionRect->bottom);
92
0
  }
93
94
0
  if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(gfx->base.log, s, meta->numRegionRects, 2ull))
95
0
  {
96
0
    error = ERROR_INVALID_DATA;
97
0
    goto error_out;
98
0
  }
99
100
0
  for (UINT32 index = 0; index < meta->numRegionRects; index++)
101
0
  {
102
0
    quantQualityVal = &(meta->quantQualityVals[index]);
103
0
    Stream_Read_UINT8(s, quantQualityVal->qpVal);      /* qpVal (1 byte) */
104
0
    Stream_Read_UINT8(s, quantQualityVal->qualityVal); /* qualityVal (1 byte) */
105
0
    quantQualityVal->qp = quantQualityVal->qpVal & 0x3F;
106
0
    quantQualityVal->r = (quantQualityVal->qpVal >> 6) & 1;
107
0
    quantQualityVal->p = (quantQualityVal->qpVal >> 7) & 1;
108
0
    WLog_Print(gfx->base.log, WLOG_TRACE,
109
0
               "quantQualityVals[%" PRIu32 "]: qp: %" PRIu8 " r: %" PRIu8 " p: %" PRIu8
110
0
               " qualityVal: %" PRIu8 "",
111
0
               index, quantQualityVal->qp, quantQualityVal->r, quantQualityVal->p,
112
0
               quantQualityVal->qualityVal);
113
0
  }
114
115
0
  return CHANNEL_RC_OK;
116
0
error_out:
117
0
  free_h264_metablock(meta);
118
0
  return error;
119
0
}
120
121
WINPR_ATTR_NODISCARD
122
static BOOL checkSurfaceCommand(const RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
123
0
{
124
0
  switch (cmd->codecId)
125
0
  {
126
0
    case RDPGFX_CODECID_UNCOMPRESSED:
127
0
    case RDPGFX_CODECID_CAVIDEO:
128
0
    case RDPGFX_CODECID_CLEARCODEC:
129
0
    case RDPGFX_CODECID_PLANAR:
130
0
    case RDPGFX_CODECID_AVC420:
131
0
    case RDPGFX_CODECID_ALPHA:
132
0
    case RDPGFX_CODECID_AVC444:
133
0
    case RDPGFX_CODECID_AVC444v2:
134
0
    case RDPGFX_CODECID_CAPROGRESSIVE:
135
0
    case RDPGFX_CODECID_CAPROGRESSIVE_V2:
136
0
      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
0
    default:
152
0
      WLog_Print(gfx->base.log, WLOG_ERROR,
153
0
                 "Unknown RDPGFX_SURFACE_COMMAND::codecId %" PRIu32, cmd->codecId);
154
0
      return FALSE;
155
0
  }
156
0
}
157
158
static UINT logSurfaceCommand(RDPGFX_PLUGIN* gfx, const RDPGFX_SURFACE_COMMAND* cmd)
159
0
{
160
0
  WINPR_ASSERT(gfx);
161
162
0
  WLog_Print(gfx->base.log, WLOG_DEBUG, "Got GFX %s",
163
0
             rdpgfx_get_codec_id_string(WINPR_ASSERTING_INT_CAST(UINT16, cmd->codecId)));
164
165
0
  RdpgfxClientContext* context = gfx->context;
166
0
  if (!context)
167
0
    return CHANNEL_RC_OK;
168
169
0
  if (!checkSurfaceCommand(gfx, cmd))
170
0
    return CHANNEL_RC_NULL_DATA;
171
172
0
  const UINT error = IFCALLRESULT(CHANNEL_RC_OK, context->SurfaceCommand, context, cmd);
173
174
0
  if (error)
175
0
    WLog_Print(gfx->base.log, WLOG_ERROR,
176
0
               "context->SurfaceCommand failed with error %" PRIu32 "", error);
177
0
  return error;
178
0
}
179
180
/**
181
 * Function description
182
 *
183
 * @return 0 on success, otherwise a Win32 error code
184
 */
185
static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
186
0
{
187
0
  RDPGFX_AVC420_BITMAP_STREAM h264 = WINPR_C_ARRAY_INIT;
188
0
  wStream* s = Stream_New(cmd->data, cmd->length);
189
190
0
  if (!s)
191
0
  {
192
0
    WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
193
0
    return CHANNEL_RC_NO_MEMORY;
194
0
  }
195
196
0
  UINT error = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta));
197
0
  if (error != CHANNEL_RC_OK)
198
0
  {
199
0
    Stream_Free(s, FALSE);
200
0
    WLog_Print(gfx->base.log, WLOG_ERROR,
201
0
               "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
202
0
    return error;
203
0
  }
204
205
0
  h264.data = Stream_Pointer(s);
206
0
  h264.length = (UINT32)Stream_GetRemainingLength(s);
207
0
  Stream_Free(s, FALSE);
208
0
  cmd->extra = (void*)&h264;
209
210
0
  error = logSurfaceCommand(gfx, cmd);
211
212
0
  free_h264_metablock(&h264.meta);
213
0
  cmd->extra = nullptr;
214
0
  return error;
215
0
}
216
217
/**
218
 * Function description
219
 *
220
 * @return 0 on success, otherwise a Win32 error code
221
 */
222
static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
223
0
{
224
0
  UINT error = CHANNEL_RC_OK;
225
0
  UINT32 tmp = 0;
226
0
  size_t pos1 = 0;
227
0
  size_t pos2 = 0;
228
229
0
  RDPGFX_AVC444_BITMAP_STREAM h264 = WINPR_C_ARRAY_INIT;
230
0
  wStream* s = Stream_New(cmd->data, cmd->length);
231
232
0
  if (!s)
233
0
  {
234
0
    WLog_Print(gfx->base.log, WLOG_ERROR, "Stream_New failed!");
235
0
    return CHANNEL_RC_NO_MEMORY;
236
0
  }
237
238
0
  if (!Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, 4))
239
0
  {
240
0
    error = ERROR_INVALID_DATA;
241
0
    goto fail;
242
0
  }
243
244
0
  Stream_Read_UINT32(s, tmp);
245
0
  h264.cbAvc420EncodedBitstream1 = tmp & 0x3FFFFFFFUL;
246
0
  h264.LC = (tmp >> 30UL) & 0x03UL;
247
248
0
  if (h264.LC == 0x03)
249
0
  {
250
0
    error = ERROR_INVALID_DATA;
251
0
    goto fail;
252
0
  }
253
254
0
  pos1 = Stream_GetPosition(s);
255
256
0
  if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[0].meta))))
257
0
  {
258
0
    WLog_Print(gfx->base.log, WLOG_ERROR,
259
0
               "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
260
0
    goto fail;
261
0
  }
262
263
0
  pos2 = Stream_GetPosition(s);
264
0
  h264.bitstream[0].data = Stream_Pointer(s);
265
266
0
  if (h264.LC == 0)
267
0
  {
268
0
    const size_t bitstreamLen = 1ULL * h264.cbAvc420EncodedBitstream1 - pos2 + pos1;
269
270
0
    if ((bitstreamLen > UINT32_MAX) ||
271
0
        !Stream_CheckAndLogRequiredLengthWLog(gfx->base.log, s, bitstreamLen))
272
0
    {
273
0
      error = ERROR_INVALID_DATA;
274
0
      goto fail;
275
0
    }
276
277
0
    h264.bitstream[0].length = (UINT32)bitstreamLen;
278
0
    Stream_Seek(s, bitstreamLen);
279
280
0
    if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[1].meta))))
281
0
    {
282
0
      WLog_Print(gfx->base.log, WLOG_ERROR,
283
0
                 "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
284
0
      goto fail;
285
0
    }
286
287
0
    h264.bitstream[1].data = Stream_Pointer(s);
288
289
0
    const size_t len = Stream_GetRemainingLength(s);
290
0
    if (len > UINT32_MAX)
291
0
      goto fail;
292
0
    h264.bitstream[1].length = (UINT32)len;
293
0
  }
294
0
  else
295
0
  {
296
0
    const size_t len = Stream_GetRemainingLength(s);
297
0
    if (len > UINT32_MAX)
298
0
      goto fail;
299
0
    h264.bitstream[0].length = (UINT32)len;
300
0
  }
301
302
0
  cmd->extra = (void*)&h264;
303
304
0
  error = logSurfaceCommand(gfx, cmd);
305
306
0
fail:
307
0
  Stream_Free(s, FALSE);
308
0
  free_h264_metablock(&h264.bitstream[0].meta);
309
0
  free_h264_metablock(&h264.bitstream[1].meta);
310
0
  cmd->extra = nullptr;
311
0
  return error;
312
0
}
313
314
/**
315
 * Function description
316
 *
317
 * @return 0 on success, otherwise a Win32 error code
318
 */
319
UINT rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
320
0
{
321
0
  UINT error = CHANNEL_RC_OK;
322
0
  PROFILER_ENTER(context->SurfaceProfiler)
323
324
0
  switch (cmd->codecId)
325
0
  {
326
#if defined(WITH_GFX_AV1)
327
    case RDPGFX_CODECID_AV1:
328
#endif
329
0
    case RDPGFX_CODECID_AVC420:
330
0
      if ((error = rdpgfx_decode_AVC420(gfx, cmd)))
331
0
        WLog_Print(gfx->base.log, WLOG_ERROR,
332
0
                   "rdpgfx_decode_AVC420 failed with error %" PRIu32 "", error);
333
334
0
      break;
335
336
0
    case RDPGFX_CODECID_AVC444:
337
0
    case RDPGFX_CODECID_AVC444v2:
338
0
      if ((error = rdpgfx_decode_AVC444(gfx, cmd)))
339
0
        WLog_Print(gfx->base.log, WLOG_ERROR,
340
0
                   "rdpgfx_decode_AVC444 failed with error %" PRIu32 "", error);
341
342
0
      break;
343
344
0
    default:
345
0
      error = logSurfaceCommand(gfx, cmd);
346
0
      break;
347
0
  }
348
349
0
  PROFILER_EXIT(context->SurfaceProfiler)
350
0
  return error;
351
0
}