28#include <freerdp/config.h>
34#include <emscripten.h>
35#include <emscripten/proxying.h>
36#include <emscripten/threading.h>
38#include <winpr/assert.h>
40#include <winpr/stream.h>
42#include <freerdp/types.h>
43#include <freerdp/codec/audio.h>
45#include "rdpsnd_main.h"
49 rdpsndDevicePlugin device;
53 volatile int queued_ms;
55} rdpsndEmscriptenPlugin;
67static uint32_t web_audio_format[3];
69EMSCRIPTEN_KEEPALIVE uint32_t* freerdp_web_audio_format(
void);
70EMSCRIPTEN_KEEPALIVE uint32_t* freerdp_web_audio_format(
void)
72 return web_audio_format;
75static void web_audio_format_set(UINT16 tag, UINT32 rate, UINT16 channels)
77 __atomic_store_n(&web_audio_format[1], rate, __ATOMIC_RELAXED);
78 __atomic_store_n(&web_audio_format[2], channels, __ATOMIC_RELAXED);
79 __atomic_store_n(&web_audio_format[0], tag, __ATOMIC_RELEASE);
84 rdpsndEmscriptenPlugin* plugin;
90extern void rdpsnd_web_play(BYTE* data,
int size,
int channels,
int rate,
int volume,
93static void play_on_main(
void* arg)
98 rdpsndEmscriptenPlugin* p = job->plugin;
101 rdpsnd_web_play(job->data, (
int)job->size, (
int)p->channels, (
int)p->rate,
102 (
int)(p->volume & 0xffff), (
int*)&p->queued_ms);
110 WINPR_ASSERT(format);
113 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->wBitsPerSample == 16) &&
114 (format->nChannels >= 1) && (format->nChannels <= 2) && (format->nSamplesPerSec > 0);
118static BOOL rdpsnd_emscripten_format_supported(rdpsndDevicePlugin* device,
121 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
123 WINPR_ASSERT(format);
126 p->asked.data =
nullptr;
127 return pcm_playable(format);
130static BOOL rdpsnd_emscripten_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
131 WINPR_ATTR_UNUSED UINT32 latency)
133 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
136 if (!format || !pcm_playable(format))
138 p->channels = format->nChannels;
139 p->rate = format->nSamplesPerSec;
142 if ((p->asked.wFormatTag != WAVE_FORMAT_UNKNOWN) &&
143 (p->asked.nSamplesPerSec == format->nSamplesPerSec) &&
144 (p->asked.nChannels == format->nChannels))
146 web_audio_format_set(wire->wFormatTag, wire->nSamplesPerSec, wire->nChannels);
150static void rdpsnd_emscripten_close(WINPR_ATTR_UNUSED rdpsndDevicePlugin* device)
154static BOOL rdpsnd_emscripten_set_volume(rdpsndDevicePlugin* device, UINT32 value)
156 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
163static UINT32 rdpsnd_emscripten_get_volume(rdpsndDevicePlugin* device)
165 const rdpsndEmscriptenPlugin* p = (
const rdpsndEmscriptenPlugin*)device;
171static UINT rdpsnd_emscripten_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
173 rdpsndEmscriptenPlugin* p = (rdpsndEmscriptenPlugin*)device;
176 if (!data || size == 0 || p->channels == 0)
179 play_job* job = calloc(1,
sizeof(play_job));
182 job->data = malloc(size);
188 memcpy(job->data, data, size);
192 if (emscripten_is_main_browser_thread())
194 else if (!emscripten_proxy_async(emscripten_proxy_get_system_queue(),
195 emscripten_main_runtime_thread_id(), play_on_main, job))
202 return (UINT)p->queued_ms;
205static void drain_on_main(WINPR_ATTR_UNUSED
void* arg)
209static void rdpsnd_emscripten_free(rdpsndDevicePlugin* device)
213 if (emscripten_is_main_browser_thread())
214 emscripten_proxy_execute_queue(emscripten_proxy_get_system_queue());
216 (
void)emscripten_proxy_sync(emscripten_proxy_get_system_queue(),
217 emscripten_main_runtime_thread_id(), drain_on_main,
nullptr);
221FREERDP_ENTRY_POINT(UINT VCAPITYPE emscripten_freerdp_rdpsnd_client_subsystem_entry(
224 WINPR_ASSERT(pEntryPoints);
226 rdpsndEmscriptenPlugin* p = calloc(1,
sizeof(rdpsndEmscriptenPlugin));
228 return CHANNEL_RC_NO_MEMORY;
230 p->volume = UINT32_MAX;
231 web_audio_format_set(WAVE_FORMAT_UNKNOWN, 0, 0);
232 p->device.Open = rdpsnd_emscripten_open;
233 p->device.FormatSupported = rdpsnd_emscripten_format_supported;
234 p->device.GetVolume = rdpsnd_emscripten_get_volume;
235 p->device.SetVolume = rdpsnd_emscripten_set_volume;
236 p->device.Play = rdpsnd_emscripten_play;
237 p->device.Close = rdpsnd_emscripten_close;
238 p->device.Free = rdpsnd_emscripten_free;
239 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, &p->device);
240 return CHANNEL_RC_OK;