Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,454 @@
#include <freerdp/config.h>
#include <math.h>
#include <winpr/crt.h>
#include <winpr/print.h>
#include <winpr/json.h>
#include <winpr/path.h>
#include <freerdp/freerdp.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/bitmap.h>
#include <freerdp/codec/interleaved.h>
#include <winpr/crypto.h>
#include <freerdp/utils/profiler.h>
#include "TestFreeRDPHelpers.h"
// #define CREATE_TEST_OUTPUT
static bool run_encode_decode_single(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder,
BITMAP_INTERLEAVED_CONTEXT* decoder
#if defined(WITH_PROFILER)
,
PROFILER* profiler_comp, PROFILER* profiler_decomp
#endif
)
{
bool rc2 = false;
bool rc = 0;
const UINT32 w = 64;
const UINT32 h = 64;
const UINT32 x = 0;
const UINT32 y = 0;
const UINT32 format = PIXEL_FORMAT_RGBX32;
const UINT32 bstep = FreeRDPGetBytesPerPixel(format);
const size_t step = (13ULL + w) * 4ULL;
const size_t SrcSize = step * h;
const int maxDiff = 4 * ((bpp < 24) ? 2 : 1);
UINT32 DstSize = SrcSize;
BYTE* pSrcData = calloc(1, SrcSize);
BYTE* pDstData = calloc(1, SrcSize);
BYTE* tmp = calloc(1, SrcSize);
if (!pSrcData || !pDstData || !tmp)
goto fail;
if (winpr_RAND(pSrcData, SrcSize) < 0)
goto fail;
if (!bitmap_interleaved_context_reset(encoder) || !bitmap_interleaved_context_reset(decoder))
goto fail;
PROFILER_ENTER(profiler_comp)
rc = interleaved_compress(encoder, tmp, &DstSize, w, h, pSrcData, format, step, x, y, nullptr,
bpp);
PROFILER_EXIT(profiler_comp)
if (!rc)
goto fail;
PROFILER_ENTER(profiler_decomp)
rc = interleaved_decompress(decoder, tmp, DstSize, w, h, bpp, pDstData, format, step, x, y, w,
h, nullptr);
PROFILER_EXIT(profiler_decomp)
if (!rc)
goto fail;
for (UINT32 i = 0; i < h; i++)
{
const BYTE* srcLine = &pSrcData[i * step];
const BYTE* dstLine = &pDstData[i * step];
for (UINT32 j = 0; j < w; j++)
{
BYTE r = 0;
BYTE g = 0;
BYTE b = 0;
BYTE dr = 0;
BYTE dg = 0;
BYTE db = 0;
const UINT32 srcColor = FreeRDPReadColor(&srcLine[1ULL * j * bstep], format);
const UINT32 dstColor = FreeRDPReadColor(&dstLine[1ULL * j * bstep], format);
FreeRDPSplitColor(srcColor, format, &r, &g, &b, nullptr, nullptr);
FreeRDPSplitColor(dstColor, format, &dr, &dg, &db, nullptr, nullptr);
if (abs(r - dr) > maxDiff)
goto fail;
if (abs(g - dg) > maxDiff)
goto fail;
if (abs(b - db) > maxDiff)
goto fail;
}
}
rc2 = true;
fail:
free(pSrcData);
free(pDstData);
free(tmp);
return rc2;
}
static const char* get_profiler_name(bool encode, UINT16 bpp)
{
switch (bpp)
{
case 24:
if (encode)
return "interleaved_compress 24bpp";
else
return "interleaved_decompress 24bpp";
case 16:
if (encode)
return "interleaved_compress 16bpp";
else
return "interleaved_decompress 16bpp";
case 15:
if (encode)
return "interleaved_compress 15bpp";
else
return "interleaved_decompress 15bpp";
default:
return "configuration error!";
}
}
static bool run_encode_decode(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder,
BITMAP_INTERLEAVED_CONTEXT* decoder)
{
bool rc = false;
PROFILER_DEFINE(profiler_comp)
PROFILER_DEFINE(profiler_decomp)
PROFILER_CREATE(profiler_comp, get_profiler_name(true, bpp))
PROFILER_CREATE(profiler_decomp, get_profiler_name(false, bpp))
for (UINT32 x = 0; x < 50; x++)
{
if (!run_encode_decode_single(bpp, encoder, decoder
#if defined(WITH_PROFILER)
,
profiler_comp, profiler_decomp
#endif
))
goto fail;
}
rc = true;
fail:
PROFILER_PRINT_HEADER
PROFILER_PRINT(profiler_comp)
PROFILER_PRINT(profiler_decomp)
PROFILER_PRINT_FOOTER
PROFILER_FREE(profiler_comp)
PROFILER_FREE(profiler_decomp)
return rc;
}
static bool TestColorConversion(void)
{
const UINT32 formats[] = { PIXEL_FORMAT_RGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_ABGR15,
PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_RGB16 };
/* Check color conversion 15/16 -> 32bit maps to proper values */
for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
{
const UINT32 dstFormat = PIXEL_FORMAT_RGBA32;
const UINT32 format = formats[x];
const UINT32 colorLow = FreeRDPGetColor(format, 0, 0, 0, 255);
const UINT32 colorHigh = FreeRDPGetColor(format, 255, 255, 255, 255);
const UINT32 colorLow32 = FreeRDPConvertColor(colorLow, format, dstFormat, nullptr);
const UINT32 colorHigh32 = FreeRDPConvertColor(colorHigh, format, dstFormat, nullptr);
BYTE r = 0;
BYTE g = 0;
BYTE b = 0;
BYTE a = 0;
FreeRDPSplitColor(colorLow32, dstFormat, &r, &g, &b, &a, nullptr);
if ((r != 0) || (g != 0) || (b != 0))
return false;
FreeRDPSplitColor(colorHigh32, dstFormat, &r, &g, &b, &a, nullptr);
if ((r != 255) || (g != 255) || (b != 255))
return false;
}
return true;
}
static bool RunEncoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height,
uint32_t step, uint32_t bpp)
{
bool rc = false;
void* data = nullptr;
void* encdata = nullptr;
BITMAP_INTERLEAVED_CONTEXT* encoder = bitmap_interleaved_context_new(true);
if (!encoder)
goto fail;
size_t srclen = 0;
data = test_codec_helper_read_data("interleaved", "bmp", name, &srclen);
if (!data)
goto fail;
encdata = calloc(srclen, 1);
if (!encdata)
goto fail;
for (size_t x = 0; x < 42; x++)
{
uint32_t enclen = WINPR_ASSERTING_INT_CAST(uint32_t, srclen);
if (!interleaved_compress(encoder, encdata, &enclen, width, height, data, format, step, 0,
0, nullptr, bpp))
goto fail;
char encname[128] = WINPR_C_ARRAY_INIT;
(void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp);
#if defined(CREATE_TEST_OUTPUT)
test_codec_helper_write_data("interleaved", encname, name, encdata, enclen);
#else
if (!test_codec_helper_compare("interleaved", encname, name, encdata, enclen))
goto fail;
#endif
}
rc = true;
fail:
free(data);
free(encdata);
bitmap_interleaved_context_free(encoder);
return rc;
}
static bool RunDecoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height,
uint32_t step, uint32_t bpp)
{
bool rc = false;
void* data = nullptr;
void* decdata = nullptr;
BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(false);
if (!decoder)
goto fail;
char encname[128] = WINPR_C_ARRAY_INIT;
(void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp);
size_t srclen = 0;
data = test_codec_helper_read_data("interleaved", encname, name, &srclen);
if (!data)
goto fail;
const size_t declen = 1ULL * step * height;
decdata = calloc(step, height);
if (!decdata)
goto fail;
for (size_t x = 0; x < 42; x++)
{
if (!interleaved_decompress(decoder, data, WINPR_ASSERTING_INT_CAST(uint32_t, srclen),
width, height, bpp, decdata, format, step, 0, 0, width, height,
nullptr))
goto fail;
char decname[128] = WINPR_C_ARRAY_INIT;
(void)_snprintf(decname, sizeof(decname), "dec-%s", encname);
#if defined(CREATE_TEST_OUTPUT)
test_codec_helper_write_data("interleaved", decname, name, decdata, declen);
#else
if (!test_codec_helper_compare("interleaved", decname, name, decdata, declen))
goto fail;
#endif
}
rc = true;
fail:
free(data);
free(decdata);
bitmap_interleaved_context_free(decoder);
return rc;
}
/* The encoder expects a JSON that describes a test cast:
*
* [
* {
* "name": "somestring",
* "format": "somestring",
* "width": <someint>,
* "height": <someint>,
* "step": <someint>,
* "bpp": <someint>
* },
* {...},
* ...
* ]
*/
static bool isObjectValid(const WINPR_JSON* obj)
{
if (!obj || !WINPR_JSON_IsObject(obj))
return false;
const char* strvalues[] = { "name", "format" };
for (size_t x = 0; x < ARRAYSIZE(strvalues); x++)
{
const char* val = strvalues[x];
if (!WINPR_JSON_HasObjectItem(obj, val))
return false;
WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
if (!jval)
return false;
if (!WINPR_JSON_IsString(jval))
return false;
}
const char* values[] = { "width", "height", "step" };
for (size_t x = 0; x < ARRAYSIZE(values); x++)
{
const char* val = values[x];
if (!WINPR_JSON_HasObjectItem(obj, val))
return false;
WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
if (!jval)
return false;
if (!WINPR_JSON_IsNumber(jval))
return false;
const double dval = WINPR_JSON_GetNumberValue(jval);
if (dval <= 0.0)
return false;
}
{
const char* val = "bpp";
if (!WINPR_JSON_HasObjectItem(obj, val))
return false;
WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
if (!jval)
return false;
if (!WINPR_JSON_IsArray(jval))
return false;
for (size_t x = 0; x < WINPR_JSON_GetArraySize(jval); x++)
{
WINPR_JSON* aval = WINPR_JSON_GetArrayItem(jval, x);
if (!jval || !WINPR_JSON_IsNumber(aval))
return false;
}
}
return true;
}
static bool TestEncoder(void)
{
bool rc = false;
WINPR_JSON* json = nullptr;
char* file = nullptr;
char* path = GetCombinedPath(CMAKE_CURRENT_SOURCE_DIR, "interleaved");
if (!path)
goto fail;
file = GetCombinedPath(path, "encoder.json");
if (!file)
goto fail;
json = WINPR_JSON_ParseFromFile(file);
if (!json)
goto fail;
if (!WINPR_JSON_IsArray(json))
goto fail;
for (size_t x = 0; x < WINPR_JSON_GetArraySize(json); x++)
{
WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x);
if (!isObjectValid(obj))
goto fail;
const char* name = WINPR_JSON_GetStringValue(WINPR_JSON_GetObjectItem(obj, "name"));
const uint32_t format = WINPR_ASSERTING_INT_CAST(
uint32_t, FreeRDPGetColorFromatFromName(
WINPR_JSON_GetStringValue(WINPR_JSON_GetObjectItem(obj, "format"))));
const uint32_t width = WINPR_ASSERTING_INT_CAST(
uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "width")));
const uint32_t height = WINPR_ASSERTING_INT_CAST(
uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "height")));
const uint32_t step = WINPR_ASSERTING_INT_CAST(
uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "step")));
WINPR_JSON* jbpp = WINPR_JSON_GetObjectItem(obj, "bpp");
for (size_t x = 0; x < WINPR_JSON_GetArraySize(jbpp); x++)
{
const uint32_t bpp = WINPR_ASSERTING_INT_CAST(
uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetArrayItem(jbpp, x)));
if (!RunEncoderTest(name, format, width, height, step, bpp))
goto fail;
if (!RunDecoderTest(name, format, width, height, step, bpp))
goto fail;
}
}
rc = true;
fail:
WINPR_JSON_Delete(json);
free(path);
free(file);
return rc;
}
int TestFreeRDPCodecInterleaved(int argc, char* argv[])
{
BITMAP_INTERLEAVED_CONTEXT* encoder = nullptr;
BITMAP_INTERLEAVED_CONTEXT* decoder = nullptr;
int rc = -1;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
encoder = bitmap_interleaved_context_new(true);
decoder = bitmap_interleaved_context_new(false);
if (!encoder || !decoder)
goto fail;
if (!run_encode_decode(24, encoder, decoder))
goto fail;
if (!run_encode_decode(16, encoder, decoder))
goto fail;
if (!run_encode_decode(15, encoder, decoder))
goto fail;
if (!TestColorConversion())
goto fail;
if (!TestEncoder())
goto fail;
rc = 0;
fail:
bitmap_interleaved_context_free(encoder);
bitmap_interleaved_context_free(decoder);
return rc;
}