28#include <freerdp/config.h>
33#include <emscripten.h>
34#include <emscripten/proxying.h>
35#include <emscripten/threading.h>
37#include <winpr/assert.h>
39#include <winpr/stream.h>
41#include <freerdp/types.h>
42#include <freerdp/codec/audio.h>
44#include "rdpsnd_main.h"
48 rdpsndDevicePlugin device;
52 volatile int queued_ms;
53} rdpsndEmscriptenPlugin;
57 rdpsndEmscriptenPlugin* plugin;
63extern void rdpsnd_web_play(BYTE* data,
int size,
int channels,
int rate,
int volume,
66static void play_on_main(
void* arg)
71 rdpsndEmscriptenPlugin* p = job->plugin;
74 rdpsnd_web_play(job->data, (
int)job->size, (
int)p->channels, (
int)p->rate,
75 (
int)(p->volume & 0xffff), (
int*)&p->queued_ms);
80static BOOL rdpsnd_emscripten_format_supported(WINPR_ATTR_UNUSED rdpsndDevicePlugin* device,
86 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->wBitsPerSample == 16) &&
87 (format->nChannels >= 1) && (format->nChannels <= 2) && (format->nSamplesPerSec > 0);
90static BOOL rdpsnd_emscripten_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
91 WINPR_ATTR_UNUSED UINT32 latency)
93 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
96 if (!format || !rdpsnd_emscripten_format_supported(device, format))
98 p->channels = format->nChannels;
99 p->rate = format->nSamplesPerSec;
103static void rdpsnd_emscripten_close(WINPR_ATTR_UNUSED rdpsndDevicePlugin* device)
107static BOOL rdpsnd_emscripten_set_volume(rdpsndDevicePlugin* device, UINT32 value)
109 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
116static UINT32 rdpsnd_emscripten_get_volume(rdpsndDevicePlugin* device)
118 const rdpsndEmscriptenPlugin* p = (
const rdpsndEmscriptenPlugin*)device;
124static UINT rdpsnd_emscripten_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
126 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
129 if (!data || size == 0 || p->channels == 0)
132 play_job* job = calloc(1,
sizeof(play_job));
135 job->data = malloc(size);
141 memcpy(job->data, data, size);
145 if (emscripten_is_main_browser_thread())
147 else if (!emscripten_proxy_async(emscripten_proxy_get_system_queue(),
148 emscripten_main_runtime_thread_id(), play_on_main, job))
155 return (UINT)p->queued_ms;
158static void drain_on_main(WINPR_ATTR_UNUSED
void* arg)
162static void rdpsnd_emscripten_free(rdpsndDevicePlugin* device)
166 if (emscripten_is_main_browser_thread())
167 emscripten_proxy_execute_queue(emscripten_proxy_get_system_queue());
169 (
void)emscripten_proxy_sync(emscripten_proxy_get_system_queue(),
170 emscripten_main_runtime_thread_id(), drain_on_main,
nullptr);
174FREERDP_ENTRY_POINT(UINT VCAPITYPE emscripten_freerdp_rdpsnd_client_subsystem_entry(
177 WINPR_ASSERT(pEntryPoints);
179 rdpsndEmscriptenPlugin* p = calloc(1,
sizeof(rdpsndEmscriptenPlugin));
181 return CHANNEL_RC_NO_MEMORY;
183 p->volume = UINT32_MAX;
184 p->device.Open = rdpsnd_emscripten_open;
185 p->device.FormatSupported = rdpsnd_emscripten_format_supported;
186 p->device.GetVolume = rdpsnd_emscripten_get_volume;
187 p->device.SetVolume = rdpsnd_emscripten_set_volume;
188 p->device.Play = rdpsnd_emscripten_play;
189 p->device.Close = rdpsnd_emscripten_close;
190 p->device.Free = rdpsnd_emscripten_free;
191 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, &p->device);
192 return CHANNEL_RC_OK;