30#include <freerdp/config.h>
35#include <emscripten.h>
36#include <emscripten/proxying.h>
37#include <emscripten/threading.h>
39#include <winpr/assert.h>
40#include <winpr/string.h>
41#include <freerdp/log.h>
45#define H264_WC_NEEDS_BUFFER 2
57} H264_CONTEXT_WEBCODECS;
75extern void h264_wc_js_open(
void* ctx,
int* handle);
76extern void h264_wc_js_close(
int handle);
77extern void h264_wc_js_decode(
void* ctx,
int handle,
const BYTE* src, UINT32 size,
int key,
78 const char* codec, BYTE* y, BYTE* u, BYTE* v, UINT32 ys, UINT32 cs,
79 UINT32 maxWidth, UINT32 maxHeight, UINT32* dim,
int* rc);
80extern void h264_wc_js_copy(
void* ctx,
int handle, BYTE* y, BYTE* u, BYTE* v, UINT32 ys, UINT32 cs,
83EMSCRIPTEN_KEEPALIVE
void freerdp_h264_wc_finish(em_proxying_ctx* ctx)
85 emscripten_proxy_finish(ctx);
88static void main_open(em_proxying_ctx* ctx,
void* arg)
90 h264_wc_js_open(ctx, arg);
93static void main_close(
void* arg)
95 h264_wc_js_close(*(
int*)arg);
98static void main_decode(em_proxying_ctx* ctx,
void* arg)
100 h264_wc_job* job = arg;
101 h264_wc_js_decode(ctx, job->handle, job->src, job->size, job->key, job->codec, job->plane[0],
102 job->plane[1], job->plane[2], job->stride[0], job->stride[1], job->maxWidth,
103 job->maxHeight, job->dim, &job->rc);
106static void main_copy(em_proxying_ctx* ctx,
void* arg)
108 h264_wc_job* job = arg;
109 h264_wc_js_copy(ctx, job->handle, job->plane[0], job->plane[1], job->plane[2], job->stride[0],
110 job->stride[1], &job->rc);
114static BOOL on_main(
void (*fn)(em_proxying_ctx*,
void*),
void* arg)
116 return emscripten_proxy_sync_with_ctx(emscripten_proxy_get_system_queue(),
117 emscripten_main_runtime_thread_id(), fn, arg) == 1;
122static BOOL next_nal(
const BYTE* data,
size_t size,
size_t* offset,
const BYTE** nal,
125 size_t pos = *offset;
126 if ((size < 3) || (pos == SIZE_MAX - size))
128 while ((pos <= size - 3ull) &&
129 !((data[pos] == 0) && (data[pos + 1] == 0) && (data[pos + 2] == 1)))
131 if (pos >= size - 3ull)
134 const size_t start = pos + 3;
136 while ((pos <= size - 3ull) &&
137 !((data[pos] == 0) && (data[pos + 1] == 0) && (data[pos + 2] == 1)))
139 size_t end = (pos <= size - 3ull) ? pos : size;
140 while ((end > start) && (data[end - 1] == 0))
144 *length = end - start;
151static BOOL save_parameters(H264_CONTEXT_WEBCODECS* sys,
const BYTE* data,
size_t size, BOOL hasSps)
153 size_t total = hasSps ? 0 : sys->paramsSize;
155 const BYTE* nal =
nullptr;
157 while (next_nal(data, size, &offset, &nal, &length))
161 const BYTE type = nal[0] & 0x1F;
162 if ((type == 7) && (length < 4))
164 if ((type == 7) || (type == 8))
168 BYTE* params = malloc(total);
172 if (!hasSps && (sys->paramsSize > 0))
174 memcpy(params, sys->params, sys->paramsSize);
175 pos = sys->paramsSize;
178 while (next_nal(data, size, &offset, &nal, &length))
182 const BYTE type = nal[0] & 0x1F;
183 if ((type == 7) && (length < 4))
185 if ((type != 7) && (type != 8))
187 const BYTE startCode[] = { 0, 0, 0, 1 };
188 memcpy(¶ms[pos], startCode,
sizeof(startCode));
189 memcpy(¶ms[pos + 4], nal, length);
192 WINPR_ASSERT(pos == total);
194 sys->params = params;
195 sys->paramsSize = total;
200static BOOL ensure_planes(H264_CONTEXT_WEBCODECS* sys, UINT32 width, UINT32 height)
202 if (sys->yuv && (width <= sys->width) && (height <= sys->height))
204 width = MAX(width, sys->width);
205 height = MAX(height, sys->height);
206 if ((width == 0) || (height == 0) || (width > 16384) || (height > 16384))
210 const UINT32 ystride = width + 32 - width % 16;
211 const UINT32 cstride = (ystride + 1) / 2;
212 const size_t lines = height + 32 - height % 16;
213 const size_t ysize = (size_t)ystride * lines;
214 const size_t csize = (size_t)cstride * (lines / 2);
215 BYTE* yuv = winpr_aligned_calloc(ysize + 2 * csize, 1, 16);
218 winpr_aligned_free(sys->yuv);
221 sys->height = height;
222 sys->stride[0] = ystride;
223 sys->stride[1] = cstride;
224 sys->stride[2] = cstride;
227 sys->plane[1] = &yuv[ysize];
228 sys->plane[2] = &yuv[ysize + csize];
233static int webcodecs_decompress(H264_CONTEXT* WINPR_RESTRICT h264,
234 const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize)
237 WINPR_ASSERT(pSrcData || (SrcSize == 0));
239 H264_CONTEXT_WEBCODECS* sys = h264->pSystemData;
242 if (emscripten_is_main_browser_thread())
245 BOOL hasSlice = FALSE;
250 const BYTE* nal =
nullptr;
252 while (next_nal(pSrcData, SrcSize, &offset, &nal, &length))
256 const BYTE type = nal[0] & 0x1F;
257 hasSlice |= (type == 1) || (type == 5);
258 hasIdr |= (type == 5);
259 hasSps |= (type == 7);
260 hasPps |= (type == 8);
262 if ((hasSps || hasPps) && !save_parameters(sys, pSrcData, SrcSize, hasSps))
268 char codec[16] =
"avc1.42e01f";
269 if ((sys->paramsSize > 7) && ((sys->params[4] & 0x1F) == 7))
270 (
void)_snprintf(codec,
sizeof(codec),
"avc1.%02x%02x%02x", sys->params[5], sys->params[6],
274 const BYTE* src = pSrcData;
275 UINT32 size = SrcSize;
276 BYTE* combined =
nullptr;
277 if (hasIdr && !hasSps && (sys->paramsSize > 0))
279 if (sys->paramsSize > UINT32_MAX - SrcSize)
281 size = (UINT32)sys->paramsSize + SrcSize;
282 combined = malloc(size);
285 memcpy(combined, sys->params, sys->paramsSize);
286 memcpy(&combined[sys->paramsSize], pSrcData, SrcSize);
291 h264_wc_job job = { .handle = sys->handle,
297 .stride = sys->stride,
299 if (!ensure_planes(sys, MAX(h264->width, 16), MAX(h264->height, 16)))
301 job.maxWidth = sys->width;
302 job.maxHeight = sys->height;
303 if (!on_main(main_decode, &job))
305 if (job.rc == H264_WC_NEEDS_BUFFER)
308 if (!ensure_planes(sys, job.dim[0], job.dim[1]) || !on_main(main_copy, &job))
313 WLog_Print(h264->log, WLOG_WARN,
"WebCodecs H.264 decode failed (%d)", rc);
317 for (
size_t x = 0; x < 3; x++)
319 h264->pYUVData[x] = sys->plane[x];
320 h264->iStride[x] = sys->stride[x];
322 h264->YUVWidth = job.dim[0];
323 h264->YUVHeight = job.dim[1];
329static void webcodecs_uninit(H264_CONTEXT* h264)
333 H264_CONTEXT_WEBCODECS* sys = h264->pSystemData;
336 if ((sys->handle > 0) && !emscripten_is_main_browser_thread())
337 (void)emscripten_proxy_sync(emscripten_proxy_get_system_queue(),
338 emscripten_main_runtime_thread_id(), main_close, &sys->handle);
339 else if (sys->handle > 0)
340 main_close(&sys->handle);
341 for (
size_t x = 0; x < 3; x++)
342 h264->pYUVData[x] =
nullptr;
343 winpr_aligned_free(sys->yuv);
346 h264->pSystemData =
nullptr;
350static BOOL webcodecs_init(H264_CONTEXT* h264)
355 if (h264->Compressor || emscripten_is_main_browser_thread())
358 H264_CONTEXT_WEBCODECS* sys = calloc(1,
sizeof(*sys));
361 if (!on_main(main_open, &sys->handle) || (sys->handle <= 0))
363 WLog_Print(h264->log, WLOG_DEBUG,
"the browser cannot decode H.264 with WebCodecs");
367 h264->pSystemData = sys;
371const H264_CONTEXT_SUBSYSTEM g_Subsystem_WebCodecs = {
"WebCodecs", webcodecs_init,
372 webcodecs_uninit, webcodecs_decompress,