diff options
Diffstat (limited to 'libfreerdp/codec/yuv.c')
-rw-r--r-- | libfreerdp/codec/yuv.c | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/libfreerdp/codec/yuv.c b/libfreerdp/codec/yuv.c new file mode 100644 index 0000000..c546566 --- /dev/null +++ b/libfreerdp/codec/yuv.c @@ -0,0 +1,887 @@ +#include <winpr/sysinfo.h> +#include <winpr/assert.h> +#include <winpr/pool.h> + +#include <freerdp/settings.h> +#include <freerdp/codec/region.h> +#include <freerdp/primitives.h> +#include <freerdp/log.h> +#include <freerdp/codec/yuv.h> + +#define TAG FREERDP_TAG("codec") + +#define TILE_SIZE 64 + +typedef struct +{ + YUV_CONTEXT* context; + const BYTE* pYUVData[3]; + UINT32 iStride[3]; + DWORD DstFormat; + BYTE* dest; + UINT32 nDstStep; + RECTANGLE_16 rect; +} YUV_PROCESS_WORK_PARAM; + +typedef struct +{ + YUV_CONTEXT* context; + const BYTE* pYUVData[3]; + UINT32 iStride[3]; + BYTE* pYUVDstData[3]; + UINT32 iDstStride[3]; + RECTANGLE_16 rect; + BYTE type; +} YUV_COMBINE_WORK_PARAM; + +typedef struct +{ + YUV_CONTEXT* context; + const BYTE* pSrcData; + + DWORD SrcFormat; + UINT32 nSrcStep; + RECTANGLE_16 rect; + BYTE version; + + BYTE* pYUVLumaData[3]; + BYTE* pYUVChromaData[3]; + UINT32 iStride[3]; +} YUV_ENCODE_WORK_PARAM; + +struct S_YUV_CONTEXT +{ + UINT32 width, height; + BOOL useThreads; + BOOL encoder; + UINT32 nthreads; + UINT32 heightStep; + + PTP_POOL threadPool; + TP_CALLBACK_ENVIRON ThreadPoolEnv; + + UINT32 work_object_count; + PTP_WORK* work_objects; + YUV_ENCODE_WORK_PARAM* work_enc_params; + YUV_PROCESS_WORK_PARAM* work_dec_params; + YUV_COMBINE_WORK_PARAM* work_combined_params; +}; + +static INLINE BOOL avc420_yuv_to_rgb(const BYTE* pYUVData[3], const UINT32 iStride[3], + const RECTANGLE_16* rect, UINT32 nDstStep, BYTE* pDstData, + DWORD DstFormat) +{ + primitives_t* prims = primitives_get(); + prim_size_t roi; + const BYTE* pYUVPoint[3]; + + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(rect); + WINPR_ASSERT(pDstData); + + const INT32 width = rect->right - rect->left; + const INT32 height = rect->bottom - rect->top; + BYTE* pDstPoint = + pDstData + rect->top * nDstStep + rect->left * FreeRDPGetBytesPerPixel(DstFormat); + + pYUVPoint[0] = pYUVData[0] + rect->top * iStride[0] + rect->left; + pYUVPoint[1] = pYUVData[1] + rect->top / 2 * iStride[1] + rect->left / 2; + pYUVPoint[2] = pYUVData[2] + rect->top / 2 * iStride[2] + rect->left / 2; + + roi.width = width; + roi.height = height; + + if (prims->YUV420ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat, &roi) != + PRIMITIVES_SUCCESS) + return FALSE; + + return TRUE; +} + +static INLINE BOOL avc444_yuv_to_rgb(const BYTE* pYUVData[3], const UINT32 iStride[3], + const RECTANGLE_16* rect, UINT32 nDstStep, BYTE* pDstData, + DWORD DstFormat) +{ + primitives_t* prims = primitives_get(); + prim_size_t roi; + const BYTE* pYUVPoint[3]; + + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(rect); + WINPR_ASSERT(pDstData); + + const INT32 width = rect->right - rect->left; + const INT32 height = rect->bottom - rect->top; + BYTE* pDstPoint = + pDstData + rect->top * nDstStep + rect->left * FreeRDPGetBytesPerPixel(DstFormat); + + pYUVPoint[0] = pYUVData[0] + rect->top * iStride[0] + rect->left; + pYUVPoint[1] = pYUVData[1] + rect->top * iStride[1] + rect->left; + pYUVPoint[2] = pYUVData[2] + rect->top * iStride[2] + rect->left; + + roi.width = width; + roi.height = height; + + if (prims->YUV444ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat, &roi) != + PRIMITIVES_SUCCESS) + return FALSE; + + return TRUE; +} + +static void CALLBACK yuv420_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context; + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + WINPR_ASSERT(param); + + if (!avc420_yuv_to_rgb(param->pYUVData, param->iStride, ¶m->rect, param->nDstStep, + param->dest, param->DstFormat)) + WLog_WARN(TAG, "avc420_yuv_to_rgb failed"); +} + +static void CALLBACK yuv444_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context; + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + WINPR_ASSERT(param); + + if (!avc444_yuv_to_rgb(param->pYUVData, param->iStride, ¶m->rect, param->nDstStep, + param->dest, param->DstFormat)) + WLog_WARN(TAG, "avc444_yuv_to_rgb failed"); +} + +BOOL yuv_context_reset(YUV_CONTEXT* context, UINT32 width, UINT32 height) +{ + BOOL rc = FALSE; + WINPR_ASSERT(context); + + context->width = width; + context->height = height; + context->heightStep = (height / context->nthreads); + + if (context->useThreads) + { + const UINT32 pw = (width + TILE_SIZE - width % TILE_SIZE) / TILE_SIZE; + const UINT32 ph = (height + TILE_SIZE - height % TILE_SIZE) / TILE_SIZE; + + /* We´ve calculated the amount of workers for 64x64 tiles, but the decoder + * might get 16x16 tiles mixed in. */ + const UINT32 count = pw * ph * 16; + + context->work_object_count = 0; + if (context->encoder) + { + void* tmp = winpr_aligned_recalloc(context->work_enc_params, count, + sizeof(YUV_ENCODE_WORK_PARAM), 32); + if (!tmp) + goto fail; + memset(tmp, 0, count * sizeof(YUV_ENCODE_WORK_PARAM)); + + context->work_enc_params = tmp; + } + else + { + void* tmp = winpr_aligned_recalloc(context->work_dec_params, count, + sizeof(YUV_PROCESS_WORK_PARAM), 32); + if (!tmp) + goto fail; + memset(tmp, 0, count * sizeof(YUV_PROCESS_WORK_PARAM)); + + context->work_dec_params = tmp; + + void* ctmp = winpr_aligned_recalloc(context->work_combined_params, count, + sizeof(YUV_COMBINE_WORK_PARAM), 32); + if (!ctmp) + goto fail; + memset(ctmp, 0, count * sizeof(YUV_COMBINE_WORK_PARAM)); + + context->work_combined_params = ctmp; + } + + void* wtmp = winpr_aligned_recalloc(context->work_objects, count, sizeof(PTP_WORK), 32); + if (!wtmp) + goto fail; + memset(wtmp, 0, count * sizeof(PTP_WORK)); + + context->work_objects = wtmp; + context->work_object_count = count; + } + rc = TRUE; +fail: + return rc; +} + +YUV_CONTEXT* yuv_context_new(BOOL encoder, UINT32 ThreadingFlags) +{ + SYSTEM_INFO sysInfos; + YUV_CONTEXT* ret = winpr_aligned_calloc(1, sizeof(*ret), 32); + if (!ret) + return NULL; + + /** do it here to avoid a race condition between threads */ + primitives_get(); + + ret->encoder = encoder; + ret->nthreads = 1; + if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS)) + { + GetNativeSystemInfo(&sysInfos); + ret->useThreads = (sysInfos.dwNumberOfProcessors > 1); + if (ret->useThreads) + { + ret->nthreads = sysInfos.dwNumberOfProcessors; + ret->threadPool = CreateThreadpool(NULL); + if (!ret->threadPool) + { + goto error_threadpool; + } + + InitializeThreadpoolEnvironment(&ret->ThreadPoolEnv); + SetThreadpoolCallbackPool(&ret->ThreadPoolEnv, ret->threadPool); + } + } + + return ret; + +error_threadpool: + yuv_context_free(ret); + return NULL; +} + +void yuv_context_free(YUV_CONTEXT* context) +{ + if (!context) + return; + if (context->useThreads) + { + if (context->threadPool) + CloseThreadpool(context->threadPool); + DestroyThreadpoolEnvironment(&context->ThreadPoolEnv); + winpr_aligned_free(context->work_objects); + winpr_aligned_free(context->work_combined_params); + winpr_aligned_free(context->work_enc_params); + winpr_aligned_free(context->work_dec_params); + } + winpr_aligned_free(context); +} + +static INLINE YUV_PROCESS_WORK_PARAM pool_decode_param(const RECTANGLE_16* rect, + YUV_CONTEXT* context, + const BYTE* pYUVData[3], + const UINT32 iStride[3], UINT32 DstFormat, + BYTE* dest, UINT32 nDstStep) +{ + YUV_PROCESS_WORK_PARAM current = { 0 }; + + WINPR_ASSERT(rect); + WINPR_ASSERT(context); + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(dest); + + current.context = context; + current.DstFormat = DstFormat; + current.pYUVData[0] = pYUVData[0]; + current.pYUVData[1] = pYUVData[1]; + current.pYUVData[2] = pYUVData[2]; + current.iStride[0] = iStride[0]; + current.iStride[1] = iStride[1]; + current.iStride[2] = iStride[2]; + current.nDstStep = nDstStep; + current.dest = dest; + current.rect = *rect; + return current; +} + +static BOOL submit_object(PTP_WORK* work_object, PTP_WORK_CALLBACK cb, const void* param, + YUV_CONTEXT* context) +{ + union + { + const void* cpv; + void* pv; + } cnv; + + cnv.cpv = param; + + if (!work_object) + return FALSE; + + *work_object = NULL; + + if (!param || !context) + return FALSE; + + *work_object = CreateThreadpoolWork(cb, cnv.pv, &context->ThreadPoolEnv); + if (!*work_object) + return FALSE; + + SubmitThreadpoolWork(*work_object); + return TRUE; +} + +static void free_objects(PTP_WORK* work_objects, UINT32 waitCount) +{ + WINPR_ASSERT(work_objects || (waitCount == 0)); + + for (UINT32 i = 0; i < waitCount; i++) + { + PTP_WORK cur = work_objects[i]; + work_objects[i] = NULL; + + if (!cur) + continue; + + WaitForThreadpoolWorkCallbacks(cur, FALSE); + CloseThreadpoolWork(cur); + } +} + +static BOOL intersects(UINT32 pos, const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + WINPR_ASSERT(regionRects || (numRegionRects == 0)); + + for (UINT32 x = pos + 1; x < numRegionRects; x++) + { + const RECTANGLE_16* what = ®ionRects[pos]; + const RECTANGLE_16* rect = ®ionRects[x]; + + if (rectangles_intersects(what, rect)) + { + WLog_WARN(TAG, "YUV decoder: intersecting rectangles, aborting"); + return TRUE; + } + } + + return FALSE; +} + +static RECTANGLE_16 clamp(YUV_CONTEXT* context, const RECTANGLE_16* rect, UINT32 srcHeight) +{ + WINPR_ASSERT(context); + WINPR_ASSERT(rect); + + RECTANGLE_16 c = *rect; + const UINT32 height = MIN(context->height, srcHeight); + if (c.top > height) + c.top = height; + if (c.bottom > height) + c.bottom = height; + return c; +} + +static BOOL pool_decode(YUV_CONTEXT* context, PTP_WORK_CALLBACK cb, const BYTE* pYUVData[3], + const UINT32 iStride[3], UINT32 yuvHeight, UINT32 DstFormat, BYTE* dest, + UINT32 nDstStep, const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + BOOL rc = FALSE; + UINT32 waitCount = 0; + primitives_t* prims = primitives_get(); + + WINPR_ASSERT(context); + WINPR_ASSERT(cb); + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(dest); + WINPR_ASSERT(regionRects || (numRegionRects == 0)); + + if (context->encoder) + { + WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting"); + return FALSE; + } + + if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU)) + { + for (UINT32 y = 0; y < numRegionRects; y++) + { + const RECTANGLE_16 rect = clamp(context, ®ionRects[y], yuvHeight); + YUV_PROCESS_WORK_PARAM current = + pool_decode_param(&rect, context, pYUVData, iStride, DstFormat, dest, nDstStep); + cb(NULL, ¤t, NULL); + } + return TRUE; + } + + /* case where we use threads */ + for (UINT32 x = 0; x < numRegionRects; x++) + { + RECTANGLE_16 r = clamp(context, ®ionRects[x], yuvHeight); + + if (intersects(x, regionRects, numRegionRects)) + continue; + + while (r.left < r.right) + { + RECTANGLE_16 y = r; + y.right = MIN(r.right, r.left + TILE_SIZE); + + while (y.top < y.bottom) + { + RECTANGLE_16 z = y; + + if (context->work_object_count <= waitCount) + { + WLog_ERR(TAG, + "YUV decoder: invalid number of tiles, only support less than %" PRIu32 + ", got %" PRIu32, + context->work_object_count, waitCount); + goto fail; + } + + YUV_PROCESS_WORK_PARAM* cur = &context->work_dec_params[waitCount]; + z.bottom = MIN(z.bottom, z.top + TILE_SIZE); + if (rectangle_is_empty(&z)) + continue; + *cur = pool_decode_param(&z, context, pYUVData, iStride, DstFormat, dest, nDstStep); + if (!submit_object(&context->work_objects[waitCount], cb, cur, context)) + goto fail; + waitCount++; + y.top += TILE_SIZE; + } + + r.left += TILE_SIZE; + } + } + rc = TRUE; +fail: + free_objects(context->work_objects, context->work_object_count); + return rc; +} + +static INLINE BOOL check_rect(const YUV_CONTEXT* yuv, const RECTANGLE_16* rect, UINT32 nDstWidth, + UINT32 nDstHeight) +{ + WINPR_ASSERT(yuv); + WINPR_ASSERT(rect); + + /* Check, if the output rectangle is valid in decoded h264 frame. */ + if ((rect->right > yuv->width) || (rect->left > yuv->width)) + return FALSE; + + if ((rect->top > yuv->height) || (rect->bottom > yuv->height)) + return FALSE; + + /* Check, if the output rectangle is valid in destination buffer. */ + if ((rect->right > nDstWidth) || (rect->left > nDstWidth)) + return FALSE; + + if ((rect->bottom > nDstHeight) || (rect->top > nDstHeight)) + return FALSE; + + return TRUE; +} + +static void CALLBACK yuv444_combine_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + YUV_COMBINE_WORK_PARAM* param = (YUV_COMBINE_WORK_PARAM*)context; + primitives_t* prims = primitives_get(); + + WINPR_ASSERT(param); + YUV_CONTEXT* yuv = param->context; + WINPR_ASSERT(yuv); + + const RECTANGLE_16* rect = ¶m->rect; + WINPR_ASSERT(rect); + + const UINT32 alignedWidth = yuv->width + ((yuv->width % 16 != 0) ? 16 - yuv->width % 16 : 0); + const UINT32 alignedHeight = + yuv->height + ((yuv->height % 16 != 0) ? 16 - yuv->height % 16 : 0); + + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + + if (!check_rect(param->context, rect, yuv->width, yuv->height)) + return; + + if (prims->YUV420CombineToYUV444(param->type, param->pYUVData, param->iStride, alignedWidth, + alignedHeight, param->pYUVDstData, param->iDstStride, + rect) != PRIMITIVES_SUCCESS) + WLog_WARN(TAG, "YUV420CombineToYUV444 failed"); +} + +static INLINE YUV_COMBINE_WORK_PARAM pool_decode_rect_param( + const RECTANGLE_16* rect, YUV_CONTEXT* context, BYTE type, const BYTE* pYUVData[3], + const UINT32 iStride[3], BYTE* pYUVDstData[3], const UINT32 iDstStride[3]) +{ + YUV_COMBINE_WORK_PARAM current = { 0 }; + + WINPR_ASSERT(rect); + WINPR_ASSERT(context); + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(pYUVDstData); + WINPR_ASSERT(iDstStride); + + current.context = context; + current.pYUVData[0] = pYUVData[0]; + current.pYUVData[1] = pYUVData[1]; + current.pYUVData[2] = pYUVData[2]; + current.pYUVDstData[0] = pYUVDstData[0]; + current.pYUVDstData[1] = pYUVDstData[1]; + current.pYUVDstData[2] = pYUVDstData[2]; + current.iStride[0] = iStride[0]; + current.iStride[1] = iStride[1]; + current.iStride[2] = iStride[2]; + current.iDstStride[0] = iDstStride[0]; + current.iDstStride[1] = iDstStride[1]; + current.iDstStride[2] = iDstStride[2]; + current.type = type; + current.rect = *rect; + return current; +} + +static BOOL pool_decode_rect(YUV_CONTEXT* context, BYTE type, const BYTE* pYUVData[3], + const UINT32 iStride[3], BYTE* pYUVDstData[3], + const UINT32 iDstStride[3], const RECTANGLE_16* regionRects, + UINT32 numRegionRects) +{ + BOOL rc = FALSE; + UINT32 waitCount = 0; + PTP_WORK_CALLBACK cb = yuv444_combine_work_callback; + primitives_t* prims = primitives_get(); + + WINPR_ASSERT(context); + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(pYUVDstData); + WINPR_ASSERT(iDstStride); + WINPR_ASSERT(regionRects || (numRegionRects == 0)); + + if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU)) + { + for (UINT32 y = 0; y < numRegionRects; y++) + { + YUV_COMBINE_WORK_PARAM current = pool_decode_rect_param( + ®ionRects[y], context, type, pYUVData, iStride, pYUVDstData, iDstStride); + cb(NULL, ¤t, NULL); + } + return TRUE; + } + + /* case where we use threads */ + for (waitCount = 0; waitCount < numRegionRects; waitCount++) + { + YUV_COMBINE_WORK_PARAM* current = NULL; + + if (context->work_object_count <= waitCount) + { + WLog_ERR(TAG, + "YUV rect decoder: invalid number of tiles, only support less than %" PRIu32 + ", got %" PRIu32, + context->work_object_count, waitCount); + goto fail; + } + current = &context->work_combined_params[waitCount]; + *current = pool_decode_rect_param(®ionRects[waitCount], context, type, pYUVData, iStride, + pYUVDstData, iDstStride); + + if (!submit_object(&context->work_objects[waitCount], cb, current, context)) + goto fail; + } + + rc = TRUE; +fail: + free_objects(context->work_objects, context->work_object_count); + return rc; +} + +BOOL yuv444_context_decode(YUV_CONTEXT* context, BYTE type, const BYTE* pYUVData[3], + const UINT32 iStride[3], UINT32 srcYuvHeight, BYTE* pYUVDstData[3], + const UINT32 iDstStride[3], DWORD DstFormat, BYTE* dest, UINT32 nDstStep, + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + const BYTE* pYUVCDstData[3]; + + WINPR_ASSERT(context); + WINPR_ASSERT(pYUVData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(pYUVDstData); + WINPR_ASSERT(iDstStride); + WINPR_ASSERT(dest); + WINPR_ASSERT(regionRects || (numRegionRects == 0)); + + if (context->encoder) + { + WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting"); + return FALSE; + } + if (!pool_decode_rect(context, type, pYUVData, iStride, pYUVDstData, iDstStride, regionRects, + numRegionRects)) + return FALSE; + + pYUVCDstData[0] = pYUVDstData[0]; + pYUVCDstData[1] = pYUVDstData[1]; + pYUVCDstData[2] = pYUVDstData[2]; + return pool_decode(context, yuv444_process_work_callback, pYUVCDstData, iDstStride, + srcYuvHeight, DstFormat, dest, nDstStep, regionRects, numRegionRects); +} + +BOOL yuv420_context_decode(YUV_CONTEXT* context, const BYTE* pYUVData[3], const UINT32 iStride[3], + UINT32 yuvHeight, DWORD DstFormat, BYTE* dest, UINT32 nDstStep, + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + return pool_decode(context, yuv420_process_work_callback, pYUVData, iStride, yuvHeight, + DstFormat, dest, nDstStep, regionRects, numRegionRects); +} + +static void CALLBACK yuv420_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + prim_size_t roi; + YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context; + primitives_t* prims = primitives_get(); + BYTE* pYUVData[3]; + const BYTE* src = NULL; + + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + WINPR_ASSERT(param); + + roi.width = param->rect.right - param->rect.left; + roi.height = param->rect.bottom - param->rect.top; + src = param->pSrcData + param->nSrcStep * param->rect.top + + param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat); + pYUVData[0] = param->pYUVLumaData[0] + param->rect.top * param->iStride[0] + param->rect.left; + pYUVData[1] = + param->pYUVLumaData[1] + param->rect.top / 2 * param->iStride[1] + param->rect.left / 2; + pYUVData[2] = + param->pYUVLumaData[2] + param->rect.top / 2 * param->iStride[2] + param->rect.left / 2; + + if (prims->RGBToYUV420_8u_P3AC4R(src, param->SrcFormat, param->nSrcStep, pYUVData, + param->iStride, &roi) != PRIMITIVES_SUCCESS) + { + WLog_ERR(TAG, "error when decoding lines"); + } +} + +static void CALLBACK yuv444v1_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + prim_size_t roi; + YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context; + primitives_t* prims = primitives_get(); + BYTE* pYUVLumaData[3]; + BYTE* pYUVChromaData[3]; + const BYTE* src = NULL; + + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + WINPR_ASSERT(param); + + roi.width = param->rect.right - param->rect.left; + roi.height = param->rect.bottom - param->rect.top; + src = param->pSrcData + param->nSrcStep * param->rect.top + + param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat); + pYUVLumaData[0] = + param->pYUVLumaData[0] + param->rect.top * param->iStride[0] + param->rect.left; + pYUVLumaData[1] = + param->pYUVLumaData[1] + param->rect.top / 2 * param->iStride[1] + param->rect.left / 2; + pYUVLumaData[2] = + param->pYUVLumaData[2] + param->rect.top / 2 * param->iStride[2] + param->rect.left / 2; + pYUVChromaData[0] = + param->pYUVChromaData[0] + param->rect.top * param->iStride[0] + param->rect.left; + pYUVChromaData[1] = + param->pYUVChromaData[1] + param->rect.top / 2 * param->iStride[1] + param->rect.left / 2; + pYUVChromaData[2] = + param->pYUVChromaData[2] + param->rect.top / 2 * param->iStride[2] + param->rect.left / 2; + if (prims->RGBToAVC444YUV(src, param->SrcFormat, param->nSrcStep, pYUVLumaData, param->iStride, + pYUVChromaData, param->iStride, &roi) != PRIMITIVES_SUCCESS) + { + WLog_ERR(TAG, "error when decoding lines"); + } +} + +static void CALLBACK yuv444v2_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, + PTP_WORK work) +{ + prim_size_t roi; + YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context; + primitives_t* prims = primitives_get(); + BYTE* pYUVLumaData[3]; + BYTE* pYUVChromaData[3]; + const BYTE* src = NULL; + + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + WINPR_ASSERT(param); + + roi.width = param->rect.right - param->rect.left; + roi.height = param->rect.bottom - param->rect.top; + src = param->pSrcData + param->nSrcStep * param->rect.top + + param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat); + pYUVLumaData[0] = + param->pYUVLumaData[0] + param->rect.top * param->iStride[0] + param->rect.left; + pYUVLumaData[1] = + param->pYUVLumaData[1] + param->rect.top / 2 * param->iStride[1] + param->rect.left / 2; + pYUVLumaData[2] = + param->pYUVLumaData[2] + param->rect.top / 2 * param->iStride[2] + param->rect.left / 2; + pYUVChromaData[0] = + param->pYUVChromaData[0] + param->rect.top * param->iStride[0] + param->rect.left; + pYUVChromaData[1] = + param->pYUVChromaData[1] + param->rect.top / 2 * param->iStride[1] + param->rect.left / 2; + pYUVChromaData[2] = + param->pYUVChromaData[2] + param->rect.top / 2 * param->iStride[2] + param->rect.left / 2; + if (prims->RGBToAVC444YUVv2(src, param->SrcFormat, param->nSrcStep, pYUVLumaData, + param->iStride, pYUVChromaData, param->iStride, + &roi) != PRIMITIVES_SUCCESS) + { + WLog_ERR(TAG, "error when decoding lines"); + } +} + +static INLINE YUV_ENCODE_WORK_PARAM pool_encode_fill(const RECTANGLE_16* rect, YUV_CONTEXT* context, + const BYTE* pSrcData, UINT32 nSrcStep, + UINT32 SrcFormat, const UINT32 iStride[], + BYTE* pYUVLumaData[], BYTE* pYUVChromaData[]) +{ + YUV_ENCODE_WORK_PARAM current = { 0 }; + + WINPR_ASSERT(rect); + WINPR_ASSERT(context); + WINPR_ASSERT(pSrcData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(pYUVLumaData); + + current.context = context; + current.pSrcData = pSrcData; + current.SrcFormat = SrcFormat; + current.nSrcStep = nSrcStep; + current.pYUVLumaData[0] = pYUVLumaData[0]; + current.pYUVLumaData[1] = pYUVLumaData[1]; + current.pYUVLumaData[2] = pYUVLumaData[2]; + if (pYUVChromaData) + { + current.pYUVChromaData[0] = pYUVChromaData[0]; + current.pYUVChromaData[1] = pYUVChromaData[1]; + current.pYUVChromaData[2] = pYUVChromaData[2]; + } + current.iStride[0] = iStride[0]; + current.iStride[1] = iStride[1]; + current.iStride[2] = iStride[2]; + + current.rect = *rect; + + return current; +} + +static BOOL pool_encode(YUV_CONTEXT* context, PTP_WORK_CALLBACK cb, const BYTE* pSrcData, + UINT32 nSrcStep, UINT32 SrcFormat, const UINT32 iStride[], + BYTE* pYUVLumaData[], BYTE* pYUVChromaData[], + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + BOOL rc = FALSE; + primitives_t* prims = primitives_get(); + UINT32 waitCount = 0; + + WINPR_ASSERT(context); + WINPR_ASSERT(cb); + WINPR_ASSERT(pSrcData); + WINPR_ASSERT(iStride); + WINPR_ASSERT(regionRects || (numRegionRects == 0)); + + if (!context->encoder) + { + + WLog_ERR(TAG, "YUV context set up for decoding, can not encode with it, aborting"); + return FALSE; + } + + if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU)) + { + for (UINT32 x = 0; x < numRegionRects; x++) + { + YUV_ENCODE_WORK_PARAM current = + pool_encode_fill(®ionRects[x], context, pSrcData, nSrcStep, SrcFormat, iStride, + pYUVLumaData, pYUVChromaData); + cb(NULL, ¤t, NULL); + } + return TRUE; + } + + /* case where we use threads */ + for (UINT32 x = 0; x < numRegionRects; x++) + { + const RECTANGLE_16* rect = ®ionRects[x]; + const UINT32 height = rect->bottom - rect->top; + const UINT32 steps = (height + context->heightStep / 2) / context->heightStep; + + waitCount += steps; + } + + for (UINT32 x = 0; x < numRegionRects; x++) + { + const RECTANGLE_16* rect = ®ionRects[x]; + const UINT32 height = rect->bottom - rect->top; + const UINT32 steps = (height + context->heightStep / 2) / context->heightStep; + + for (UINT32 y = 0; y < steps; y++) + { + RECTANGLE_16 r = *rect; + YUV_ENCODE_WORK_PARAM* current = NULL; + + if (context->work_object_count <= waitCount) + { + WLog_ERR(TAG, + "YUV encoder: invalid number of tiles, only support less than %" PRIu32 + ", got %" PRIu32, + context->work_object_count, waitCount); + goto fail; + } + + current = &context->work_enc_params[waitCount]; + r.top += y * context->heightStep; + *current = pool_encode_fill(&r, context, pSrcData, nSrcStep, SrcFormat, iStride, + pYUVLumaData, pYUVChromaData); + if (!submit_object(&context->work_objects[waitCount], cb, current, context)) + goto fail; + waitCount++; + } + } + + rc = TRUE; +fail: + free_objects(context->work_objects, context->work_object_count); + return rc; +} + +BOOL yuv420_context_encode(YUV_CONTEXT* context, const BYTE* pSrcData, UINT32 nSrcStep, + UINT32 SrcFormat, const UINT32 iStride[3], BYTE* pYUVData[3], + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + if (!context || !pSrcData || !iStride || !pYUVData || !regionRects) + return FALSE; + + return pool_encode(context, yuv420_encode_work_callback, pSrcData, nSrcStep, SrcFormat, iStride, + pYUVData, NULL, regionRects, numRegionRects); +} + +BOOL yuv444_context_encode(YUV_CONTEXT* context, BYTE version, const BYTE* pSrcData, + UINT32 nSrcStep, UINT32 SrcFormat, const UINT32 iStride[3], + BYTE* pYUVLumaData[3], BYTE* pYUVChromaData[3], + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + PTP_WORK_CALLBACK cb = NULL; + switch (version) + { + case 1: + cb = yuv444v1_encode_work_callback; + break; + case 2: + cb = yuv444v2_encode_work_callback; + break; + default: + return FALSE; + } + + return pool_encode(context, cb, pSrcData, nSrcStep, SrcFormat, iStride, pYUVLumaData, + pYUVChromaData, regionRects, numRegionRects); +} |