Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/rdpgfx/client/rdpgfx_codec.c
Line
Count
Source (jump to first uncovered line)
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
#define TAG CHANNELS_TAG("rdpgfx.client")
34
35
/**
36
 * Function description
37
 *
38
 * @return 0 on success, otherwise a Win32 error code
39
 */
40
static UINT rdpgfx_read_h264_metablock(WINPR_ATTR_UNUSED RDPGFX_PLUGIN* gfx, wStream* s,
41
                                       RDPGFX_H264_METABLOCK* meta)
42
0
{
43
0
  RECTANGLE_16* regionRect = NULL;
44
0
  RDPGFX_H264_QUANT_QUALITY* quantQualityVal = NULL;
45
0
  UINT error = ERROR_INVALID_DATA;
46
0
  meta->regionRects = NULL;
47
0
  meta->quantQualityVals = NULL;
48
49
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
50
0
    goto error_out;
51
52
0
  Stream_Read_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */
53
54
0
  if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, meta->numRegionRects, 8ull))
55
0
    goto error_out;
56
57
0
  meta->regionRects = (RECTANGLE_16*)calloc(meta->numRegionRects, sizeof(RECTANGLE_16));
58
59
0
  if (!meta->regionRects)
60
0
  {
61
0
    WLog_ERR(TAG, "malloc failed!");
62
0
    error = CHANNEL_RC_NO_MEMORY;
63
0
    goto error_out;
64
0
  }
65
66
0
  meta->quantQualityVals =
67
0
      (RDPGFX_H264_QUANT_QUALITY*)calloc(meta->numRegionRects, sizeof(RDPGFX_H264_QUANT_QUALITY));
68
69
0
  if (!meta->quantQualityVals)
70
0
  {
71
0
    WLog_ERR(TAG, "malloc failed!");
72
0
    error = CHANNEL_RC_NO_MEMORY;
73
0
    goto error_out;
74
0
  }
75
76
0
  WLog_DBG(TAG, "H264_METABLOCK: numRegionRects: %" PRIu32 "", meta->numRegionRects);
77
78
0
  for (UINT32 index = 0; index < meta->numRegionRects; index++)
79
0
  {
80
0
    regionRect = &(meta->regionRects[index]);
81
82
0
    if ((error = rdpgfx_read_rect16(s, regionRect)))
83
0
    {
84
0
      WLog_ERR(TAG, "rdpgfx_read_rect16 failed with error %" PRIu32 "!", error);
85
0
      goto error_out;
86
0
    }
87
88
0
    WLog_DBG(TAG,
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_CheckAndLogRequiredLengthOfSize(TAG, 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_DBG(TAG,
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
/**
122
 * Function description
123
 *
124
 * @return 0 on success, otherwise a Win32 error code
125
 */
126
static UINT rdpgfx_decode_AVC420(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
127
0
{
128
0
  UINT error = 0;
129
0
  RDPGFX_AVC420_BITMAP_STREAM h264 = { 0 };
130
0
  RdpgfxClientContext* context = gfx->context;
131
0
  wStream* s = Stream_New(cmd->data, cmd->length);
132
133
0
  if (!s)
134
0
  {
135
0
    WLog_ERR(TAG, "Stream_New failed!");
136
0
    return CHANNEL_RC_NO_MEMORY;
137
0
  }
138
139
0
  if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.meta))))
140
0
  {
141
0
    Stream_Free(s, FALSE);
142
0
    WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
143
0
    return error;
144
0
  }
145
146
0
  h264.data = Stream_Pointer(s);
147
0
  h264.length = (UINT32)Stream_GetRemainingLength(s);
148
0
  Stream_Free(s, FALSE);
149
0
  cmd->extra = (void*)&h264;
150
151
0
  if (context)
152
0
  {
153
0
    IFCALLRET(context->SurfaceCommand, error, context, cmd);
154
155
0
    if (error)
156
0
      WLog_ERR(TAG, "context->SurfaceCommand failed with error %" PRIu32 "", error);
157
0
  }
158
159
0
  free_h264_metablock(&h264.meta);
160
0
  cmd->extra = NULL;
161
0
  return error;
162
0
}
163
164
/**
165
 * Function description
166
 *
167
 * @return 0 on success, otherwise a Win32 error code
168
 */
169
static UINT rdpgfx_decode_AVC444(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
170
0
{
171
0
  UINT error = 0;
172
0
  UINT32 tmp = 0;
173
0
  size_t pos1 = 0;
174
0
  size_t pos2 = 0;
175
176
0
  RDPGFX_AVC444_BITMAP_STREAM h264 = { 0 };
177
0
  RdpgfxClientContext* context = gfx->context;
178
0
  wStream* s = Stream_New(cmd->data, cmd->length);
179
180
0
  if (!s)
181
0
  {
182
0
    WLog_ERR(TAG, "Stream_New failed!");
183
0
    return CHANNEL_RC_NO_MEMORY;
184
0
  }
185
186
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
187
0
  {
188
0
    error = ERROR_INVALID_DATA;
189
0
    goto fail;
190
0
  }
191
192
0
  Stream_Read_UINT32(s, tmp);
193
0
  h264.cbAvc420EncodedBitstream1 = tmp & 0x3FFFFFFFUL;
194
0
  h264.LC = (tmp >> 30UL) & 0x03UL;
195
196
0
  if (h264.LC == 0x03)
197
0
  {
198
0
    error = ERROR_INVALID_DATA;
199
0
    goto fail;
200
0
  }
201
202
0
  pos1 = Stream_GetPosition(s);
203
204
0
  if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[0].meta))))
205
0
  {
206
0
    WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
207
0
    goto fail;
208
0
  }
209
210
0
  pos2 = Stream_GetPosition(s);
211
0
  h264.bitstream[0].data = Stream_Pointer(s);
212
213
0
  if (h264.LC == 0)
214
0
  {
215
0
    const size_t bitstreamLen = 1ULL * h264.cbAvc420EncodedBitstream1 - pos2 + pos1;
216
217
0
    if ((bitstreamLen > UINT32_MAX) || !Stream_CheckAndLogRequiredLength(TAG, s, bitstreamLen))
218
0
    {
219
0
      error = ERROR_INVALID_DATA;
220
0
      goto fail;
221
0
    }
222
223
0
    h264.bitstream[0].length = (UINT32)bitstreamLen;
224
0
    Stream_Seek(s, bitstreamLen);
225
226
0
    if ((error = rdpgfx_read_h264_metablock(gfx, s, &(h264.bitstream[1].meta))))
227
0
    {
228
0
      WLog_ERR(TAG, "rdpgfx_read_h264_metablock failed with error %" PRIu32 "!", error);
229
0
      goto fail;
230
0
    }
231
232
0
    h264.bitstream[1].data = Stream_Pointer(s);
233
234
0
    const size_t len = Stream_GetRemainingLength(s);
235
0
    if (len > UINT32_MAX)
236
0
      goto fail;
237
0
    h264.bitstream[1].length = (UINT32)len;
238
0
  }
239
0
  else
240
0
  {
241
0
    const size_t len = Stream_GetRemainingLength(s);
242
0
    if (len > UINT32_MAX)
243
0
      goto fail;
244
0
    h264.bitstream[0].length = (UINT32)len;
245
0
  }
246
247
0
  cmd->extra = (void*)&h264;
248
249
0
  if (context)
250
0
  {
251
0
    IFCALLRET(context->SurfaceCommand, error, context, cmd);
252
253
0
    if (error)
254
0
      WLog_ERR(TAG, "context->SurfaceCommand failed with error %" PRIu32 "", error);
255
0
  }
256
257
0
fail:
258
0
  Stream_Free(s, FALSE);
259
0
  free_h264_metablock(&h264.bitstream[0].meta);
260
0
  free_h264_metablock(&h264.bitstream[1].meta);
261
0
  cmd->extra = NULL;
262
0
  return error;
263
0
}
264
265
/**
266
 * Function description
267
 *
268
 * @return 0 on success, otherwise a Win32 error code
269
 */
270
UINT rdpgfx_decode(RDPGFX_PLUGIN* gfx, RDPGFX_SURFACE_COMMAND* cmd)
271
0
{
272
0
  UINT error = CHANNEL_RC_OK;
273
0
  RdpgfxClientContext* context = gfx->context;
274
0
  PROFILER_ENTER(context->SurfaceProfiler)
275
276
0
  switch (cmd->codecId)
277
0
  {
278
0
    case RDPGFX_CODECID_AVC420:
279
0
      if ((error = rdpgfx_decode_AVC420(gfx, cmd)))
280
0
        WLog_ERR(TAG, "rdpgfx_decode_AVC420 failed with error %" PRIu32 "", error);
281
282
0
      break;
283
284
0
    case RDPGFX_CODECID_AVC444:
285
0
    case RDPGFX_CODECID_AVC444v2:
286
0
      if ((error = rdpgfx_decode_AVC444(gfx, cmd)))
287
0
        WLog_ERR(TAG, "rdpgfx_decode_AVC444 failed with error %" PRIu32 "", error);
288
289
0
      break;
290
291
0
    default:
292
0
      if (context)
293
0
      {
294
0
        IFCALLRET(context->SurfaceCommand, error, context, cmd);
295
296
0
        if (error)
297
0
          WLog_ERR(TAG, "context->SurfaceCommand failed with error %" PRIu32 "", error);
298
0
      }
299
300
0
      break;
301
0
  }
302
303
0
  PROFILER_EXIT(context->SurfaceProfiler)
304
0
  return error;
305
0
}