From 6cbbb6f6102fbf429a4cc9a8cb6c5071ec146f4b Mon Sep 17 00:00:00 2001 From: Stasonych Date: Mon, 25 Aug 2025 18:53:23 +0500 Subject: [PATCH 1/6] Added additional fields for devices. It can be useful, for example, if there is a need in the application code to set a priority for selecting a playback device. I also added a patch that fixes an empty device GUID on macOS. --- .../macos/patch_for_mac_deviceId.patch | 40 ++++++ .../src/main/cpp/include/media/Device.h | 26 ++++ .../src/main/cpp/include/media/DeviceList.h | 7 ++ .../cpp/include/media/audio/AudioDevice.h | 9 ++ .../audio/linux/PulseAudioDeviceManager.h | 8 +- .../audio/macos/CoreAudioDeviceManager.h | 3 + .../audio/windows/WindowsAudioDeviceManager.h | 6 +- .../cpp/include/platform/windows/WinUtils.h | 30 +++++ .../main/cpp/src/JNI_AudioDeviceModule.cpp | 3 +- .../src/main/cpp/src/JNI_MediaDevices.cpp | 6 +- webrtc-jni/src/main/cpp/src/WebRTCContext.cpp | 3 + webrtc-jni/src/main/cpp/src/media/Device.cpp | 28 +++++ .../main/cpp/src/media/audio/AudioDevice.cpp | 17 +++ .../audio/linux/PulseAudioDeviceManager.cpp | 89 ++++++++++++-- .../audio/macos/CoreAudioDeviceManager.cpp | 72 +++++++++++ .../windows/WindowsAudioDeviceManager.cpp | 115 +++++++++++++++++- .../windows/WindowsVideoDeviceManager.cpp | 2 + .../media/AudioDeviceDirectionType.java | 7 ++ .../java/dev/onvoid/webrtc/media/Device.java | 23 +++- .../onvoid/webrtc/media/DeviceFormFactor.java | 25 ++++ .../onvoid/webrtc/media/DeviceTransport.java | 22 ++++ .../webrtc/media/audio/AudioDevice.java | 10 ++ 22 files changed, 528 insertions(+), 23 deletions(-) create mode 100644 webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch create mode 100644 webrtc/src/main/java/dev/onvoid/webrtc/media/AudioDeviceDirectionType.java create mode 100644 webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java create mode 100644 webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java diff --git a/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch b/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch new file mode 100644 index 00000000..4d60020b --- /dev/null +++ b/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch @@ -0,0 +1,40 @@ +Subject: [PATCH] patch for mac deviceId +--- +Index: modules/audio_device/mac/audio_device_mac.cc +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc +--- a/modules/audio_device/mac/audio_device_mac.cc (revision d75b9e9ff07ee42841b4e416629c9fbd4b058905) ++++ b/modules/audio_device/mac/audio_device_mac.cc (date 1696335540383) +@@ -831,9 +831,11 @@ + + memset(name, 0, kAdmMaxDeviceNameSize); + +- if (guid != NULL) { +- memset(guid, 0, kAdmMaxGuidSize); +- } ++ memset(guid, 0, kAdmMaxGuidSize); ++ AudioDeviceID deviceIds[MaxNumberDevices]; ++ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput, deviceIds, MaxNumberDevices); ++ std::string deviceId = std::to_string(deviceIds[index]); ++ deviceId.copy(guid, kAdmMaxGuidSize); + + return GetDeviceName(kAudioDevicePropertyScopeOutput, index, + rtc::ArrayView(name, kAdmMaxDeviceNameSize)); +@@ -850,9 +852,11 @@ + + memset(name, 0, kAdmMaxDeviceNameSize); + +- if (guid != NULL) { +- memset(guid, 0, kAdmMaxGuidSize); +- } ++ memset(guid, 0, kAdmMaxGuidSize); ++ AudioDeviceID deviceIds[MaxNumberDevices]; ++ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeInput, deviceIds, MaxNumberDevices); ++ std::string deviceId = std::to_string(deviceIds[index]); ++ deviceId.copy(guid, kAdmMaxGuidSize); + + return GetDeviceName(kAudioDevicePropertyScopeInput, index, + rtc::ArrayView(name, kAdmMaxDeviceNameSize)); diff --git a/webrtc-jni/src/main/cpp/include/media/Device.h b/webrtc-jni/src/main/cpp/include/media/Device.h index f51d5014..f083267f 100644 --- a/webrtc-jni/src/main/cpp/include/media/Device.h +++ b/webrtc-jni/src/main/cpp/include/media/Device.h @@ -28,6 +28,24 @@ namespace jni { namespace avdev { + /** + DeviceTransport and DeviceFormFactor only foe audio devices. + */ + enum class DeviceTransport { + trUnknown, + trHdmi, + trUsb, + trWireless, + }; + + enum class DeviceFormFactor { + ffUnknown, + ffSpeaker, + ffMicrophone, + ffHeadset, + ffHeadphone + }; + class Device { public: @@ -39,6 +57,10 @@ namespace jni std::string getName() const; std::string getDescriptor() const; + DeviceTransport getDeviceTransport(); + DeviceFormFactor getDeviceFormFactor(); + void setDeviceTransport(DeviceTransport deviceTransport); + void setDeviceFormFactor(DeviceFormFactor deviceFormFactor); protected: Device(std::string name, std::string descriptor); @@ -46,6 +68,8 @@ namespace jni private: const std::string name; const std::string descriptor; + DeviceTransport deviceTransport; + DeviceFormFactor deviceFormFactor; }; @@ -63,6 +87,8 @@ namespace jni jmethodID ctor; jfieldID name; jfieldID descriptor; + jfieldID deviceTransport; + jfieldID deviceFormFactor; }; JavaLocalRef toJavaDevice(JNIEnv * env, avdev::DevicePtr device); diff --git a/webrtc-jni/src/main/cpp/include/media/DeviceList.h b/webrtc-jni/src/main/cpp/include/media/DeviceList.h index 8882396d..b6b91786 100644 --- a/webrtc-jni/src/main/cpp/include/media/DeviceList.h +++ b/webrtc-jni/src/main/cpp/include/media/DeviceList.h @@ -94,6 +94,13 @@ namespace jni return devicesSet.empty(); } + void clearDevices() + { + std::unique_lock mlock(mutex); + devicesSet.clear(); + mlock.unlock(); + } + std::set devices() { std::unique_lock mlock(mutex); diff --git a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h index c91b7530..b67b5fbe 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h @@ -27,11 +27,19 @@ namespace jni { namespace avdev { + + enum class AudioDeviceDirectionType { + adtUnknown, + adtCapture, + adtRender + }; + class AudioDevice : public Device { public: AudioDevice(std::string name, std::string descriptor); virtual ~AudioDevice() {}; + AudioDeviceDirectionType audioDeviceDirectionType; }; } @@ -46,6 +54,7 @@ namespace jni jmethodID ctor; jfieldID name; jfieldID descriptor; + jfieldID audioDeviceDirectionType; }; JavaLocalRef toJavaAudioDevice(JNIEnv * env, avdev::DevicePtr device); diff --git a/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioDeviceManager.h b/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioDeviceManager.h index 24ff3e15..b07b429b 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioDeviceManager.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioDeviceManager.h @@ -54,8 +54,12 @@ namespace jni static void getSinkCallback(pa_context * ctx, const pa_sink_info * info, int last, void * userdata); static void newSinkCallback(pa_context * ctx, const pa_sink_info * info, int last, void * userdata); - void insertDevice(DeviceList & devices, const char * name, const char * desc, uint32_t index, bool notify); - void removeDevice(DeviceList & devices, uint32_t index); + void insertDevice(DeviceList & devices, pa_proplist * proplist, const char * name, const char * desc, uint32_t index, bool notify, bool isCapture); + void removeDevice(DeviceList & devices, uint32_t index, bool isCapture); + + void fillAdditionalTypes(AudioDevicePtr device, pa_proplist * proplist); + DeviceFormFactor getActualFormFactor(std::string formFactor); + DeviceTransport getActualTransport(std::string transport); private: pa_threaded_mainloop * mainloop; diff --git a/webrtc-jni/src/main/cpp/include/media/audio/macos/CoreAudioDeviceManager.h b/webrtc-jni/src/main/cpp/include/media/audio/macos/CoreAudioDeviceManager.h index e1289634..7eb36f26 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/macos/CoreAudioDeviceManager.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/macos/CoreAudioDeviceManager.h @@ -47,6 +47,9 @@ namespace jni bool insertAudioDevice(const AudioDevicePtr & device, const AudioObjectPropertyScope & scope); int getChannelCount(const AudioDeviceID & deviceID, const AudioObjectPropertyScope & scope); AudioDeviceID getDefaultDeviceID(const AudioObjectPropertyScope & scope); + DeviceFormFactor getActualFormFactor(const unsigned int sourceID); + DeviceTransport getActualTransport(const unsigned int transportType); + void fillAdditionalTypes(const AudioDeviceID & deviceID, const AudioObjectPropertyScope & scope, AudioDevicePtr device); static OSStatus deviceListenerProc(AudioObjectID objectID, UInt32 numberAddresses, const AudioObjectPropertyAddress addresses[], void * clientData); }; diff --git a/webrtc-jni/src/main/cpp/include/media/audio/windows/WindowsAudioDeviceManager.h b/webrtc-jni/src/main/cpp/include/media/audio/windows/WindowsAudioDeviceManager.h index dee044db..1e8fcbbe 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/windows/WindowsAudioDeviceManager.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/windows/WindowsAudioDeviceManager.h @@ -17,6 +17,7 @@ #ifndef JNI_WEBRTC_MEDIA_MF_AUDIO_DEVICE_MANAGER_H_ #define JNI_WEBRTC_MEDIA_MF_AUDIO_DEVICE_MANAGER_H_ +#include #include "media/audio/AudioDeviceManager.h" #include "platform/windows/ComPtr.h" #include "platform/windows/ComInitializer.h" @@ -46,7 +47,10 @@ namespace jni AudioDevicePtr createDefaultAudioDevice(const EDataFlow & dataFlow); AudioDevicePtr createAudioDevice(LPCWSTR deviceId, EDataFlow * dataFlow); bool insertAudioDevice(AudioDevicePtr device, EDataFlow dataFlow); - void removeAudioDevice(DeviceList & devices, std::string id); + void removeAudioDevice(DeviceList & devices, std::string id, EDataFlow dataFlow); + DeviceFormFactor getActualFormFactor(EndpointFormFactor formFactor); + DeviceTransport getActualTransport(EndpointFormFactor formFactor); + void fillAdditionalTypes(AudioDevicePtr device); // IMMNotificationClient implementation. STDMETHOD_(ULONG, AddRef)(); diff --git a/webrtc-jni/src/main/cpp/include/platform/windows/WinUtils.h b/webrtc-jni/src/main/cpp/include/platform/windows/WinUtils.h index 059649c9..47e87f47 100644 --- a/webrtc-jni/src/main/cpp/include/platform/windows/WinUtils.h +++ b/webrtc-jni/src/main/cpp/include/platform/windows/WinUtils.h @@ -102,4 +102,34 @@ inline std::string RoleToStr(ERole role) } } +inline std::string FormFactorToStr(EndpointFormFactor formFactor) +{ + switch (formFactor) { + case RemoteNetworkDevice: + return "RemoteNetworkDevice"; + case Speakers: + return "Speakers"; + case Headphones: + return "Headphones"; + case Microphone: + return "Microphone"; + case Headset: + return "Headset"; + case Handset: + return "Handset"; + case UnknownDigitalPassthrough: + return "UnknownDigitalPassthrough"; + case SPDIF: + return "SPDIF"; + case DigitalAudioDisplayDevice: + return "DigitalAudioDisplayDevice"; + case UnknownFormFactor: + return "UnknownFormFactor"; + case EndpointFormFactor_enum_count: + return "EndpointFormFactor_enum_count"; + default: + return "Unknown Form Factor"; + } +} + #endif \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp index 11e48233..f3252a69 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp @@ -121,7 +121,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_g for (int i = 0; i < deviceCount; ++i) { if (audioModule->PlayoutDeviceName(i, name, guid) == 0) { auto device = std::make_shared(name, guid); - + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } } @@ -145,6 +145,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_g for (int i = 0; i < deviceCount; ++i) { if (audioModule->RecordingDeviceName(i, name, guid) == 0) { auto device = std::make_shared(name, guid); + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp index 125ec53b..7df91b3a 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp @@ -67,7 +67,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioPlaybackDevice(); - + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { @@ -84,7 +84,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioCaptureDevice(); - + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { @@ -105,6 +105,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getAudioRend jni::JavaArrayList deviceList(env, devices.size()); for (const auto & device : devices) { + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } @@ -128,6 +129,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getAudioCapt jni::JavaArrayList deviceList(env, devices.size()); for (const auto & device : devices) { + device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } diff --git a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp index 8c2769e0..4e7c62b1 100644 --- a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp +++ b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp @@ -87,6 +87,9 @@ namespace jni JavaEnums::add(env, PKG_AUDIO"AudioLayer"); JavaEnums::add(env, PKG_AUDIO"AudioProcessingConfig$NoiseSuppression$Level"); JavaEnums::add(env, PKG"RTCStatsType"); + JavaEnums::add(env, PKG_MEDIA"DeviceFormFactor"); + JavaEnums::add(env, PKG_MEDIA"DeviceTransport"); + JavaEnums::add(env, PKG_MEDIA"AudioDeviceDirectionType"); JavaFactories::add(env, PKG_MEDIA"audio/AudioTrackSource"); JavaFactories::add(env, PKG_MEDIA"audio/AudioTrack"); diff --git a/webrtc-jni/src/main/cpp/src/media/Device.cpp b/webrtc-jni/src/main/cpp/src/media/Device.cpp index 09d22518..0910381a 100644 --- a/webrtc-jni/src/main/cpp/src/media/Device.cpp +++ b/webrtc-jni/src/main/cpp/src/media/Device.cpp @@ -18,6 +18,7 @@ #include "JavaClasses.h" #include "JavaString.h" #include "JNI_WebRTC.h" +#include "JavaEnums.h" namespace jni { @@ -27,6 +28,8 @@ namespace jni name(name), descriptor(descriptor) { + deviceTransport = DeviceTransport::trUnknown; + deviceFormFactor = DeviceFormFactor::ffUnknown; } bool Device::operator==(const Device & other) @@ -53,6 +56,24 @@ namespace jni { return name; } + + DeviceTransport Device::getDeviceTransport() + { + return deviceTransport; + } + + DeviceFormFactor Device::getDeviceFormFactor() + { + return deviceFormFactor; + } + + void Device::setDeviceTransport(DeviceTransport newDeviceTransport) { + deviceTransport = newDeviceTransport; + } + + void Device::setDeviceFormFactor(DeviceFormFactor newDeviceFormFactor) { + deviceFormFactor = newDeviceFormFactor; + } } namespace Device @@ -65,6 +86,13 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); + jclass cls = env->GetObjectClass(obj); + jmethodID setTransportMethod = env->GetMethodID(cls, "setDeviceTransport", "(L" PKG_MEDIA "DeviceTransport;)V"); + env->CallVoidMethod(obj, setTransportMethod, JavaEnums::toJava(env, device->getDeviceTransport()).release()); + + jmethodID setFormFactorMethod = env->GetMethodID(cls, "setDeviceFormFactor", "(L" PKG_MEDIA "DeviceFormFactor;)V"); + env->CallVoidMethod(obj, setFormFactorMethod, JavaEnums::toJava(env, device->getDeviceFormFactor()).release()); + return JavaLocalRef(env, obj); } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp index 9069c726..c474e0e1 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp @@ -19,6 +19,7 @@ #include "JavaClasses.h" #include "JavaString.h" #include "JNI_WebRTC.h" +#include "JavaEnums.h" namespace jni { @@ -27,6 +28,7 @@ namespace jni AudioDevice::AudioDevice(std::string name, std::string descriptor) : Device(name, descriptor) { + audioDeviceDirectionType = AudioDeviceDirectionType::adtUnknown; } } @@ -40,6 +42,21 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); + jclass cls = env->GetObjectClass(obj); + jmethodID setTransportMethod = env->GetMethodID(cls, "setDeviceTransport", "(L" PKG_MEDIA "DeviceTransport;)V"); + env->CallVoidMethod(obj, setTransportMethod, JavaEnums::toJava(env, device->getDeviceTransport()).release()); + + jmethodID setFormFactorMethod = env->GetMethodID(cls, "setDeviceFormFactor", "(L" PKG_MEDIA "DeviceFormFactor;)V"); + env->CallVoidMethod(obj, setFormFactorMethod, JavaEnums::toJava(env, device->getDeviceFormFactor()).release()); + + auto audioDevice = dynamic_cast(device.get());//std::static_pointer_cast(device); + auto type = JavaEnums::toJava(env, audioDevice->audioDeviceDirectionType).release(); + jfieldID audioDeviceDirectionTypeField = env -> GetFieldID(cls, "audioDeviceDirectionType", "L" PKG_MEDIA "AudioDeviceDirectionType;"); + // почему-то не работает установка значения напрямую только через сеттер, хотя в том же RTCConfiguration.cpp это используется + // env->SetObjectField(cls, audioDeviceDirectionTypeField, type.get()); + jmethodID setAudioDeviceDirectionTypeMethod = env->GetMethodID(cls, "setAudioDeviceDirectionType", "(L" PKG_MEDIA "AudioDeviceDirectionType;)V"); + env->CallVoidMethod(obj, setAudioDeviceDirectionTypeMethod, type); + return JavaLocalRef(env, obj); } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp index 60a06549..33eda072 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp @@ -139,7 +139,9 @@ namespace jni if (!pa_threaded_mainloop_in_thread(mainloop)) pa_threaded_mainloop_unlock(mainloop); - return std::make_shared(defaultCaptureDescName, defaultCaptureName); + AudioDevicePtr defaultDevice = std::make_shared(defaultCaptureDescName, defaultCaptureName); + defaultDevice->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + return defaultDevice; } std::set PulseAudioDeviceManager::getAudioCaptureDevices() @@ -174,7 +176,9 @@ namespace jni if (!pa_threaded_mainloop_in_thread(mainloop)) pa_threaded_mainloop_unlock(mainloop); - return std::make_shared(defaultPlaybackDescName, defaultPlaybackName); + AudioDevicePtr defaultDevice = std::make_shared(defaultPlaybackDescName, defaultPlaybackName); + defaultDevice->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + return defaultDevice; } std::set PulseAudioDeviceManager::getAudioPlaybackDevices() @@ -213,7 +217,7 @@ namespace jni } if (info->monitor_of_sink == PA_INVALID_INDEX) { - engine->insertDevice(engine->captureDevices, info->description, info->name, info->index, false); + engine->insertDevice(engine->captureDevices, info->proplist, info->description, info->name, info->index, false, true); } } @@ -226,7 +230,7 @@ namespace jni return; } - engine->insertDevice(engine->captureDevices, info->description, info->name, info->index, true); + engine->insertDevice(engine->captureDevices, info->proplist, info->description, info->name, info->index, true, true); } void PulseAudioDeviceManager::getSinkInfoCallback(pa_context * ctx, const pa_sink_info * info, int last, void * userdata) @@ -250,7 +254,7 @@ namespace jni return; } - engine->insertDevice(engine->playbackDevices, info->description, info->name, info->index, false); + engine->insertDevice(engine->playbackDevices, info->proplist, info->description, info->name, info->index, false, false); } void PulseAudioDeviceManager::newSinkCallback(pa_context * ctx, const pa_sink_info * info, int last, void * userdata) @@ -262,14 +266,20 @@ namespace jni return; } - engine->insertDevice(engine->playbackDevices, info->description, info->name, info->index, true); + engine->insertDevice(engine->playbackDevices, info->proplist, info->description, info->name, info->index, true, false); } - void PulseAudioDeviceManager::insertDevice(DeviceList & devices, const char * name, const char * desc, uint32_t index, bool notify) + void PulseAudioDeviceManager::insertDevice(DeviceList & devices, pa_proplist * proplist, const char * name, const char * desc, uint32_t index, bool notify, bool isCapture) { auto device = std::make_shared(name, desc); if (devices.insertDevice(device)) { + if (isCapture) { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + } else { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + } + fillAdditionalTypes(device, proplist); deviceMap[index] = device; } if (notify) { @@ -277,7 +287,7 @@ namespace jni } } - void PulseAudioDeviceManager::removeDevice(DeviceList & devices, uint32_t index) + void PulseAudioDeviceManager::removeDevice(DeviceList & devices, uint32_t index, bool isCapture) { auto it = deviceMap.find(index); if (it == deviceMap.end()) { @@ -285,6 +295,12 @@ namespace jni } if (devices.removeDevice(it->second)) { + if (isCapture) { + it->second->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + } else { + it->second->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + } + notifyDeviceDisconnected(it->second); deviceMap.erase(it); } @@ -317,7 +333,7 @@ namespace jni op = pa_context_get_source_info_by_index(ctx, idx, newSourceCallback, engine); } if (operation == PA_SUBSCRIPTION_EVENT_REMOVE) { - engine->removeDevice(engine->captureDevices, idx); + engine->removeDevice(engine->captureDevices, idx, true); } } if (facility == PA_SUBSCRIPTION_EVENT_SINK) { @@ -325,7 +341,7 @@ namespace jni op = pa_context_get_sink_info_by_index(ctx, idx, newSinkCallback, engine); } if (operation == PA_SUBSCRIPTION_EVENT_REMOVE) { - engine->removeDevice(engine->playbackDevices, idx); + engine->removeDevice(engine->playbackDevices, idx, false); } } @@ -334,5 +350,58 @@ namespace jni } } + void PulseAudioDeviceManager::fillAdditionalTypes(AudioDevicePtr device, pa_proplist * proplist) { + // all property values see here https://docs.rs/libpulse-sys/latest/libpulse_sys/proplist/ + const char *formFactor; + formFactor = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR); + std::string formFactorStr = ""; + if (formFactor) { + formFactorStr = std::string(formFactor); + } + + const char *deviceTransport; + deviceTransport = pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS); + std::string deviceTransportStr = ""; + if (deviceTransport) { + deviceTransportStr = std::string(deviceTransport); + } + + const char *propHDMI; + propHDMI = pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS_PATH); + if (propHDMI && std::string(propHDMI).find("_hdmi") != std::string::npos) { + deviceTransportStr = "hdmi"; + } + + + device->setDeviceFormFactor(getActualFormFactor(formFactorStr)); + device->setDeviceTransport(getActualTransport(deviceTransportStr)); + } + + DeviceFormFactor PulseAudioDeviceManager::getActualFormFactor(std::string formFactor) { + if (formFactor == "speaker") { + return DeviceFormFactor::ffSpeaker; + } else if (formFactor == "microphone") { + return DeviceFormFactor::ffMicrophone; + } else if (formFactor == "headset") { + return DeviceFormFactor::ffHeadset; + } else if (formFactor == "headphone") { + return DeviceFormFactor::ffHeadphone; + } else { + return DeviceFormFactor::ffUnknown; + } +// return DeviceFormFactor::ffUnknown; + } + + DeviceTransport PulseAudioDeviceManager::getActualTransport(std::string transport) { + if (transport == "usb") { + return DeviceTransport::trUsb; + } else if (transport == "bluetooth") { + return DeviceTransport::trWireless; + } else if (transport == "hdmi") { + return DeviceTransport::trHdmi; + } else { + return DeviceTransport::trUnknown; + } + } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp index 607fd259..6504bb4b 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp @@ -20,6 +20,7 @@ #include "rtc_base/logging.h" #include +#include #if !defined(MAC_OS_VERSION_12_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster @@ -224,9 +225,11 @@ namespace jni } if (scope == kAudioObjectPropertyScopeInput) { + found->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; setDefaultCaptureDevice(found); } else if (scope == kAudioObjectPropertyScopeOutput) { + found->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; setDefaultPlaybackDevice(found); } } @@ -257,6 +260,7 @@ namespace jni if (removed == def) { onDefaultDeviceChanged(scope, captureDevices, def); } + removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; } else if (scope == kAudioObjectPropertyScopeOutput) { AudioDevicePtr def = getDefaultAudioPlaybackDevice(); @@ -264,6 +268,7 @@ namespace jni if (removed == def) { onDefaultDeviceChanged(scope, playbackDevices, def); } + removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; } notifyDeviceDisconnected(removed); @@ -300,10 +305,13 @@ namespace jni if (channels > 0) { if (scope == kAudioObjectPropertyScopeOutput) { device = std::make_shared(name, id); + device-> audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; } else if (scope == kAudioObjectPropertyScopeInput) { device = std::make_shared(name, id); + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; } + fillAdditionalTypes(deviceID, scope, device); } return device; @@ -316,9 +324,11 @@ namespace jni } if (scope == kAudioObjectPropertyScopeOutput) { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; return playbackDevices.insertDevice(device); } else if (scope == kAudioObjectPropertyScopeInput) { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; return captureDevices.insertDevice(device); } @@ -404,5 +414,67 @@ namespace jni return noErr; } + + void CoreAudioDeviceManager::fillAdditionalTypes(const AudioDeviceID & deviceID, const AudioObjectPropertyScope & scope, AudioDevicePtr device) { + // Get Transport Type + AudioObjectPropertyAddress address = { + kAudioDevicePropertyTransportType, + scope, + kAudioObjectPropertyElementMaster + }; + unsigned int transportType = 0; + UInt32 size = 0; + size = sizeof(transportType); + OSStatus status = AudioObjectGetPropertyData(deviceID, &address, 0, nullptr, &size, &transportType); + // Get Transport Type -- end + + address.mSelector = kAudioDevicePropertyDataSource; + UInt32 sourceCode = 0; + size = sizeof(UInt32); + status = AudioObjectGetPropertyData(deviceID, &address, 0, nullptr, &size, &sourceCode); + + device->setDeviceTransport(getActualTransport(transportType)); + device->setDeviceFormFactor(getActualFormFactor(sourceCode)); + } + + DeviceFormFactor CoreAudioDeviceManager::getActualFormFactor(const unsigned int sourceID) { + switch(sourceID) { + case kIOAudioOutputPortSubTypeInternalSpeaker: + case kIOAudioOutputPortSubTypeExternalSpeaker: + return DeviceFormFactor::ffSpeaker; + case kIOAudioOutputPortSubTypeHeadphones: + return DeviceFormFactor::ffHeadphone; + case kIOAudioInputPortSubTypeInternalMicrophone: + case kIOAudioInputPortSubTypeExternalMicrophone: + return DeviceFormFactor::ffMicrophone; + case kIOAudioInputPortSubTypeCD: + case kIOAudioOutputPortSubTypeSPDIF: + case kIOAudioOutputPortSubTypeLine: + return DeviceFormFactor::ffUnknown; + default: + return DeviceFormFactor::ffUnknown; + } + } + + DeviceTransport CoreAudioDeviceManager::getActualTransport(const unsigned int transportType) { + switch(transportType) { + case kAudioDeviceTransportTypeUSB: + return DeviceTransport::trUsb; + case kAudioDeviceTransportTypeBluetooth: + return DeviceTransport::trWireless; + case kAudioDeviceTransportTypeHDMI: + return DeviceTransport::trHdmi; + case kAudioDeviceTransportTypeFireWire: + case kAudioDeviceTransportTypeDisplayPort: + case kAudioDeviceTransportTypePCI: + case kAudioDeviceTransportTypeVirtual: + case kAudioDeviceTransportTypeAutoAggregate: + case kAudioDeviceTransportTypeBuiltIn: + case kAudioDeviceTransportTypeUnknown: + return DeviceTransport::trUnknown; + default: + return DeviceTransport::trUnknown; + } + } } } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp index ad3fb4aa..14e32e9b 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp @@ -27,6 +27,8 @@ #include "modules/audio_device/include/audio_device.h" #include "rtc_base/logging.h" +#include + namespace jni { namespace avdev @@ -112,28 +114,77 @@ namespace jni if (enumCaptureDevices) { int16_t deviceCount = audioModule->RecordingDevices(); + // fix duplicated item when device reconect to port. The comparator does not always work correctly. + if (deviceCount > 0) { + captureDevices.clearDevices(); + } for (int i = 0; i < deviceCount; ++i) { if (audioModule->RecordingDeviceName(i, name, guid) == 0) { AudioDevicePtr device = std::make_shared(name, guid); - insertAudioDevice(device, EDataFlow::eCapture); + fillAdditionalTypes(device); + + insertAudioDevice(device, EDataFlow::eCapture); } } } if (enumRenderDevices) { int16_t deviceCount = audioModule->PlayoutDevices(); + // Fix duplicated item when device reconect to port. The comparator does not always work correctly. + if (deviceCount > 0) { + playbackDevices.clearDevices(); + } + for (int i = 0; i < deviceCount; ++i) { if (audioModule->PlayoutDeviceName(i, name, guid) == 0) { AudioDevicePtr device = std::make_shared(name, guid); - insertAudioDevice(device, EDataFlow::eRender); + fillAdditionalTypes(device); + + insertAudioDevice(device, EDataFlow::eRender); } } } } + void WindowsAudioDeviceManager::fillAdditionalTypes(AudioDevicePtr device) + { + MFInitializer initializer; + ComPtr pDevice; + ComPtr endpoint; + ComPtr propertyStore; + HRESULT hr; + PROPVARIANT ff; + PropVariantInit(&ff); + + // convert std::string to LPWSTR + wchar_t* wDeviceID=new wchar_t[4096]; + size_t convertedChars = 0; + mbstowcs_s(&convertedChars, wDeviceID, device->getDescriptor().length() + 1, device->getDescriptor().c_str(), _TRUNCATE); + + hr = deviceEnumerator->GetDevice(wDeviceID, &pDevice); + delete[] wDeviceID; + + THROW_IF_FAILED(hr, "Audio Device Manager: Enumerator get device with ID: %S failed", device->getDescriptor().c_str()); + + hr = pDevice->QueryInterface(__uuidof(IMMEndpoint), (void**)&endpoint); + THROW_IF_FAILED(hr, "Audio Device Manager: Device get endpoint failed"); + + hr = pDevice->OpenPropertyStore(STGM_READ, &propertyStore); + THROW_IF_FAILED(hr, "Audio Device Manager: Device open property store failed"); + + hr = propertyStore->GetValue(PKEY_AudioEndpoint_FormFactor, &ff); + THROW_IF_FAILED(hr, "Audio Device Manager: PropertyStore get form factor property name failed"); + + EndpointFormFactor formFactor = static_cast(ff.uintVal); + PropVariantClear(&ff); + + device->setDeviceTransport(getActualTransport(formFactor)); + device->setDeviceFormFactor(getActualFormFactor(formFactor)); + } + void WindowsAudioDeviceManager::addDevice(LPCWSTR deviceId) { if (deviceId == nullptr) { @@ -156,8 +207,8 @@ namespace jni std::string id = WideStrToStr(deviceId); - removeAudioDevice(captureDevices, id); - removeAudioDevice(playbackDevices, id); + removeAudioDevice(captureDevices, id, eCapture); + removeAudioDevice(playbackDevices, id, eRender); } AudioDevicePtr WindowsAudioDeviceManager::createDefaultAudioDevice(const EDataFlow& dataFlow) @@ -186,6 +237,13 @@ namespace jni std::string name = WideStrToStr(pv.pwszVal); AudioDevicePtr device = std::make_shared(name, id); + if (dataFlow == eCapture) { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + } else { + device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + } + + fillAdditionalTypes(device); PropVariantClear(&pv); @@ -223,6 +281,8 @@ namespace jni AudioDevicePtr device = std::make_shared(name, id); + fillAdditionalTypes(device); + if (dataFlow != nullptr) { *dataFlow = flow; } @@ -239,10 +299,14 @@ namespace jni } if (dataFlow == eCapture) { + device->audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; + captureDevices.insertDevice(device); return true; } else if (dataFlow == eRender) { + device->audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; + playbackDevices.insertDevice(device); return true; } @@ -250,7 +314,7 @@ namespace jni return false; } - void WindowsAudioDeviceManager::removeAudioDevice(DeviceList & devices, std::string id) + void WindowsAudioDeviceManager::removeAudioDevice(DeviceList & devices, std::string id, EDataFlow dataFlow) { auto predicate = [id](const AudioDevicePtr & dev) { return id == dev->getDescriptor(); @@ -259,6 +323,12 @@ namespace jni AudioDevicePtr removed = devices.removeDevice(predicate); if (removed) { + if (dataFlow == eCapture) { + removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + } else { + removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + } + notifyDeviceDisconnected(removed); } } @@ -379,5 +449,40 @@ namespace jni { return S_OK; } + + DeviceFormFactor WindowsAudioDeviceManager::getActualFormFactor(EndpointFormFactor formFactor) { + switch (formFactor) { + case RemoteNetworkDevice: + case UnknownDigitalPassthrough: + case EndpointFormFactor_enum_count: + case UnknownFormFactor: + case LineLevel: + case SPDIF: + return DeviceFormFactor::ffUnknown; + case Speakers: + return DeviceFormFactor::ffSpeaker; + case Headphones: + return DeviceFormFactor::ffHeadphone; + case Headset: + case Handset: + return DeviceFormFactor::ffHeadset; + case Microphone: + return DeviceFormFactor::ffMicrophone; + case DigitalAudioDisplayDevice: // для адуио устройств - это должно быть hdmi устройство + return DeviceFormFactor::ffUnknown; + default: + return DeviceFormFactor::ffUnknown; + } + } + + DeviceTransport WindowsAudioDeviceManager::getActualTransport(EndpointFormFactor formFactor) { + // пока для винды не понятно как достоверно определить транспорт аудио устройства. Только HDMI могу могу + switch (formFactor) { + case DigitalAudioDisplayDevice: + return DeviceTransport::trHdmi; + default: + return DeviceTransport::trUnknown; + } + } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/video/windows/WindowsVideoDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/video/windows/WindowsVideoDeviceManager.cpp index ae1e9817..936c59cb 100644 --- a/webrtc-jni/src/main/cpp/src/media/video/windows/WindowsVideoDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/video/windows/WindowsVideoDeviceManager.cpp @@ -87,6 +87,8 @@ namespace jni uint32_t deviceCount = info->NumberOfDevices(); if (deviceCount > 0) { + // fix duplicated item when device reconect to port. The comparator does not always work correctly. + captureDevices.clearDevices(); const uint32_t size = webrtc::kVideoCaptureDeviceNameLength; for (uint32_t i = 0; i < deviceCount; ++i) { diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/AudioDeviceDirectionType.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/AudioDeviceDirectionType.java new file mode 100644 index 00000000..1338624c --- /dev/null +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/AudioDeviceDirectionType.java @@ -0,0 +1,7 @@ +package dev.onvoid.webrtc.media; + +public enum AudioDeviceDirectionType { + UNKNOWN, + CAPTURE, + RENDER +} diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java index 9b0c87f1..58ca66c3 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java @@ -24,6 +24,9 @@ public abstract class Device { private final String name; + private DeviceTransport deviceTransport = DeviceTransport.UNKNOWN; + + private DeviceFormFactor deviceFormFactor = DeviceFormFactor.UNKNOWN; protected Device(String name, String descriptor) { this.name = name; @@ -38,6 +41,22 @@ public String getName() { return name; } + public DeviceTransport getDeviceTransport() { + return deviceTransport; + } + + public DeviceFormFactor getDeviceFormFactor() { + return deviceFormFactor; + } + + public void setDeviceFormFactor(DeviceFormFactor deviceFormFactor) { + this.deviceFormFactor = deviceFormFactor; + } + + public void setDeviceTransport(DeviceTransport deviceTransport) { + this.deviceTransport = deviceTransport; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -60,7 +79,7 @@ public int hashCode() { @Override public String toString() { - return String.format("%s [name=%s, descriptor=%s]", - Device.class.getSimpleName(), name, descriptor); + return String.format("%s [name=%s, descriptor=%s, transport=%s, formFactor=%s]", + Device.class.getSimpleName(), name, descriptor, deviceTransport.toString(), deviceFormFactor.toString()); } } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java new file mode 100644 index 00000000..973af83c --- /dev/null +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java @@ -0,0 +1,25 @@ +package dev.onvoid.webrtc.media; + +public enum DeviceFormFactor { + UNKNOWN, + SPEAKER, + MICROPHONE, + HEADSET, + HEADPHONE; + + @Override + public String toString() { + switch (this) { + case SPEAKER: + return "SPEAKER"; + case MICROPHONE: + return "MICROPHONE"; + case HEADSET: + return "HEADSET"; + case HEADPHONE: + return "HEADPHONE"; + default: + return "UNKNOWN"; + } + } +} diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java new file mode 100644 index 00000000..d3ed5f23 --- /dev/null +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java @@ -0,0 +1,22 @@ +package dev.onvoid.webrtc.media; + +public enum DeviceTransport { + UNKNOWN, + HDMI, + USB, + WIRELESS; + + @Override + public String toString() { + switch (this) { + case HDMI: + return "HDMI"; + case USB: + return "USB"; + case WIRELESS: + return "WIRELESS"; + default: + return "UNKNOWN"; + } + } +} diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java index 23b19c7e..28819cd6 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java @@ -16,12 +16,22 @@ package dev.onvoid.webrtc.media.audio; +import dev.onvoid.webrtc.media.AudioDeviceDirectionType; import dev.onvoid.webrtc.media.Device; public class AudioDevice extends Device { + private AudioDeviceDirectionType audioDeviceDirectionType; protected AudioDevice(String name, String descriptor) { super(name, descriptor); + audioDeviceDirectionType = AudioDeviceDirectionType.UNKNOWN; } + public void setAudioDeviceDirectionType(AudioDeviceDirectionType newAudioDeviceDirectionType) { + this.audioDeviceDirectionType = newAudioDeviceDirectionType; + } + + public AudioDeviceDirectionType getAudioDeviceDirectionType() { + return audioDeviceDirectionType; + } } From 9fa29ff4491eb00c1605063392e53a74b1c1f29c Mon Sep 17 00:00:00 2001 From: Stasonych Date: Wed, 10 Sep 2025 15:18:51 +0500 Subject: [PATCH 2/6] Added additional fields for devices and fixes merge request. It can be useful, for example, if there is a need in the application code to set a priority for selecting a playback device. I also added a patch that fixes an empty device GUID on macOS. --- webrtc-jni/src/main/cpp/CMakeLists.txt | 2 + .../cpp/dependencies/webrtc/CMakeLists.txt | 6 +- .../macos/patch_for_mac_deviceId.patch | 52 +-- .../src/main/cpp/include/media/Device.h | 4 +- .../cpp/include/media/audio/AudioDevice.h | 14 +- .../main/cpp/src/JNI_AudioDeviceModule.cpp | 440 ------------------ .../cpp/src/JNI_AudioDeviceModuleBase.cpp | 2 + .../src/main/cpp/src/JNI_MediaDevices.cpp | 4 - webrtc-jni/src/main/cpp/src/media/Device.cpp | 21 +- .../main/cpp/src/media/audio/AudioDevice.cpp | 21 +- .../audio/linux/PulseAudioDeviceManager.cpp | 41 +- .../windows/WindowsAudioDeviceManager.cpp | 4 +- .../java/dev/onvoid/webrtc/media/Device.java | 10 +- .../onvoid/webrtc/media/DeviceFormFactor.java | 16 - .../onvoid/webrtc/media/DeviceTransport.java | 14 - .../webrtc/media/audio/AudioDevice.java | 13 +- 16 files changed, 80 insertions(+), 584 deletions(-) diff --git a/webrtc-jni/src/main/cpp/CMakeLists.txt b/webrtc-jni/src/main/cpp/CMakeLists.txt index c171e94a..a761f4c0 100644 --- a/webrtc-jni/src/main/cpp/CMakeLists.txt +++ b/webrtc-jni/src/main/cpp/CMakeLists.txt @@ -50,6 +50,7 @@ add_subdirectory(dependencies/jni-voithos) file(GLOB SOURCES_ROOT "src/*.cpp") file(GLOB SOURCES_API "src/api/*.cpp") +file(GLOB SOURCES_API_AUDIO "src/api/audio/*.cpp") file(GLOB SOURCES_MEDIA "src/media/*.cpp") file(GLOB SOURCES_MEDIA_AUDIO "src/media/audio/*.cpp") file(GLOB SOURCES_MEDIA_AUDIO_OS "src/media/audio/${SOURCE_TARGET}/*.cpp") @@ -63,6 +64,7 @@ file(GLOB SOURCES_RTC "src/rtc/*.cpp") list(APPEND SOURCES ${SOURCES_ROOT} ${SOURCES_API} + ${SOURCES_API_AUDIO} ${SOURCES_MEDIA} ${SOURCES_MEDIA_AUDIO} ${SOURCES_MEDIA_AUDIO_OS} diff --git a/webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt b/webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt index b4b83696..a11a7b5e 100644 --- a/webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt +++ b/webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt @@ -237,14 +237,16 @@ if (PATCHES) message(STATUS "Applying ${PATCH}") execute_process( - COMMAND patch -p0 --forward - WORKING_DIRECTORY "${WEBRTC_SRC_DIR}" + COMMAND git apply + WORKING_DIRECTORY "${WEBRTC_SRC}" INPUT_FILE "${PATCH}" OUTPUT_VARIABLE OUTPUT RESULT_VARIABLE RESULT) if (RESULT EQUAL 0) message(STATUS "Patch applied: ${PATCH}") + else() + message(STATUS "Warning: Patch ${PATCH} not applied ....") endif() endforeach(PATCH) endif() diff --git a/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch b/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch index 4d60020b..af35cefb 100644 --- a/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch +++ b/webrtc-jni/src/main/cpp/dependencies/webrtc/patches/macos/patch_for_mac_deviceId.patch @@ -6,35 +6,27 @@ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc ---- a/modules/audio_device/mac/audio_device_mac.cc (revision d75b9e9ff07ee42841b4e416629c9fbd4b058905) -+++ b/modules/audio_device/mac/audio_device_mac.cc (date 1696335540383) -@@ -831,9 +831,11 @@ - - memset(name, 0, kAdmMaxDeviceNameSize); - -- if (guid != NULL) { -- memset(guid, 0, kAdmMaxGuidSize); -- } -+ memset(guid, 0, kAdmMaxGuidSize); -+ AudioDeviceID deviceIds[MaxNumberDevices]; -+ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput, deviceIds, MaxNumberDevices); -+ std::string deviceId = std::to_string(deviceIds[index]); -+ deviceId.copy(guid, kAdmMaxGuidSize); - +--- a/modules/audio_device/mac/audio_device_mac.cc (revision e4445e46a910eb407571ec0b0b8b7043562678cf) ++++ b/modules/audio_device/mac/audio_device_mac.cc (date 1757498230183) +@@ -834,6 +834,10 @@ + + if (guid != NULL) { + memset(guid, 0, kAdmMaxGuidSize); ++ AudioDeviceID deviceIds[MaxNumberDevices]; ++ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput, deviceIds, MaxNumberDevices); ++ std::string deviceId = std::to_string(deviceIds[index]); ++ deviceId.copy(guid, kAdmMaxGuidSize); + } + return GetDeviceName(kAudioDevicePropertyScopeOutput, index, - rtc::ArrayView(name, kAdmMaxDeviceNameSize)); -@@ -850,9 +852,11 @@ - - memset(name, 0, kAdmMaxDeviceNameSize); - -- if (guid != NULL) { -- memset(guid, 0, kAdmMaxGuidSize); -- } -+ memset(guid, 0, kAdmMaxGuidSize); -+ AudioDeviceID deviceIds[MaxNumberDevices]; -+ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeInput, deviceIds, MaxNumberDevices); -+ std::string deviceId = std::to_string(deviceIds[index]); -+ deviceId.copy(guid, kAdmMaxGuidSize); - +@@ -853,6 +857,10 @@ + + if (guid != NULL) { + memset(guid, 0, kAdmMaxGuidSize); ++ AudioDeviceID deviceIds[MaxNumberDevices]; ++ int numberDevices = GetNumberDevices(kAudioDevicePropertyScopeInput, deviceIds, MaxNumberDevices); ++ std::string deviceId = std::to_string(deviceIds[index]); ++ deviceId.copy(guid, kAdmMaxGuidSize); + } + return GetDeviceName(kAudioDevicePropertyScopeInput, index, - rtc::ArrayView(name, kAdmMaxDeviceNameSize)); diff --git a/webrtc-jni/src/main/cpp/include/media/Device.h b/webrtc-jni/src/main/cpp/include/media/Device.h index f083267f..d8f22cf5 100644 --- a/webrtc-jni/src/main/cpp/include/media/Device.h +++ b/webrtc-jni/src/main/cpp/include/media/Device.h @@ -29,7 +29,7 @@ namespace jni namespace avdev { /** - DeviceTransport and DeviceFormFactor only foe audio devices. + DeviceTransport and DeviceFormFactor only for audio devices. */ enum class DeviceTransport { trUnknown, @@ -59,8 +59,6 @@ namespace jni std::string getDescriptor() const; DeviceTransport getDeviceTransport(); DeviceFormFactor getDeviceFormFactor(); - void setDeviceTransport(DeviceTransport deviceTransport); - void setDeviceFormFactor(DeviceFormFactor deviceFormFactor); protected: Device(std::string name, std::string descriptor); diff --git a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h index b67b5fbe..80d7ab0d 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h @@ -28,18 +28,18 @@ namespace jni namespace avdev { - enum class AudioDeviceDirectionType { - adtUnknown, - adtCapture, - adtRender - }; +enum class AudioDeviceDirectionType { + adtUnknown, + adtCapture, + adtRender + }; class AudioDevice : public Device { public: AudioDevice(std::string name, std::string descriptor); virtual ~AudioDevice() {}; - AudioDeviceDirectionType audioDeviceDirectionType; + AudioDeviceDirectionType directionType; }; } @@ -54,7 +54,7 @@ namespace jni jmethodID ctor; jfieldID name; jfieldID descriptor; - jfieldID audioDeviceDirectionType; + jfieldID directionType; }; JavaLocalRef toJavaAudioDevice(JNIEnv * env, avdev::DevicePtr device); diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp index 313c2fc9..2528d252 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp @@ -24,446 +24,6 @@ #include "api/audio/create_audio_device_module.h" #include "api/scoped_refptr.h" -#include "modules/audio_device/include/audio_device.h" -#include "rtc_base/logging.h" - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_initPlayout -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->InitPlayout() != 0) { - env->Throw(jni::JavaError(env, "Init playout failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_stopPlayout -(JNIEnv* env, jobject caller) -{ - webrtc::AudioDeviceModule* audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->StopPlayout() != 0) { - env->Throw(jni::JavaError(env, "Stop playout failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_startPlayout -(JNIEnv* env, jobject caller) -{ - webrtc::AudioDeviceModule* audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->StartPlayout() != 0) { - env->Throw(jni::JavaError(env, "Start playout failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_initRecording -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->InitRecording() != 0) { - env->Throw(jni::JavaError(env, "Init recording failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_stopRecording -(JNIEnv* env, jobject caller) -{ - webrtc::AudioDeviceModule* audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->StopRecording() != 0) { - env->Throw(jni::JavaError(env, "Stop recording failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_startRecording -(JNIEnv* env, jobject caller) -{ - webrtc::AudioDeviceModule* audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->StartRecording() != 0) { - env->Throw(jni::JavaError(env, "Start recording failed")); - return; - } -} - -JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getPlayoutDevices -(JNIEnv* env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, nullptr); - - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - - int16_t deviceCount = audioModule->PlayoutDevices(); - - jni::JavaArrayList deviceList(env, deviceCount); - - for (int i = 0; i < deviceCount; ++i) { - if (audioModule->PlayoutDeviceName(i, name, guid) == 0) { - auto device = std::make_shared(name, guid); - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; - deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); - } - } - - return deviceList.listObject().release(); -} - -JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getRecordingDevices -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, nullptr); - - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - - int16_t deviceCount = audioModule->RecordingDevices(); - - jni::JavaArrayList deviceList(env, deviceCount); - - for (int i = 0; i < deviceCount; ++i) { - if (audioModule->RecordingDeviceName(i, name, guid) == 0) { - auto device = std::make_shared(name, guid); - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; - - deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); - } - } - - return deviceList.listObject().release(); -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setPlayoutDevice -(JNIEnv * env, jobject caller, jobject device) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (!device) { - env->Throw(jni::JavaNullPointerException(env, "AudioDevice is null")); - return; - } - - jni::JavaObject obj(env, jni::JavaLocalRef(env, device)); - - const auto javaClass = jni::JavaClasses::get(env); - const std::string devGuid = jni::JavaString::toNative(env, obj.getString(javaClass->descriptor)); - - uint16_t index = 0; - int16_t deviceCount = audioModule->PlayoutDevices(); - - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - - for (int i = 0; i < deviceCount; ++i) { - if ((audioModule->PlayoutDeviceName(i, name, guid) == 0) && devGuid == std::string(guid)) { - index = i; - break; - } - } - - if (audioModule->SetPlayoutDevice(index) != 0) { - env->Throw(jni::JavaError(env, "Set playout device failed")); - return; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setRecordingDevice -(JNIEnv * env, jobject caller, jobject device) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (!device) { - env->Throw(jni::JavaNullPointerException(env, "AudioDevice is null")); - return; - } - - jni::JavaObject obj(env, jni::JavaLocalRef(env, device)); - - const auto javaClass = jni::JavaClasses::get(env); - const std::string devGuid = jni::JavaString::toNative(env, obj.getString(javaClass->descriptor)); - - uint16_t index = 0; - int16_t deviceCount = audioModule->RecordingDevices(); - - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - - for (int i = 0; i < deviceCount; ++i) { - if ((audioModule->RecordingDeviceName(i, name, guid) == 0) && devGuid == std::string(guid)) { - index = i; - break; - } - } - - if (audioModule->SetRecordingDevice(index) != 0) { - env->Throw(jni::JavaError(env, "Set recording device failed")); - return; - } -} - -JNIEXPORT jboolean JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_isSpeakerMuted -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, true); - - bool mute = true; - - audioModule->SpeakerMute(&mute); - - return mute; -} - -JNIEXPORT jboolean JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_isMicrophoneMuted -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, true); - - bool mute = true; - - audioModule->MicrophoneMute(&mute); - - return mute; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getSpeakerVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->SpeakerVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get speaker volume failed")); - } - - return volume; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getMaxSpeakerVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->MaxSpeakerVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get max speaker volume failed")); - } - - return volume; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getMinSpeakerVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->MinSpeakerVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get min speaker volume failed")); - } - - return volume; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getMicrophoneVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->MicrophoneVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get microphone volume failed")); - } - - return volume; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getMaxMicrophoneVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->MaxMicrophoneVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get max microphone volume failed")); - } - - return volume; -} - -JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_getMinMicrophoneVolume -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - uint32_t volume = 0; - - if (audioModule->MinMicrophoneVolume(&volume) != 0) { - env->Throw(jni::JavaError(env, "Get min microphone volume failed")); - } - - return volume; -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setSpeakerVolume -(JNIEnv * env, jobject caller, jint volume) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->SetSpeakerVolume(static_cast(volume)) != 0) { - env->Throw(jni::JavaError(env, "Set speaker volume failed")); - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setSpeakerMute -(JNIEnv * env, jobject caller, jboolean mute) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->SetSpeakerMute(mute) != 0) { - env->Throw(jni::JavaError(env, "Set speaker mute failed")); - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setMicrophoneVolume -(JNIEnv * env, jobject caller, jint volume) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->SetMicrophoneVolume(static_cast(volume)) != 0) { - env->Throw(jni::JavaError(env, "Set microphone volume failed")); - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_setMicrophoneMute -(JNIEnv * env, jobject caller, jboolean mute) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->SetMicrophoneMute(mute) != 0) { - env->Throw(jni::JavaError(env, "Set microphone mute failed")); - } -} - -JNIEXPORT jlong JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_addSinkInternal -(JNIEnv * env, jobject caller, jobject jSink) -{ - if (jSink == nullptr) { - env->Throw(jni::JavaNullPointerException(env, "AudioSink must not be null")); - return 0; - } - - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - auto sink = new jni::AudioTransportSink(env, jni::JavaGlobalRef(env, jSink)); - - audioModule->RegisterAudioCallback(sink); - - return reinterpret_cast(sink); -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_removeSinkInternal -(JNIEnv * env, jobject caller, jlong sinkHandle) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - auto sink = reinterpret_cast(sinkHandle); - - if (sink != nullptr) { - audioModule->RegisterAudioCallback(nullptr); - - delete sink; - } -} - -JNIEXPORT jlong JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_addSourceInternal -(JNIEnv * env, jobject caller, jobject jSource) -{ - if (jSource == nullptr) { - env->Throw(jni::JavaNullPointerException(env, "AudioSource must not be null")); - return 0; - } - - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLEV(audioModule, 0); - - auto source = new jni::AudioTransportSource(env, jni::JavaGlobalRef(env, jSource)); - - audioModule->RegisterAudioCallback(source); - - return reinterpret_cast(source); -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_removeSourceInternal -(JNIEnv * env, jobject caller, jlong sourceHandle) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - auto source = reinterpret_cast(sourceHandle); - - if (source != nullptr) { - audioModule->RegisterAudioCallback(nullptr); - - delete source; - } -} - -JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_disposeInternal -(JNIEnv * env, jobject caller) -{ - webrtc::AudioDeviceModule * audioModule = GetHandle(env, caller); - CHECK_HANDLE(audioModule); - - if (audioModule->Initialized()) { - audioModule->Terminate(); - } - - webrtc::RefCountReleaseStatus status = audioModule->Release(); - - if (status != webrtc::RefCountReleaseStatus::kDroppedLastRef) { - RTC_LOG(LS_WARNING) << "Native object was not deleted. A reference is still around somewhere."; - } - - SetHandle(env, caller, nullptr); - - audioModule = nullptr; -} JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModule_initialize (JNIEnv * env, jobject caller, jobject jAudioLayer) diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp index c35558e5..38b82567 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp @@ -121,6 +121,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModuleBa for (int i = 0; i < deviceCount; ++i) { if (audioModule->PlayoutDeviceName(i, name, guid) == 0) { auto device = std::make_shared(name, guid); + device -> directionType = jni::avdev::AudioDeviceDirectionType::adtRender; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } @@ -145,6 +146,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_audio_AudioDeviceModuleBa for (int i = 0; i < deviceCount; ++i) { if (audioModule->RecordingDeviceName(i, name, guid) == 0) { auto device = std::make_shared(name, guid); + device -> directionType = jni::avdev::AudioDeviceDirectionType::adtCapture; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp index 7df91b3a..cbf777d1 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp @@ -67,7 +67,6 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioPlaybackDevice(); - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { @@ -84,7 +83,6 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioCaptureDevice(); - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { @@ -105,7 +103,6 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getAudioRend jni::JavaArrayList deviceList(env, devices.size()); for (const auto & device : devices) { - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } @@ -129,7 +126,6 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getAudioCapt jni::JavaArrayList deviceList(env, devices.size()); for (const auto & device : devices) { - device -> audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; deviceList.add(jni::AudioDevice::toJavaAudioDevice(env, device)); } diff --git a/webrtc-jni/src/main/cpp/src/media/Device.cpp b/webrtc-jni/src/main/cpp/src/media/Device.cpp index 0910381a..2a68b865 100644 --- a/webrtc-jni/src/main/cpp/src/media/Device.cpp +++ b/webrtc-jni/src/main/cpp/src/media/Device.cpp @@ -28,8 +28,6 @@ namespace jni name(name), descriptor(descriptor) { - deviceTransport = DeviceTransport::trUnknown; - deviceFormFactor = DeviceFormFactor::ffUnknown; } bool Device::operator==(const Device & other) @@ -66,14 +64,6 @@ namespace jni { return deviceFormFactor; } - - void Device::setDeviceTransport(DeviceTransport newDeviceTransport) { - deviceTransport = newDeviceTransport; - } - - void Device::setDeviceFormFactor(DeviceFormFactor newDeviceFormFactor) { - deviceFormFactor = newDeviceFormFactor; - } } namespace Device @@ -86,12 +76,11 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); - jclass cls = env->GetObjectClass(obj); - jmethodID setTransportMethod = env->GetMethodID(cls, "setDeviceTransport", "(L" PKG_MEDIA "DeviceTransport;)V"); - env->CallVoidMethod(obj, setTransportMethod, JavaEnums::toJava(env, device->getDeviceTransport()).release()); + auto deviceTransport = JavaEnums::toJava(env, device->deviceTransport); + env->SetObjectField(obj, javaClass->deviceTransport, deviceTransport.get()); - jmethodID setFormFactorMethod = env->GetMethodID(cls, "setDeviceFormFactor", "(L" PKG_MEDIA "DeviceFormFactor;)V"); - env->CallVoidMethod(obj, setFormFactorMethod, JavaEnums::toJava(env, device->getDeviceFormFactor()).release()); + auto deviceFormFactor = JavaEnums::toJava(env, device->deviceFormFactor); + env->SetObjectField(obj, javaClass->deviceFormFactor, deviceFormFactor.get()); return JavaLocalRef(env, obj); } @@ -104,6 +93,8 @@ namespace jni name = GetFieldID(env, cls, "name", STRING_SIG); descriptor = GetFieldID(env, cls, "descriptor", STRING_SIG); + deviceTransport = GetFieldID(env, cls, "deviceTransport", "L" PKG "DeviceTransport;"); + deviceFormFactor = GetFieldID(env, cls, "deviceFormFactor", "L" PKG "DeviceFormFactor;"); } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp index c474e0e1..1b56dca9 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp @@ -28,7 +28,6 @@ namespace jni AudioDevice::AudioDevice(std::string name, std::string descriptor) : Device(name, descriptor) { - audioDeviceDirectionType = AudioDeviceDirectionType::adtUnknown; } } @@ -42,20 +41,15 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); - jclass cls = env->GetObjectClass(obj); - jmethodID setTransportMethod = env->GetMethodID(cls, "setDeviceTransport", "(L" PKG_MEDIA "DeviceTransport;)V"); - env->CallVoidMethod(obj, setTransportMethod, JavaEnums::toJava(env, device->getDeviceTransport()).release()); + auto deviceTransport = JavaEnums::toJava(env, device->getDeviceTransport); + env->SetObjectField(obj, javaClass->deviceTransport, deviceTransport.get()); - jmethodID setFormFactorMethod = env->GetMethodID(cls, "setDeviceFormFactor", "(L" PKG_MEDIA "DeviceFormFactor;)V"); - env->CallVoidMethod(obj, setFormFactorMethod, JavaEnums::toJava(env, device->getDeviceFormFactor()).release()); + auto deviceFormFactor = JavaEnums::toJava(env, device->getDeviceFormFactor); + env->SetObjectField(obj, javaClass->deviceFormFactor, deviceFormFactor.get()); - auto audioDevice = dynamic_cast(device.get());//std::static_pointer_cast(device); - auto type = JavaEnums::toJava(env, audioDevice->audioDeviceDirectionType).release(); - jfieldID audioDeviceDirectionTypeField = env -> GetFieldID(cls, "audioDeviceDirectionType", "L" PKG_MEDIA "AudioDeviceDirectionType;"); - // почему-то не работает установка значения напрямую только через сеттер, хотя в том же RTCConfiguration.cpp это используется - // env->SetObjectField(cls, audioDeviceDirectionTypeField, type.get()); - jmethodID setAudioDeviceDirectionTypeMethod = env->GetMethodID(cls, "setAudioDeviceDirectionType", "(L" PKG_MEDIA "AudioDeviceDirectionType;)V"); - env->CallVoidMethod(obj, setAudioDeviceDirectionTypeMethod, type); + auto audioDevice = dynamic_cast(device.get()); + auto directionType = JavaEnums::toJava(env, audioDevice->directionType); + env->SetObjectField(obj, javaClass->directionType, directionType.get()); return JavaLocalRef(env, obj); } @@ -68,6 +62,7 @@ namespace jni name = GetFieldID(env, cls, "name", STRING_SIG); descriptor = GetFieldID(env, cls, "descriptor", STRING_SIG); + directionType = GetFieldID(env, cls, "directionType", "L" PKG "AudioDeviceDirectionType;"); } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp index 33eda072..61dd8c21 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp @@ -378,30 +378,29 @@ namespace jni } DeviceFormFactor PulseAudioDeviceManager::getActualFormFactor(std::string formFactor) { - if (formFactor == "speaker") { - return DeviceFormFactor::ffSpeaker; - } else if (formFactor == "microphone") { - return DeviceFormFactor::ffMicrophone; - } else if (formFactor == "headset") { - return DeviceFormFactor::ffHeadset; - } else if (formFactor == "headphone") { - return DeviceFormFactor::ffHeadphone; - } else { - return DeviceFormFactor::ffUnknown; - } -// return DeviceFormFactor::ffUnknown; + switch(formFactor) { + case "speaker": + return DeviceFormFactor::ffSpeaker; + case "microphone": + return DeviceFormFactor::ffMicrophone; + case "headset": + return DeviceFormFactor::ffHeadset; + case "headphone": + return DeviceFormFactor::ffHeadphone; + default: + return DeviceFormFactor::ffUnknown; } DeviceTransport PulseAudioDeviceManager::getActualTransport(std::string transport) { - if (transport == "usb") { - return DeviceTransport::trUsb; - } else if (transport == "bluetooth") { - return DeviceTransport::trWireless; - } else if (transport == "hdmi") { - return DeviceTransport::trHdmi; - } else { - return DeviceTransport::trUnknown; - } + switch(transport) { + case "usb": + return DeviceTransport::trUsb; + case "bluetooth": + return DeviceTransport::trWireless; + case "hdmi": + return DeviceTransport::trHdmi; + default: + return DeviceTransport::trUnknown; } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp index 14e32e9b..561fe639 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp @@ -468,7 +468,7 @@ namespace jni return DeviceFormFactor::ffHeadset; case Microphone: return DeviceFormFactor::ffMicrophone; - case DigitalAudioDisplayDevice: // для адуио устройств - это должно быть hdmi устройство + case DigitalAudioDisplayDevice: // for audio devices this means hdmi return DeviceFormFactor::ffUnknown; default: return DeviceFormFactor::ffUnknown; @@ -476,7 +476,7 @@ namespace jni } DeviceTransport WindowsAudioDeviceManager::getActualTransport(EndpointFormFactor formFactor) { - // пока для винды не понятно как достоверно определить транспорт аудио устройства. Только HDMI могу могу + // for Windows, only HDMI can be reliably determined switch (formFactor) { case DigitalAudioDisplayDevice: return DeviceTransport::trHdmi; diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java index 58ca66c3..e31c0edf 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/Device.java @@ -49,14 +49,6 @@ public DeviceFormFactor getDeviceFormFactor() { return deviceFormFactor; } - public void setDeviceFormFactor(DeviceFormFactor deviceFormFactor) { - this.deviceFormFactor = deviceFormFactor; - } - - public void setDeviceTransport(DeviceTransport deviceTransport) { - this.deviceTransport = deviceTransport; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -80,6 +72,6 @@ public int hashCode() { @Override public String toString() { return String.format("%s [name=%s, descriptor=%s, transport=%s, formFactor=%s]", - Device.class.getSimpleName(), name, descriptor, deviceTransport.toString(), deviceFormFactor.toString()); + Device.class.getSimpleName(), name, descriptor, deviceTransport, deviceFormFactor); } } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java index 973af83c..5783b7c6 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceFormFactor.java @@ -6,20 +6,4 @@ public enum DeviceFormFactor { MICROPHONE, HEADSET, HEADPHONE; - - @Override - public String toString() { - switch (this) { - case SPEAKER: - return "SPEAKER"; - case MICROPHONE: - return "MICROPHONE"; - case HEADSET: - return "HEADSET"; - case HEADPHONE: - return "HEADPHONE"; - default: - return "UNKNOWN"; - } - } } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java index d3ed5f23..fa31c86b 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/DeviceTransport.java @@ -5,18 +5,4 @@ public enum DeviceTransport { HDMI, USB, WIRELESS; - - @Override - public String toString() { - switch (this) { - case HDMI: - return "HDMI"; - case USB: - return "USB"; - case WIRELESS: - return "WIRELESS"; - default: - return "UNKNOWN"; - } - } } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java index 28819cd6..c2a53357 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioDevice.java @@ -21,17 +21,14 @@ public class AudioDevice extends Device { - private AudioDeviceDirectionType audioDeviceDirectionType; + private AudioDeviceDirectionType directionType = AudioDeviceDirectionType.UNKNOWN; + + protected AudioDevice(String name, String descriptor) { super(name, descriptor); - audioDeviceDirectionType = AudioDeviceDirectionType.UNKNOWN; - } - - public void setAudioDeviceDirectionType(AudioDeviceDirectionType newAudioDeviceDirectionType) { - this.audioDeviceDirectionType = newAudioDeviceDirectionType; } - public AudioDeviceDirectionType getAudioDeviceDirectionType() { - return audioDeviceDirectionType; + public AudioDeviceDirectionType getDirectionType() { + return directionType; } } From 8214d95b974e9eacfd1d7ba4398516b811ba147c Mon Sep 17 00:00:00 2001 From: Stasonych Date: Mon, 15 Sep 2025 15:44:11 +0500 Subject: [PATCH 3/6] fixes merge request --- .../src/main/cpp/include/media/Device.h | 2 ++ .../cpp/include/media/audio/AudioDevice.h | 4 +++- webrtc-jni/src/main/cpp/src/media/Device.cpp | 22 ++++++++++++++----- .../main/cpp/src/media/audio/AudioDevice.cpp | 11 ++++++---- .../audio/linux/PulseAudioDeviceManager.cpp | 13 +++++------ .../audio/macos/CoreAudioDeviceManager.cpp | 16 +++++++------- .../windows/WindowsAudioDeviceManager.cpp | 12 +++++----- webrtc/pom.xml | 2 ++ 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/webrtc-jni/src/main/cpp/include/media/Device.h b/webrtc-jni/src/main/cpp/include/media/Device.h index d8f22cf5..a8f5896f 100644 --- a/webrtc-jni/src/main/cpp/include/media/Device.h +++ b/webrtc-jni/src/main/cpp/include/media/Device.h @@ -59,6 +59,8 @@ namespace jni std::string getDescriptor() const; DeviceTransport getDeviceTransport(); DeviceFormFactor getDeviceFormFactor(); + void setDeviceTransport(DeviceTransport deviceTransport); + void setDeviceFormFactor(DeviceFormFactor deviceFormFactor); protected: Device(std::string name, std::string descriptor); diff --git a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h index 80d7ab0d..2edf4873 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/AudioDevice.h @@ -28,7 +28,7 @@ namespace jni namespace avdev { -enum class AudioDeviceDirectionType { + enum class AudioDeviceDirectionType { adtUnknown, adtCapture, adtRender @@ -54,6 +54,8 @@ enum class AudioDeviceDirectionType { jmethodID ctor; jfieldID name; jfieldID descriptor; + jfieldID deviceTransport; + jfieldID deviceFormFactor; jfieldID directionType; }; diff --git a/webrtc-jni/src/main/cpp/src/media/Device.cpp b/webrtc-jni/src/main/cpp/src/media/Device.cpp index 2a68b865..5156bf3d 100644 --- a/webrtc-jni/src/main/cpp/src/media/Device.cpp +++ b/webrtc-jni/src/main/cpp/src/media/Device.cpp @@ -26,7 +26,9 @@ namespace jni { Device::Device(std::string name, std::string descriptor) : name(name), - descriptor(descriptor) + descriptor(descriptor), + deviceTransport(DeviceTransport::trUnknown), + deviceFormFactor(DeviceFormFactor::ffUnknown) { } @@ -64,6 +66,16 @@ namespace jni { return deviceFormFactor; } + + void Device::setDeviceTransport(DeviceTransport newDeviceTransport) + { + deviceTransport = newDeviceTransport; + } + + void Device::setDeviceFormFactor(DeviceFormFactor newDeviceFormFactor) + { + deviceFormFactor = newDeviceFormFactor; + } } namespace Device @@ -76,10 +88,10 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); - auto deviceTransport = JavaEnums::toJava(env, device->deviceTransport); + auto deviceTransport = JavaEnums::toJava(env, device->getDeviceTransport()); env->SetObjectField(obj, javaClass->deviceTransport, deviceTransport.get()); - auto deviceFormFactor = JavaEnums::toJava(env, device->deviceFormFactor); + auto deviceFormFactor = JavaEnums::toJava(env, device->getDeviceFormFactor()); env->SetObjectField(obj, javaClass->deviceFormFactor, deviceFormFactor.get()); return JavaLocalRef(env, obj); @@ -93,8 +105,8 @@ namespace jni name = GetFieldID(env, cls, "name", STRING_SIG); descriptor = GetFieldID(env, cls, "descriptor", STRING_SIG); - deviceTransport = GetFieldID(env, cls, "deviceTransport", "L" PKG "DeviceTransport;"); - deviceFormFactor = GetFieldID(env, cls, "deviceFormFactor", "L" PKG "DeviceFormFactor;"); + deviceTransport = GetFieldID(env, cls, "deviceTransport", "L" PKG_MEDIA "DeviceTransport;"); + deviceFormFactor = GetFieldID(env, cls, "deviceFormFactor", "L" PKG_MEDIA "DeviceFormFactor;"); } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp index 1b56dca9..0737d8b8 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/AudioDevice.cpp @@ -26,7 +26,8 @@ namespace jni namespace avdev { AudioDevice::AudioDevice(std::string name, std::string descriptor) : - Device(name, descriptor) + Device(name, descriptor), + directionType(AudioDeviceDirectionType::adtUnknown) { } } @@ -41,10 +42,10 @@ namespace jni JavaString::toJava(env, device->getName()).get(), JavaString::toJava(env, device->getDescriptor()).get()); - auto deviceTransport = JavaEnums::toJava(env, device->getDeviceTransport); + auto deviceTransport = JavaEnums::toJava(env, device->getDeviceTransport()); env->SetObjectField(obj, javaClass->deviceTransport, deviceTransport.get()); - auto deviceFormFactor = JavaEnums::toJava(env, device->getDeviceFormFactor); + auto deviceFormFactor = JavaEnums::toJava(env, device->getDeviceFormFactor()); env->SetObjectField(obj, javaClass->deviceFormFactor, deviceFormFactor.get()); auto audioDevice = dynamic_cast(device.get()); @@ -62,7 +63,9 @@ namespace jni name = GetFieldID(env, cls, "name", STRING_SIG); descriptor = GetFieldID(env, cls, "descriptor", STRING_SIG); - directionType = GetFieldID(env, cls, "directionType", "L" PKG "AudioDeviceDirectionType;"); + directionType = GetFieldID(env, cls, "directionType", "L" PKG_MEDIA "AudioDeviceDirectionType;"); + deviceTransport = GetFieldID(env, cls, "deviceTransport", "L" PKG_MEDIA "DeviceTransport;"); + deviceFormFactor = GetFieldID(env, cls, "deviceFormFactor", "L" PKG_MEDIA "DeviceFormFactor;"); } } } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp index 61dd8c21..eef0b612 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp @@ -140,7 +140,7 @@ namespace jni pa_threaded_mainloop_unlock(mainloop); AudioDevicePtr defaultDevice = std::make_shared(defaultCaptureDescName, defaultCaptureName); - defaultDevice->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + defaultDevice->directionType = AudioDeviceDirectionType::adtCapture; return defaultDevice; } @@ -177,7 +177,7 @@ namespace jni pa_threaded_mainloop_unlock(mainloop); AudioDevicePtr defaultDevice = std::make_shared(defaultPlaybackDescName, defaultPlaybackName); - defaultDevice->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + defaultDevice->directionType = AudioDeviceDirectionType::adtRender; return defaultDevice; } @@ -275,9 +275,9 @@ namespace jni if (devices.insertDevice(device)) { if (isCapture) { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + device->directionType = AudioDeviceDirectionType::adtCapture; } else { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + device->directionType = AudioDeviceDirectionType::adtRender; } fillAdditionalTypes(device, proplist); deviceMap[index] = device; @@ -296,9 +296,9 @@ namespace jni if (devices.removeDevice(it->second)) { if (isCapture) { - it->second->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + it->second->directionType = AudioDeviceDirectionType::adtCapture; } else { - it->second->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + it->second->directionType = AudioDeviceDirectionType::adtRender; } notifyDeviceDisconnected(it->second); @@ -372,7 +372,6 @@ namespace jni deviceTransportStr = "hdmi"; } - device->setDeviceFormFactor(getActualFormFactor(formFactorStr)); device->setDeviceTransport(getActualTransport(deviceTransportStr)); } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp index 6504bb4b..cb6e12ec 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/macos/CoreAudioDeviceManager.cpp @@ -225,11 +225,11 @@ namespace jni } if (scope == kAudioObjectPropertyScopeInput) { - found->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + found->directionType = AudioDeviceDirectionType::adtCapture; setDefaultCaptureDevice(found); } else if (scope == kAudioObjectPropertyScopeOutput) { - found->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + found->directionType = AudioDeviceDirectionType::adtRender; setDefaultPlaybackDevice(found); } } @@ -260,7 +260,7 @@ namespace jni if (removed == def) { onDefaultDeviceChanged(scope, captureDevices, def); } - removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + removed->directionType = AudioDeviceDirectionType::adtCapture; } else if (scope == kAudioObjectPropertyScopeOutput) { AudioDevicePtr def = getDefaultAudioPlaybackDevice(); @@ -268,7 +268,7 @@ namespace jni if (removed == def) { onDefaultDeviceChanged(scope, playbackDevices, def); } - removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + removed->directionType = AudioDeviceDirectionType::adtRender; } notifyDeviceDisconnected(removed); @@ -305,11 +305,11 @@ namespace jni if (channels > 0) { if (scope == kAudioObjectPropertyScopeOutput) { device = std::make_shared(name, id); - device-> audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + device-> directionType = AudioDeviceDirectionType::adtRender; } else if (scope == kAudioObjectPropertyScopeInput) { device = std::make_shared(name, id); - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + device->directionType = AudioDeviceDirectionType::adtCapture; } fillAdditionalTypes(deviceID, scope, device); } @@ -324,11 +324,11 @@ namespace jni } if (scope == kAudioObjectPropertyScopeOutput) { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + device->directionType = AudioDeviceDirectionType::adtRender; return playbackDevices.insertDevice(device); } else if (scope == kAudioObjectPropertyScopeInput) { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + device->directionType = AudioDeviceDirectionType::adtCapture; return captureDevices.insertDevice(device); } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp index 561fe639..705d8dd7 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/windows/WindowsAudioDeviceManager.cpp @@ -238,9 +238,9 @@ namespace jni AudioDevicePtr device = std::make_shared(name, id); if (dataFlow == eCapture) { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + device->directionType = AudioDeviceDirectionType::adtCapture; } else { - device->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + device->directionType = AudioDeviceDirectionType::adtRender; } fillAdditionalTypes(device); @@ -299,13 +299,13 @@ namespace jni } if (dataFlow == eCapture) { - device->audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtCapture; + device->directionType = jni::avdev::AudioDeviceDirectionType::adtCapture; captureDevices.insertDevice(device); return true; } else if (dataFlow == eRender) { - device->audioDeviceDirectionType = jni::avdev::AudioDeviceDirectionType::adtRender; + device->directionType = jni::avdev::AudioDeviceDirectionType::adtRender; playbackDevices.insertDevice(device); return true; @@ -324,9 +324,9 @@ namespace jni if (removed) { if (dataFlow == eCapture) { - removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtCapture; + removed->directionType = AudioDeviceDirectionType::adtCapture; } else { - removed->audioDeviceDirectionType = AudioDeviceDirectionType::adtRender; + removed->directionType = AudioDeviceDirectionType::adtRender; } notifyDeviceDisconnected(removed); diff --git a/webrtc/pom.xml b/webrtc/pom.xml index e06b6b10..a7a7d421 100644 --- a/webrtc/pom.xml +++ b/webrtc/pom.xml @@ -36,6 +36,8 @@ org.apache.maven.plugins maven-surefire-plugin + + --add-opens webrtc.java/dev.onvoid.webrtc=ALL-UNNAMED --add-opens webrtc.java/dev.onvoid.webrtc.logging=ALL-UNNAMED From 055f980c8ead69c16416b9e3b124a110bf6def4a Mon Sep 17 00:00:00 2001 From: Stasonych Date: Tue, 16 Sep 2025 18:22:00 +0500 Subject: [PATCH 4/6] fixes linux additional types --- .../audio/linux/PulseAudioDeviceManager.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp index eef0b612..dd1db433 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp @@ -377,29 +377,29 @@ namespace jni } DeviceFormFactor PulseAudioDeviceManager::getActualFormFactor(std::string formFactor) { - switch(formFactor) { - case "speaker": - return DeviceFormFactor::ffSpeaker; - case "microphone": - return DeviceFormFactor::ffMicrophone; - case "headset": - return DeviceFormFactor::ffHeadset; - case "headphone": - return DeviceFormFactor::ffHeadphone; - default: - return DeviceFormFactor::ffUnknown; + if (formFactor == "speaker") { + return DeviceFormFactor::ffSpeaker; + } else if (formFactor == "microphone") { + return DeviceFormFactor::ffMicrophone; + } else if (formFactor == "headset") { + return DeviceFormFactor::ffHeadset; + } else if (formFactor == "headphone") { + return DeviceFormFactor::ffHeadphone; + } + + return DeviceFormFactor::ffUnknown; } DeviceTransport PulseAudioDeviceManager::getActualTransport(std::string transport) { - switch(transport) { - case "usb": - return DeviceTransport::trUsb; - case "bluetooth": - return DeviceTransport::trWireless; - case "hdmi": - return DeviceTransport::trHdmi; - default: - return DeviceTransport::trUnknown; + if (transport == "usb") { + return DeviceTransport::trUsb; + } else if (transport == "bluetooth") { + return DeviceTransport::trWireless; + } else if (transport == "hdmi") { + return DeviceTransport::trHdmi; + } + + return DeviceTransport::trUnknown; } } } \ No newline at end of file From 9da2dc5685a659b6fdb289b8f6965fda19e48e6b Mon Sep 17 00:00:00 2001 From: Stasonych Date: Wed, 17 Sep 2025 11:40:39 +0500 Subject: [PATCH 5/6] actualized branch --- .github/actions/build-macos-x86_64/action.yml | 1 + .github/actions/build/action.yml | 1 + .github/changelog/cliff-release.toml | 97 +++ .github/changelog/cliff.toml | 94 +++ .github/workflows/build.yml | 6 +- .github/workflows/release.yml | 92 ++- CHANGELOG.md | 598 +++++++++++++----- docs/_sidebar.md | 44 +- docs/assets/versions.js | 4 +- docs/guide/{ => audio}/audio_devices.md | 0 docs/guide/{ => audio}/audio_processing.md | 52 +- docs/guide/{ => audio}/custom_audio_source.md | 382 +++++------ docs/guide/{ => audio}/dtmf_sender.md | 0 .../headless_audio_device_module.md | 4 +- docs/guide/{ => data}/data_channels.md | 0 docs/guide/{ => media}/constraints.md | 0 docs/guide/{ => media}/media_devices.md | 0 .../{ => media}/send_receive_direction.md | 0 docs/guide/{ => monitoring}/logging.md | 0 docs/guide/{ => monitoring}/rtc_stats.md | 0 .../{ => networking}/port_allocator_config.md | 2 +- docs/guide/overview.md | 44 +- docs/guide/{ => utilities}/audio_converter.md | 6 +- docs/guide/utilities/audio_player.md | 97 +++ docs/guide/utilities/audio_recorder.md | 65 ++ .../guide/{ => utilities}/power_management.md | 4 +- docs/guide/{ => utilities}/screen_capturer.md | 4 +- .../guide/utilities/video_buffer_converter.md | 10 +- docs/guide/{ => utilities}/video_capturer.md | 10 +- .../voice_activity_detector.md | 2 +- docs/guide/{ => utilities}/window_capturer.md | 4 +- docs/guide/{ => video}/camera_capture.md | 0 docs/guide/{ => video}/custom_video_source.md | 0 docs/guide/{ => video}/desktop_capture.md | 0 docs/quickstart.md | 31 +- pom.xml | 2 +- webrtc-examples/pom.xml | 2 +- webrtc-jni/pom.xml | 2 +- .../cpp/include/api/RTCDataChannelObserver.h | 2 + .../media/audio/AudioProcessingConfig.h | 101 ++- .../src/main/cpp/src/JNI_MediaDevices.cpp | 2 + webrtc-jni/src/main/cpp/src/WebRTCContext.cpp | 3 + .../cpp/src/api/RTCDataChannelObserver.cpp | 10 + .../src/media/audio/AudioProcessingConfig.cpp | 185 +++++- webrtc/pom.xml | 4 +- .../webrtc/media/audio/AudioOptions.java | 10 +- .../webrtc/media/audio/AudioPlayer.java | 44 +- .../media/audio/AudioProcessingConfig.java | 428 ++++++++++++- .../webrtc/media/audio/AudioRecorder.java | 32 +- .../media/video/VideoBufferConverter.java | 12 +- .../PortAllocatorConfigIntegrationTest.java | 4 +- .../onvoid/webrtc/RTCDataChannelTests.java | 51 ++ .../webrtc/media/MediaDevicesTests.java | 77 --- .../media/audio/AudioProcessingTest.java | 165 ++++- 54 files changed, 2206 insertions(+), 584 deletions(-) create mode 100644 .github/changelog/cliff-release.toml create mode 100644 .github/changelog/cliff.toml rename docs/guide/{ => audio}/audio_devices.md (100%) rename docs/guide/{ => audio}/audio_processing.md (83%) rename docs/guide/{ => audio}/custom_audio_source.md (96%) rename docs/guide/{ => audio}/dtmf_sender.md (100%) rename docs/guide/{ => audio}/headless_audio_device_module.md (97%) rename docs/guide/{ => data}/data_channels.md (100%) rename docs/guide/{ => media}/constraints.md (100%) rename docs/guide/{ => media}/media_devices.md (100%) rename docs/guide/{ => media}/send_receive_direction.md (100%) rename docs/guide/{ => monitoring}/logging.md (100%) rename docs/guide/{ => monitoring}/rtc_stats.md (100%) rename docs/guide/{ => networking}/port_allocator_config.md (98%) rename docs/guide/{ => utilities}/audio_converter.md (95%) create mode 100644 docs/guide/utilities/audio_player.md create mode 100644 docs/guide/utilities/audio_recorder.md rename docs/guide/{ => utilities}/power_management.md (96%) rename docs/guide/{ => utilities}/screen_capturer.md (97%) rename docs/guide/{ => utilities}/video_capturer.md (93%) rename docs/guide/{ => utilities}/voice_activity_detector.md (98%) rename docs/guide/{ => utilities}/window_capturer.md (96%) rename docs/guide/{ => video}/camera_capture.md (100%) rename docs/guide/{ => video}/custom_video_source.md (100%) rename docs/guide/{ => video}/desktop_capture.md (100%) delete mode 100644 webrtc/src/test/java/dev/onvoid/webrtc/media/MediaDevicesTests.java diff --git a/.github/actions/build-macos-x86_64/action.yml b/.github/actions/build-macos-x86_64/action.yml index eae5106c..70647c55 100644 --- a/.github/actions/build-macos-x86_64/action.yml +++ b/.github/actions/build-macos-x86_64/action.yml @@ -56,6 +56,7 @@ runs: shell: bash - name: Deploy + if: ${{ github.event_name != 'pull_request' }} env: MAVEN_USERNAME: ${{ inputs.maven-username }} MAVEN_TOKEN: ${{ inputs.maven-password }} diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 40179b3a..ac9969b1 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -63,6 +63,7 @@ runs: shell: bash - name: Deploy + if: ${{ github.event_name != 'pull_request' }} env: MAVEN_USERNAME: ${{ inputs.maven-username }} MAVEN_TOKEN: ${{ inputs.maven-password }} diff --git a/.github/changelog/cliff-release.toml b/.github/changelog/cliff-release.toml new file mode 100644 index 00000000..63167b6a --- /dev/null +++ b/.github/changelog/cliff-release.toml @@ -0,0 +1,97 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +# A Tera template to be rendered as the changelog's header. +# See https://keats.github.io/tera/docs/#introduction +header = """ +## What's Changed +""" +# A Tera template to be rendered for each release in the changelog. +# See https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {%- for commit in commits %} + - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ + {% if commit.remote.username and commit.remote.pr_number %} by @{{ commit.remote.username }}{%- endif -%} + {% if commit.remote.pr_number %} in \ + [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) \ + {%- endif -%} + {% endfor %} +{% endfor %} + +{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + ## New Contributors +{%- endif -%} + +{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} +{%- endfor %}\n +""" +# A Tera template to be rendered as the changelog's footer. +# See https://keats.github.io/tera/docs/#introduction +footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% for release in releases -%} + {% if release.version -%} + {% if release.previous.version -%} + [{{ release.version | trim_start_matches(pat="v") }}]: \ + {{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }} + {% endif -%} + {% else -%} + [unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD + {% endif -%} +{% endfor %} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = true +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = true +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Remove issue numbers. + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, +] +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^style", group = "Styling" }, + { message = "^test", group = "Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "Miscellaneous Tasks" }, + { body = ".*security", group = "Security" }, + { message = "^revert", group = "Revert" }, + { message = ".*", group = "Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "newest" diff --git a/.github/changelog/cliff.toml b/.github/changelog/cliff.toml new file mode 100644 index 00000000..75fa08d7 --- /dev/null +++ b/.github/changelog/cliff.toml @@ -0,0 +1,94 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +# A Tera template to be rendered as the changelog's header. +# See https://keats.github.io/tera/docs/#introduction +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +# A Tera template to be rendered for each release in the changelog. +# See https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% if version -%} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else -%} + ## [Unreleased] +{% endif -%} + +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {%- for commit in commits %} + - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ + {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%} + {% if commit.remote.pr_number %} in \ + [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) \ + {%- endif -%} + {% endfor %} +{% endfor %} + +{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + ## New Contributors +{%- endif -%} + +{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} +{%- endfor %}\n + +""" +# A Tera template to be rendered as the changelog's footer. +# See https://keats.github.io/tera/docs/#introduction +footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = true +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = false +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Remove issue numbers. + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, +] +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^style", group = "Styling" }, + { message = "^test", group = "Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "Miscellaneous Tasks" }, + { body = ".*security", group = "Security" }, + { message = "^revert", group = "Revert" }, + { message = ".*", group = "Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "newest" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66764882..7af6c0ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - id: prepare name: Prepare build @@ -62,7 +62,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - id: prepare name: Prepare build @@ -90,7 +90,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - id: prepare-build name: Prepare build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97f3e802..0191aab7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,51 @@ env: WEBRTC_INSTALL_FOLDER: webrtc/build jobs: + changelog: + name: Generate changelog + runs-on: ubuntu-latest + outputs: + release_body: ${{ steps.git-cliff.outputs.content }} + release_version: ${{ steps.git-cliff.outputs.version }} + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Git config + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Generate changelog for the current build + uses: orhun/git-cliff-action@v4 + id: git-cliff + with: + config: .github/changelog/cliff-release.toml + args: -v -u --strip footer --bump --use-branch-tags + + - name: Update CHANGELOG.md + uses: orhun/git-cliff-action@v4 + with: + config: .github/changelog/cliff.toml + args: -v --bump --use-branch-tags + env: + OUTPUT: CHANGELOG.md + + - name: Commit CHANGELOG.md + run: | + git add CHANGELOG.md + git commit -m "chore(release): Update CHANGELOG.md for ${{ steps.git-cliff.outputs.version }}" + git push + prepare-release: + name: Prepare release + needs: changelog + outputs: + release_body: ${{ needs.changelog.outputs.release_body }} + release_version: ${{ needs.changelog.outputs.release_version }} strategy: fail-fast: false matrix: @@ -24,7 +68,9 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 + with: + ref: main - run: | git config --global user.name "${{ github.actor }}" git config --global user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" @@ -45,7 +91,9 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Update development version - uses: actions/checkout@v4 + uses: actions/checkout@v5 + with: + ref: main env: MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} @@ -71,6 +119,9 @@ jobs: build-windows: needs: prepare-release + outputs: + release_body: ${{ needs.prepare-release.outputs.release_body }} + release_version: ${{ needs.prepare-release.outputs.release_version }} strategy: fail-fast: false matrix: @@ -81,7 +132,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-tags: true @@ -125,7 +176,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-tags: true @@ -141,17 +192,6 @@ jobs: name: Prepare release build uses: ./.github/actions/prepare-linux - - name: Create Release # Create the release only once on Linux - if: ${{ matrix.platform.name == 'linux_x86-64' }} - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.tag-selector.outputs.tag }} - release_name: Release ${{ steps.tag-selector.outputs.tag }} - draft: false - prerelease: false - - id: maven-build name: Maven build uses: ./.github/actions/release @@ -177,7 +217,7 @@ jobs: runs-on: ${{ matrix.platform.runs-on }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-tags: true @@ -215,4 +255,22 @@ jobs: maven-username: ${{ secrets.MAVEN_USERNAME }} maven-password: ${{ secrets.MAVEN_TOKEN }} maven-gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - maven-gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} \ No newline at end of file + maven-gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + publish-release: + name: Publish GitHub release + needs: [build-windows, build-linux, build-macos] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Publish + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + version="${{ needs.build-windows.outputs.release_version }}" + body="${{ needs.build-windows.outputs.release_body }}" + + gh release create $version -d -t "Release $version" -n "$body" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ce29cbb9..f77e02aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,189 +1,447 @@ -## [0.13.0]() (2025-07-25) -### Bug Fixes -- macOS screen capturing only the first frame -- Crash in `MediaDevices` by validating COM initialization in audio device management +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.14.0] - 2025-08-28 -### Feature -- Enable H.264 support for Linux, macOS and Windows -- Added RTCDtmfSender implementation for sending DTMF tones +### Features +- Restrict ICE port ranges and control candidate gathering behavior by @devopvoid in [#202](https://github.com/devopvoid/webrtc-java/pull/202) +- Push or pull audio frames with a headless audio device module by @devopvoid in [#201](https://github.com/devopvoid/webrtc-java/pull/201) +- Add guide for configuring send-only and receive-only media by @devopvoid +- Add the platform classifier to the native library filename by @SnirDecartAI in [#194](https://github.com/devopvoid/webrtc-java/pull/194) +- Add VideoBufferConverter.convertToI420 by @SnirDecartAI in [#192](https://github.com/devopvoid/webrtc-java/pull/192) +- Add SyncClock for audio and video synchronization support by @devopvoid +- Add CustomVideoSource and related JNI bindings for video frame handling by @devopvoid +- Added a new web-based example by @devopvoid +- Custom audio source that can be provided to audio tracks by @devopvoid in [#188](https://github.com/devopvoid/webrtc-java/pull/188) + +### Bug Fixes +- RTCDtmfSenderTests timeouts by @devopvoid +- Update error handling to stop capturing on permanent errors #186 by @devopvoid +- Ensure JNIEnv is properly attached before deleting global reference 146 by @devopvoid +- Return a valid NtpTime from SyncClock::GetNtpTime by @SnirDecartAI in [#193](https://github.com/devopvoid/webrtc-java/pull/193) +- Release video frame after processing in WHEP example #191 by @devopvoid +- Made RTCRtpCodecCapability's int fields optional by @SnirDecartAI in [#190](https://github.com/devopvoid/webrtc-java/pull/190) ### Documentation -- Comprehensive guide and examples are now available @ https://jrtc.dev +- Add AudioRecorder and AudioPlayer documentation by @devopvoid +- Documentation for VideoBufferConverter by @devopvoid +- Documentation for MediaStreamTrack, VideoBufferConverter, and VideoCapture by @devopvoid +- Add Video Capture guide by @devopvoid +- Add Screen Capturer and Window Capturer guides by @devopvoid +- Add Power Management utility by @devopvoid +- Add Audio Converter guide by @devopvoid +- Add Voice Activity Detector guide by @devopvoid +- Reorganize media guides into categories by @devopvoid +- Add section on disabling audio processing with dummy audio layer by @devopvoid +- Add Custom Video Source guide and update documentation by @devopvoid +- Update example descriptions and add WebClientExample by @devopvoid +- Add Custom Audio Source guide by @devopvoid +- Update asset paths and add a privacy policy by @devopvoid in [#187](https://github.com/devopvoid/webrtc-java/pull/187) +- Add camera capture guide by @devopvoid +- Update CHANGELOG.md by @devopvoid + +### Miscellaneous Tasks +- Add custom video source and generator to the web example by @devopvoid + +### Other +- Update release workflow to use main branch by @devopvoid +- Update GitHub Actions workflows to use changelog generation by @devopvoid +- Update all GitHub Actions setup-java to v5 by @maxkratz in [#200](https://github.com/devopvoid/webrtc-java/pull/200) +- Prepare for next development iteration by @devopvoid + +## New Contributors +* @maxkratz made their first contribution in [#200](https://github.com/devopvoid/webrtc-java/pull/200) +* @SnirDecartAI made their first contribution in [#194](https://github.com/devopvoid/webrtc-java/pull/194) + +## [0.13.0] - 2025-07-25 -### Refactoring -- Moved `master` branch to `main` +### Features +- Add H.264 support and improve linking options for Apple platforms by @devopvoid +- Add RTCDtmfSender implementation for sending DTMF tones #115 by @devopvoid in [#183](https://github.com/devopvoid/webrtc-java/pull/183) +- Enable H.264 support for Linux and Windows builds #155 #168 #169 by @devopvoid -## [0.12.0]() (2025-07-18) ### Bug Fixes -- Maven release action for macOS platforms +- Update references from master to main in build configuration and documentation by @devopvoid +- Enhance documentation for audio related topics by @devopvoid +- Validate COM initialization in audio device management #181 by @devopvoid +- MacOS screen capturing with iOSurface and platform thread management by @devopvoid -### Feature -- Add the missing macOS camera capture functionality (#180) -- Add a copy method for NativeI420Buffer and VideoFrame +### Documentation +- Update README with logo by @devopvoid +- Improve responsive design for the cover by @devopvoid +- Enhance coverpage and sidebar styles with new logo and theme colors by @devopvoid +- Update homepage and quickstart guide links in documentation by @devopvoid +- Comprehensive guide and examples by @devopvoid in [#182](https://github.com/devopvoid/webrtc-java/pull/182) -### Build -- Update package installation commands and enhance sysroot configuration for Linux (#178) +### Miscellaneous Tasks +- Update readme to 0.12.0 and update changelog by @devopvoid -### Refactoring -- Updated the native interface to m138 (#179) +### Other +- Prepare release v0.13.0 by @devopvoid +- Update release workflow to update versions.js in docs by @devopvoid +- Create CNAME by @devopvoid +- Prepare for next development iteration by @devopvoid -## [0.11.0]() (2025-06-02) -### Bug Fixes -- Screen/Window capturer memory leak. -### Build -- Updated project to a more recent stable WebRTC branch, m134 (6998). -- Link against the WebRTC custom libc++ and libc++abi on Linux, and against libc++ on Windows. -- Added support for Linux ARM and ARM64 builds. -- Build with Clang on all platforms. +## [0.12.0] - 2025-07-18 -## [0.10.0]() (2025-03-18) -### Bug Fixes -- Fixed crashes under Linux caused by unlinked libraries -- Fixed crashes caused by video frame conversion -- Added missing _libX_ libraries to be installed in GH actions +### Features +- Add copy method for NativeI420Buffer and VideoFrame by @devopvoid +- Added the missing macOS camera capture functionality by @devopvoid in [#180](https://github.com/devopvoid/webrtc-java/pull/180) -## [0.9.0]() (2025-01-02) ### Bug Fixes -- Fixed NPE in MediaDevices when no audio device is connected -- Fixed crashs with jdk version > 17 -- Fixed Apple Silicon builds -- Fixed build and release workflow - -### Feature -- Added screen inhibition - -### Enhancements -- Added Linux ARM architectures for cross compilation - -## [0.8.0]() (2023-10-14) -### Enhancement -- Link libm from sysroot to increase GLIBC (<= 2.18) compatibility #108 -- Initialized configs to be used with GraalVM -- Added setFocusSelectedSource to DesktopCapturer and capture with DesktopAndCursorComposer - -### Demo -- Dropped demo code. New demo will be in a new repository. - -## [0.7.0]() (2022-11-14) -### API changes -- Set native branch to 4844 (Milestone 99) - -### Enhancement -- Compile release with backward compatibility for Java 8 -- Added setFocusSelectedSource to VideoTrackDesktopSource -- Set maximum width and height for captured screen frames -- DesktopCaptureCallback converts DesktopFrame to I420 -- VideoTrackDesktopSource crops black window borders on Windows -- Added MediaStreamTrack "ended" and "muted" event observation - -### Fixed bugs -- Fixed desktop capturing -- Fixed setting codec preferences -- Fixed setting sender parameters -- Fixed memory leak in AudioTransportSink -- Fixed memory leak with audio & video frames related to JNI local references -- Fixed audio device names on mac with Cyrillic and other characters - -### Demo -- Removed demos since AppRTC services have been turned down -- A new demo app will follow soon - -## [0.6.0]() (2022-01-17) -### API changes -- Moved to WebRTC branch 4692 (M97) ([29a6e3b](https://github.com/devopvoid/webrtc-java/commit/29a6e3b)) - -### Enhancement -- Lazy-load audio/video managers #44 ([674b6af](https://github.com/devopvoid/webrtc-java/commit/674b6af)) -- Get default audio devices #50 ([a630f28](https://github.com/devopvoid/webrtc-java/commit/a630f28)) - -### Fixed bugs -- Fixed AudioProcessing native ProcessReverseStream function call #46 ([e912543](https://github.com/devopvoid/webrtc-java/commit/e912543)) -- Fixed loading classes from native threads with detached class-loaders #49 ([e50249b](https://github.com/devopvoid/webrtc-java/commit/e50249b)) -- Fixed camera device selection for a video track on Unix systems #30 ([8b0fb4f](https://github.com/devopvoid/webrtc-java/commit/8b0fb4f)) - -## [0.5.0]() (2021-11-25) -### Enhancement -- Improved audio processing by basic channel up-mixing to avoid additional audio converter ([e665a4a](https://github.com/devopvoid/webrtc-java/commit/e665a4a)) -- AudioConverter implementation for remixing and resampling ([3182d06](https://github.com/devopvoid/webrtc-java/commit/3182d06)) -- Updated RTCStats.cpp to Java mapping ([76769e2](https://github.com/devopvoid/webrtc-java/commit/76769e2)) -- Use new api calls which support RTCError ([e665a4a](https://github.com/devopvoid/webrtc-java/commit/e665a4a)) - -### Fixed bugs -- Fixed CoInitialize has not been called #43 ([73df466](https://github.com/devopvoid/webrtc-java/commit/73df466)) -- Fixed NPE on RTCPeerConnection.getConnectionState when peer-connectio n has been closed #35 ([e075d60](https://github.com/devopvoid/webrtc-java/commit/e075d60)) -- Fixed NPE in AudioProcessingConfig when no NS level was set ([81164ef](https://github.com/devopvoid/webrtc-java/commit/81164ef)) -- Fixed AudioTransportSource buffer reading ([071cbe9](https://github.com/devopvoid/webrtc-java/commit/071cbe9)) -- Fixed basic channel up-mixing in AudioProcessing ([4717db1](https://github.com/devopvoid/webrtc-java/commit/4717db1)) - -## [0.4.0]() (2021-11-21) -### API changes -- Switch to WebRTC branch M95/4638 ([9173836](https://github.com/devopvoid/webrtc-java/commit/9173836)) -- Audio/VideoSource renamed to Audio/VideoTrackSource ([68a61b9](https://github.com/devopvoid/webrtc-java/commit/68a61b9)) - -### Enhancement -- AudioDeviceModule: added stop/start methods for playout/recording ([7070622](https://github.com/devopvoid/webrtc-java/commit/7070622)) -- AudioOptions: added more audio processing options ([3faa4a8](https://github.com/devopvoid/webrtc-java/commit/3faa4a8)) -- AudioProcessing implementation ([6383a11](https://github.com/devopvoid/webrtc-java/commit/6383a11)) -- Introduced AudioSink and AudioSource interfaces which can be passed to the AudioDeviceModule ([8b33c7c](https://github.com/devopvoid/webrtc-java/commit/8b33c7c)) -- AudioPlayer and AudioRecorder utility classes which utilize the AudioDeviceModule ([caca82c](https://github.com/devopvoid/webrtc-java/commit/caca82c)) -- AudioResampler utility class which can be used for sampling rate conversion ([9e31c27](https://github.com/devopvoid/webrtc-java/commit/9e31c27)) -- Add option to provide AudioProcessing to the PeerConnectionFactory ([0c51a2d](https://github.com/devopvoid/webrtc-java/commit/0c51a2d)) - -### Fixed bugs -- Fixed workflow on Linux machines ([ac2aa58](https://github.com/devopvoid/webrtc-java/commit/ac2aa58)) -- Fixed action workflow saving and loading cache ([9528c2d](https://github.com/devopvoid/webrtc-java/commit/9528c2d)) -- Fixed GLIBCXX_3.4.26 not found ([da18d11](https://github.com/devopvoid/webrtc-java/commit/da18d11)) -- Fixed JavaFX demo crash ([89896fd](https://github.com/devopvoid/webrtc-java/commit/89896fd)) - -## [0.3.0]() (2021-08-19) -### API changes -- Switch to WebRTC branch M92/4515 ([bb3bbfe](https://github.com/devopvoid/webrtc-java/commit/bb3bbfe)) - -### Enhancement -- Audio/video device (Windows) handling consistent with the internal API ([702e58c](https://github.com/devopvoid/webrtc-java/commit/702e58c)) -- Add usage of audio layers and with it the possibility to use dummy audio for testing purposes ([daec7d8](https://github.com/devopvoid/webrtc-java/commit/daec7d8)) **Thanks to Rafael Berne (@rmberne)** -- Add VideoCapture class to capture frames without a running track ([eec4cfd](https://github.com/devopvoid/webrtc-java/commit/eec4cfd)) -- MediaDevice change listener implementation ([1029fe4](https://github.com/devopvoid/webrtc-java/commit/1029fe4)) -- Build with H264 codec ([f09aba0](https://github.com/devopvoid/webrtc-java/commit/f09aba0)) - -### Fixed bugs -- Fixed AudioModule NPE in PeerConnectionFactory ([c9fc55e](https://github.com/devopvoid/webrtc-java/commit/c9fc55e)) -- Fixed NativeI420Buffer allocate ([9bf8169](https://github.com/devopvoid/webrtc-java/commit/9bf8169)) -- Fixed desktop capture ([0314dec](https://github.com/devopvoid/webrtc-java/commit/0314dec)) -- Fixed demo audio/video config settings ([86bd776](https://github.com/devopvoid/webrtc-java/commit/86bd776)) -- Fixed possible arithmetic overflow ([b6d4aec](https://github.com/devopvoid/webrtc-java/commit/b6d4aec)) - -### Project -- Removed Azure Pipelines CI/CD configurations ([824aaf1](https://github.com/devopvoid/webrtc-java/commit/824aaf1)) - -### Refactoring -- Removed usage of webrtc::VideoType::kNV12 ([32a8ff9](https://github.com/devopvoid/webrtc-java/commit/32a8ff9)) - -## [0.2.0]() (2020-02-25) -### API changes -- Switch to WebRTC branch M80/3987 ([5a7bba5](https://github.com/devopvoid/webrtc-java/commit/5a7bba5)) - -### Enhancement -- MediaDevices: device and capabilities retrieval ([2f92e11](https://github.com/devopvoid/webrtc-java/commit/2f92e11)) +- Add Maven release action for macOS platforms by @devopvoid + +### Refactor +- Updated native interface to m138 by @devopvoid in [#179](https://github.com/devopvoid/webrtc-java/pull/179) + +### Documentation +- Update badge links in README for build status and Maven Central by @devopvoid +- Update README for version 0.11.0 and clarify build notes by @devopvoid + +### Other +- Prepare release v0.12.0 by @devopvoid +- Fix release workflow to support matrix.platform.name by @devopvoid +- Update package installation commands and enhance sysroot configuration by @devopvoid in [#178](https://github.com/devopvoid/webrtc-java/pull/178) +- Prepare for next development iteration by @devopvoid -### Features -- AppRTC signaling implementation ([4bda5bc](https://github.com/devopvoid/webrtc-java/commit/4bda5bc)) -- AppRTC demo integration ([f4f2636](https://github.com/devopvoid/webrtc-java/commit/f4f2636)) -### Fixed bugs -- Java module fixes ([52b9a44](https://github.com/devopvoid/webrtc-java/commit/52b9a44)) -- Fixed module tests ([483d0f8](https://github.com/devopvoid/webrtc-java/commit/483d0f8)) +## [0.11.0] - 2025-07-02 -### Project -- Update issue templates ([ecc3eb5](https://github.com/devopvoid/webrtc-java/commit/ecc3eb5)) -- GitHub Actions CI/CD configurations ([967b6d9](https://github.com/devopvoid/webrtc-java/commit/967b6d9)) -- Azure Pipelines CI/CD configurations ([5e8f90c](https://github.com/devopvoid/webrtc-java/commit/5e8f90c)) +### Bug Fixes +- Windows clang profile activation by @devopvoid + +### Refactor +- VideoTrackDesktopSource extends AdaptedVideoTrackSource by @devopvoid +- Rtc and cricket to webrtc namespace by @devopvoid + +### Other +- Prepare release v0.11.0 by @devopvoid +- Add project name to pom.xml by @devopvoid +- Update release workflow to include code checkout and linux arm support by @devopvoid +- Link with libc++ and libc++abi on Linux and Windows by @devopvoid in [#175](https://github.com/devopvoid/webrtc-java/pull/175) +- Do not build with clang-cl on main by @devopvoid +- Fix screen-share memory leak by @devopvoid in [#174](https://github.com/devopvoid/webrtc-java/pull/174) +- Revert "refactor: rtc and cricket to webrtc namespace" by @devopvoid +- Revert "build: updated webrtc version to m137 in GitHub workflows" by @devopvoid +- Updated webrtc version to m137 in GitHub workflows by @devopvoid +- Updated project to the stable WebRTC branch 6998 by @devopvoid in [#163](https://github.com/devopvoid/webrtc-java/pull/163) +- Build.yml pipewire activated by @devopvoid +- Update README.md by @devopvoid +- Prepare for next development iteration by @devopvoid + + +## [0.10.0] - 2025-03-18 + +### Bug Fixes +- Added missing libx libraries to be installed in linux release workflow by @devopvoid +- Added missing libx libraries to be installed in GH actions by @devopvoid +- Crashes caused by video frame conversion by @devopvoid +- Linux crashes caused by unlinked libraries by @devopvoid + +### Other +- Prepare release v0.10.0 by @devopvoid +- Update README.md by @devopvoid +- Prepare for next development iteration by @devopvoid + + +## [0.9.0] - 2025-01-02 + +### Other +- Prepare release v0.9.0 by @devopvoid +- Crash after java callback if exception occurred by @arosov in [#154](https://github.com/devopvoid/webrtc-java/pull/154) +- Workflow build and release fixes by @devopvoid in [#156](https://github.com/devopvoid/webrtc-java/pull/156) +- Removed gpg from action workflow by @devopvoid +- Fixed action workflow deployment by @devopvoid +- Update action.yml by @devopvoid +- Updated build badge in README.md by @devopvoid +- Linux pm rtc logging by @devopvoid +- Fixed build workflow by @devopvoid +- Fixed and improved GitHub build workflow by @devopvoid in [#152](https://github.com/devopvoid/webrtc-java/pull/152) +- Screen inhibition by @devopvoid in [#150](https://github.com/devopvoid/webrtc-java/pull/150) +- Fixed crash with jdk version > 17 by @devopvoid +- Revert version of cmake-maven-plugin for compatibility with older maven by @devopvoid +- Updated maven plugin versions. by @devopvoid +- Fixed NPE in MediaDevices when no audio device is connected. by @devopvoid +- Update README.md with Linux ARM architectures by @devopvoid +- Added Linux ARM architectures for cross compilation by @devopvoid +- Fixed Apple Silicon build by @devopvoid +- Update README.md macOS arm64 release by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Prepare for next development iteration by @devopvoid + +## New Contributors +* @arosov made their first contribution in [#154](https://github.com/devopvoid/webrtc-java/pull/154) + +## [0.8.0] - 2023-10-14 + +### Other +- Prepare release v0.8.0 by @devopvoid +- Updated action runners by @devopvoid +- Dropped old demo code. New demo will be in a new repository by @devopvoid +- Added setFocusSelectedSource to DesktopCapturer and capture with DesktopAndCursorComposer by @devopvoid +- Add a build support for mac/aarch64 by @stevebriskin in [#109](https://github.com/devopvoid/webrtc-java/pull/109) +- Merge remote-tracking branch 'origin/master' by @devopvoid +- Update README.md by @doctorpangloss in [#96](https://github.com/devopvoid/webrtc-java/pull/96) +- Initialized configs to be used with GraalVM by @devopvoid +- Set linux version to ubuntu-20.04 due to audio system tests by @devopvoid +- Link libm from sysroot to increase GLIBC (<= 2.18) compatibility #108 by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Added workflow_dispatch to the release workflow by @devopvoid +- Removed demo tasks from the release workflow by @devopvoid +- Prepare for next development iteration by @devopvoid + +## New Contributors +* @stevebriskin made their first contribution in [#109](https://github.com/devopvoid/webrtc-java/pull/109) +* @doctorpangloss made their first contribution in [#96](https://github.com/devopvoid/webrtc-java/pull/96) + +## [0.7.0] - 2022-11-14 + +### Bug Fixes +- Fix memory leak by @kalgecin in [#86](https://github.com/devopvoid/webrtc-java/pull/86) + +### Other +- Prepare release v0.7.0 by @devopvoid +- Fixed audio device names on mac #28 by @devopvoid +- Compile with backward compatibility for Java 8 by @devopvoid +- Terminate (end) VideoTrackDesktopSource if source cannot be selected by @devopvoid +- Fixed RTCRtpCodecParameters conversion when setting sender params by @devopvoid +- Fixed RTCRtpCodecParameters conversion by @devopvoid +- Notify the track to end on permanent error when capturing a desktop frame by @devopvoid +- Improved MediaStreamTrack "ended" and "muted" event observation by @devopvoid +- Added MediaStreamTrack "ended" and "muted" event observation by @devopvoid +- Removed Xcode version selection from the build workflow by @devopvoid +- VideoTrackDesktopSource crop black window borders by @devopvoid +- DesktopCaptureCallback convert DesktopFrame to I420 by @devopvoid +- Added setMaxFrameSize to VideoDesktopSource by @devopvoid +- Enable set min,max bitrate and max framerate in RTCRtpSender by @devopvoid +- Fixed crashes caused by DesktopCaptureCallback by @devopvoid +- MacOS build with Xcode 11.7 by @devopvoid +- Use new RemoveTrackOrError API call in RTCPeerConnection to remove a sender by @devopvoid +- Removed deprecated Windows wstring_convert calls by @devopvoid +- Build demo code with Maven 'demo' profile by @devopvoid +- Fixed RTCDataChannelTests by @devopvoid +- Fixed JavaEnums returning JavaLocalRef by @devopvoid +- Fixed JavaFactories returning jobjectArray by @devopvoid +- Fixed JavaLocalRefs for enums and factories by @devopvoid +- CI build with windows-2019 by @devopvoid +- Set maximum width and height for captured screen frames by @devopvoid +- Fixed null pointer handling with RtpSender by @devopvoid +- Added setFocusSelectedSource to VideoTrackDesktopSource by @devopvoid +- Compile with CXX_STANDARD 17 by @devopvoid +- Minor option changes for DesktopCapturer by @devopvoid +- Bump cmake-maven-plugin version to 3.22.1-b1 by @devopvoid +- Fixed memory leak in AudioTransportSink by @devopvoid +- Updated RTC_LOG macros by @devopvoid +- Removed deprecated code by @devopvoid +- Bump native branch to 4844 by @devopvoid +- Removed demos since AppRTC services have been turned down by @devopvoid +- Fixed setting codec preferences #57 by @devopvoid +- Removed setDesktopCapturer call on VideoDesktopSource by @devopvoid +- Fixed desktop capturing by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Prepare for next development iteration by @devopvoid + +## New Contributors +* @kalgecin made their first contribution in [#86](https://github.com/devopvoid/webrtc-java/pull/86) + +## [0.6.0] - 2022-01-17 + +### Other +- Prepare release v0.6.0 by @devopvoid +- Fixed camera device selection for a video track on Unix systems #30 by @devopvoid +- Get default audio devices by @devopvoid in [#50](https://github.com/devopvoid/webrtc-java/pull/50) +- Lazy-load audio/video managers #44 by @devopvoid +- Fixed loading classes from native threads with detached class-loaders #49 by @devopvoid +- Moved to WebRTC branch 4692 (M97) by @devopvoid +- Updated workflow actions to WebRTC branch 4664 (M96) by @devopvoid +- Moved to WebRTC branch 4664 (M96) by @devopvoid +- Fixed AudioProcessing native ProcessReverseStream function call #46 by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Prepare for next development iteration by @devopvoid + + +## [0.5.0] - 2021-11-25 + +### Other +- Prepare release v0.5.0 by @devopvoid +- Use new api calls which support RTCError by @devopvoid +- Fixed AudioTransportSource.cpp buffer passing by @devopvoid +- Updated RTCStats.cpp to Java mapping by @devopvoid +- Got rid of warnings in the audio section by @devopvoid +- Fixed NPE on RTCPeerConnection.getConnectionState when peer-connection has been closed #35 by @devopvoid +- AudioConverter implementation for remixing and resampling by @devopvoid +- Minor audio procession improvements by @devopvoid +- Fixed basic channel up-mixing in AudioProcessing by @devopvoid +- Improved audio processing by basic channel up-mixing to avoid additional audio converter by @devopvoid +- Fixed AudioTransportSource buffer reading by @devopvoid +- Fixed NPE in AudioProcessingConfig when no NS level was set by @devopvoid +- Fixed CoInitialize has not been called #43 by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Prepare for next development iteration by @devopvoid + + +## [0.4.0] - 2021-11-21 + +### Other +- Prepare release v0.4.0 by @devopvoid +- Add settings-path for JDK setup to the release workflow by @devopvoid +- Split AudioProcessingConfig into individual config classes. Added get/set of stream-delay to AudioProcessing. by @devopvoid +- Add option to provide AudioProcessing to the PeerConnectionFactory by @devopvoid +- AudioResampler fixes for more than one channel by @devopvoid +- AudioResampler utility class which can be used for sampling rate conversion by @devopvoid +- AudioProcessing to use only 16 Bit audio samples by @devopvoid +- AudioPlayer and AudioRecorder utility classes which utilize the AudioDeviceModule by @devopvoid +- Introduced AudioSink and AudioSource interfaces which can be passed to the AudioDeviceModule by @devopvoid +- Audio/VideoSource renamed to Audio/VideoTrackSource by @devopvoid +- Fixed temporary MTA<>STA threading issue #39 by @devopvoid +- Static linking libgcc and libstdc++ #27 by @devopvoid +- Fixed action workflow cache saving and loading by @devopvoid +- AudioProcessing implementation by @devopvoid +- Native JavaObject field setters by @devopvoid +- Init version of Java implementation of AudioProcessing by @devopvoid +- Workflow 'apt-get update' for linux action by @devopvoid +- Added more audio processing options by @devopvoid +- Moved to WebRTC branch 4638 (M95) by @devopvoid +- Added stop/start methods for playout/recording by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Updated CHANGELOG and README by @devopvoid +- Prepare for next development iteration by @devopvoid + + +## [0.3.0] - 2021-08-19 + +### Other +- Prepare release v0.3.0 by @devopvoid +- Fixed local video is sometimes empty #9 by @devopvoid +- Fixed WebRTC cmake build by @devopvoid +- Fixed NPE in JNI_PeerConnectionFactory by @devopvoid +- Switch to a more stable branch (4515) by @devopvoid +- Update pom.xml by @devopvoid +- Update CMakeLists.txt by @devopvoid +- Update pom.xml by @devopvoid +- Merge remote-tracking branch 'origin/master' by @devopvoid +- Update README.md by @devopvoid +- Audio/video device (Windows) handling consistent with the internal API by @devopvoid +- Audio device retrieval with the AudioDeviceModule by @devopvoid +- Removed Azure Pipelines by @devopvoid +- Merge pull request #20 from rmberne/dummy-audio-for-testing by @devopvoid in [#20](https://github.com/devopvoid/webrtc-java/pull/20) +- Use dummy audio in the unit test by @rmberne +- Add possibility to use dummy audio for testing purposes by @rmberne +- Fixed NativeI420Buffer allocate by @devopvoid +- Add VideoCapture to capture frames without a running track by @devopvoid +- Removed usage of webrtc::VideoType::kARGB1555 by @devopvoid +- Updated pipelines by @devopvoid +- Removed usage of webrtc::VideoType::kNV12 by @devopvoid +- Updated WebRTC branch to 4472 (M91) and updated dependency versions by @devopvoid +- Updated Sonatype provision URL by @devopvoid +- Fixed desktop capture by @devopvoid +- Bind transceiver direction to the configuration by @devopvoid +- Fixed demo audio/video config settings by @devopvoid +- Revert "Create codeql-analysis.yml" by @devopvoid +- Create codeql-analysis.yml by @devopvoid +- Updated GitHub Actions workflows by @devopvoid +- Update README.md by @devopvoid +- Merge remote-tracking branch 'origin/master' by @devopvoid +- Update README.md by @devopvoid +- Fixed possible arithmetic overflow by @devopvoid +- Linux M88 patch. Updated pipelines. by @devopvoid +- Bump WebRTC branch to 4324 (M88) by @devopvoid +- Merge pull request #6 from averyzhong/master by @devopvoid in [#6](https://github.com/devopvoid/webrtc-java/pull/6) +- Update AVFVideoDeviceManager.cpp by @averyzhong +- Change GitHub Actions env names for Maven credentials by @devopvoid +- Fixed MF init scope by @devopvoid +- Fixed linux build dependencies in CI builds by @devopvoid +- Fixed macOS CoreAudioDeviceManager by @devopvoid +- Advance WebRTC branch to 4044 (m81) by @devopvoid +- MediaDevice change listener implementation by @devopvoid +- Refactored JNI helper classes by @devopvoid +- Show settings link in the start view by @devopvoid +- Add Maven Central badge to README by @devopvoid +- Reference javafx demo app name to 'webrtc-javafx-demo-(platform)' by @devopvoid +- Prepare for next development iteration by @devopvoid + +## New Contributors +* @rmberne made their first contribution +* @averyzhong made their first contribution + +## [0.2.0] - 2020-02-25 + +### Other +- Prepare release v0.2.0 by @devopvoid +- Added CHANGELOG by @devopvoid +- Update README by @devopvoid +- Cleanup Azure build pipeline by @devopvoid +- GitHub Actions CI/CD configurations by @devopvoid +- Merge branch 'master' of github.com:devopvoid/webrtc-java by @devopvoid +- Cleanup GitHub Actions workflow by @devopvoid +- Provide Azure Pipelines CI/CD configurations by @devopvoid +- Switched to maven-release-plugin by @devopvoid +- Improved platform classifier handling by @devopvoid +- Improved native CMake builds by @devopvoid +- Update maven build procedure by @devopvoid +- Update maven build procedure by @devopvoid +- Update README.md by @devopvoid +- Maven CI credentials by @devopvoid +- JavaFX demo code cleanup by @devopvoid +- Refactor bloated project structure by @devopvoid +- Java module fixes by @devopvoid +- Updated JavaFX version by @devopvoid +- Patch DEPS to reduce time running gclient by @devopvoid +- Update issue templates by @devopvoid +- RTP header extension refactoring by @devopvoid +- Fetch WebRTC with no history by @devopvoid +- Added Windows to CI builds by @devopvoid +- Refactored cmake builds for WebRTC by @devopvoid +- Linux and macOS CI builds by @devopvoid +- Removed travis config due to GitHub Actions by @devopvoid +- Create maven.yml by @devopvoid +- Fixed travis install-build-deps.sh URL by @devopvoid +- Travis config before_install by @devopvoid +- Travis config by @devopvoid +- Device and capabilities retrieval by @devopvoid +- ICE candidate error api implementation by @devopvoid +- Merge remote-tracking branch 'origin/master' by @devopvoid +- Update README.md by @devopvoid +- ICE candidate removal by @devopvoid +- Updated WebRTC license URL by @devopvoid +- AppRTC demo integration by @devopvoid +- Fixed fork crash when test is finished by @devopvoid +- Removed debug output and updated JUnit version to 5.6.0 by @devopvoid +- Fixed module tests by @devopvoid +- Fixed JavaFX maven-shade-plugin main class by @devopvoid +- Signaling room JavaFX api update by @devopvoid +- AppRTC signaling implementation by @devopvoid +- Native peer connection demo signaling client by @devopvoid +- Signaling room api by @devopvoid +- Change default demo logging by @devopvoid +- Switch to WebRTC branch M80/3987 by @devopvoid +- Update README.md by @devopvoid +- Added module-infos and changed JSON api dependencies by @devopvoid +- Add dynamic libraries to .gitignore by @devopvoid +- Removed unnecessary assert by @devopvoid +- Fixed Unix builds by @devopvoid +- Update README.md by @devopvoid +- Update README.md by @devopvoid +- Mac visibility compiler flags by @devopvoid +- Removed unnecessary includes +- Fixed and improved CMake builds by @devopvoid +- Updated JUnit version to 5.5.2 by @devopvoid +- Improved test cases by @devopvoid +- Fixed non-Windows build +- Prepare for next development iteration + + +## [0.1.0] - 2019-12-22 + +### Other +- Prepare release webrtc-java-0.1.0 +- Minor changes to the NOTICE and parent POM +- Initial commit by @devopvoid + +## New Contributors +* @ made their first contribution +* @devopvoid made their first contribution -### Refactoring -- RTP header extension refactoring ([be2c4c9](https://github.com/devopvoid/webrtc-java/commit/be2c4c9)) -- Refactor bloated project structure ([ff4da53](https://github.com/devopvoid/webrtc-java/commit/ff4da53)) -## [0.1.0](https://github.com/devopvoid/webrtc-java/tree/v0.1.0) (2019-12-22) -### Project -- Initial commit ([178b458](https://github.com/devopvoid/webrtc-java/commit/178b458)) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 2ccc8220..e0e548d5 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -6,34 +6,36 @@ - Guide - [Overview](guide/overview.md) - Media Basics - - [Media Devices](guide/media_devices.md) - - [Bitrate and Framerate Constraints](guide/constraints.md) - - [Send-only and Receive-only](guide/send_receive_direction.md) + - [Media Devices](guide/media/media_devices.md) + - [Bitrate and Framerate Constraints](guide/media/constraints.md) + - [Send-only and Receive-only](guide/media/send_receive_direction.md) - Audio - - [Audio Device Selection](guide/audio_devices.md) - - [Audio Processing](guide/audio_processing.md) - - [Custom Audio Source](guide/custom_audio_source.md) - - [Headless Audio](guide/headless_audio_device_module.md) - - [DTMF Sender](guide/dtmf_sender.md) + - [Audio Device Selection](guide/audio/audio_devices.md) + - [Audio Processing](guide/audio/audio_processing.md) + - [Custom Audio Source](guide/audio/custom_audio_source.md) + - [Headless Audio](guide/audio/headless_audio_device_module.md) + - [DTMF Sender](guide/audio/dtmf_sender.md) - Video - - [Camera Capture](guide/camera_capture.md) - - [Desktop Capture](guide/desktop_capture.md) - - [Custom Video Source](guide/custom_video_source.md) + - [Camera Capture](guide/video/camera_capture.md) + - [Desktop Capture](guide/video/desktop_capture.md) + - [Custom Video Source](guide/video/custom_video_source.md) - Data - - [Data Channels](guide/data_channels.md) + - [Data Channels](guide/data/data_channels.md) - Networking and ICE - - [Port Allocator Config](guide/port_allocator_config.md) + - [Port Allocator Config](guide/networking/port_allocator_config.md) - Monitoring and Debugging - - [RTC Stats](guide/rtc_stats.md) - - [Logging](guide/logging.md) + - [RTC Stats](guide/monitoring/rtc_stats.md) + - [Logging](guide/monitoring/logging.md) - Utilities - - [Audio Converter](guide/audio_converter.md) + - [Audio Converter](guide/utilities/audio_converter.md) + - [Audio Recorder](guide/utilities/audio_recorder.md) + - [Audio Player](guide/utilities/audio_player.md) - [Video Buffer Converter](guide/utilities/video_buffer_converter.md) - - [Video Capture](guide/video_capturer.md) - - [Screen Capture](guide/screen_capturer.md) - - [Window Capture](guide/window_capturer.md) - - [Voice Activity Detector](guide/voice_activity_detector.md) - - [Power Management](guide/power_management.md) + - [Video Capture](guide/utilities/video_capturer.md) + - [Screen Capture](guide/utilities/screen_capturer.md) + - [Window Capture](guide/utilities/window_capturer.md) + - [Voice Activity Detector](guide/utilities/voice_activity_detector.md) + - [Power Management](guide/utilities/power_management.md) - [**Build Notes**](build.md) - [**Changelog**](changelog.md) \ No newline at end of file diff --git a/docs/assets/versions.js b/docs/assets/versions.js index 2a69742b..8e2c9a07 100644 --- a/docs/assets/versions.js +++ b/docs/assets/versions.js @@ -1,4 +1,4 @@ window.PROJECT_VARS = { - VERSION: '0.13.0', - VERSION_SNAPSHOT: '0.14.0-SNAPSHOT' + VERSION: '0.14.0', + VERSION_SNAPSHOT: '0.15.0-SNAPSHOT' }; \ No newline at end of file diff --git a/docs/guide/audio_devices.md b/docs/guide/audio/audio_devices.md similarity index 100% rename from docs/guide/audio_devices.md rename to docs/guide/audio/audio_devices.md diff --git a/docs/guide/audio_processing.md b/docs/guide/audio/audio_processing.md similarity index 83% rename from docs/guide/audio_processing.md rename to docs/guide/audio/audio_processing.md index 60d514eb..cdc48901 100644 --- a/docs/guide/audio_processing.md +++ b/docs/guide/audio/audio_processing.md @@ -123,22 +123,54 @@ config.noiseSuppression.level = NoiseSuppression.Level.MODERATE; ### Gain Control -Gain control adjusts the audio level automatically: +Gain control adjusts the audio level automatically. WebRTC provides two gain controllers: +- GainController (AGC1, legacy/classic) +- GainControllerDigital (newer digital AGC) + +In most applications you should enable only one of them at a time. + +#### Legacy Gain Controller (AGC1) +AGC1 can operate in analog or digital modes and includes an optional limiter. + +```java +// Enable the legacy AGC1 +config.gainController.enabled = true; + +// Select mode: AdaptiveAnalog, AdaptiveDigital, or FixedDigital +config.gainController.mode = AudioProcessingConfig.GainController.Mode.AdaptiveAnalog; + +// Set the target level (in dBFS) and compression gain (in dB) +config.gainController.targetLevelDbfs = 3; // common default +config.gainController.compressionGainDb = 9; // common default + +// Enable the limiter to reduce clipping +config.gainController.enableLimiter = true; + +// Optional: tune analog gain controller sub-settings +config.gainController.analogGainController.enabled = true; +config.gainController.analogGainController.enableDigitalAdaptive = true; +// Clipping predictor (optional) +config.gainController.analogGainController.clippingPredictor.enabled = false; +``` + +When using AdaptiveAnalog mode, integrate with your system microphone level if possible so AGC1 can adjust the hardware/OS capture volume. Use AdaptiveDigital if you cannot control device gain. FixedDigital applies a constant digital gain. + +#### Digital Gain Controller ```java -// Enable gain control -config.gainControl.enabled = true; +// Enable the newer digital AGC +config.gainControllerDigital.enabled = true; // Configure fixed digital gain (in dB) -config.gainControl.fixedDigital.gainDb = 5.0f; +config.gainControllerDigital.fixedDigital.gainDb = 5.0f; // Or configure adaptive digital gain -config.gainControl.adaptiveDigital.enabled = true; -config.gainControl.adaptiveDigital.headroomDb = 3.0f; -config.gainControl.adaptiveDigital.maxGainDb = 30.0f; -config.gainControl.adaptiveDigital.initialGainDb = 8.0f; -config.gainControl.adaptiveDigital.maxGainChangeDbPerSecond = 3.0f; -config.gainControl.adaptiveDigital.maxOutputNoiseLevelDbfs = -50.0f; +config.gainControllerDigital.adaptiveDigital.enabled = true; +config.gainControllerDigital.adaptiveDigital.headroomDb = 3.0f; +config.gainControllerDigital.adaptiveDigital.maxGainDb = 30.0f; +config.gainControllerDigital.adaptiveDigital.initialGainDb = 8.0f; +config.gainControllerDigital.adaptiveDigital.maxGainChangeDbPerSecond = 3.0f; +config.gainControllerDigital.adaptiveDigital.maxOutputNoiseLevelDbfs = -50.0f; ``` ### High-Pass Filter diff --git a/docs/guide/custom_audio_source.md b/docs/guide/audio/custom_audio_source.md similarity index 96% rename from docs/guide/custom_audio_source.md rename to docs/guide/audio/custom_audio_source.md index 5e0881eb..3af276f5 100644 --- a/docs/guide/custom_audio_source.md +++ b/docs/guide/audio/custom_audio_source.md @@ -1,192 +1,192 @@ -# Custom Audio Source - -This guide explains how to use the `CustomAudioSource` class to push audio data from external sources directly to the WebRTC audio pipeline. - -## Overview - -The `CustomAudioSource` allows you to provide audio data from custom sources such as: -- Audio files -- Network streams -- Generated audio (tones, noise, etc.) -- Audio processing libraries -- Any other source of raw audio data - -This is particularly useful when you need to: -- Stream pre-recorded audio -- Process audio before sending it -- Generate synthetic audio -- Integrate with external audio APIs - -## Basic Usage - -### Creating a Custom Audio Source - -To use a custom audio source, you first need to create an instance: - -```java -import dev.onvoid.webrtc.media.audio.CustomAudioSource; - -// Create a new CustomAudioSource instance -CustomAudioSource audioSource = new CustomAudioSource(); -``` - -### Creating an Audio Track - -Once you have a custom audio source, you can create an audio track with it: - -```java -import dev.onvoid.webrtc.PeerConnectionFactory; -import dev.onvoid.webrtc.media.audio.AudioTrack; - -// Create a PeerConnectionFactory (you should already have this in your WebRTC setup) -PeerConnectionFactory factory = new PeerConnectionFactory(); - -// Create an audio track using the custom audio source -AudioTrack audioTrack = factory.createAudioTrack("audio-track-id", audioSource); -``` - -### Pushing Audio Data - -The key feature of `CustomAudioSource` is the ability to push audio data directly to the WebRTC pipeline: - -```java -// Parameters for the audio data -int bitsPerSample = 16; // Common values: 8, 16, 32 -int sampleRate = 48000; // Common values: 8000, 16000, 44100, 48000 -int channels = 2; // 1 for mono, 2 for stereo -int frameCount = 480; // For 10ms of audio at 48kHz - -// Create a buffer for the audio data -// Size = frameCount * channels * (bitsPerSample / 8) -int bytesPerSample = bitsPerSample / 8; -byte[] audioData = new byte[frameCount * channels * bytesPerSample]; - -// Fill the buffer with your audio data -// ... - -// Push the audio data to the WebRTC pipeline -audioSource.pushAudio(audioData, bitsPerSample, sampleRate, channels, frameCount); -``` - -## Audio Format Considerations - -When pushing audio data, you need to consider the following parameters: - -### Bits Per Sample -- **8-bit**: Lower quality, smaller data size -- **16-bit**: Standard quality for most applications -- **32-bit**: Higher quality, larger data size - -### Sample Rate -- **8000 Hz**: Telephone quality -- **16000 Hz**: Good for speech -- **44100 Hz**: CD quality -- **48000 Hz**: Standard for digital audio workstations and professional audio - -### Channels -- **1 (Mono)**: Single channel audio -- **2 (Stereo)**: Dual channel audio - -### Frame Count -The number of frames depends on the desired buffer size and sample rate. For a 10ms buffer: -- At 8000 Hz: 80 frames -- At 16000 Hz: 160 frames -- At 44100 Hz: 441 frames -- At 48000 Hz: 480 frames - -## Advanced Usage - -### Continuous Audio Streaming - -For continuous streaming, you'll typically push audio data in a separate thread: - -```java -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -public class AudioStreamer { - private final CustomAudioSource audioSource; - private final ScheduledExecutorService executor; - private final int bitsPerSample = 16; - private final int sampleRate = 48000; - private final int channels = 2; - private final int frameCount = 480; // 10ms at 48kHz - - public AudioStreamer(CustomAudioSource audioSource) { - this.audioSource = audioSource; - this.executor = Executors.newSingleThreadScheduledExecutor(); - } - - public void start() { - // Schedule task to run every 10ms - executor.scheduleAtFixedRate(this::pushNextAudioBuffer, 0, 10, TimeUnit.MILLISECONDS); - } - - public void stop() { - executor.shutdown(); - } - - private void pushNextAudioBuffer() { - // Create and fill audio buffer - int bytesPerSample = bitsPerSample / 8; - byte[] audioData = new byte[frameCount * channels * bytesPerSample]; - - // Fill audioData with your audio samples - // ... - - // Push to WebRTC - audioSource.pushAudio(audioData, bitsPerSample, sampleRate, channels, frameCount); - } -} -``` - -## Integration with Audio Tracks - -### Adding Sinks to Monitor Audio - -You can add sinks to the audio track to monitor the audio data: - -```java -import dev.onvoid.webrtc.media.audio.AudioTrackSink; - -// Create a sink to monitor the audio data -AudioTrackSink monitorSink = (data, bitsPerSample, sampleRate, channels, frames) -> { - System.out.println("Received audio: " + - bitsPerSample + " bits, " + - sampleRate + " Hz, " + - channels + " channels, " + - frames + " frames"); - - // You can process or analyze the audio data here -}; - -// Add the sink to the audio track -audioTrack.addSink(monitorSink); - -// When done, remove the sink -audioTrack.removeSink(monitorSink); -``` - -## Cleanup - -When you're done with the custom audio source, make sure to clean up resources: - -```java -// Dispose of the audio track -audioTrack.dispose(); - -// Dispose of the audio source to prevent memory leaks -audioSource.dispose(); - -// If you're using a scheduled executor for pushing audio -audioStreamer.stop(); -``` - ---- - -## Conclusion - -The `CustomAudioSource` provides a flexible way to integrate external audio sources with WebRTC. By understanding the audio format parameters and properly managing the audio data flow, you can create applications that use custom audio from virtually any source. - +# Custom Audio Source + +This guide explains how to use the `CustomAudioSource` class to push audio data from external sources directly to the WebRTC audio pipeline. + +## Overview + +The `CustomAudioSource` allows you to provide audio data from custom sources such as: +- Audio files +- Network streams +- Generated audio (tones, noise, etc.) +- Audio processing libraries +- Any other source of raw audio data + +This is particularly useful when you need to: +- Stream pre-recorded audio +- Process audio before sending it +- Generate synthetic audio +- Integrate with external audio APIs + +## Basic Usage + +### Creating a Custom Audio Source + +To use a custom audio source, you first need to create an instance: + +```java +import dev.onvoid.webrtc.media.audio.CustomAudioSource; + +// Create a new CustomAudioSource instance +CustomAudioSource audioSource = new CustomAudioSource(); +``` + +### Creating an Audio Track + +Once you have a custom audio source, you can create an audio track with it: + +```java +import dev.onvoid.webrtc.PeerConnectionFactory; +import dev.onvoid.webrtc.media.audio.AudioTrack; + +// Create a PeerConnectionFactory (you should already have this in your WebRTC setup) +PeerConnectionFactory factory = new PeerConnectionFactory(); + +// Create an audio track using the custom audio source +AudioTrack audioTrack = factory.createAudioTrack("audio-track-id", audioSource); +``` + +### Pushing Audio Data + +The key feature of `CustomAudioSource` is the ability to push audio data directly to the WebRTC pipeline: + +```java +// Parameters for the audio data +int bitsPerSample = 16; // Common values: 8, 16, 32 +int sampleRate = 48000; // Common values: 8000, 16000, 44100, 48000 +int channels = 2; // 1 for mono, 2 for stereo +int frameCount = 480; // For 10ms of audio at 48kHz + +// Create a buffer for the audio data +// Size = frameCount * channels * (bitsPerSample / 8) +int bytesPerSample = bitsPerSample / 8; +byte[] audioData = new byte[frameCount * channels * bytesPerSample]; + +// Fill the buffer with your audio data +// ... + +// Push the audio data to the WebRTC pipeline +audioSource.pushAudio(audioData, bitsPerSample, sampleRate, channels, frameCount); +``` + +## Audio Format Considerations + +When pushing audio data, you need to consider the following parameters: + +### Bits Per Sample +- **8-bit**: Lower quality, smaller data size +- **16-bit**: Standard quality for most applications +- **32-bit**: Higher quality, larger data size + +### Sample Rate +- **8000 Hz**: Telephone quality +- **16000 Hz**: Good for speech +- **44100 Hz**: CD quality +- **48000 Hz**: Standard for digital audio workstations and professional audio + +### Channels +- **1 (Mono)**: Single channel audio +- **2 (Stereo)**: Dual channel audio + +### Frame Count +The number of frames depends on the desired buffer size and sample rate. For a 10ms buffer: +- At 8000 Hz: 80 frames +- At 16000 Hz: 160 frames +- At 44100 Hz: 441 frames +- At 48000 Hz: 480 frames + +## Advanced Usage + +### Continuous Audio Streaming + +For continuous streaming, you'll typically push audio data in a separate thread: + +```java +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class AudioStreamer { + private final CustomAudioSource audioSource; + private final ScheduledExecutorService executor; + private final int bitsPerSample = 16; + private final int sampleRate = 48000; + private final int channels = 2; + private final int frameCount = 480; // 10ms at 48kHz + + public AudioStreamer(CustomAudioSource audioSource) { + this.audioSource = audioSource; + this.executor = Executors.newSingleThreadScheduledExecutor(); + } + + public void start() { + // Schedule task to run every 10ms + executor.scheduleAtFixedRate(this::pushNextAudioBuffer, 0, 10, TimeUnit.MILLISECONDS); + } + + public void stop() { + executor.shutdown(); + } + + private void pushNextAudioBuffer() { + // Create and fill audio buffer + int bytesPerSample = bitsPerSample / 8; + byte[] audioData = new byte[frameCount * channels * bytesPerSample]; + + // Fill audioData with your audio samples + // ... + + // Push to WebRTC + audioSource.pushAudio(audioData, bitsPerSample, sampleRate, channels, frameCount); + } +} +``` + +## Integration with Audio Tracks + +### Adding Sinks to Monitor Audio + +You can add sinks to the audio track to monitor the audio data: + +```java +import dev.onvoid.webrtc.media.audio.AudioTrackSink; + +// Create a sink to monitor the audio data +AudioTrackSink monitorSink = (data, bitsPerSample, sampleRate, channels, frames) -> { + System.out.println("Received audio: " + + bitsPerSample + " bits, " + + sampleRate + " Hz, " + + channels + " channels, " + + frames + " frames"); + + // You can process or analyze the audio data here +}; + +// Add the sink to the audio track +audioTrack.addSink(monitorSink); + +// When done, remove the sink +audioTrack.removeSink(monitorSink); +``` + +## Cleanup + +When you're done with the custom audio source, make sure to clean up resources: + +```java +// Dispose of the audio track +audioTrack.dispose(); + +// Dispose of the audio source to prevent memory leaks +audioSource.dispose(); + +// If you're using a scheduled executor for pushing audio +audioStreamer.stop(); +``` + +--- + +## Conclusion + +The `CustomAudioSource` provides a flexible way to integrate external audio sources with WebRTC. By understanding the audio format parameters and properly managing the audio data flow, you can create applications that use custom audio from virtually any source. + For more advanced audio processing options, consider exploring the audio processing APIs available in this documentation. \ No newline at end of file diff --git a/docs/guide/dtmf_sender.md b/docs/guide/audio/dtmf_sender.md similarity index 100% rename from docs/guide/dtmf_sender.md rename to docs/guide/audio/dtmf_sender.md diff --git a/docs/guide/headless_audio_device_module.md b/docs/guide/audio/headless_audio_device_module.md similarity index 97% rename from docs/guide/headless_audio_device_module.md rename to docs/guide/audio/headless_audio_device_module.md index 83c281e0..3c75ee7d 100644 --- a/docs/guide/headless_audio_device_module.md +++ b/docs/guide/audio/headless_audio_device_module.md @@ -99,8 +99,8 @@ Details: - Using a standard `AudioDeviceModule` with `AudioLayer.kDummyAudio` disables actual audio I/O; the audio pipeline is not started for playout and sinks will typically not receive audio frame callbacks. Use this only when you intentionally do not want any audio delivery (e.g., video‑only or fully custom audio). Related guides: -- [Audio Device Selection](audio_devices.md) -- [Custom Audio Source](custom_audio_source.md) +- [Audio Device Selection](guide/audio/audio_devices.md) +- [Custom Audio Source](guide/audio/custom_audio_source.md) --- diff --git a/docs/guide/data_channels.md b/docs/guide/data/data_channels.md similarity index 100% rename from docs/guide/data_channels.md rename to docs/guide/data/data_channels.md diff --git a/docs/guide/constraints.md b/docs/guide/media/constraints.md similarity index 100% rename from docs/guide/constraints.md rename to docs/guide/media/constraints.md diff --git a/docs/guide/media_devices.md b/docs/guide/media/media_devices.md similarity index 100% rename from docs/guide/media_devices.md rename to docs/guide/media/media_devices.md diff --git a/docs/guide/send_receive_direction.md b/docs/guide/media/send_receive_direction.md similarity index 100% rename from docs/guide/send_receive_direction.md rename to docs/guide/media/send_receive_direction.md diff --git a/docs/guide/logging.md b/docs/guide/monitoring/logging.md similarity index 100% rename from docs/guide/logging.md rename to docs/guide/monitoring/logging.md diff --git a/docs/guide/rtc_stats.md b/docs/guide/monitoring/rtc_stats.md similarity index 100% rename from docs/guide/rtc_stats.md rename to docs/guide/monitoring/rtc_stats.md diff --git a/docs/guide/port_allocator_config.md b/docs/guide/networking/port_allocator_config.md similarity index 98% rename from docs/guide/port_allocator_config.md rename to docs/guide/networking/port_allocator_config.md index 46a90232..fa153388 100644 --- a/docs/guide/port_allocator_config.md +++ b/docs/guide/networking/port_allocator_config.md @@ -1,4 +1,4 @@ -# Port Allocator Configuration (ICE) +# Port Allocator Configuration (ICE) This guide explains how to configure the ICE port allocator using `dev.onvoid.webrtc.PortAllocatorConfig` and how to use it with `RTCConfiguration` when creating a peer connection. diff --git a/docs/guide/overview.md b/docs/guide/overview.md index 8947996b..49146d8b 100644 --- a/docs/guide/overview.md +++ b/docs/guide/overview.md @@ -4,46 +4,48 @@ This section provides detailed guides for various features of the webrtc-java li ## Media Basics -- [Media Devices](guide/media_devices.md) - Working with audio and video devices -- [Bitrate and Framerate Constraints](guide/constraints.md) - Controlling media quality -- [Send-only and Receive-only](guide/send_receive_direction.md) - Configure transceiver directions (send-only, receive-only or inactive) +- [Media Devices](guide/media/media_devices.md) - Working with audio and video devices +- [Bitrate and Framerate Constraints](guide/media/constraints.md) - Controlling media quality +- [Send-only and Receive-only](guide/media/send_receive_direction.md) - Configure transceiver directions (send-only, receive-only or inactive) ## Audio -- [Audio Device Selection](guide/audio_devices.md) - Selecting and configuring audio devices -- [Audio Processing](guide/audio_processing.md) - Voice processing components -- [Custom Audio Source](guide/custom_audio_source.md) - Using custom audio sources with WebRTC -- [Headless Audio](guide/headless_audio_device_module.md) - Playout pull without touching real OS audio devices -- [DTMF Sender](guide/dtmf_sender.md) - Sending DTMF tones in a call +- [Audio Device Selection](guide/audio/audio_devices.md) - Selecting and configuring audio devices +- [Audio Processing](guide/audio/audio_processing.md) - Voice processing components +- [Custom Audio Source](guide/audio/custom_audio_source.md) - Using custom audio sources with WebRTC +- [Headless Audio](guide/audio/headless_audio_device_module.md) - Playout pull without touching real OS audio devices +- [DTMF Sender](guide/audio/dtmf_sender.md) - Sending DTMF tones in a call ## Video -- [Camera Capture](guide/camera_capture.md) - Capturing video from cameras -- [Desktop Capture](guide/desktop_capture.md) - Capturing and sharing screens and windows -- [Custom Video Source](guide/custom_video_source.md) - Using custom video sources with WebRTC +- [Camera Capture](guide/video/camera_capture.md) - Capturing video from cameras +- [Desktop Capture](guide/video/desktop_capture.md) - Capturing and sharing screens and windows +- [Custom Video Source](guide/video/custom_video_source.md) - Using custom video sources with WebRTC ## Data Communication -- [Data Channels](guide/data_channels.md) - Sending and receiving arbitrary data between peers +- [Data Channels](guide/data/data_channels.md) - Sending and receiving arbitrary data between peers ## Networking and ICE -- [Port Allocator Config](guide/port_allocator_config.md) - Restrict ICE port ranges and control candidate gathering behavior +- [Port Allocator Config](guide/networking/port_allocator_config.md) - Restrict ICE port ranges and control candidate gathering behavior ## Monitoring and Debugging -- [RTC Stats](guide/rtc_stats.md) - Monitoring connection quality and performance -- [Logging](guide/logging.md) - Configuring and using the logging system +- [RTC Stats](guide/monitoring/rtc_stats.md) - Monitoring connection quality and performance +- [Logging](guide/monitoring/logging.md) - Configuring and using the logging system ## Utilities -- [Audio Converter](guide/audio_converter.md) - Resample and remix 10 ms PCM frames between different rates and channel layouts +- [Audio Converter](guide/utilities/audio_converter.md) - Resample and remix 10 ms PCM frames between different rates and channel layouts +- [Audio Recorder](guide/utilities/audio_recorder.md) - Capture microphone input and receive 10 ms PCM frames via an AudioSink +- [Audio Player](guide/utilities/audio_player.md) - Play PCM audio to an output device by supplying frames via an AudioSource - [Video Buffer Converter](guide/utilities/video_buffer_converter.md) - Convert between I420 and common FourCC pixel formats (e.g., RGBA, NV12) -- [Video Capture](guide/video_capturer.md) - Control a camera device, configure capabilities, and deliver frames to a sink -- [Screen Capture](guide/screen_capturer.md) - Enumerate and capture full desktop screens/monitors -- [Window Capture](guide/window_capturer.md) - Enumerate and capture individual application windows -- [Voice Activity Detector](guide/voice_activity_detector.md) - Detect speech activity in PCM audio streams -- [Power Management](guide/power_management.md) - Prevent the display from sleeping during desktop capture or presentations +- [Video Capture](guide/utilities/video_capturer.md) - Control a camera device, configure capabilities, and deliver frames to a sink +- [Screen Capture](guide/utilities/screen_capturer.md) - Enumerate and capture full desktop screens/monitors +- [Window Capture](guide/utilities/window_capturer.md) - Enumerate and capture individual application windows +- [Voice Activity Detector](guide/utilities/voice_activity_detector.md) - Detect speech activity in PCM audio streams +- [Power Management](guide/utilities/power_management.md) - Prevent the display from sleeping during desktop capture or presentations ## Additional Resources diff --git a/docs/guide/audio_converter.md b/docs/guide/utilities/audio_converter.md similarity index 95% rename from docs/guide/audio_converter.md rename to docs/guide/utilities/audio_converter.md index 6c816b91..50845bc6 100644 --- a/docs/guide/audio_converter.md +++ b/docs/guide/utilities/audio_converter.md @@ -102,6 +102,6 @@ finally { ## Related guides -- [Audio Processing](guide/audio_processing.md) -- [Headless Audio](guide/headless_audio_device_module.md) -- [Voice Activity Detector](guide/voice_activity_detector.md) +- [Audio Processing](guide/audio/audio_processing.md) +- [Headless Audio](guide/audio/headless_audio_device_module.md) +- [Voice Activity Detector](guide/utilities/voice_activity_detector.md) diff --git a/docs/guide/utilities/audio_player.md b/docs/guide/utilities/audio_player.md new file mode 100644 index 00000000..e0ae489d --- /dev/null +++ b/docs/guide/utilities/audio_player.md @@ -0,0 +1,97 @@ +# Audio Player + +The AudioPlayer is a small helper that plays audio using a selected output device by pulling frames from your implementation of AudioSource. It manages a native AudioDeviceModule internally and provides idempotent start/stop. + +API: `dev.onvoid.webrtc.media.audio.AudioPlayer` + +## When to use it +- You want to render raw PCM audio (generated or decoded by your app) to an OS output device. +- You need a simple, high‑level start/stop wrapper around WebRTC’s audio playout. + +See also: [Audio Device Selection](guide/audio/audio_devices.md), [Custom Audio Source](guide/audio/custom_audio_source.md), [Headless Audio](guide/audio/headless_audio_device_module.md). + +## Key concepts +- Device selection: Provide an `AudioDevice` representing the output device (speaker) before starting. +- Data supply: Implement `AudioSource` to provide 10 ms PCM frames on demand. +- Lifecycle: `start()` initializes output and begins pulling; `stop()` halts playout and releases resources. + +## Basic usage + +```java +import dev.onvoid.webrtc.media.audio.AudioPlayer; +import dev.onvoid.webrtc.media.audio.AudioSource; +import dev.onvoid.webrtc.media.audio.AudioDevice; +import dev.onvoid.webrtc.media.audio.AudioDeviceModule; + +import java.nio.ByteBuffer; + +public class TonePlayerExample { + + public static void main(String[] args) { + // Choose a playout device (speaker). Enumerate via a temporary ADM. + AudioDeviceModule adm = new AudioDeviceModule(); + AudioDevice speaker = adm.getPlayoutDevices().stream() + .findFirst() + .orElseThrow(() -> new IllegalStateException("No playout device found")); + adm.dispose(); + + // Provide 10 ms frames of PCM 16‑bit audio. This example generates a sine tone. + final int sampleRate = 48000; + final int channels = 2; + final int bytesPerSample = channels * 2; // 16‑bit + final double frequency = 440.0; // A4 + final double twoPiFDivFs = 2 * Math.PI * frequency / sampleRate; + final int samplesPer10msPerChannel = sampleRate / 100; // 480 samples/channel + final int totalSamplesPer10ms = samplesPer10msPerChannel * channels; // e.g., 960 samples + final double[] phase = new double[] {0.0}; + + AudioSource source = (audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec) -> { + // Ensure caller requested matches our configuration + if (nBytesPerSample != bytesPerSample || nChannels != channels || samplesPerSec != sampleRate) { + // Fill silence if formats mismatch + java.util.Arrays.fill(audioSamples, (byte) 0); + return totalSamplesPer10ms; + } + + // Generate interleaved stereo sine wave + int idx = 0; + for (int i = 0; i < samplesPer10msPerChannel; i++) { + short s = (short) (Math.sin(phase[0]) * 32767); + // left + audioSamples[idx++] = (byte) (s & 0xFF); + audioSamples[idx++] = (byte) ((s >> 8) & 0xFF); + // right + audioSamples[idx++] = (byte) (s & 0xFF); + audioSamples[idx++] = (byte) ((s >> 8) & 0xFF); + + phase[0] += twoPiFDivFs; + + if (phase[0] > Math.PI * 2) { + phase[0] -= Math.PI * 2; + } + } + return totalSamplesPer10ms; // number of samples written across all channels + }; + + AudioPlayer player = new AudioPlayer(); + player.setAudioDevice(speaker); + player.setAudioSource(source); + + player.start(); + // ... playout running ... + player.stop(); + } +} +``` + +## Data format +- The player requests 10 ms frames as 16‑bit little‑endian PCM via `AudioSource#onPlaybackData`. +- Return value must be the number of samples written across all channels for that 10 ms frame. + +## Tips +- If your synthesis/decoder operates at a different rate or channel layout, convert using the [Audio Converter](guide/utilities/audio_converter.md) before writing into the output buffer. + +## API reference +- `setAudioDevice(AudioDevice device)` – choose output device +- `setAudioSource(AudioSource source)` – provide playout frames +- `start()` / `stop()` – control the playout lifecycle diff --git a/docs/guide/utilities/audio_recorder.md b/docs/guide/utilities/audio_recorder.md new file mode 100644 index 00000000..b78a3517 --- /dev/null +++ b/docs/guide/utilities/audio_recorder.md @@ -0,0 +1,65 @@ +# Audio Recorder + +The AudioRecorder is a small helper that captures audio from a selected input device and forwards PCM frames to your implementation of AudioSink. It manages a native AudioDeviceModule internally and provides idempotent start/stop. + +API: `dev.onvoid.webrtc.media.audio.AudioRecorder` + +## When to use it +- You want a simple way to record microphone input without wiring a full PeerConnection. +- You need raw 16‑bit PCM frames (10 ms) delivered to your code for analysis, file writing, or custom processing. + +See also: [Audio Device Selection](guide/audio/audio_devices.md), [Audio Processing](guide/audio/audio_processing.md), [Custom Audio Source](guide/audio/custom_audio_source.md). + +## Key concepts +- Device selection: Provide an `AudioDevice` representing the input device (microphone) before starting. +- Data delivery: Implement `AudioSink` to receive recorded frames. +- Lifecycle: `start()` initializes and starts capture once; `stop()` halts and releases native resources. + +## Basic usage + +```java +import dev.onvoid.webrtc.media.audio.AudioRecorder; +import dev.onvoid.webrtc.media.audio.AudioSink; +import dev.onvoid.webrtc.media.audio.AudioDevice; +import dev.onvoid.webrtc.media.audio.AudioDeviceModule; + +public class MicRecorderExample { + public static void main(String[] args) { + // Pick a recording device (microphone). You can enumerate via MediaDevices.getAudioCaptureDevices() + AudioDevice mic = ... + + // Implement a sink to receive 10 ms PCM frames + AudioSink sink = (audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec, totalDelayMS, clockDrift) -> { + // audioSamples: little‑endian 16‑bit PCM + // nSamples: total samples across all channels for this 10 ms frame + // nBytesPerSample: typically 2 for 16‑bit + // nChannels: number of channels (1 = mono, 2 = stereo) + // samplesPerSec: sample rate (e.g., 48000) + // totalDelayMS, clockDrift: diagnostics + // TODO: write to file, analyzer, encoder, etc. + }; + + AudioRecorder recorder = new AudioRecorder(); + recorder.setAudioDevice(mic); + recorder.setAudioSink(sink); + + recorder.start(); + // ... capture running ... + recorder.stop(); + } +} +``` + +## Data format +- Frames are delivered every 10 ms as 16‑bit little‑endian PCM in a `byte[]`. +- `nSamples` refers to the number of samples across all channels within the 10 ms frame. Example: 48 kHz stereo → (48000/100)×2 = 960×2 = 1920 samples. + +## Tips +- Apply echo cancellation, AGC, and noise suppression via the [Audio Processing](guide/audio/audio_processing.md) guide if needed. +- If you need to resample or remix, use the [Audio Converter](guide/utilities/audio_converter.md). +- Device selection details and best practices are covered in [Audio Device Selection](guide/audio/audio_devices.md). + +## API reference +- `setAudioDevice(AudioDevice device)` – choose input device +- `setAudioSink(AudioSink sink)` – receive captured frames +- `start()` / `stop()` – control the capture lifecycle diff --git a/docs/guide/power_management.md b/docs/guide/utilities/power_management.md similarity index 96% rename from docs/guide/power_management.md rename to docs/guide/utilities/power_management.md index d0d9c847..38d4177d 100644 --- a/docs/guide/power_management.md +++ b/docs/guide/utilities/power_management.md @@ -57,5 +57,5 @@ Exact mechanisms are handled by the native layer; your Java code remains the sam ## Related guides -- [Desktop Capture](guide/desktop_capture.md) -- [Logging](guide/logging.md) +- [Desktop Capture](guide/video/desktop_capture.md) +- [Logging](guide/monitoring/logging.md) diff --git a/docs/guide/screen_capturer.md b/docs/guide/utilities/screen_capturer.md similarity index 97% rename from docs/guide/screen_capturer.md rename to docs/guide/utilities/screen_capturer.md index fdb69927..a76204b7 100644 --- a/docs/guide/screen_capturer.md +++ b/docs/guide/utilities/screen_capturer.md @@ -108,5 +108,5 @@ sc.dispose(); ## Related guides -- [Desktop Capture](guide/desktop_capture.md) -- [Power Management](guide/power_management.md) +- [Desktop Capture](guide/video/desktop_capture.md) +- [Power Management](guide/utilities/power_management.md) diff --git a/docs/guide/utilities/video_buffer_converter.md b/docs/guide/utilities/video_buffer_converter.md index 6afc0b31..d2db2110 100644 --- a/docs/guide/utilities/video_buffer_converter.md +++ b/docs/guide/utilities/video_buffer_converter.md @@ -9,7 +9,7 @@ API: `dev.onvoid.webrtc.media.video.VideoBufferConverter` - Preparing frames for encoders/decoders that require specific pixel formats. - Importing external pixel data (e.g., RGBA, NV12) into the WebRTC pipeline as I420. -See also: [Video Capture](../video_capture.md), [Custom Video Source](../custom_video_source.md). +See also: [Video Capture](guide/utilities/video_capturer.md), [Custom Video Source](guide/video/custom_video_source.md). ## Supported operations @@ -139,7 +139,7 @@ public void onImage(BufferedImage image) { - Beware of frame rotation metadata; conversions do not rotate pixels. Handle `VideoFrame.rotation` separately if your renderer requires upright images. ## Related -- [Video Capture](../video_capture.md) -- [Custom Video Source](../custom_video_source.md) -- [Screen Capturer](../screen_capturer.md) -- [Window Capturer](../window_capturer.md) +- [Video Capture](guide/utilities/video_capturer.md) +- [Custom Video Source](guide/video/custom_video_source.md) +- [Screen Capturer](guide/utilities/screen_capturer.md) +- [Window Capturer](guide/utilities/window_capturer.md) diff --git a/docs/guide/video_capturer.md b/docs/guide/utilities/video_capturer.md similarity index 93% rename from docs/guide/video_capturer.md rename to docs/guide/utilities/video_capturer.md index 4d9b2282..9edaa588 100644 --- a/docs/guide/video_capturer.md +++ b/docs/guide/utilities/video_capturer.md @@ -103,8 +103,8 @@ If you need pixel format conversion, see `VideoBufferConverter`. ## Related guides -- [Camera Capture](guide/camera_capture.md) -- [Custom Video Source](guide/custom_video_source.md) -- [Screen Capturer](guide/screen_capturer.md) -- [Window Capturer](guide/window_capturer.md) -- [Power Management](guide/power_management.md) +- [Camera Capture](guide/video/camera_capture.md) +- [Custom Video Source](guide/video/custom_video_source.md) +- [Screen Capturer](guide/utilities/screen_capturer.md) +- [Window Capturer](guide/utilities/window_capturer.md) +- [Power Management](guide/utilities/power_management.md) diff --git a/docs/guide/voice_activity_detector.md b/docs/guide/utilities/voice_activity_detector.md similarity index 98% rename from docs/guide/voice_activity_detector.md rename to docs/guide/utilities/voice_activity_detector.md index bd575b87..d116ac1e 100644 --- a/docs/guide/voice_activity_detector.md +++ b/docs/guide/utilities/voice_activity_detector.md @@ -101,4 +101,4 @@ finally { ## Related guides -- [Audio Processing](guide/audio_processing.md) \ No newline at end of file +- [Audio Processing](guide/audio/audio_processing.md) \ No newline at end of file diff --git a/docs/guide/window_capturer.md b/docs/guide/utilities/window_capturer.md similarity index 96% rename from docs/guide/window_capturer.md rename to docs/guide/utilities/window_capturer.md index c0fb8baf..1d1571d0 100644 --- a/docs/guide/window_capturer.md +++ b/docs/guide/utilities/window_capturer.md @@ -101,5 +101,5 @@ wc.dispose(); ## Related guides -- [Desktop Capture](guide/desktop_capture.md) -- [Power Management](guide/power_management.md) +- [Desktop Capture](guide/video/desktop_capture.md) +- [Power Management](guide/utilities/power_management.md) diff --git a/docs/guide/camera_capture.md b/docs/guide/video/camera_capture.md similarity index 100% rename from docs/guide/camera_capture.md rename to docs/guide/video/camera_capture.md diff --git a/docs/guide/custom_video_source.md b/docs/guide/video/custom_video_source.md similarity index 100% rename from docs/guide/custom_video_source.md rename to docs/guide/video/custom_video_source.md diff --git a/docs/guide/desktop_capture.md b/docs/guide/video/desktop_capture.md similarity index 100% rename from docs/guide/desktop_capture.md rename to docs/guide/video/desktop_capture.md diff --git a/docs/quickstart.md b/docs/quickstart.md index 415dd3aa..6c5ed3f6 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -46,6 +46,27 @@ Add the following dependency to your `pom.xml`: ``` +Note about native library classifiers: +- The webrtc-java artifact will automatically add the dependency for the native library artifact matching the currently running operating system and architecture at runtime. +- If you encounter issues loading the native library "webrtc-java", you can declare the platform-specific classifier explicitly. For example, for Windows 64-Bit: + +```xml + + dev.onvoid.webrtc + webrtc-java + {{VERSION}} + windows-x86_64 + +``` + +Complete list of available classifiers: +- windows-x86_64 +- macos-x86_64 +- macos-aarch64 +- linux-x86_64 +- linux-aarch64 +- linux-aarch32 + #### Using SNAPSHOT Versions If you want to use the latest development version, you can use a SNAPSHOT release: @@ -246,7 +267,7 @@ AudioTrackSource audioSource = factory.createAudioSource(audioOptions); AudioTrack audioTrack = factory.createAudioTrack("audio0", audioSource); ``` -> Audio devices can be specified via the `AudioDeviceModule`, see the guide on [Audio Devices](guide/audio_devices) for more details. +> Audio devices can be specified via the `AudioDeviceModule`, see the guide on [Audio Devices](guide/audio/audio_devices) for more details. ### Adding Tracks to Peer Connection @@ -284,9 +305,9 @@ factory.dispose(); Now that you have a basic understanding of webrtc-java, you can explore more advanced features: -- [Data Channels](guide/data_channels) - Learn more about data channels -- [RTC Stats](guide/rtc_stats) - Monitor connection quality -- [Desktop Capture](guide/desktop_capture) - Share screens and windows -- [All Guides](guide) - Complete list of guides +- [Data Channels](guide/data/data_channels) - Learn more about data channels +- [RTC Stats](guide/monitoring/rtc_stats) - Monitor connection quality +- [Desktop Capture](guide/video/desktop_capture) - Share screens and windows +- [All Guides](guide/overview) - Complete list of guides For a complete API reference, check the [JavaDoc](https://javadoc.io/doc/dev.onvoid.webrtc/webrtc-java/latest/index.html). \ No newline at end of file diff --git a/pom.xml b/pom.xml index dcba8272..e33daced 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ dev.onvoid.webrtc webrtc-java-parent - 0.14.0-SNAPSHOT + 0.15.0-SNAPSHOT pom webrtc-java-parent diff --git a/webrtc-examples/pom.xml b/webrtc-examples/pom.xml index 37329111..6664b761 100644 --- a/webrtc-examples/pom.xml +++ b/webrtc-examples/pom.xml @@ -5,7 +5,7 @@ dev.onvoid.webrtc webrtc-java-parent - 0.14.0-SNAPSHOT + 0.15.0-SNAPSHOT webrtc-java-examples diff --git a/webrtc-jni/pom.xml b/webrtc-jni/pom.xml index 32259987..25a2f05b 100644 --- a/webrtc-jni/pom.xml +++ b/webrtc-jni/pom.xml @@ -5,7 +5,7 @@ dev.onvoid.webrtc webrtc-java-parent - 0.14.0-SNAPSHOT + 0.15.0-SNAPSHOT webrtc-java-jni diff --git a/webrtc-jni/src/main/cpp/include/api/RTCDataChannelObserver.h b/webrtc-jni/src/main/cpp/include/api/RTCDataChannelObserver.h index 28053613..ed791db8 100644 --- a/webrtc-jni/src/main/cpp/include/api/RTCDataChannelObserver.h +++ b/webrtc-jni/src/main/cpp/include/api/RTCDataChannelObserver.h @@ -37,6 +37,7 @@ namespace jni // DataChannelObserver implementation. void OnStateChange() override; void OnMessage(const webrtc::DataBuffer & buffer) override; + void OnBufferedAmountChange(uint64_t sent_data_size) override; private: class JavaRTCDataChannelObserverClass : public JavaClass @@ -46,6 +47,7 @@ namespace jni jmethodID onStateChange; jmethodID onMessage; + jmethodID onBufferedAmountChange; }; private: diff --git a/webrtc-jni/src/main/cpp/include/media/audio/AudioProcessingConfig.h b/webrtc-jni/src/main/cpp/include/media/audio/AudioProcessingConfig.h index 5c96f4c3..5fb98d1b 100644 --- a/webrtc-jni/src/main/cpp/include/media/audio/AudioProcessingConfig.h +++ b/webrtc-jni/src/main/cpp/include/media/audio/AudioProcessingConfig.h @@ -34,16 +34,33 @@ namespace jni explicit JavaAudioProcessingConfigClass(JNIEnv * env); jclass cls; + jfieldID pipeline; jfieldID echoCanceller; - jfieldID gainControl; + jfieldID gainController; + jfieldID gainControllerDigital; jfieldID highPassFilter; jfieldID noiseSuppression; + jfieldID captureLevelAdjustment; }; webrtc::AudioProcessing::Config toNative(JNIEnv * env, const JavaRef & javaType); + webrtc::AudioProcessing::Config::Pipeline toPipeline(JNIEnv* env, const JavaLocalRef & javaType); + webrtc::AudioProcessing::Config::GainController1 toGainController1(JNIEnv * env, const JavaLocalRef & javaType); webrtc::AudioProcessing::Config::GainController2 toGainController2(JNIEnv * env, const JavaLocalRef & javaType); + class JavaPipelineClass : public JavaClass + { + public: + explicit JavaPipelineClass(JNIEnv * env); + + jclass cls; + jfieldID maximumInternalProcessingRate; + jfieldID multiChannelRender; + jfieldID multiChannelCapture; + jfieldID captureDownmixMethod; + }; + class JavaEchoCancellerClass : public JavaClass { public: @@ -54,17 +71,27 @@ namespace jni jfieldID enforceHighPassFiltering; }; - class JavaGainControlClass : public JavaClass + class JavaGainControllerDigitalClass : public JavaClass { public: - explicit JavaGainControlClass(JNIEnv * env); + explicit JavaGainControllerDigitalClass(JNIEnv * env); jclass cls; jfieldID enabled; + jfieldID inputVolumeController; jfieldID fixedDigital; jfieldID adaptiveDigital; }; + class JavaInputVolumeControllerClass : public JavaClass + { + public: + explicit JavaInputVolumeControllerClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + }; + class JavaGainControlFixedDigitalClass : public JavaClass { public: @@ -88,6 +115,52 @@ namespace jni jfieldID maxOutputNoiseLevelDbfs; }; + class JavaGainControllerClass : public JavaClass + { + public: + explicit JavaGainControllerClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + jfieldID targetLevelDbfs; + jfieldID compressionGainDb; + jfieldID enableLimiter; + jfieldID mode; + jfieldID analogGainController; + }; + + class JavaAgc1AnalogGainControllerClass : public JavaClass + { + public: + explicit JavaAgc1AnalogGainControllerClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + jfieldID startupMinVolume; + jfieldID clippedLevelMin; + jfieldID enableDigitalAdaptive; + jfieldID clippedLevelStep; + jfieldID clippedRatioThreshold; + jfieldID clippedWaitFrames; + jfieldID clippingPredictor; + }; + + class JavaAgc1ClippingPredictorClass : public JavaClass + { + public: + explicit JavaAgc1ClippingPredictorClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + jfieldID mode; + jfieldID windowLength; + jfieldID referenceWindowLength; + jfieldID referenceWindowDelay; + jfieldID clippingThreshold; + jfieldID crestFactorMargin; + jfieldID usePredictedStep; + }; + class JavaHighPassFilterClass : public JavaClass { public: @@ -106,6 +179,28 @@ namespace jni jfieldID enabled; jfieldID level; }; + + class JavaCaptureLevelAdjustmentClass : public JavaClass + { + public: + explicit JavaCaptureLevelAdjustmentClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + jfieldID preGainFactor; + jfieldID postGainFactor; + jfieldID analogMicGainEmulation; + }; + + class JavaAnalogMicGainEmulationClass : public JavaClass + { + public: + explicit JavaAnalogMicGainEmulationClass(JNIEnv * env); + + jclass cls; + jfieldID enabled; + jfieldID initialLevel; + }; } } diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp index cbf777d1..125ec53b 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaDevices.cpp @@ -67,6 +67,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioPlaybackDevice(); + return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { @@ -83,6 +84,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_media_MediaDevices_getDefaultAu try { auto device = context->getAudioDeviceManager()->getDefaultAudioCaptureDevice(); + return jni::AudioDevice::toJavaAudioDevice(env, device).release(); } catch (...) { diff --git a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp index 4e7c62b1..165411e0 100644 --- a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp +++ b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp @@ -85,6 +85,9 @@ namespace jni JavaEnums::add(env, PKG"RTCRtpTransceiverDirection"); JavaEnums::add(env, PKG"RTCSdpType"); JavaEnums::add(env, PKG_AUDIO"AudioLayer"); + JavaEnums::add(env, PKG_AUDIO"AudioProcessingConfig$GainController$Mode"); + JavaEnums::add(env, PKG_AUDIO"AudioProcessingConfig$GainController$AnalogGainController$ClippingPredictor$Mode"); + JavaEnums::add(env, PKG_AUDIO"AudioProcessingConfig$Pipeline$DownmixMethod"); JavaEnums::add(env, PKG_AUDIO"AudioProcessingConfig$NoiseSuppression$Level"); JavaEnums::add(env, PKG"RTCStatsType"); JavaEnums::add(env, PKG_MEDIA"DeviceFormFactor"); diff --git a/webrtc-jni/src/main/cpp/src/api/RTCDataChannelObserver.cpp b/webrtc-jni/src/main/cpp/src/api/RTCDataChannelObserver.cpp index 1b3c60d6..fef868e2 100644 --- a/webrtc-jni/src/main/cpp/src/api/RTCDataChannelObserver.cpp +++ b/webrtc-jni/src/main/cpp/src/api/RTCDataChannelObserver.cpp @@ -48,11 +48,21 @@ namespace jni ExceptionCheck(env); } + void RTCDataChannelObserver::OnBufferedAmountChange(uint64_t sent_data_size) + { + JNIEnv * env = AttachCurrentThread(); + + env->CallVoidMethod(observer, javaClass->onBufferedAmountChange, static_cast(sent_data_size)); + + ExceptionCheck(env); + } + RTCDataChannelObserver::JavaRTCDataChannelObserverClass::JavaRTCDataChannelObserverClass(JNIEnv * env) { jclass cls = FindClass(env, PKG"RTCDataChannelObserver"); onStateChange = GetMethod(env, cls, "onStateChange", "()V"); onMessage = GetMethod(env, cls, "onMessage", "(L" PKG "RTCDataChannelBuffer;)V"); + onBufferedAmountChange = GetMethod(env, cls, "onBufferedAmountChange", "(J)V"); } } diff --git a/webrtc-jni/src/main/cpp/src/media/audio/AudioProcessingConfig.cpp b/webrtc-jni/src/main/cpp/src/media/audio/AudioProcessingConfig.cpp index 66026969..a27f095a 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/AudioProcessingConfig.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/AudioProcessingConfig.cpp @@ -33,42 +33,74 @@ namespace jni const auto javaEchoCancellerClass = JavaClasses::get(env); const auto javaHighPassFilterClass = JavaClasses::get(env); const auto javaNoiseSuppressionClass = JavaClasses::get(env); - + const auto javaCaptureLevelAdjustmentClass = JavaClasses::get(env); + const auto javaAnalogMicGainEmulationClass = JavaClasses::get(env); + JavaObject obj(env, javaType); JavaObject echoCanceller(env, obj.getObject(javaClass->echoCanceller)); JavaObject highPassFilter(env, obj.getObject(javaClass->highPassFilter)); JavaObject noiseSuppression(env, obj.getObject(javaClass->noiseSuppression)); + JavaObject captureLevelAdjustment(env, obj.getObject(javaClass->captureLevelAdjustment)); + JavaObject analogMicGainEmulation(env, captureLevelAdjustment.getObject(javaCaptureLevelAdjustmentClass->analogMicGainEmulation)); webrtc::AudioProcessing::Config config; - + + config.pipeline = toPipeline(env, obj.getObject(javaClass->pipeline)); config.echo_canceller.enabled = echoCanceller.getBoolean(javaEchoCancellerClass->enabled); - config.echo_canceller.enforce_high_pass_filtering = echoCanceller.getBoolean(javaEchoCancellerClass->enforceHighPassFiltering); - config.gain_controller2 = toGainController2(env, obj.getObject(javaClass->gainControl)); config.high_pass_filter.enabled = highPassFilter.getBoolean(javaHighPassFilterClass->enabled); config.noise_suppression.enabled = noiseSuppression.getBoolean(javaNoiseSuppressionClass->enabled); + config.echo_canceller.enforce_high_pass_filtering = echoCanceller.getBoolean(javaEchoCancellerClass->enforceHighPassFiltering); + config.capture_level_adjustment.enabled = captureLevelAdjustment.getBoolean(javaCaptureLevelAdjustmentClass->enabled); + config.capture_level_adjustment.pre_gain_factor = captureLevelAdjustment.getFloat(javaCaptureLevelAdjustmentClass->preGainFactor); + config.capture_level_adjustment.post_gain_factor = captureLevelAdjustment.getFloat(javaCaptureLevelAdjustmentClass->postGainFactor); + config.capture_level_adjustment.analog_mic_gain_emulation.enabled = analogMicGainEmulation.getBoolean(javaAnalogMicGainEmulationClass->enabled); + config.capture_level_adjustment.analog_mic_gain_emulation.initial_level = analogMicGainEmulation.getInt(javaAnalogMicGainEmulationClass->initialLevel); + config.gain_controller1 = toGainController1(env, obj.getObject(javaClass->gainController)); + config.gain_controller2 = toGainController2(env, obj.getObject(javaClass->gainControllerDigital)); JavaLocalRef nsLevel = noiseSuppression.getObject(javaNoiseSuppressionClass->level); if (nsLevel.get()) { config.noise_suppression.level = jni::JavaEnums::toNative(env, nsLevel); } - + return config; } + webrtc::AudioProcessing::Config::Pipeline toPipeline(JNIEnv * env, const JavaLocalRef & javaType) + { + const auto javaPipelineClass = JavaClasses::get(env); + + JavaObject jPipeline(env, javaType); + + JavaLocalRef downmixMethod = jPipeline.getObject(javaPipelineClass->captureDownmixMethod); + + webrtc::AudioProcessing::Config::Pipeline pipeline; + + pipeline.maximum_internal_processing_rate = jPipeline.getInt(javaPipelineClass->maximumInternalProcessingRate); + pipeline.multi_channel_render = jPipeline.getBoolean(javaPipelineClass->multiChannelRender); + pipeline.multi_channel_capture = jPipeline.getBoolean(javaPipelineClass->multiChannelCapture); + pipeline.capture_downmix_method = JavaEnums::toNative(env, downmixMethod); + + return pipeline; + } + webrtc::AudioProcessing::Config::GainController2 toGainController2(JNIEnv * env, const JavaLocalRef & javaType) { - const auto javaGainControlClass = JavaClasses::get(env); + const auto javaGainControlClass = JavaClasses::get(env); + const auto javaInputVolumeControllerClass = JavaClasses::get(env); const auto javaGainControlFixedDigitalClass = JavaClasses::get(env); const auto javaGainControlAdaptiveDigitalClass = JavaClasses::get(env); JavaObject gainControl(env, javaType); + JavaObject inputVolumeController(env, gainControl.getObject(javaGainControlClass->inputVolumeController)); JavaObject gainControlFixedDigital(env, gainControl.getObject(javaGainControlClass->fixedDigital)); JavaObject gainControlAdaptiveDigital(env, gainControl.getObject(javaGainControlClass->adaptiveDigital)); webrtc::AudioProcessing::Config::GainController2 gainController; gainController.enabled = gainControl.getBoolean(javaGainControlClass->enabled); + gainController.input_volume_controller.enabled = inputVolumeController.getBoolean(javaInputVolumeControllerClass->enabled); gainController.fixed_digital.gain_db = gainControlFixedDigital.getFloat(javaGainControlFixedDigitalClass->gainDb); gainController.adaptive_digital.enabled = gainControlAdaptiveDigital.getBoolean(javaGainControlAdaptiveDigitalClass->enabled); gainController.adaptive_digital.headroom_db = gainControlAdaptiveDigital.getFloat(javaGainControlAdaptiveDigitalClass->headroomDb); @@ -80,14 +112,75 @@ namespace jni return gainController; } + webrtc::AudioProcessing::Config::GainController1 toGainController1(JNIEnv * env, const JavaLocalRef & javaType) + { + const auto javaGainControllerClass = JavaClasses::get(env); + const auto javaAgc1AnalogClass = JavaClasses::get(env); + const auto javaAgc1ClipPredClass = JavaClasses::get(env); + + JavaObject jGc1(env, javaType); + webrtc::AudioProcessing::Config::GainController1 gc1; + + gc1.enabled = jGc1.getBoolean(javaGainControllerClass->enabled); + gc1.target_level_dbfs = jGc1.getInt(javaGainControllerClass->targetLevelDbfs); + gc1.compression_gain_db = jGc1.getInt(javaGainControllerClass->compressionGainDb); + gc1.enable_limiter = jGc1.getBoolean(javaGainControllerClass->enableLimiter); + + JavaLocalRef mode = jGc1.getObject(javaGainControllerClass->mode); + if (mode.get()) { + gc1.mode = JavaEnums::toNative(env, mode); + } + + JavaObject jAgc(env, jGc1.getObject(javaGainControllerClass->analogGainController)); + + gc1.analog_gain_controller.enabled = jAgc.getBoolean(javaAgc1AnalogClass->enabled); + gc1.analog_gain_controller.startup_min_volume = jAgc.getInt(javaAgc1AnalogClass->startupMinVolume); + gc1.analog_gain_controller.clipped_level_min = jAgc.getInt(javaAgc1AnalogClass->clippedLevelMin); + gc1.analog_gain_controller.enable_digital_adaptive = jAgc.getBoolean(javaAgc1AnalogClass->enableDigitalAdaptive); + gc1.analog_gain_controller.clipped_level_step = jAgc.getInt(javaAgc1AnalogClass->clippedLevelStep); + gc1.analog_gain_controller.clipped_ratio_threshold = jAgc.getFloat(javaAgc1AnalogClass->clippedRatioThreshold); + gc1.analog_gain_controller.clipped_wait_frames = jAgc.getInt(javaAgc1AnalogClass->clippedWaitFrames); + + JavaObject jPred(env, jAgc.getObject(javaAgc1AnalogClass->clippingPredictor)); + + gc1.analog_gain_controller.clipping_predictor.enabled = jPred.getBoolean(javaAgc1ClipPredClass->enabled); + JavaLocalRef predMode = jPred.getObject(javaAgc1ClipPredClass->mode); + + if (predMode.get()) { + gc1.analog_gain_controller.clipping_predictor.mode = JavaEnums::toNative(env, predMode); + } + + gc1.analog_gain_controller.clipping_predictor.window_length = jPred.getInt(javaAgc1ClipPredClass->windowLength); + gc1.analog_gain_controller.clipping_predictor.reference_window_length = jPred.getInt(javaAgc1ClipPredClass->referenceWindowLength); + gc1.analog_gain_controller.clipping_predictor.reference_window_delay = jPred.getInt(javaAgc1ClipPredClass->referenceWindowDelay); + gc1.analog_gain_controller.clipping_predictor.clipping_threshold = jPred.getFloat(javaAgc1ClipPredClass->clippingThreshold); + gc1.analog_gain_controller.clipping_predictor.crest_factor_margin = jPred.getFloat(javaAgc1ClipPredClass->crestFactorMargin); + gc1.analog_gain_controller.clipping_predictor.use_predicted_step = jPred.getBoolean(javaAgc1ClipPredClass->usePredictedStep); + + return gc1; + } + JavaAudioProcessingConfigClass::JavaAudioProcessingConfigClass(JNIEnv * env) { cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig"); + pipeline = GetFieldID(env, cls, "pipeline", "L" PKG_AUDIO "AudioProcessingConfig$Pipeline;"); echoCanceller = GetFieldID(env, cls, "echoCanceller", "L" PKG_AUDIO "AudioProcessingConfig$EchoCanceller;"); - gainControl = GetFieldID(env, cls, "gainControl", "L" PKG_AUDIO "AudioProcessingConfig$GainControl;"); + gainControllerDigital = GetFieldID(env, cls, "gainControllerDigital", "L" PKG_AUDIO "AudioProcessingConfig$GainControllerDigital;"); + gainController = GetFieldID(env, cls, "gainController", "L" PKG_AUDIO "AudioProcessingConfig$GainController;"); highPassFilter = GetFieldID(env, cls, "highPassFilter", "L" PKG_AUDIO "AudioProcessingConfig$HighPassFilter;"); noiseSuppression = GetFieldID(env, cls, "noiseSuppression", "L" PKG_AUDIO "AudioProcessingConfig$NoiseSuppression;"); + captureLevelAdjustment = GetFieldID(env, cls, "captureLevelAdjustment", "L" PKG_AUDIO "AudioProcessingConfig$CaptureLevelAdjustment;"); + } + + JavaPipelineClass::JavaPipelineClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$Pipeline"); + + maximumInternalProcessingRate = GetFieldID(env, cls, "maximumInternalProcessingRate", "I"); + multiChannelRender = GetFieldID(env, cls, "multiChannelRender", "Z"); + multiChannelCapture = GetFieldID(env, cls, "multiChannelCapture", "Z"); + captureDownmixMethod = GetFieldID(env, cls, "captureDownmixMethod", "L" PKG_AUDIO "AudioProcessingConfig$Pipeline$DownmixMethod;"); } JavaEchoCancellerClass::JavaEchoCancellerClass(JNIEnv* env) @@ -98,25 +191,26 @@ namespace jni enforceHighPassFiltering = GetFieldID(env, cls, "enforceHighPassFiltering", "Z"); } - JavaGainControlClass::JavaGainControlClass(JNIEnv* env) + JavaGainControllerDigitalClass::JavaGainControllerDigitalClass(JNIEnv* env) { - cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControl"); + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControllerDigital"); enabled = GetFieldID(env, cls, "enabled", "Z"); - fixedDigital = GetFieldID(env, cls, "fixedDigital", "L" PKG_AUDIO "AudioProcessingConfig$GainControl$FixedDigital;"); - adaptiveDigital = GetFieldID(env, cls, "adaptiveDigital", "L" PKG_AUDIO "AudioProcessingConfig$GainControl$AdaptiveDigital;"); + inputVolumeController = GetFieldID(env, cls, "inputVolumeController", "L" PKG_AUDIO "AudioProcessingConfig$GainControllerDigital$InputVolumeController;"); + fixedDigital = GetFieldID(env, cls, "fixedDigital", "L" PKG_AUDIO "AudioProcessingConfig$GainControllerDigital$FixedDigital;"); + adaptiveDigital = GetFieldID(env, cls, "adaptiveDigital", "L" PKG_AUDIO "AudioProcessingConfig$GainControllerDigital$AdaptiveDigital;"); } JavaGainControlFixedDigitalClass::JavaGainControlFixedDigitalClass(JNIEnv* env) { - cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControl$FixedDigital"); + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControllerDigital$FixedDigital"); gainDb = GetFieldID(env, cls, "gainDb", "F"); } JavaGainControlAdaptiveDigitalClass::JavaGainControlAdaptiveDigitalClass(JNIEnv* env) { - cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControl$AdaptiveDigital"); + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControllerDigital$AdaptiveDigital"); enabled = GetFieldID(env, cls, "enabled", "Z"); headroomDb = GetFieldID(env, cls, "headroomDb", "F"); @@ -133,6 +227,46 @@ namespace jni enabled = GetFieldID(env, cls, "enabled", "Z"); } + JavaGainControllerClass::JavaGainControllerClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainController"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + targetLevelDbfs = GetFieldID(env, cls, "targetLevelDbfs", "I"); + compressionGainDb = GetFieldID(env, cls, "compressionGainDb", "I"); + enableLimiter = GetFieldID(env, cls, "enableLimiter", "Z"); + mode = GetFieldID(env, cls, "mode", "L" PKG_AUDIO "AudioProcessingConfig$GainController$Mode;"); + analogGainController = GetFieldID(env, cls, "analogGainController", "L" PKG_AUDIO "AudioProcessingConfig$GainController$AnalogGainController;"); + } + + JavaAgc1AnalogGainControllerClass::JavaAgc1AnalogGainControllerClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainController$AnalogGainController"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + startupMinVolume = GetFieldID(env, cls, "startupMinVolume", "I"); + clippedLevelMin = GetFieldID(env, cls, "clippedLevelMin", "I"); + enableDigitalAdaptive = GetFieldID(env, cls, "enableDigitalAdaptive", "Z"); + clippedLevelStep = GetFieldID(env, cls, "clippedLevelStep", "I"); + clippedRatioThreshold = GetFieldID(env, cls, "clippedRatioThreshold", "F"); + clippedWaitFrames = GetFieldID(env, cls, "clippedWaitFrames", "I"); + clippingPredictor = GetFieldID(env, cls, "clippingPredictor", "L" PKG_AUDIO "AudioProcessingConfig$GainController$AnalogGainController$ClippingPredictor;"); + } + + JavaAgc1ClippingPredictorClass::JavaAgc1ClippingPredictorClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainController$AnalogGainController$ClippingPredictor"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + mode = GetFieldID(env, cls, "mode", "L" PKG_AUDIO "AudioProcessingConfig$GainController$AnalogGainController$ClippingPredictor$Mode;"); + windowLength = GetFieldID(env, cls, "windowLength", "I"); + referenceWindowLength = GetFieldID(env, cls, "referenceWindowLength", "I"); + referenceWindowDelay = GetFieldID(env, cls, "referenceWindowDelay", "I"); + clippingThreshold = GetFieldID(env, cls, "clippingThreshold", "F"); + crestFactorMargin = GetFieldID(env, cls, "crestFactorMargin", "F"); + usePredictedStep = GetFieldID(env, cls, "usePredictedStep", "Z"); + } + JavaNoiseSuppressionClass::JavaNoiseSuppressionClass(JNIEnv* env) { cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$NoiseSuppression"); @@ -140,5 +274,30 @@ namespace jni enabled = GetFieldID(env, cls, "enabled", "Z"); level = GetFieldID(env, cls, "level", "L" PKG_AUDIO "AudioProcessingConfig$NoiseSuppression$Level;"); } + + JavaCaptureLevelAdjustmentClass::JavaCaptureLevelAdjustmentClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$CaptureLevelAdjustment"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + preGainFactor = GetFieldID(env, cls, "preGainFactor", "F"); + postGainFactor = GetFieldID(env, cls, "postGainFactor", "F"); + analogMicGainEmulation = GetFieldID(env, cls, "analogMicGainEmulation", "L" PKG_AUDIO "AudioProcessingConfig$CaptureLevelAdjustment$AnalogMicGainEmulation;"); + } + + JavaAnalogMicGainEmulationClass::JavaAnalogMicGainEmulationClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$CaptureLevelAdjustment$AnalogMicGainEmulation"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + initialLevel = GetFieldID(env, cls, "initialLevel", "I"); + } + + JavaInputVolumeControllerClass::JavaInputVolumeControllerClass(JNIEnv* env) + { + cls = FindClass(env, PKG_AUDIO"AudioProcessingConfig$GainControllerDigital$InputVolumeController"); + + enabled = GetFieldID(env, cls, "enabled", "Z"); + } } } \ No newline at end of file diff --git a/webrtc/pom.xml b/webrtc/pom.xml index a7a7d421..c7670948 100644 --- a/webrtc/pom.xml +++ b/webrtc/pom.xml @@ -5,7 +5,7 @@ dev.onvoid.webrtc webrtc-java-parent - 0.14.0-SNAPSHOT + 0.15.0-SNAPSHOT webrtc-java @@ -36,8 +36,6 @@ org.apache.maven.plugins maven-surefire-plugin - - --add-opens webrtc.java/dev.onvoid.webrtc=ALL-UNNAMED --add-opens webrtc.java/dev.onvoid.webrtc.logging=ALL-UNNAMED diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioOptions.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioOptions.java index 8808965a..3e840ae8 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioOptions.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioOptions.java @@ -19,17 +19,17 @@ /** * Audio options to control AudioTracks. * - * @link https://www.w3.org/TR/mediacapture-streams/ + * @link Media Capture and Streams * * @author Alex Andres */ public class AudioOptions { /** - * When one or more audio streams is being played in the processes of + * When one or more audio streams are being played in the processes of * various microphones, it is often desirable to attempt to remove the sound * being played from the input signals recorded by the microphones. This is - * referred to as echo cancellation. There are cases where it is not needed + * referred to as echo cancellation. There are cases where it is unnecessary, * and it is desirable to turn it off so that no audio artifacts are * introduced. This allows applications to control this behavior. */ @@ -37,7 +37,7 @@ public class AudioOptions { /** * Automatic gain control is often desirable on the input signal recorded by - * the microphone. There are cases where it is not needed and it is + * the microphone. There are cases where it is unnecessary, and it is * desirable to turn it off so that the audio is not altered. This allows * applications to control this behavior. */ @@ -45,7 +45,7 @@ public class AudioOptions { /** * Noise suppression is often desirable on the input signal recorded by the - * microphone. There are cases where it is not needed and it is desirable to + * microphone. There are cases where it is unnecessary, and it is desirable to * turn it off so that the audio is not altered. This allows applications to * control this behavior. */ diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioPlayer.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioPlayer.java index df607d96..6ee21001 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioPlayer.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioPlayer.java @@ -18,29 +18,68 @@ import java.util.concurrent.atomic.AtomicBoolean; +/** + * High-level wrapper to control audio playout. Provides idempotent start/stop semantics + * around an underlying AudioDeviceModule. + * + * @author Alex Andres + */ public class AudioPlayer { + /** + * Indicates if the player is currently started. Used atomically to provide + * a thread-safe idempotent start/stop sequence. + */ private final AtomicBoolean playing; + /** + * Underlying audio device module managing the native playout resources. + * Created lazily on {@link #start()} and disposed on {@link #stop()}. + */ private AudioDeviceModule module; + /** + * The audio output device to use for playout. Should be set before calling {@link #start()}. + */ private AudioDevice device; + /** + * The audio source providing audio frames to be rendered by the device. + */ private AudioSource source; + /** + * Creates a new AudioPlayer instance in a non-playing state. + */ public AudioPlayer() { playing = new AtomicBoolean(); } + /** + * Assigns the audio output device. + * + * @param device the target playout device (may be null to clear). + */ public void setAudioDevice(AudioDevice device) { this.device = device; } + /** + * Assigns the audio source supplying audio data. + * + * @param source the audio source (may be null to clear). + */ public void setAudioSource(AudioSource source) { this.source = source; } + /** + * Starts audio playout if not already running. This method is idempotent; subsequent + * calls while already playing have no effect. + *

+ * Initializes and configures the underlying {@link AudioDeviceModule}. + */ public void start() { if (playing.compareAndSet(false, true)) { module = new AudioDeviceModule(); @@ -51,11 +90,14 @@ public void start() { } } + /** + * Stops audio playout if currently running and releases underlying resources. + * This method is idempotent; calling when already stopped has no effect. + */ public void stop() { if (playing.compareAndSet(true, false)) { module.stopPlayout(); module.dispose(); } } - } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioProcessingConfig.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioProcessingConfig.java index 5631a3c1..da90149d 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioProcessingConfig.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioProcessingConfig.java @@ -24,77 +24,489 @@ */ public class AudioProcessingConfig { + /** + * Configuration for the audio processing pipeline, including settings for + * downmixing and processing rates. + */ + public final Pipeline pipeline = new Pipeline(); + + /** + * Echo cancellation configuration to remove acoustic echo from capture signals. + * Controls whether echo cancellation is enabled and its filtering behavior. + */ public final EchoCanceller echoCanceller = new EchoCanceller(); - public final GainControl gainControl = new GainControl(); - + /** + * Legacy Gain Controller 1 (AGC1) configuration. This is the classic AGC used + * by WebRTC with optional limiter. If enabled, AGC1 will run alongside other + * modules as configured. If not set, defaults are used by the native side. + */ + public final GainController gainController = new GainController(); + + /** + * Digital gain control configuration for automatic volume adjustment. + * Automatic Gain Control (AGC) sub-module which replaces the Legacy Gain Controller 1 + * (AGC1). Includes both fixed and adaptive digital gain control settings. + */ + public final GainControllerDigital gainControllerDigital = new GainControllerDigital(); + + /** + * High-pass filter configuration for removing low-frequency components from the + * processed audio signal. + */ public final HighPassFilter highPassFilter = new HighPassFilter(); + /** + * Noise suppression configuration for reducing background noise in captured audio. + * Supports multiple suppression levels. + */ public final NoiseSuppression noiseSuppression = new NoiseSuppression(); - - + /** + * Capture level adjustment configuration to enable platform-driven capture + * level corrections in the APM when available. + */ + public final CaptureLevelAdjustment captureLevelAdjustment = new CaptureLevelAdjustment(); + + + /** + * Configures the audio processing pipeline settings. + */ + public static class Pipeline { + + /** + * Specifies the method to use when downmixing multiple audio channels. + * This determines how channels are combined when reducing the number of channels. + */ + public enum DownmixMethod { + /** Average across channels. */ + AverageChannels, + /** Use the first channel. */ + UseFirstChannel + } + + /** + * Maximum allowed processing rate used internally. + * May only be set to 32000 or 48000, and any differing values will be treated as 48000. + */ + int maximumInternalProcessingRate = 48000; + + /** + * When enabled, audio rendering can process multiple channels. + * Default is false. + */ + boolean multiChannelRender = false; + + /** + * When enabled, allows multi-channel processing of capture audio when AEC3 is active + * or a custom AEC is injected. + * Default is false. + */ + boolean multiChannelCapture = false; + + /** + * Determines how multiple capture channels are combined when a single-channel + * signal is required. + * Defaults to averaging all available channels. + */ + DownmixMethod captureDownmixMethod = DownmixMethod.AverageChannels; + } + + + /** + * Configuration for the echo cancellation module in audio processing. + * Controls whether echo cancellation is enabled and its filtering behavior. + */ public static class EchoCanceller { + /** + * Determines whether echo cancellation is enabled. + * When true, acoustic echo will be removed from the capture signal. + */ public boolean enabled; - public boolean enforceHighPassFiltering; + /** + * Controls whether to enforce high-pass filtering with echo cancellation. + * When true, additional filtering is applied to improve echo cancellation. + * Default is true. + */ + public boolean enforceHighPassFiltering = true; } - public static class GainControl { + /** + * Configuration for digital gain control in audio processing. + * Provides settings for both fixed and adaptive digital gain control. + */ + public static class GainControllerDigital { + + /** + * Adjusts the input volume applied when the audio is captured. + * This is a pre-amplifier stage before any digital gain control is applied. + */ + public static class InputVolumeController { + + /** + * Determines whether the input volume controller is enabled. + * When true, the input volume will be automatically adjusted. + */ + public boolean enabled = false; + + } + + /** + * Configuration for fixed digital gain control. + * Applies a constant gain value to the audio signal. + */ public static class FixedDigital { + /** + * The fixed gain to apply to the audio signal in decibels. + * The default value is 0.0 dB (no gain). + */ public float gainDb = 0.0f; } + /** + * Configuration for adaptive digital gain control. + * Automatically adjusts gain based on input signal characteristics. + */ public static class AdaptiveDigital { + /** + * Determines whether adaptive digital gain control is enabled. + */ public boolean enabled; + /** + * Headroom in decibels to maintain above the signal level. + * The default value is 5.0 dB. + */ public float headroomDb = 5.0f; + + /** + * Maximum gain in decibels that can be applied to the signal. + * The default value is 50.0 dB. + */ public float maxGainDb = 50.0f; + + /** + * Initial gain in decibels to apply when processing starts. + * The default value is 15.0 dB. + */ public float initialGainDb = 15.0f; + + /** + * The maximum rate at which gain can change, in decibels per second. + * Controls how quickly the gain adapts to changing conditions. + * The default value is 6.0 dB/s. + */ public float maxGainChangeDbPerSecond = 6.0f; + + /** + * Maximum allowable noise level in the output, in decibels relative to full scale. + * The default value is -50.0 dBFS. + */ public float maxOutputNoiseLevelDbfs = -50.0f; } - + /** + * Fixed digital gain control settings. + */ public final FixedDigital fixedDigital = new FixedDigital(); + /** + * Adaptive digital gain control settings. + */ public final AdaptiveDigital adaptiveDigital = new AdaptiveDigital(); + /** + * Input volume controller settings. + */ + public final InputVolumeController inputVolumeController = new InputVolumeController(); + + /** + * Determines whether digital gain control is enabled. + */ public boolean enabled; } + + /** + * Configuration for classic Gain Controller 1 (AGC1). + * Provides simple controls commonly exposed for AGC1. + */ + public static class GainController { + + /** + * AGC1 mode selection. + */ + public enum Mode { + /** + * Adjusts the analog microphone level using hardware/OS controls to reach the target. + * Prefer this when the capture device gain can be modified without adding digital noise. + * This will require the coupling between the OS mixer controls and AGC through the + * stream_analog_level() functions. + */ + AdaptiveAnalog, + + /** + * Adjusts the signal level in the digital processing pipeline (software gain/compression). + * Use when analog control is unavailable or insufficient. + */ + AdaptiveDigital, + + /** + * Applies a constant digital gain (non-adaptive). Suitable for stable, well-calibrated inputs. + * This mode is preferred on embedded devices where the capture signal level is predictable so + * that a known gain can be applied. + */ + FixedDigital + } + + /** + * Determines whether AGC1 is enabled. + */ + public boolean enabled; + + /** + * Target level in dBFS that AGC1 aims for. Common default is 3 dB. + */ + public int targetLevelDbfs = 3; + + /** + * Compression gain in dB. Common default is 9 dB. + */ + public int compressionGainDb = 9; + + /** + * Enables the limiter to avoid clipping at the output. Default true. + */ + public boolean enableLimiter = true; + + /** + * Selected mode. Default matches native default kAdaptiveAnalog. + */ + public Mode mode = Mode.AdaptiveAnalog; + + /** + * Analog gain controller sub-config. + */ + public final AnalogGainController analogGainController = new AnalogGainController(); + + + public static class AnalogGainController { + + /** + * Enables the analog gain controller stage. + * When false, the analog (and coupled digital adaptive) logic is skipped. + */ + public boolean enabled = true; + + /** + * Deprecated upstream (kept for compatibility). Historical lower bound for the + * initial microphone volume. Has no effect in newer pipelines. + */ + public int startupMinVolume = 0; // deprecated upstream but retained for compatibility + + /** + * Minimum microphone level allowed after clipping mitigation logic adjusts gain. + */ + public int clippedLevelMin = 70; + + /** + * Enables the digital adaptive component that further refines gain after analog changes. + */ + public boolean enableDigitalAdaptive = true; + + /** + * Step size (in discrete volume units) to reduce the level when clipping is detected. + */ + public int clippedLevelStep = 15; + + /** + * Ratio threshold (0..1) of clipped samples in a window that triggers a gain reduction. + */ + public float clippedRatioThreshold = 0.1f; + + /** + * Number of frames to wait after a clipping event before applying another step change. + */ + public int clippedWaitFrames = 300; + + /** + * Predictor that anticipates clipping and adjusts gain preemptively. + */ + public static class ClippingPredictor { + + /** + * Available prediction modes describing how clipping is estimated. + */ + public enum Mode { + /** Reacts to observed clipping events only. */ + ClippingEventPrediction, + /** Adapts step size based on predicted peak evolution. */ + AdaptiveStepClippingPeakPrediction, + /** Uses a fixed step size for peak prediction adjustments. */ + FixedStepClippingPeakPrediction + } + + /** + * Enables the clipping prediction feature. + */ + public boolean enabled = false; + + /** + * Prediction mode governing the algorithm behavior. + */ + public Mode mode = Mode.ClippingEventPrediction; + + /** + * Sliding window length (frames) used to analyze recent signal peaks. + */ + public int windowLength = 5; + + /** + * Length (frames) of the reference window for comparative peak analysis. + */ + public int referenceWindowLength = 5; + + /** + * Delay (frames) applied to the reference window to form a historical baseline. + */ + public int referenceWindowDelay = 5; + + /** + * Threshold (dBFS) above which samples are considered clipped; -1.0f means auto. + */ + public float clippingThreshold = -1.0f; + + /** + * Margin (dB) added to the estimated crest factor to decide preemptive gain changes. + */ + public float crestFactorMargin = 3.0f; + + /** + * When true, uses the predicted step size; otherwise a default/fixed step is used. + */ + public boolean usePredictedStep = true; + } + + /** + * Instance holding the clipping prediction configuration. + */ + public final ClippingPredictor clippingPredictor = new ClippingPredictor(); + } + } + + + /** + * Configuration for capture level adjustment in audio processing. When enabled, the + * audio processing module may apply platform-informed adjustments to the capture level + * to improve overall gain staging. + */ + public static class CaptureLevelAdjustment { + + /** + * Indicates whether capture level adjustment is enabled. + */ + public boolean enabled; + + /** + * Scales the signal before any processing is done. Default 1.0. + */ + public float preGainFactor = 1.0f; + + /** + * Scales the signal after all processing is done. Default 1.0. + */ + public float postGainFactor = 1.0f; + + /** Emulates analog mic gain behavior when enabled. */ + public static class AnalogMicGainEmulation { + /** Enable the analog mic gain emulation. */ + public boolean enabled = false; + /** Initial analog level [0..255]. Default 255. */ + public int initialLevel = 255; + } + + /** + * Configuration for analog mic gain emulation. + */ + public final AnalogMicGainEmulation analogMicGainEmulation = new AnalogMicGainEmulation(); + } + + + /** + * Configuration for the high-pass filter in audio processing. + */ public static class HighPassFilter { + /** + * Determines whether the high-pass filter is enabled. + * When true, low-frequency components will be filtered out of the audio signal. + */ public boolean enabled; + /** + * Controls whether the filter is applied across the full frequency band. + * When true, the filter affects the entire frequency spectrum. + * Default is true. + */ + public boolean applyInFullBand = true; + } + + /** + * Configuration for audio level estimation. + * Provides settings to enable/disable the measurement of audio signal levels. + */ public static class LevelEstimation { + /** + * Determines whether audio level estimation is enabled. + * When true, the audio processing module will calculate and report signal levels. + */ public boolean enabled; } + + /** + * Configuration for noise suppression in audio processing. + */ public static class NoiseSuppression { + /** + * Defines the available intensity levels for noise suppression. + * Higher levels provide more aggressive noise reduction but may affect voice quality. + */ public enum Level { + /** Minimal noise suppression, preserves most audio quality. */ LOW, + /** Balanced noise suppression with moderate effect on audio quality. */ MODERATE, + /** Strong noise suppression that may slightly affect voice quality. */ HIGH, + /** Maximum noise suppression that prioritizes noise removal over voice quality. */ VERY_HIGH } - + /** + * Determines whether noise suppression is enabled. + * When true, background noise will be reduced in the processed audio. + */ public boolean enabled; + /** + * Specifies the intensity level of noise suppression to apply. + * Controls how aggressively noise is filtered from the audio signal. + */ public Level level; } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioRecorder.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioRecorder.java index fa06a897..bda5ec18 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioRecorder.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioRecorder.java @@ -18,29 +18,57 @@ import java.util.concurrent.atomic.AtomicBoolean; +/** + * High-level recorder that captures audio from a configured {@link AudioDevice} + * and forwards frames to an {@link AudioSink}. Manages the lifecycle of the + * underlying {@link AudioDeviceModule} and offers thread-safe start/stop control. + * + * @author Alex Andres + */ public class AudioRecorder { + /** Indicates whether the recording is currently active (thread-safe). */ private final AtomicBoolean capturing; + /** Underlying audio device module handling the native capture. */ private AudioDeviceModule module; + /** The audio input device to record from. */ private AudioDevice device; + /** The sink receiving captured audio data. */ private AudioSink sink; + /** + * Creates a new AudioRecorder with an inactive capture state. + */ public AudioRecorder() { capturing = new AtomicBoolean(); } + /** + * Set the audio input device to be used for recording. + * + * @param device the recording device; may be null until set. + */ public void setAudioDevice(AudioDevice device) { this.device = device; } + /** + * Set the sink that will receive captured audio frames. + * + * @param sink the audio sink implementation. + */ public void setAudioSink(AudioSink sink) { this.sink = sink; } + /** + * Start recording if not already active. Initializes and starts the underlying + * AudioDeviceModule. + */ public void start() { if (capturing.compareAndSet(false, true)) { module = new AudioDeviceModule(); @@ -51,11 +79,13 @@ public void start() { } } + /** + * Stop recording if active and release resources. + */ public void stop() { if (capturing.compareAndSet(true, false)) { module.stopRecording(); module.dispose(); } } - } diff --git a/webrtc/src/main/java/dev/onvoid/webrtc/media/video/VideoBufferConverter.java b/webrtc/src/main/java/dev/onvoid/webrtc/media/video/VideoBufferConverter.java index eec22146..1b8694f3 100644 --- a/webrtc/src/main/java/dev/onvoid/webrtc/media/video/VideoBufferConverter.java +++ b/webrtc/src/main/java/dev/onvoid/webrtc/media/video/VideoBufferConverter.java @@ -43,7 +43,8 @@ public final class VideoBufferConverter { * @throws NullPointerException if {@code src} or {@code dst} is {@code null}. * @throws Exception if the native conversion fails. */ - public static void convertFromI420(VideoFrameBuffer src, byte[] dst, FourCC fourCC) throws Exception { + public static void convertFromI420(VideoFrameBuffer src, byte[] dst, FourCC fourCC) + throws Exception { if (src == null) { throw new NullPointerException("Source buffer must not be null"); } @@ -76,7 +77,8 @@ public static void convertFromI420(VideoFrameBuffer src, byte[] dst, FourCC four * @throws IllegalArgumentException if {@code dst} is read-only. * @throws Exception if the native conversion fails. */ - public static void convertFromI420(VideoFrameBuffer src, ByteBuffer dst, FourCC fourCC) throws Exception { + public static void convertFromI420(VideoFrameBuffer src, ByteBuffer dst, FourCC fourCC) + throws Exception { if (src == null) { throw new NullPointerException("Source buffer must not be null"); } @@ -130,7 +132,8 @@ public static void convertFromI420(VideoFrameBuffer src, ByteBuffer dst, FourCC * @throws NullPointerException if {@code src} or {@code dst} is {@code null}. * @throws Exception if the native conversion fails. */ - public static void convertToI420(byte[] src, I420Buffer dst, FourCC fourCC) throws Exception { + public static void convertToI420(byte[] src, I420Buffer dst, FourCC fourCC) + throws Exception { if (src == null) { throw new NullPointerException("Source buffer must not be null"); } @@ -160,7 +163,8 @@ public static void convertToI420(byte[] src, I420Buffer dst, FourCC fourCC) thro * @throws NullPointerException if {@code src} or {@code dst} is {@code null}. * @throws Exception if the native conversion fails. */ - public static void convertToI420(ByteBuffer src, I420Buffer dst, FourCC fourCC) throws Exception { + public static void convertToI420(ByteBuffer src, I420Buffer dst, FourCC fourCC) + throws Exception { if (src == null) { throw new NullPointerException("Source buffer must not be null"); } diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/PortAllocatorConfigIntegrationTest.java b/webrtc/src/test/java/dev/onvoid/webrtc/PortAllocatorConfigIntegrationTest.java index 5c62c66d..d97cc68c 100644 --- a/webrtc/src/test/java/dev/onvoid/webrtc/PortAllocatorConfigIntegrationTest.java +++ b/webrtc/src/test/java/dev/onvoid/webrtc/PortAllocatorConfigIntegrationTest.java @@ -61,8 +61,8 @@ void iceCandidatesRespectPortAllocatorConfig() throws Exception { caller.setRemoteDescription(callee.createAnswer()); // Wait until connected (with a timeout to avoid hanging tests). - assertTrue(caller.awaitConnected(10, TimeUnit.SECONDS), "Caller failed to connect in time"); - assertTrue(callee.awaitConnected(10, TimeUnit.SECONDS), "Callee failed to connect in time"); + assertTrue(caller.awaitConnected(30, TimeUnit.SECONDS), "Caller failed to connect in time"); + assertTrue(callee.awaitConnected(30, TimeUnit.SECONDS), "Callee failed to connect in time"); // Give ICE gathering a brief moment. Thread.sleep(500); diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/RTCDataChannelTests.java b/webrtc/src/test/java/dev/onvoid/webrtc/RTCDataChannelTests.java index 60a6e167..3a50a605 100644 --- a/webrtc/src/test/java/dev/onvoid/webrtc/RTCDataChannelTests.java +++ b/webrtc/src/test/java/dev/onvoid/webrtc/RTCDataChannelTests.java @@ -18,18 +18,65 @@ import static java.util.Objects.nonNull; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class RTCDataChannelTests extends TestBase { + @Test + void bufferedAmountChangeCallback() throws Exception { + DataPeerConnection caller = new DataPeerConnection(factory); + DataPeerConnection callee = new DataPeerConnection(factory); + + caller.setRemotePeerConnection(callee); + callee.setRemotePeerConnection(caller); + + callee.setRemoteDescription(caller.createOffer()); + caller.setRemoteDescription(callee.createAnswer()); + + caller.waitUntilConnected(); + callee.waitUntilConnected(); + + // Prepare a latch-based observer to detect buffered amount change. + CountDownLatch latch = new CountDownLatch(1); + + caller.getLocalDataChannel().registerObserver(new RTCDataChannelObserver() { + @Override + public void onBufferedAmountChange(long previousAmount) { + latch.countDown(); + } + + @Override + public void onStateChange() { } + + @Override + public void onMessage(RTCDataChannelBuffer buffer) { } + }); + + // Send a large enough message to cause buffering (increase from 0). + byte[] big = new byte[64 * 1024]; // 64 KB + ByteBuffer data = ByteBuffer.wrap(big); + RTCDataChannelBuffer buffer = new RTCDataChannelBuffer(data, true); + caller.getLocalDataChannel().send(buffer); + + // Wait for the callback to fire to avoid flakiness. + boolean signaled = latch.await(5, java.util.concurrent.TimeUnit.SECONDS); + + assertTrue(signaled, "onBufferedAmountChange should be called when sending data"); + + caller.close(); + callee.close(); + } + @Test void textMessage() throws Exception { DataPeerConnection caller = new DataPeerConnection(factory); @@ -98,6 +145,10 @@ public void onMessage(RTCDataChannelBuffer buffer) { }); } + RTCDataChannel getLocalDataChannel() { + return localDataChannel; + } + List getReceivedTexts() { return receivedTexts; } diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaDevicesTests.java b/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaDevicesTests.java deleted file mode 100644 index 235b9509..00000000 --- a/webrtc/src/test/java/dev/onvoid/webrtc/media/MediaDevicesTests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2019 Alex Andres - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.onvoid.webrtc.media; - -import static org.junit.jupiter.api.Assertions.*; - -import dev.onvoid.webrtc.media.audio.AudioDevice; -import dev.onvoid.webrtc.media.video.VideoCaptureCapability; -import dev.onvoid.webrtc.media.video.VideoDevice; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -class MediaDevicesTests { - - @Test - void getAudioDevices() { - /* - List captureDevices = MediaDevices.getAudioCaptureDevices(); - List renderDevices = MediaDevices.getAudioRenderDevices(); - - assertNotNull(captureDevices); - assertNotNull(renderDevices); - */ - } - - @Test - void getVideoDevices() { - /* - List captureDevices = MediaDevices.getVideoCaptureDevices(); - - assertNotNull(captureDevices); - - for (VideoDevice device : captureDevices) { - List capabilities = MediaDevices.getVideoCaptureCapabilities(device); - - assertNotNull(capabilities); - } - */ - } - - @Test - void deviceChangeListener() { - /* - DeviceChangeListener listener = new DeviceChangeListener() { - - @Override - public void deviceConnected(Device device) { - - } - - @Override - public void deviceDisconnected(Device device) { - - } - }; - - MediaDevices.addDeviceChangeListener(listener); - MediaDevices.removeDeviceChangeListener(listener); - */ - } -} diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/media/audio/AudioProcessingTest.java b/webrtc/src/test/java/dev/onvoid/webrtc/media/audio/AudioProcessingTest.java index 25ba3c86..b5b19f01 100644 --- a/webrtc/src/test/java/dev/onvoid/webrtc/media/audio/AudioProcessingTest.java +++ b/webrtc/src/test/java/dev/onvoid/webrtc/media/audio/AudioProcessingTest.java @@ -18,6 +18,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; import dev.onvoid.webrtc.media.audio.AudioProcessingConfig.NoiseSuppression; @@ -43,10 +45,16 @@ void dispose() { @Test void applyConfig() { AudioProcessingConfig config = new AudioProcessingConfig(); + config.pipeline.maximumInternalProcessingRate = 48000; + config.pipeline.multiChannelRender = false; + config.pipeline.multiChannelCapture = false; + config.pipeline.captureDownmixMethod = AudioProcessingConfig.Pipeline.DownmixMethod.AverageChannels; + config.echoCanceller.enabled = true; config.echoCanceller.enforceHighPassFiltering = true; - config.gainControl.enabled = true; + config.gainControllerDigital.enabled = true; + config.gainControllerDigital.inputVolumeController.enabled = true; config.highPassFilter.enabled = true; @@ -56,6 +64,161 @@ void applyConfig() { audioProcessing.applyConfig(config); } + @Test + void applyConfig_withGainController() { + AudioProcessingConfig config = new AudioProcessingConfig(); + config.gainController.enabled = true; + config.gainController.mode = AudioProcessingConfig.GainController.Mode.AdaptiveDigital; + config.gainController.targetLevelDbfs = 6; + config.gainController.compressionGainDb = 10; + config.gainController.enableLimiter = true; + + // Analog sub-config + AudioProcessingConfig.GainController.AnalogGainController agc = config.gainController.analogGainController; + agc.enabled = true; + agc.enableDigitalAdaptive = true; + agc.clippedLevelMin = 60; + agc.clippedLevelStep = 10; + agc.clippedRatioThreshold = 0.2f; + agc.clippedWaitFrames = 200; + + // Clipping predictor sub-config + AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor cp = agc.clippingPredictor; + cp.enabled = true; + cp.mode = AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor.Mode.AdaptiveStepClippingPeakPrediction; + cp.windowLength = 6; + cp.referenceWindowLength = 6; + cp.referenceWindowDelay = 4; + cp.clippingThreshold = -2.0f; + cp.crestFactorMargin = 2.5f; + cp.usePredictedStep = true; + + audioProcessing.applyConfig(config); + } + + @Test + void gainController_defaults() { + AudioProcessingConfig cfg = new AudioProcessingConfig(); + AudioProcessingConfig.GainController gc = cfg.gainController; + + assertFalse(gc.enabled, "AGC1 should be disabled by default"); + assertEquals(3, gc.targetLevelDbfs, "Default targetLevelDbfs"); + assertEquals(9, gc.compressionGainDb, "Default compressionGainDb"); + assertTrue(gc.enableLimiter, "Limiter enabled by default"); + assertEquals(AudioProcessingConfig.GainController.Mode.AdaptiveAnalog, gc.mode, + "Default mode is AdaptiveAnalog"); + + AudioProcessingConfig.GainController.AnalogGainController agc = gc.analogGainController; + assertTrue(agc.enabled, "AnalogGainController enabled by default"); + assertEquals(0, agc.startupMinVolume, "startupMinVolume default"); + assertEquals(70, agc.clippedLevelMin, "clippedLevelMin default"); + assertTrue(agc.enableDigitalAdaptive, "enableDigitalAdaptive default"); + assertEquals(15, agc.clippedLevelStep, "clippedLevelStep default"); + assertEquals(0.1f, agc.clippedRatioThreshold, 0.0001f, + "clippedRatioThreshold default"); + assertEquals(300, agc.clippedWaitFrames, "clippedWaitFrames default"); + + AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor cp = agc.clippingPredictor; + assertFalse(cp.enabled, "ClippingPredictor disabled by default"); + assertEquals(AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor.Mode.ClippingEventPrediction, cp.mode); + assertEquals(5, cp.windowLength); + assertEquals(5, cp.referenceWindowLength); + assertEquals(5, cp.referenceWindowDelay); + assertEquals(-1.0f, cp.clippingThreshold, 0.0001f); + assertEquals(3.0f, cp.crestFactorMargin, 0.0001f); + assertTrue(cp.usePredictedStep); + } + + @Test + void gainController_mutate_all_fields() { + AudioProcessingConfig cfg = new AudioProcessingConfig(); + AudioProcessingConfig.GainController gc = cfg.gainController; + + gc.enabled = true; + gc.targetLevelDbfs = 6; + gc.compressionGainDb = 12; + gc.enableLimiter = false; + gc.mode = AudioProcessingConfig.GainController.Mode.AdaptiveDigital; + + AudioProcessingConfig.GainController.AnalogGainController agc = gc.analogGainController; + agc.enabled = false; + agc.startupMinVolume = 7; + agc.clippedLevelMin = 50; + agc.enableDigitalAdaptive = false; + agc.clippedLevelStep = 10; + agc.clippedRatioThreshold = 0.25f; + agc.clippedWaitFrames = 123; + + AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor cp = agc.clippingPredictor; + cp.enabled = true; + cp.mode = AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor.Mode.AdaptiveStepClippingPeakPrediction; + cp.windowLength = 9; + cp.referenceWindowLength = 8; + cp.referenceWindowDelay = 2; + cp.clippingThreshold = -3.5f; + cp.crestFactorMargin = 1.25f; + cp.usePredictedStep = false; + + // Assertions reflect mutations + assertTrue(gc.enabled); + assertEquals(6, gc.targetLevelDbfs); + assertEquals(12, gc.compressionGainDb); + assertFalse(gc.enableLimiter); + assertEquals(AudioProcessingConfig.GainController.Mode.AdaptiveDigital, gc.mode); + + assertFalse(agc.enabled); + assertEquals(7, agc.startupMinVolume); + assertEquals(50, agc.clippedLevelMin); + assertFalse(agc.enableDigitalAdaptive); + assertEquals(10, agc.clippedLevelStep); + assertEquals(0.25f, agc.clippedRatioThreshold, 0.0001f); + assertEquals(123, agc.clippedWaitFrames); + + assertTrue(cp.enabled); + assertEquals(AudioProcessingConfig.GainController.AnalogGainController.ClippingPredictor.Mode.AdaptiveStepClippingPeakPrediction, cp.mode); + assertEquals(9, cp.windowLength); + assertEquals(8, cp.referenceWindowLength); + assertEquals(2, cp.referenceWindowDelay); + assertEquals(-3.5f, cp.clippingThreshold, 0.0001f); + assertEquals(1.25f, cp.crestFactorMargin, 0.0001f); + assertFalse(cp.usePredictedStep); + } + + @Test + void captureLevelAdjustment_defaults() { + AudioProcessingConfig cfg = new AudioProcessingConfig(); + AudioProcessingConfig.CaptureLevelAdjustment cla = cfg.captureLevelAdjustment; + + assertFalse(cla.enabled, "CLA disabled by default"); + assertEquals(1.0f, cla.preGainFactor, 0.0001f); + assertEquals(1.0f, cla.postGainFactor, 0.0001f); + AudioProcessingConfig.CaptureLevelAdjustment.AnalogMicGainEmulation ame = cla.analogMicGainEmulation; + assertFalse(ame.enabled, "AnalogMicGainEmulation disabled by default"); + assertEquals(255, ame.initialLevel, "AnalogMicGainEmulation initialLevel default"); + } + + @Test + void captureLevelAdjustment_mutate_all_fields() { + AudioProcessingConfig cfg = new AudioProcessingConfig(); + AudioProcessingConfig.CaptureLevelAdjustment cla = cfg.captureLevelAdjustment; + + cla.enabled = true; + cla.preGainFactor = 0.75f; + cla.postGainFactor = 1.25f; + cla.analogMicGainEmulation.enabled = true; + cla.analogMicGainEmulation.initialLevel = 200; + + // Should not throw and should be accepted by native ApplyConfig + audioProcessing.applyConfig(cfg); + + // Validate mutations remain in Java object + assertTrue(cla.enabled); + assertEquals(0.75f, cla.preGainFactor, 0.0001f); + assertEquals(1.25f, cla.postGainFactor, 0.0001f); + assertTrue(cla.analogMicGainEmulation.enabled); + assertEquals(200, cla.analogMicGainEmulation.initialLevel); + } + @Test void getStats() { assertNotNull(audioProcessing.getStatistics()); From 0fd035ab43c5cd66c0f01c0f7ed37ea0232e77ac Mon Sep 17 00:00:00 2001 From: Stasonych Date: Wed, 17 Sep 2025 11:44:31 +0500 Subject: [PATCH 6/6] actualized branch --- .github/funding.yml | 1 + .github/workflows/pages.yml | 62 + .github/workflows/release.yml | 10 +- README.md | 10 +- docs/.gitignore | 4 + docs/.nojekyll | 0 docs/.vitepress/config.mts | 49 + docs/.vitepress/footer.ts | 6 + docs/.vitepress/head.ts | 27 + docs/.vitepress/nav.ts | 30 + docs/.vitepress/sidebar.ts | 102 + docs/.vitepress/theme/index.ts | 17 + docs/.vitepress/theme/style.css | 139 + docs/.vitepress/versions.ts | 4 + docs/CNAME | 1 - docs/_coverpage.md | 42 - docs/_sidebar.md | 41 - docs/assets/css/styles.css | 277 -- docs/assets/css/vue.css | 1 - docs/assets/fonts/material-icons.woff2 | Bin 128352 -> 0 bytes docs/assets/fonts/roboto-mono-400.woff2 | Bin 12680 -> 0 bytes docs/assets/fonts/source-sans-pro-300.woff2 | Bin 14780 -> 0 bytes docs/assets/fonts/source-sans-pro-400.woff2 | Bin 14892 -> 0 bytes docs/assets/fonts/source-sans-pro-600.woff2 | Bin 14824 -> 0 bytes docs/assets/images/logo.png | Bin 57254 -> 0 bytes docs/assets/js/docsify-copy-code.min.js | 9 - docs/assets/js/docsify-pagination.min.js | 1 - docs/assets/js/docsify@4.js | 1 - docs/assets/js/prism-gradle.min.js | 1 - docs/assets/js/prism-java.min.js | 1 - docs/assets/js/search.min.js | 1 - docs/assets/versions.js | 4 - .../{audio_devices.md => audio-devices.md} | 8 +- ...udio_processing.md => audio-processing.md} | 2 - ...audio_source.md => custom-audio-source.md} | 2 - .../audio/{dtmf_sender.md => dtmf-sender.md} | 2 - ...dio_device_module.md => headless-audio.md} | 19 +- docs/{ => guide}/build.md | 23 +- .../{data_channels.md => data-channels.md} | 4 +- docs/{ => guide}/examples.md | 16 +- docs/{quickstart.md => guide/get-started.md} | 89 +- docs/guide/index.md | 40 + docs/guide/introduction.md | 24 + docs/guide/media/constraints.md | 8 +- ...receive_direction.md => directionality.md} | 35 +- .../{media_devices.md => media-devices.md} | 6 +- docs/guide/monitoring/logging.md | 2 - .../monitoring/{rtc_stats.md => rtc-stats.md} | 2 - ...tor_config.md => port-allocator-config.md} | 0 docs/guide/overview.md | 52 - .../{camera_capture.md => camera-capture.md} | 8 +- ...video_source.md => custom-video-source.md} | 0 ...{desktop_capture.md => desktop-capture.md} | 6 +- docs/index.html | 77 - docs/index.md | 36 + docs/package-lock.json | 2261 +++++++++++++++++ docs/package.json | 12 + docs/privacy_policy.md | 155 -- docs/public/apple-touch-icon.png | Bin 0 -> 10117 bytes docs/public/favicon-96x96.png | Bin 0 -> 5478 bytes docs/public/favicon.ico | Bin 0 -> 15086 bytes docs/public/favicon.svg | 3 + docs/public/logo.png | Bin 0 -> 36847 bytes docs/public/site.webmanifest | 21 + docs/public/web-app-manifest-192x192.png | Bin 0 -> 8989 bytes docs/public/web-app-manifest-512x512.png | Bin 0 -> 23100 bytes .../audio/audio-converter.md} | 6 +- .../audio/audio-player.md} | 6 +- .../audio/audio-recorder.md} | 10 +- .../audio/voice-activity-detector.md} | 2 +- .../desktop/power-management.md} | 18 +- .../desktop/screen-capturer.md} | 4 +- .../desktop/window-capturer.md} | 4 +- docs/tools/index.md | 41 + .../video/video-buffer-converter.md} | 20 +- .../video/video-capturer.md} | 10 +- docs/tsconfig.json | 12 + 77 files changed, 3043 insertions(+), 844 deletions(-) create mode 100644 .github/funding.yml create mode 100644 .github/workflows/pages.yml create mode 100644 docs/.gitignore delete mode 100644 docs/.nojekyll create mode 100644 docs/.vitepress/config.mts create mode 100644 docs/.vitepress/footer.ts create mode 100644 docs/.vitepress/head.ts create mode 100644 docs/.vitepress/nav.ts create mode 100644 docs/.vitepress/sidebar.ts create mode 100644 docs/.vitepress/theme/index.ts create mode 100644 docs/.vitepress/theme/style.css create mode 100644 docs/.vitepress/versions.ts delete mode 100644 docs/CNAME delete mode 100644 docs/_coverpage.md delete mode 100644 docs/_sidebar.md delete mode 100644 docs/assets/css/styles.css delete mode 100644 docs/assets/css/vue.css delete mode 100644 docs/assets/fonts/material-icons.woff2 delete mode 100644 docs/assets/fonts/roboto-mono-400.woff2 delete mode 100644 docs/assets/fonts/source-sans-pro-300.woff2 delete mode 100644 docs/assets/fonts/source-sans-pro-400.woff2 delete mode 100644 docs/assets/fonts/source-sans-pro-600.woff2 delete mode 100644 docs/assets/images/logo.png delete mode 100644 docs/assets/js/docsify-copy-code.min.js delete mode 100644 docs/assets/js/docsify-pagination.min.js delete mode 100644 docs/assets/js/docsify@4.js delete mode 100644 docs/assets/js/prism-gradle.min.js delete mode 100644 docs/assets/js/prism-java.min.js delete mode 100644 docs/assets/js/search.min.js delete mode 100644 docs/assets/versions.js rename docs/guide/audio/{audio_devices.md => audio-devices.md} (97%) rename docs/guide/audio/{audio_processing.md => audio-processing.md} (99%) rename docs/guide/audio/{custom_audio_source.md => custom-audio-source.md} (99%) rename docs/guide/audio/{dtmf_sender.md => dtmf-sender.md} (99%) rename docs/guide/audio/{headless_audio_device_module.md => headless-audio.md} (93%) rename docs/{ => guide}/build.md (58%) rename docs/guide/data/{data_channels.md => data-channels.md} (99%) rename docs/{ => guide}/examples.md (89%) rename docs/{quickstart.md => guide/get-started.md} (80%) create mode 100644 docs/guide/index.md create mode 100644 docs/guide/introduction.md rename docs/guide/media/{send_receive_direction.md => directionality.md} (78%) rename docs/guide/media/{media_devices.md => media-devices.md} (98%) rename docs/guide/monitoring/{rtc_stats.md => rtc-stats.md} (99%) rename docs/guide/networking/{port_allocator_config.md => port-allocator-config.md} (100%) delete mode 100644 docs/guide/overview.md rename docs/guide/video/{camera_capture.md => camera-capture.md} (98%) rename docs/guide/video/{custom_video_source.md => custom-video-source.md} (100%) rename docs/guide/video/{desktop_capture.md => desktop-capture.md} (98%) delete mode 100644 docs/index.html create mode 100644 docs/index.md create mode 100644 docs/package-lock.json create mode 100644 docs/package.json delete mode 100644 docs/privacy_policy.md create mode 100644 docs/public/apple-touch-icon.png create mode 100644 docs/public/favicon-96x96.png create mode 100644 docs/public/favicon.ico create mode 100644 docs/public/favicon.svg create mode 100644 docs/public/logo.png create mode 100644 docs/public/site.webmanifest create mode 100644 docs/public/web-app-manifest-192x192.png create mode 100644 docs/public/web-app-manifest-512x512.png rename docs/{guide/utilities/audio_converter.md => tools/audio/audio-converter.md} (95%) rename docs/{guide/utilities/audio_player.md => tools/audio/audio-player.md} (90%) rename docs/{guide/utilities/audio_recorder.md => tools/audio/audio-recorder.md} (83%) rename docs/{guide/utilities/voice_activity_detector.md => tools/audio/voice-activity-detector.md} (98%) rename docs/{guide/utilities/power_management.md => tools/desktop/power-management.md} (74%) rename docs/{guide/utilities/screen_capturer.md => tools/desktop/screen-capturer.md} (97%) rename docs/{guide/utilities/window_capturer.md => tools/desktop/window-capturer.md} (96%) create mode 100644 docs/tools/index.md rename docs/{guide/utilities/video_buffer_converter.md => tools/video/video-buffer-converter.md} (87%) rename docs/{guide/utilities/video_capturer.md => tools/video/video-capturer.md} (93%) create mode 100644 docs/tsconfig.json diff --git a/.github/funding.yml b/.github/funding.yml new file mode 100644 index 00000000..5757a0ab --- /dev/null +++ b/.github/funding.yml @@ -0,0 +1 @@ +buy_me_a_coffee: devopvoid \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..15219856 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,62 @@ +name: Deploy Documentation to Pages + +on: + push: + branches: [main] + paths: + - 'docs/**' + + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node + uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + cache-dependency-path: docs/package-lock.json + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Install dependencies + run: | + cd docs + npm ci + + - name: Build with VitePress + run: | + cd docs + npm run build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: docs/.vitepress/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0191aab7..05fe6d12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,13 +105,13 @@ jobs: RELEASE_VERSION=$(grep "project.rel.dev.onvoid.webrtc\\\:webrtc-java=" release.properties | cut -d'=' -f2) echo "Extracted release version from release.properties: $RELEASE_VERSION" - # Update versions.js file - echo "Updating versions.js with release version: $RELEASE_VERSION and development version: ${{ github.event.inputs.developmentVersion }}" - sed -i "s/VERSION: '.*'/VERSION: '$RELEASE_VERSION'/g" docs/assets/versions.js - sed -i "s/VERSION_SNAPSHOT: '.*'/VERSION_SNAPSHOT: '${{ github.event.inputs.developmentVersion }}'/g" docs/assets/versions.js + # Update versions.ts file + echo "Updating versions.ts with release version: $RELEASE_VERSION and development version: ${{ github.event.inputs.developmentVersion }}" + sed -i "s/VERSION: '.*'/VERSION: '$RELEASE_VERSION'/g" docs/.vitepress/versions.ts + sed -i "s/VERSION_SNAPSHOT: '.*'/VERSION_SNAPSHOT: '${{ github.event.inputs.developmentVersion }}'/g" docs/.vitepress/versions.ts # Add the updated file to the existing commit - git add docs/assets/versions.js + git add docs/.vitepress/versions.ts git commit --amend --no-edit git push diff --git a/README.md b/README.md index 409e7cdc..6a93e40e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Maven Central](https://img.shields.io/maven-central/v/dev.onvoid.webrtc/webrtc-java?label=Maven%20Central&logo=apache-maven)](https://search.maven.org/artifact/dev.onvoid.webrtc/webrtc-java)

- webrtc-java + webrtc-java

Connecting the Java world through WebRTC

@@ -25,10 +25,10 @@ The library provides a comprehensive set of Java classes that map to the WebRTC For more detailed information, check out the documentation: -- [Quickstart](https://jrtc.dev/#/quickstart) - Get up and running quickly with webrtc-java -- [Guides](https://jrtc.dev/#/guide/overview) - Comprehensive documentation on using the library -- [Examples](https://jrtc.dev/#/examples) - Sample code demonstrating various features -- [Build Notes](https://jrtc.dev/#/build) - Instructions for building the library from source +- [Quickstart](https://jrtc.dev/guide/get-started) - Get up and running quickly with webrtc-java +- [Guides](https://jrtc.dev/guide/) - Comprehensive documentation on using the library +- [Examples](https://jrtc.dev/guide/examples) - Sample code demonstrating various features +- [Build Notes](https://jrtc.dev/guide/build) - Instructions for building the library from source ## License diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..3041a047 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +.vitepress/cache +.vitepress/dist + +node_modules \ No newline at end of file diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 00000000..0c626a51 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,49 @@ +import { defineConfig } from 'vitepress' +import { footer } from "./footer"; +import { head } from "./head"; +import { navbar } from "./nav"; +import { sidebar } from "./sidebar"; +import { PROJECT_VARS } from './versions' + +export default defineConfig({ + title: "webrtc-java", + description: "Java native interface for WebRTC", + cleanUrls: true, + + head: head, + + themeConfig: { + logo: { + light: '/logo.png', + dark: '/logo.png', + alt: 'logo' + }, + + externalLinkIcon: true, + + nav: navbar, + sidebar: sidebar, + footer: footer, + socialLinks: [ + { icon: 'github', link: 'https://github.com/devopvoid/webrtc-java' }, + ], + search: { + provider: 'local', + }, + }, + + markdown: { + config: (md) => { + md.use(variableInterpolationPlugin) + }, + }, +}) + +function variableInterpolationPlugin(md) { + md.core.ruler.after('normalize', 'variable-interpolation', (state) => { + Object.entries(PROJECT_VARS).forEach(([key, value]) => { + const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g') + state.src = state.src.replace(regex, value) + }) + }) +} diff --git a/docs/.vitepress/footer.ts b/docs/.vitepress/footer.ts new file mode 100644 index 00000000..a6b949dd --- /dev/null +++ b/docs/.vitepress/footer.ts @@ -0,0 +1,6 @@ +import { DefaultTheme } from "vitepress"; + +export const footer: DefaultTheme.Footer = { + message: 'Released under the Apache-2.0 License.', + copyright: 'Copyright © 2019-present Alex Andres', +} \ No newline at end of file diff --git a/docs/.vitepress/head.ts b/docs/.vitepress/head.ts new file mode 100644 index 00000000..f0009790 --- /dev/null +++ b/docs/.vitepress/head.ts @@ -0,0 +1,27 @@ +import { HeadConfig } from "vitepress/types/shared"; + +const isProd = process.env.NODE_ENV === 'production'; + +const head: HeadConfig[] = [ + ['link', { rel: "icon", type: "image/png", sizes: "96x96", href: "/favicon-96x96.png" }], + ['link', { rel: "icon", type: "image/svg+xml", href: "/favicon.svg" }], + ['link', { rel: "shortcut icon", href: "/favicon.ico" }], + ['link', { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" }], + ['link', { rel: "manifest", href: "/site.webmanifest" }], + ['meta', { name: "apple-mobile-web-app-title", content: "JRTC" }], +] + +if (isProd) { + head.push([ + 'script', + { + async: '', + src: 'https://eu.umami.is/script.js', + 'data-website-id': '126654a9-5f07-4b57-ad7e-023eda3980ff', + 'data-domains': 'jrtc.dev', + 'data-do-not-track': 'true' + }, + ]) +} + +export { head } \ No newline at end of file diff --git a/docs/.vitepress/nav.ts b/docs/.vitepress/nav.ts new file mode 100644 index 00000000..a9883850 --- /dev/null +++ b/docs/.vitepress/nav.ts @@ -0,0 +1,30 @@ +import { DefaultTheme } from "vitepress"; +import { PROJECT_VARS } from './versions' + +export const navbar: DefaultTheme.NavItem[] = [ + { + text: 'Guide', + link: '/guide/', + }, + { + text: 'Examples', + link: '/guide/examples', + }, + { + text: 'Tools', + link: '/tools', + }, + { + text: PROJECT_VARS.VERSION, + items: [ + { + text: 'Changelog', + link: 'https://github.com/devopvoid/webrtc-java/blob/main/CHANGELOG.md' + }, + { + text: 'Build Notes', + link: '/guide/build' + } + ] + } +] \ No newline at end of file diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts new file mode 100644 index 00000000..402c6128 --- /dev/null +++ b/docs/.vitepress/sidebar.ts @@ -0,0 +1,102 @@ +import { DefaultTheme } from "vitepress"; + +export const sidebar: DefaultTheme.Sidebar = { + '/guide/': { base: '/guide/', items: sidebarGuide() }, + '/tools/': { base: '/tools/', items: sidebarTools() }, +} + +function sidebarGuide(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Introduction', + collapsed: false, + items: [ + { text: 'What is webrtc-java?', link: '/introduction' }, + { text: 'Getting Started', link: '/get-started' }, + ], + }, + { + text: 'Media Basics', + collapsed: false, + items: [ + { text: 'Media Devices', link: '/media/media-devices' }, + { text: 'Media Constraints', link: '/media/constraints' }, + { text: 'Media Directionality', link: '/media/directionality' }, + ], + }, + { + text: 'Audio', + collapsed: false, + items: [ + { text: 'Audio Devices', link: '/audio/audio-devices' }, + { text: 'Audio Processing', link: '/audio/audio-processing' }, + { text: 'Custom Audio Source', link: '/audio/custom-audio-source' }, + { text: 'Headless Audio', link: '/audio/headless-audio' }, + { text: 'DTMF Sender', link: '/audio/dtmf-sender' }, + ], + }, + { + text: 'Video', + collapsed: false, + items: [ + { text: 'Camera Video', link: '/video/camera-capture' }, + { text: 'Desktop Video', link: '/video/desktop-capture' }, + { text: 'Custom Video Source', link: '/video/custom-video-source' }, + ], + }, + { + text: 'Data Communication', + collapsed: false, + items: [ + { text: 'Data Channels', link: '/data/data-channels' }, + ], + }, + { + text: 'Networking and ICE', + collapsed: false, + items: [ + { text: 'Port Allocator Configuration', link: '/networking/port-allocator-config' }, + ], + }, + { + text: 'Monitoring and Debugging', + collapsed: false, + items: [ + { text: 'RTC Stats', link: '/monitoring/rtc-stats' }, + { text: 'Logging', link: '/monitoring/logging' }, + ], + }, + ] +} + +function sidebarTools(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Audio', + collapsed: false, + items: [ + { text: 'Audio Converter', link: '/audio/audio-converter' }, + { text: 'Audio Recorder', link: '/audio/audio-recorder' }, + { text: 'Audio Player', link: '/audio/audio-player' }, + { text: 'Voice Activity Detector', link: '/audio/voice-activity-detector' }, + ], + }, + { + text: 'Video', + collapsed: false, + items: [ + { text: 'Video Buffer Converter', link: '/video/video-buffer-converter' }, + { text: 'Video Capture', link: '/video/video-capturer' }, + ], + }, + { + text: 'Desktop', + collapsed: false, + items: [ + { text: 'Screen Capturer', link: '/desktop/screen-capturer' }, + { text: 'Window Capturer ', link: '/desktop/window-capturer' }, + { text: 'Power Management', link: '/desktop/power-management' }, + ], + }, + ] +} \ No newline at end of file diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..def4cfc8 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,17 @@ +// https://vitepress.dev/guide/custom-theme +import { h } from 'vue' +import type { Theme } from 'vitepress' +import DefaultTheme from 'vitepress/theme' +import './style.css' + +export default { + extends: DefaultTheme, + Layout: () => { + return h(DefaultTheme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + }) + }, + enhanceApp({ app, router, siteData }) { + // ... + } +} satisfies Theme diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 00000000..329e989e --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,139 @@ +/** + * Customize default theme styling by overriding CSS variables: + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +/** + * Colors + * + * Each colors have exact same color scale system with 3 levels of solid + * colors with different brightness, and 1 soft color. + * + * - `XXX-1`: The most solid color used mainly for colored text. It must + * satisfy the contrast ratio against when used on top of `XXX-soft`. + * + * - `XXX-2`: The color used mainly for hover state of the button. + * + * - `XXX-3`: The color for solid background, such as bg color of the button. + * It must satisfy the contrast ratio with pure white (#ffffff) text on + * top of it. + * + * - `XXX-soft`: The color used for subtle background such as custom container + * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors + * on top of it. + * + * The soft color must be semi transparent alpha channel. This is crucial + * because it allows adding multiple "soft" colors on top of each other + * to create an accent, such as when having inline code block inside + * custom containers. + * + * - `default`: The color used purely for subtle indication without any + * special meanings attached to it such as bg color for menu hover state. + * + * - `brand`: Used for primary brand colors, such as link text, button with + * brand theme, etc. + * + * - `tip`: Used to indicate useful information. The default theme uses the + * brand color for this by default. + * + * - `warning`: Used to indicate warning to the users. Used in custom + * container, badges, etc. + * + * - `danger`: Used to show error, or dangerous message to the users. Used + * in custom container, badges, etc. + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: var(--vp-c-indigo-1); + --vp-c-brand-2: var(--vp-c-indigo-2); + --vp-c-brand-3: var(--vp-c-indigo-3); + --vp-c-brand-soft: var(--vp-c-indigo-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-c-brand-1); +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #1d4ed8 30%, + #1e3a8a + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #60a5fa 50%, + #1d4ed8 50% + ); + --vp-home-hero-image-filter: blur(44px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(68px); + } +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); +} + +/** + * Component: Algolia + * -------------------------------------------------------------------------- */ + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} + diff --git a/docs/.vitepress/versions.ts b/docs/.vitepress/versions.ts new file mode 100644 index 00000000..dd942dc0 --- /dev/null +++ b/docs/.vitepress/versions.ts @@ -0,0 +1,4 @@ +export const PROJECT_VARS: Record = { + VERSION: '0.14.0', + VERSION_SNAPSHOT: '0.15.0-SNAPSHOT' +}; \ No newline at end of file diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index f20db8f2..00000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -jrtc.dev \ No newline at end of file diff --git a/docs/_coverpage.md b/docs/_coverpage.md deleted file mode 100644 index df754624..00000000 --- a/docs/_coverpage.md +++ /dev/null @@ -1,42 +0,0 @@ -webrtc-java - -

- webrtc-java -

- -> Connecting the Java world through WebRTC - -
    -
  • - devices - Cross-platform (Windows, macOS, Linux) -
  • -
  • - connect_without_contact - Peer-to-peer communication -
  • -
  • - videocam - Audio and video streaming -
  • -
  • - screen_share - Screen Sharing -
  • -
  • - swap_calls - Data Channels -
  • -
  • - bar_chart - Statistics API for monitoring -
  • -
- - - - -![color](#ffffff) \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md deleted file mode 100644 index e0e548d5..00000000 --- a/docs/_sidebar.md +++ /dev/null @@ -1,41 +0,0 @@ -- Getting started - - [Overview](README.md) - - [Quick start](quickstart.md) - - [Examples](examples.md) - -- Guide - - [Overview](guide/overview.md) - - Media Basics - - [Media Devices](guide/media/media_devices.md) - - [Bitrate and Framerate Constraints](guide/media/constraints.md) - - [Send-only and Receive-only](guide/media/send_receive_direction.md) - - Audio - - [Audio Device Selection](guide/audio/audio_devices.md) - - [Audio Processing](guide/audio/audio_processing.md) - - [Custom Audio Source](guide/audio/custom_audio_source.md) - - [Headless Audio](guide/audio/headless_audio_device_module.md) - - [DTMF Sender](guide/audio/dtmf_sender.md) - - Video - - [Camera Capture](guide/video/camera_capture.md) - - [Desktop Capture](guide/video/desktop_capture.md) - - [Custom Video Source](guide/video/custom_video_source.md) - - Data - - [Data Channels](guide/data/data_channels.md) - - Networking and ICE - - [Port Allocator Config](guide/networking/port_allocator_config.md) - - Monitoring and Debugging - - [RTC Stats](guide/monitoring/rtc_stats.md) - - [Logging](guide/monitoring/logging.md) - - Utilities - - [Audio Converter](guide/utilities/audio_converter.md) - - [Audio Recorder](guide/utilities/audio_recorder.md) - - [Audio Player](guide/utilities/audio_player.md) - - [Video Buffer Converter](guide/utilities/video_buffer_converter.md) - - [Video Capture](guide/utilities/video_capturer.md) - - [Screen Capture](guide/utilities/screen_capturer.md) - - [Window Capture](guide/utilities/window_capturer.md) - - [Voice Activity Detector](guide/utilities/voice_activity_detector.md) - - [Power Management](guide/utilities/power_management.md) - -- [**Build Notes**](build.md) -- [**Changelog**](changelog.md) \ No newline at end of file diff --git a/docs/assets/css/styles.css b/docs/assets/css/styles.css deleted file mode 100644 index 26cc2e17..00000000 --- a/docs/assets/css/styles.css +++ /dev/null @@ -1,277 +0,0 @@ -:root { - --theme-color: #0284c7; - --theme-secondary-color: #14b8a6; - --theme-hover-color: #0ea5e9; -} - -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: url(/assets/fonts/material-icons.woff2) format('woff2'); -} -@font-face { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - src: url(/assets/fonts/roboto-mono-400.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 300; - src: url(/assets/fonts/source-sans-pro-300.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 400; - src: url(/assets/fonts/source-sans-pro-400.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 600; - src: url(/assets/fonts/source-sans-pro-600.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - line-height: 1; - letter-spacing: normal; - text-transform: none; - display: inline-block; - white-space: nowrap; - word-wrap: normal; - direction: ltr; - -moz-font-feature-settings: 'liga'; - -moz-osx-font-smoothing: grayscale; -} - -.cover img { - width: 300px; -} - -/* Custom button styles for coverpage */ -.cover .buttons { - margin-top: 40px; - display: flex; - justify-content: center; - gap: 20px; - flex-wrap: wrap; /* Ensure buttons wrap on small screens */ -} - -.cover .buttons a { - display: inline-block; - padding: 10px 20px; - font-size: 1rem; - font-weight: 500; - text-decoration: none; - border-radius: 4px; - transition: all 0.2s ease; - margin: 5px; /* Add margin for when buttons wrap */ - background-color: transparent; /* Outlined button */ - color: var(--theme-color); /* Text color matches border for outlined style */ - border: 1px solid var(--theme-color); -} - -.cover .buttons a:hover { - background-color: #e0f2fe; /* Light background on hover */ - border-color: var(--theme-hover-color); -} - -.cover .buttons a:active { - background-color: rgba(99, 102, 241, 0.2); /* Slightly darker background when active */ -} - -.cover .buttons a span { - display: inline-block; /* Ensure the span behaves properly */ -} - -/* Feature list styles */ -.cover .features-list { - background: #f0f9ffaa; - border-radius: 4px; - list-style-type: none; - padding: 0; - margin: 30px auto; - max-width: 700px; - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-gap: 6px; -} - -.cover .feature-list-item { - display: flex; - align-items: center; - margin: 0; - padding: 8px 12px; -} - -.cover .feature-icon { - margin-right: 15px; - font-size: 28px; - color: var(--theme-secondary-color); - flex-shrink: 0; -} - -.cover .feature-text { - font-size: 16px; - line-height: 1.4; - text-align: left; - margin: 0; -} - -footer { - display: flex; - justify-content: center; -} - -/* Media queries for responsive design */ -@media screen and (max-width: 768px) { - .cover img { - width: 250px; - } - - .cover .buttons { - gap: 15px; - } - - .cover .buttons a { - padding: 9px 18px; - font-size: 0.95rem; - } - - .cover .features-list { - max-width: 90%; - grid-gap: 5px; - grid-template-columns: repeat(2, 1fr); - } - - .cover .feature-list-item { - padding: 6px 10px; - } - - .cover .feature-icon { - font-size: 24px; - margin-right: 10px; - } - - .cover .feature-text { - font-size: 15px; - } -} - -@media screen and (max-width: 576px) { - .cover .features-list { - max-width: 95%; - grid-gap: 4px; - grid-template-columns: 1fr; - } - - .cover .feature-list-item { - padding: 5px 8px; - } - - .cover .feature-icon { - font-size: 20px; - margin-right: 8px; - } - - .cover .feature-text { - font-size: 14px; - } -} - -@media screen and (max-width: 480px) { - .cover img { - width: 200px; - } - - .cover .buttons { - flex-direction: column; - align-items: center; - gap: 12px; - } - - .cover .buttons a { - padding: 8px 16px; - font-size: 0.9rem; - width: 80%; - max-width: 200px; - text-align: center; - } -} - -/* Additional styles for the sidebar */ -.sidebar .app-name-link img { - max-width: 50%; -} -.sidebar-nav > ul > li > p strong { - font-size: 15px; -} - -/* Sidebar Toggle */ -.sidebar-toggle { - cursor: pointer; -} - -body .sidebar-toggle { - background: transparent; - top: 1.5rem; - left: calc(300px + 1.5rem); - cursor: pointer; - width: 1.5rem; - height: 1.5rem; - padding: 0; - transition: left 0.25s ease-out; -} - -body .sidebar-toggle span { - background-color: var(--theme-color); - height: 0.2rem; - width: 1.5rem; - position: absolute; - left: 0; - margin: 0; - transform-origin: 0; - border-radius: 1px; -} - -body.close .sidebar-toggle { - transition: left 0.25s ease-out; - width: 1.5rem; - height: 1.5rem; - left: 1.5rem; -} - -body.close .sidebar-toggle span { - transform-origin: center; -} - -body .sidebar-toggle span:nth-child(1) { - top: 0; -} -body .sidebar-toggle span:nth-child(2) { - top: 0.5rem; -} -body .sidebar-toggle span:nth-child(3) { - top: 1rem; -} - -@media screen and (max-width: 768px) { - body .sidebar-toggle { - left: 1rem; - } - - body.close .sidebar-toggle { - left: calc(300px + 1.5rem); - } -} \ No newline at end of file diff --git a/docs/assets/css/vue.css b/docs/assets/css/vue.css deleted file mode 100644 index ce96229d..00000000 --- a/docs/assets/css/vue.css +++ /dev/null @@ -1 +0,0 @@ -*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}img.emoji{height:1.2em}img.emoji,span.emoji{vertical-align:middle}span.emoji{font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1.2em}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{position:relative;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100vh;width:100%;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;bottom:0;width:100%}section.cover .cover-main{flex:1;margin:0 16px;text-align:center;position:relative}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid #42b983;border-color:var(--theme-color,#42b983);box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0;content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto} \ No newline at end of file diff --git a/docs/assets/fonts/material-icons.woff2 b/docs/assets/fonts/material-icons.woff2 deleted file mode 100644 index 5492a6e75937db19f9ba860cd2575744887fa26b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128352 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

l*K8_>P7#5$l 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

~h(($n;NJ25OR8A#8hkUVAN##@mwDQ{0 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 diff --git a/docs/assets/fonts/source-sans-pro-600.woff2 b/docs/assets/fonts/source-sans-pro-600.woff2 deleted file mode 100644 index 3ca3e26c7c59273ab2ed0c5e2176c4ee7ccd7789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14824 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 O?Pat$Aap3vwOt3Q)wA6I diff --git a/docs/assets/images/logo.png b/docs/assets/images/logo.png deleted file mode 100644 index 1cdf565199bc6ecce82a737cc1daab50da795ee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57254 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 z 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 Ua39;bT!tl567r979~iv;f0|lBuK)l5 diff --git a/docs/assets/js/docsify-copy-code.min.js b/docs/assets/js/docsify-copy-code.min.js deleted file mode 100644 index c91d4203..00000000 --- a/docs/assets/js/docsify-copy-code.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * docsify-copy-code - * v3.0.2 - * https://github.com/jperasmus/docsify-copy-code - * (c) 2017-2025 JP Erasmus - * MIT license - */ -!function(){"use strict";function e(o){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(o)}!function(e,o){void 0===o&&(o={});var t=o.insertAt;if(e&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=e:c.appendChild(document.createTextNode(e))}}(".docsify-copy-code-button,.docsify-copy-code-button>span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{background:grey;background:var(--theme-color,grey);border:0;border-radius:0;color:#fff;font-size:1em;opacity:0;outline:0;overflow:visible;padding:.65em .8em;position:absolute;right:0;top:0;z-index:1}.docsify-copy-code-button>span{background:inherit;border-radius:3px;pointer-events:none}.docsify-copy-code-button>.error,.docsify-copy-code-button>.success{font-size:.825em;opacity:0;padding:.5em .65em;position:absolute;right:0;top:50%;transform:translateY(-50%);z-index:-100}.docsify-copy-code-button.error>.error,.docsify-copy-code-button.success>.success{opacity:1;right:100%;transform:translate(-25%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}.docsify-copy-code-button>[aria-live]{height:1px;left:-10000px;overflow:hidden;position:absolute;top:auto;width:1px}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(e,o){e.ready((function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")}))}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,t){var n={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};o.doneEach((function(){var o=Array.from(document.querySelectorAll("pre[data-lang]"));t.config.copyCode&&Object.keys(n).forEach((function(o){var c=t.config.copyCode[o];"string"==typeof c?n[o]=c:"object"===e(c)&&Object.keys(c).some((function(e){var t=location.href.indexOf(e)>-1;return n[o]=t?c[e]:n[o],t}))}));var c=['"].join("");o.forEach((function(e){e.insertAdjacentHTML("beforeend",c)}))})),o.mounted((function(){var e=document.querySelector(".content");e&&e.addEventListener("click",(function(e){if(e.target.classList.contains("docsify-copy-code-button")){var o="BUTTON"===e.target.tagName?e.target:e.target.parentNode,t=document.createRange(),c=o.parentNode.querySelector("code"),i=o.querySelector("[aria-live]"),r=window.getSelection();t.selectNode(c),r&&(r.removeAllRanges(),r.addRange(t));try{document.execCommand("copy")&&(o.classList.add("success"),i.innerText=n.successText,setTimeout((function(){o.classList.remove("success"),i.innerText=""}),1e3))}catch(e){console.error("docsify-copy-code: ".concat(e)),o.classList.add("error"),i.innerText=n.errorText,setTimeout((function(){o.classList.remove("error"),i.innerText=""}),1e3)}setTimeout((function(){e.target.blur()}),1500),(r=window.getSelection())&&("function"==typeof r.removeRange?r.removeRange(t):"function"==typeof r.removeAllRanges&&r.removeAllRanges())}}))}))}].concat(window.$docsify.plugins||[])}(); -//# sourceMappingURL=docsify-copy-code.min.js.map diff --git a/docs/assets/js/docsify-pagination.min.js b/docs/assets/js/docsify-pagination.min.js deleted file mode 100644 index 83a77fae..00000000 --- a/docs/assets/js/docsify-pagination.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){("object"!=typeof exports||"undefined"==typeof module)&&"function"==typeof define&&define.amd?define(t):t()}(function(){"use strict";var i="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(t,e){return t(e={exports:{}},e.exports),e.exports}var c=t(function(t,e){function n(t,e){return e.querySelector(t)}(e=t.exports=function(t,e){return n(t,e=e||document)}).all=function(t,e){return(e=e||document).querySelectorAll(t)},e.engine=function(t){if(!t.one)throw new Error(".one callback required");if(t.all)return n=t.one,e.all=t.all,e;throw new Error(".all callback required")}}),e=(c.all,c.engine,t(function(e){var n=eval;try{n("export default global")}catch(t){try{n("export default self")}catch(t){try{e.exports=i}catch(t){try{self.global=self}catch(t){window.global=window}}}}}));try{var a=c}catch(t){a=c}var e=e.Element,e=e&&e.prototype||{},r=e.matches||e.webkitMatchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector,s=function(t,e){if(!t||1!==t.nodeType)return!1;if(r)return r.call(t,e);for(var n=a.all(e,t.parentNode),i=0;i ul > li"),c("p",e)),this.hyperlink=m(t))}var b=function(){return'

'},k=function(t,e){a=e,r=t.route.path,o={},["previousText","nextText"].forEach(function(n){var i=a[n];"string"==typeof i?o[n]=i:Object.keys(i).some(function(t){var e=r&&-1\n \n
\n \n \n \n '+i+'\n
\n
'+t.prev.name+"
\n ",t.prev&&e.crossChapterText&&'
'+t.prev.chapterName+"
",t.prev&&"
\n \n ",t.next&&'\n \n "].filter(Boolean).join("")};window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(t,e){var n=d({},(e.config,{previousText:"PREVIOUS",nextText:"NEXT",crossChapter:!1,crossChapterText:!1}),e.config.pagination||{});function i(){var t=c("."+h);t&&(t.innerHTML=k(function(t,e){e=e.crossChapter;try{var n=t.router.toURL(t.route.path),i=g(c.all(".sidebar-nav li a")).filter(function(t){return!s(t,".section-link")}),a=i.find(x(n)),r=g((p(a,"ul")||{}).children).filter(function(t){return"LI"===t.tagName.toUpperCase()}),o=e?i.findIndex(x(n)):r.findIndex(function(t){t=m(t);return t&&x(n,t)}),l=e?i:r;return{route:t.route,prev:new y(l[o-1]).toJSON(),next:new y(l[o+1]).toJSON()}}catch(t){return{route:{}}}}(e,n),n))}t.afterEach(function(t){return t+b()}),t.doneEach(i)}].concat(window.$docsify.plugins||[])}); \ No newline at end of file diff --git a/docs/assets/js/docsify@4.js b/docs/assets/js/docsify@4.js deleted file mode 100644 index 80be8c5f..00000000 --- a/docs/assets/js/docsify@4.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function c(i){var o=Object.create(null);return function(e){var n=f(e)?e:JSON.stringify(e);return o[n]||(o[n]=i(e))}}var a=c(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),u=Object.prototype.hasOwnProperty,m=Object.assign||function(e){for(var n=arguments,i=1;i=e||n.classList.contains("hidden")?S(h,"add","sticky"):S(h,"remove","sticky"))}function ee(e,n,o,i){var t=[];null!=(n=l(n))&&(t=k(n,"a"));var a,r=decodeURI(e.toURL(e.getCurrentPath()));return t.sort(function(e,n){return n.href.length-e.href.length}).forEach(function(e){var n=decodeURI(e.getAttribute("href")),i=o?e.parentNode:e;e.title=e.title||e.innerText,0!==r.indexOf(n)||a?S(i,"remove","active"):(a=e,S(i,"add","active"))}),i&&(v.title=a?a.title||a.innerText+" - "+J:J),a}function ne(e,n){for(var i=0;ithis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,n,i,o){return(e/=o/2)<1?i/2*e*e+n:-i/2*(--e*(e-2)-1)+n}}]),re);function re(){var e=0c){n=n||p;break}n=p}!n||(r=fe[ve(e,n.getAttribute("data-id"))])&&r!==a&&(a&&a.classList.remove("active"),r.classList.add("active"),a=r,!pe&&h.classList.contains("sticky")&&(e=i.clientHeight,r=a.offsetTop+a.clientHeight+40,a=a.offsetTop>=t.scrollTop&&r<=t.scrollTop+e,i.scrollTop=a?t.scrollTop:+r"']/),xe=/[&<>"']/g,Se=/[<>"']|&(?!#?\w+;)/,Ae=/[<>"']|&(?!#?\w+;)/g,$e={"&":"&","<":"<",">":">",'"':""","'":"'"};var ze=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function Fe(e){return e.replace(ze,function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""})}var Ee=/(^|[^\[])\^/g;var Re=/[^\w:]/g,Te=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var Ce={},je=/^[^:]+:\/*[^/]*$/,Le=/^([^:]+:)[\s\S]*$/,Oe=/^([^:]+:\/*[^/]*)[\s\S]*$/;function qe(e,n){Ce[" "+e]||(je.test(e)?Ce[" "+e]=e+"/":Ce[" "+e]=Pe(e,"/",!0));var i=-1===(e=Ce[" "+e]).indexOf(":");return"//"===n.substring(0,2)?i?n:e.replace(Le,"$1")+n:"/"===n.charAt(0)?i?n:e.replace(Oe,"$1")+n:e+n}function Pe(e,n,i){var o=e.length;if(0===o)return"";for(var t=0;tn)i.splice(n);else for(;i.length>=1,e+=e;return i+e},We=we.defaults,Xe=Be,Qe=Ze,Je=Me,Ke=Ve;function en(e,n,i){var o=n.href,t=n.title?Je(n.title):null,n=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:i,href:o,title:t,text:n}:{type:"image",raw:i,href:o,title:t,text:Je(n)}}var nn=function(){function e(e){this.options=e||We}return e.prototype.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=i.length?e.slice(i.length):e}).join("\n")}(i,n[3]||"");return{type:"code",raw:i,lang:n[2]&&n[2].trim(),text:e}}},e.prototype.heading=function(e){var n=this.rules.block.heading.exec(e);if(n){var i=n[2].trim();return/#$/.test(i)&&(e=Xe(i,"#"),!this.options.pedantic&&e&&!/ $/.test(e)||(i=e.trim())),{type:"heading",raw:n[0],depth:n[1].length,text:i}}},e.prototype.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var n={type:"table",header:Qe(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(n.header.length===n.align.length){for(var i=n.align.length,o=0;o ?/gm,"");return{type:"blockquote",raw:n[0],text:e}}},e.prototype.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var n,i,o,t,a,r=e[0],c=e[2],u=1s[1].length:o[1].length>s[0].length||3/i.test(e[0])&&(n=!1),!i&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?i=!0:i&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(i=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:n,inRawBlock:i,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]}},e.prototype.link=function(e){var n=this.rules.inline.link.exec(e);if(n){e=n[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;var i=Xe(e.slice(0,-1),"\\");if((e.length-i.length)%2==0)return}else{var o=Ke(n[2],"()");-1$/.test(e)?i.slice(1):i.slice(1,-1):i)&&i.replace(this.rules.inline._escapes,"$1"),title:o&&o.replace(this.rules.inline._escapes,"$1")},n[0])}},e.prototype.reflink=function(e,n){if((i=this.rules.inline.reflink.exec(e))||(i=this.rules.inline.nolink.exec(e))){var e=(i[2]||i[1]).replace(/\s+/g," ");if((e=n[e.toLowerCase()])&&e.href)return en(i,e,i[0]);var i=i[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.strong.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="**"===o[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.strong.middle.exec(n.slice(0,o.index+3)))return{type:"strong",raw:e.slice(0,t[0].length),text:e.slice(2,t[0].length-2)}}},e.prototype.em=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.em.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="*"===o[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.em.middle.exec(n.slice(0,o.index+2)))return{type:"em",raw:e.slice(0,t[0].length),text:e.slice(1,t[0].length-1)}}},e.prototype.codespan=function(e){var n=this.rules.inline.code.exec(e);if(n){var i=n[2].replace(/\n/g," "),o=/[^ ]/.test(i),e=/^ /.test(i)&&/ $/.test(i);return o&&e&&(i=i.substring(1,i.length-1)),i=Je(i,!0),{type:"codespan",raw:n[0],text:i}}},e.prototype.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},e.prototype.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},e.prototype.autolink=function(e,n){e=this.rules.inline.autolink.exec(e);if(e){var i,n="@"===e[2]?"mailto:"+(i=Je(this.options.mangle?n(e[1]):e[1])):i=Je(e[1]);return{type:"link",raw:e[0],text:i,href:n,tokens:[{type:"text",raw:i,text:i}]}}},e.prototype.url=function(e,n){var i,o,t,a;if(i=this.rules.inline.url.exec(e)){if("@"===i[2])t="mailto:"+(o=Je(this.options.mangle?n(i[0]):i[0]));else{for(;a=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0],a!==i[0];);o=Je(i[0]),t="www."===i[1]?"http://"+o:o}return{type:"link",raw:i[0],text:o,href:t,tokens:[{type:"text",raw:o,text:o}]}}},e.prototype.inlineText=function(e,n,i){e=this.rules.inline.text.exec(e);if(e){i=n?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]:Je(this.options.smartypants?i(e[0]):e[0]);return{type:"text",raw:e[0],text:i}}},e}(),Ze=De,Ve=Ne,De=Ue,Ne={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:Ze,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ne.def=Ve(Ne.def).replace("label",Ne._label).replace("title",Ne._title).getRegex(),Ne.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ne.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,Ne.item=Ve(Ne.item,"gm").replace(/bull/g,Ne.bullet).getRegex(),Ne.listItemStart=Ve(/^( *)(bull)/).replace("bull",Ne.bullet).getRegex(),Ne.list=Ve(Ne.list).replace(/bull/g,Ne.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ne.def.source+")").getRegex(),Ne._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ne._comment=/|$)/,Ne.html=Ve(Ne.html,"i").replace("comment",Ne._comment).replace("tag",Ne._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ne.paragraph=Ve(Ne._paragraph).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.blockquote=Ve(Ne.blockquote).replace("paragraph",Ne.paragraph).getRegex(),Ne.normal=De({},Ne),Ne.gfm=De({},Ne.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Ne.gfm.nptable=Ve(Ne.gfm.nptable).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.gfm.table=Ve(Ne.gfm.table).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.pedantic=De({},Ne.normal,{html:Ve("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ne._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:Ve(Ne.normal._paragraph).replace("hr",Ne.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ne.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});Ze={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};Ze.punctuation=Ve(Ze.punctuation).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Ze._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Ze._comment=Ve(Ne._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ze.em.start=Ve(Ze.em.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.middle=Ve(Ze.em.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.em.endAst=Ve(Ze.em.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.endUnd=Ve(Ze.em.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.start=Ve(Ze.strong.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.middle=Ve(Ze.strong.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.strong.endAst=Ve(Ze.strong.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.endUnd=Ve(Ze.strong.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.blockSkip=Ve(Ze._blockSkip,"g").getRegex(),Ze.overlapSkip=Ve(Ze._overlapSkip,"g").getRegex(),Ze._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ze._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ze._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ze.autolink=Ve(Ze.autolink).replace("scheme",Ze._scheme).replace("email",Ze._email).getRegex(),Ze._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ze.tag=Ve(Ze.tag).replace("comment",Ze._comment).replace("attribute",Ze._attribute).getRegex(),Ze._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ze._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ze._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ze.link=Ve(Ze.link).replace("label",Ze._label).replace("href",Ze._href).replace("title",Ze._title).getRegex(),Ze.reflink=Ve(Ze.reflink).replace("label",Ze._label).getRegex(),Ze.reflinkSearch=Ve(Ze.reflinkSearch,"g").replace("reflink",Ze.reflink).replace("nolink",Ze.nolink).getRegex(),Ze.normal=De({},Ze),Ze.pedantic=De({},Ze.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:Ve(/^!?\[(label)\]\((.*?)\)/).replace("label",Ze._label).getRegex(),reflink:Ve(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ze._label).getRegex()}),Ze.gfm=De({},Ze.normal,{escape:Ve(Ze.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(i?e:gn(e,!0))+"\n":"
"+(i?e:gn(e,!0))+"
\n"},e.prototype.blockquote=function(e){return"
\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,i,o){return this.options.headerIds?"'+e+"\n":""+e+"\n"},e.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},e.prototype.list=function(e,n,i){var o=n?"ol":"ul";return"<"+o+(n&&1!==i?' start="'+i+'"':"")+">\n"+e+"\n"},e.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},e.prototype.checkbox=function(e){return" "},e.prototype.paragraph=function(e){return"

    "+e+"

    \n"},e.prototype.table=function(e,n){return"\n\n"+e+"\n"+(n=n&&""+n+"")+"
    \n"},e.prototype.tablerow=function(e){return"\n"+e+"\n"},e.prototype.tablecell=function(e,n){var i=n.header?"th":"td";return(n.align?"<"+i+' align="'+n.align+'">':"<"+i+">")+e+"\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;e='"},e.prototype.image=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;i=''+i+'":">"},e.prototype.text=function(e){return e},e}(),ln=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,i){return""+i},e.prototype.image=function(e,n,i){return""+i},e.prototype.br=function(){return""},e}(),vn=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var i=e,o=0;if(this.seen.hasOwnProperty(i))for(o=this.seen[e];i=e+"-"+ ++o,this.seen.hasOwnProperty(i););return n||(this.seen[e]=o,this.seen[i]=0),i},e.prototype.slug=function(e,n){void 0===n&&(n={});e=this.serialize(e);return this.getNextSafeSlug(e,n.dryrun)},e}(),hn=we.defaults,_n=Ie,mn=function(){function i(e){this.options=e||hn,this.options.renderer=this.options.renderer||new sn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ln,this.slugger=new vn}return i.parse=function(e,n){return new i(n).parse(e)},i.parseInline=function(e,n){return new i(n).parseInline(e)},i.prototype.parse=function(e,n){void 0===n&&(n=!0);for(var i,o,t,a,r,c,u,f,p,d,g,s,l,v,h,_="",m=e.length,b=0;bAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}}xn.options=xn.setOptions=function(e){return bn(xn.defaults,e),yn(xn.defaults),xn},xn.getDefaults=Me,xn.defaults=we,xn.use=function(a){var n,e=bn({},a);if(a.renderer){var i,r=xn.defaults.renderer||new sn;for(i in a.renderer)!function(o){var t=r[o];r[o]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.renderer[o].apply(r,e);return i=!1===i?t.apply(r,e):i}}(i);e.renderer=r}if(a.tokenizer){var t,c=xn.defaults.tokenizer||new nn;for(t in a.tokenizer)!function(){var o=c[t];c[t]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.tokenizer[t].apply(c,e);return i=!1===i?o.apply(c,e):i}}();e.tokenizer=c}a.walkTokens&&(n=xn.defaults.walkTokens,e.walkTokens=function(e){a.walkTokens(e),n&&n(e)}),xn.setOptions(e)},xn.walkTokens=function(e,n){for(var i=0,o=e;iAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}},xn.Parser=mn,xn.parser=mn.parse,xn.Renderer=sn,xn.TextRenderer=ln,xn.Lexer=fn,xn.lexer=fn.lex,xn.Tokenizer=nn,xn.Slugger=vn;var Sn=xn.parse=xn;function An(e,i){if(void 0===i&&(i='
      {inner}
    '),!e||!e.length)return"";var o="";return e.forEach(function(e){var n=e.title.replace(/(<([^>]+)>)/g,"");o+='
  • '+e.title+"
  • ",e.children&&(o+=An(e.children,i))}),i.replace("{inner}",o)}function $n(e,n){return'

    '+n.slice(5).trim()+"

    "}function zn(e,o){var t=[],a={};return e.forEach(function(e){var n=e.level||1,i=n-1;o?@[\]^`{|}~]/g;function Rn(e){return e.toLowerCase()}function Tn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Rn).replace(/<[^>]+>/g,"").replace(En,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=Fn[n],e=u.call(Fn,n)?e+1:0;return n=(Fn[n]=e)?n+"-"+e:n}Tn.clear=function(){Fn={}};var Cn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function jn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=Cn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return"&#x"+e+";"}).join("‍").concat("︎")+"":''+o+'':i;var i,o}).replace(/__colon__/g,":")}function Ln(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}function On(e){return(e=void 0===e?"":e).replace(/(<\/?a.*?>)/gi,"")}var qn,Pn=be(function(e){var u,f,p,d,n,g=function(u){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},R={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof T?new T(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=r.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof T)){var k,w=1;if(l){if(!(k=C(h,m,n,s))||k.index>=n.length)break;var y=k.index,x=k.index+k[0].length,S=m;for(S+=_.value.length;S<=y;)_=_.next,S+=_.value.length;if(S-=_.value.length,m=S,_.value instanceof T)continue;for(var A=_;A!==i.tail&&(Sr.reach&&(r.reach=E);b=_.prev;z&&(b=j(i,b,z),m+=z.length),L(i,b,w);$=new T(c,g?R.tokenize($,g):$,v,$);_=j(i,b,$),F&&j(i,_,F),1r.reach&&(r.reach=E.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=R.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=R.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:T};function T(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function C(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function a(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function j(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function L(e,n,i){for(var o=n.next,t=0;t"+t.content+""},!u.document)return u.addEventListener&&(R.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;u.postMessage(R.highlight(e,R.languages[i],i)),n&&u.close()},!1)),R;var o=R.util.currentScript();function t(){R.manual||R.highlightAll()}return o&&(R.filename=o.src,o.hasAttribute("data-manual")&&(R.manual=!0)),R.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),R}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=g),void 0!==me&&(me.Prism=g),g.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},g.languages.markup.tag.inside["attr-value"].inside.entity=g.languages.markup.entity,g.languages.markup.doctype.inside["internal-subset"].inside=g.languages.markup,g.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(g.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:g.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:g.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},g.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(g.languages.markup.tag,"addAttribute",{value:function(e,n){g.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:g.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),g.languages.html=g.languages.markup,g.languages.mathml=g.languages.markup,g.languages.svg=g.languages.markup,g.languages.xml=g.languages.extend("markup",{}),g.languages.ssml=g.languages.xml,g.languages.atom=g.languages.xml,g.languages.rss=g.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+n.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(g),g.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},g.languages.javascript=g.languages.extend("clike",{"class-name":[g.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),g.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,g.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:g.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:g.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:g.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:g.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:g.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),g.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:g.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),g.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),g.languages.markup&&(g.languages.markup.tag.addInlined("script","javascript"),g.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),g.languages.js=g.languages.javascript,void 0!==g&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),u={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',g.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),g.hooks.add("before-sanity-check",function(e){var t,n,i,o,a,r,c=e.element;c.matches(d)&&(e.code="",c.setAttribute(f,p),(t=c.appendChild(document.createElement("CODE"))).textContent="Loading…",i=c.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=u[n]||n),g.util.setLanguage(t,e),g.util.setLanguage(c,e),(n=g.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){c.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(c.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),c.hasAttribute("data-start")||c.setAttribute("data-start",String(i+1))),t.textContent=e,g.highlightElement(t)},a=function(e){c.setAttribute(f,"failed"),t.textContent=e},(r=new XMLHttpRequest).open("GET",i,!0),r.onreadystatechange=function(){4==r.readyState&&(r.status<400&&r.responseText?o(r.responseText):400<=r.status?a("✖ Error "+r.status+" while fetching file: "+r.statusText):a("✖ Error: File does not exist or is empty"))},r.send(null))}),n=!(g.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)g.highlightElement(n)}}),g.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),g.plugins.fileHighlight.highlight.apply(this,arguments)})});function Mn(e,n){return"___"+e.toUpperCase()+n+"___"}qn=Prism,Object.defineProperties(qn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,a){var r;o.language===t&&(r=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var n,i=r.length;-1!==o.code.indexOf(n=Mn(t,i));)++i;return r[i]=e,n}),o.grammar=qn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,g;f.language===p&&f.tokenStack&&(f.grammar=qn.languages[p],d=0,g=Object.keys(f.tokenStack),function e(n){for(var i=0;i=g.length);i++){var o,t,a,r,c,u=n[i];"string"==typeof u||u.content&&"string"==typeof u.content?(t=g[d],a=f.tokenStack[t],o="string"==typeof u?u:u.content,c=Mn(p,t),-1<(r=o.indexOf(c))&&(++d,t=o.substring(0,r),a=new qn.Token(p,qn.tokenize(a,f.grammar),"language-"+p,a),r=o.substring(r+c.length),c=[],t&&c.push.apply(c,e([t])),c.push(a),r&&c.push.apply(c,e([r])),"string"==typeof u?n.splice.apply(n,[i,1].concat(c)):u.content=c)):u.content&&e(u.content)}return n}(f.tokens))}}});function In(t,e){var a=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var r=o(e=t.markdown||{})?e(Sn,n):(Sn.setOptions(m(e,{renderer:m(n,e.renderer)})),Sn);this._marked=r,this.compile=function(i){var o=!0,e=c(function(e){o=!1;var n="";return i&&(n=f(i)?r(i):r.parser(i),n=t.noEmoji?n:jn(n,t.nativeEmoji),Tn.clear(),n)})(i),n=a.router.parse().file;return o?a.toc=a.cacheTOC[n]:a.cacheTOC[n]=[].concat(a.toc),e}}var Nn={},Hn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};In.prototype.compileEmbed=function(e,n){var i,o,t=Ln(n),a=t.str,t=t.config;if(n=a,t.include)return R(e)||(e=q(this.contentBase,C(this.router.getCurrentPath()),e)),t.type&&(o=Hn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Hn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},In.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore} --\x3e",""),e.title=On(o),e.ignoreSubHeading=!0),/{docsify-ignore}/g.test(o)&&(o=o.replace("{docsify-ignore}",""),e.title=On(o),e.ignoreSubHeading=!0),//g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),e.title=On(o),e.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(o)&&(o=o.replace("{docsify-ignore-all}",""),e.title=On(o),e.ignoreAllSubs=!0);i=Tn(t.id||o),t=a.toURL(a.getCurrentPath(),{id:i});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=Pn.languages[n=void 0===n?"markup":n]||Pn.languages.markup;return'
    '+Pn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
    "},t.link=(i=(n={renderer:e,router:a,linkTarget:n,linkRel:i,compilerClass:g}).renderer,c=n.router,u=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=Ln(n=void 0===n?"":n),a=t.str,t=t.config;return u=t.target||u,r="_blank"===u?f.config.externalLinkRel||"noopener":"",n=a,R(e)||f._matchNotCompileLink(e)||t.ignore?(R(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+u+'"'),o.push(0!==e.indexOf("mailto:")&&""!==r?' rel="'+r+'"':"")):(e===f.config.homepage&&(e="README"),e=c.toURL(e,null,c.getCurrentPath())),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?$n("tip",e):/^\?>/.test(e)?$n("warn",e):"

    "+e+"

    ";return e},t.image=(o=(i={renderer:e,contentBase:o,router:a}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],a=Ln(n),r=a.str,a=a.config;return n=r,a["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),a.size&&(n=(r=a.size.split("x"))[0],(r=r[1])?t.push('width="'+n+'" height="'+r+'"'):t.push('width="'+n+'"')),a.class&&t.push('class="'+a.class+'"'),a.id&&t.push('id="'+a.id+'"'),R(e)||(o=q(p,C(d.getCurrentPath()),e)),0":''+i+'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},In.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var a=0;a{inner}");this.cacheTree[o]=n}return t},In.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),a({token:t,embedToken:n}),++u>=c&&a({})}}(n);n.embed.url?X(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:c,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),r=r.slice(0,n).concat(i,r.slice(n+1)),p.push({start:n,length:i.length-1})):(Bn[t]=r.concat(),r.links=Bn[t].links=f,o(r))})}function Yn(e,n,i){var o,t,a,r;return n="function"==typeof i?i(n):"string"==typeof i?(a=[],r=0,(o=i).replace(V,function(n,e,i){a.push(o.substring(r,i-1)),r=i+=n.length+1,a.push(t&&t[n]||function(e){return("00"+("string"==typeof Y[n]?e[Y[n]]():Y[n](e))).slice(-n.length)})}),r!==o.length&&a.push(o.substring(r)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var a=0,r=k(".markdown-section > *").filter(n);ascript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var u,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!Un&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(Un=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(u=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,k(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||u.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return Un&&(n.data=function(){return Un}),[e,n]})));for(var g=0,s=p;g([^<]*?)

    $'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],S(o,"add","has-mask"),R(n[1])||(e=q(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):S(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'
    \x3c!--cover--\x3e
    ')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=q(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"","
    "+('')+'
    \x3c!--main--\x3e
    '),this._renderTo(u,f,!0)):this.rendered=!0,e.mergeNavbar&&s?p=b(".sidebar"):(c.classList.add("app-nav"),e.repo||c.classList.add("no-badge")),e.loadNavbar&&y(p,c),e.themeColor&&(v.head.appendChild(w("div","").firstElementChild),a=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=k("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?Q(e,a):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&X(e).then(function(e){e=w("style",e);_.appendChild(e),Q(e,a)}))}))),this._updateRender(),S(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var a=this.routes(),r=Object.keys(a),c=function(){return null};function u(){var e=r.shift();if(!e)return c(null);var n=A(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return u();var o=a[e];if("string"==typeof o)return c(o);if("function"!=typeof o)return u();n=o,e=Xn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?c(e):!1===e?c(null):u()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){c=e,u()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?D:H)(e);this.router=e,this.updateRender(),U=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),U.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),U=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,a){void 0===a&&(a=d);var r=this._hooks[e],c=this.config.catchPluginErrors,u=function(n){var e=r[n];if(n>=r.length)a(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,u(n+1)})}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}else try{var o=e(t);t=void 0===o?t:o,u(n+1)}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}}else u(n+1)};u(0)},n}(we))))))));function Kn(e,n,i){return Qn&&Qn.abort&&Qn.abort(),Qn=X(e,!0,i)}window.Docsify={util:Me,dom:n,get:X,slugify:Tn,version:"4.13.1"},window.DocsifyCompiler=In,window.marked=Sn,window.Prism=Pn,e(function(e){return new Jn})}(); diff --git a/docs/assets/js/prism-gradle.min.js b/docs/assets/js/prism-gradle.min.js deleted file mode 100644 index 96aae9aa..00000000 --- a/docs/assets/js/prism-gradle.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var n={pattern:/((?:^|[^\\$])(?:\\{2})*)\$(?:\w+|\{[^{}]*\})/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:null}}};e.languages.gradle=e.languages.extend("clike",{string:{pattern:/'''(?:[^\\]|\\[\s\S])*?'''|'(?:\\.|[^\\'\r\n])*'/,greedy:!0},keyword:/\b(?:apply|def|dependencies|else|if|implementation|import|plugin|plugins|project|repositories|repository|sourceSets|tasks|val)\b/,number:/\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?\d+)?)[glidf]?\b/i,operator:{pattern:/(^|[^.])(?:~|==?~?|\?[.:]?|\*(?:[.=]|\*=?)?|\.[@&]|\.\.<|\.\.(?!\.)|-[-=>]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),e.languages.insertBefore("gradle","string",{shebang:{pattern:/#!.+/,alias:"comment",greedy:!0},"interpolation-string":{pattern:/"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/,greedy:!0,inside:{interpolation:n,string:/[\s\S]+/}}}),e.languages.insertBefore("gradle","punctuation",{"spock-block":/\b(?:and|cleanup|expect|given|setup|then|when|where):/}),e.languages.insertBefore("gradle","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0,alias:"punctuation"}}),n.inside.expression.inside=e.languages.gradle}(Prism); \ No newline at end of file diff --git a/docs/assets/js/prism-java.min.js b/docs/assets/js/prism-java.min.js deleted file mode 100644 index 9b90cf3c..00000000 --- a/docs/assets/js/prism-java.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var n=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,t="(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*",s={pattern:RegExp("(^|[^\\w.])"+t+"[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[s,{pattern:RegExp("(^|[^\\w.])"+t+"[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()]|\\s*(?:\\[[\\s,]*\\]\\s*)?::\\s*new\\b)"),lookbehind:!0,inside:s.inside},{pattern:RegExp("(\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\s+)"+t+"[A-Z]\\w*\\b"),lookbehind:!0,inside:s.inside}],keyword:n,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":s,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp("(\\bimport\\s+)"+t+"(?:[A-Z]\\w*|\\*)(?=\\s*;)"),lookbehind:!0,inside:{namespace:s.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp("(\\bimport\\s+static\\s+)"+t+"(?:\\w+|\\*)(?=\\s*;)"),lookbehind:!0,alias:"static",inside:{namespace:s.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp("(\\b(?:exports|import(?:\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\s+)(?!)[a-z]\\w*(?:\\.[a-z]\\w*)*\\.?".replace(//g,(function(){return n.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(Prism); \ No newline at end of file diff --git a/docs/assets/js/search.min.js b/docs/assets/js/search.min.js deleted file mode 100644 index 63b5bc7e..00000000 --- a/docs/assets/js/search.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=e.text||""):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n

    '+e.title+"

    \n

    "+e.content+"

    \n
    \n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||'

    '+c+"

    ",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-1 { }); ``` ---- - ## Conclusion This guide has walked you through the process of implementing audio device selection in your WebRTC application. diff --git a/docs/guide/audio/audio_processing.md b/docs/guide/audio/audio-processing.md similarity index 99% rename from docs/guide/audio/audio_processing.md rename to docs/guide/audio/audio-processing.md index cdc48901..92d5c29b 100644 --- a/docs/guide/audio/audio_processing.md +++ b/docs/guide/audio/audio-processing.md @@ -333,8 +333,6 @@ These statistics are particularly useful for monitoring the performance of echo 5. **Monitor statistics**: Use the statistics to monitor the performance of echo cancellation and adjust settings if needed. ---- - ## Conclusion The `AudioProcessing` class provides powerful audio processing capabilities for real-time communications. By properly configuring and using these features, you can significantly improve the audio quality in your applications. diff --git a/docs/guide/audio/custom_audio_source.md b/docs/guide/audio/custom-audio-source.md similarity index 99% rename from docs/guide/audio/custom_audio_source.md rename to docs/guide/audio/custom-audio-source.md index 3af276f5..33d90a67 100644 --- a/docs/guide/audio/custom_audio_source.md +++ b/docs/guide/audio/custom-audio-source.md @@ -183,8 +183,6 @@ audioSource.dispose(); audioStreamer.stop(); ``` ---- - ## Conclusion The `CustomAudioSource` provides a flexible way to integrate external audio sources with WebRTC. By understanding the audio format parameters and properly managing the audio data flow, you can create applications that use custom audio from virtually any source. diff --git a/docs/guide/audio/dtmf_sender.md b/docs/guide/audio/dtmf-sender.md similarity index 99% rename from docs/guide/audio/dtmf_sender.md rename to docs/guide/audio/dtmf-sender.md index c08426b8..0b2b7bf8 100644 --- a/docs/guide/audio/dtmf_sender.md +++ b/docs/guide/audio/dtmf-sender.md @@ -155,8 +155,6 @@ Note that you don't need to explicitly dispose of the DTMF sender, as it will be 5. **Tone Buffering**: Be aware that tones are queued and played sequentially. If you need to cancel queued tones, you can call `insertDtmf("")` to clear the queue. ---- - ## Conclusion The RTCDtmfSender provides a standard way to send DTMF tones over WebRTC audio connections. This functionality is particularly useful for applications that need to interact with traditional telephony systems, IVR systems, or any service that uses DTMF for signaling. diff --git a/docs/guide/audio/headless_audio_device_module.md b/docs/guide/audio/headless-audio.md similarity index 93% rename from docs/guide/audio/headless_audio_device_module.md rename to docs/guide/audio/headless-audio.md index 3c75ee7d..35a5132e 100644 --- a/docs/guide/audio/headless_audio_device_module.md +++ b/docs/guide/audio/headless-audio.md @@ -1,4 +1,4 @@ -# Headless Audio Device Module +# Headless Audio The `HeadlessAudioDeviceModule` is a convenience implementation of the `AudioDeviceModule` that uses WebRTC's dummy audio layer. It avoids touching real OS audio devices while still enabling the WebRTC audio pipeline to pull and render audio frames (headless playout) and to simulate capture (recording path). @@ -18,7 +18,7 @@ This is ideal for: ## Playout path -Create the module and pass it to the PeerConnectionFactory. This ensures your peer connection stack uses a headless (dummy) audio backend. +Create the module and pass it to the `PeerConnectionFactory`. This ensures your peer connection stack uses a headless (dummy) audio backend. ```java import dev.onvoid.webrtc.PeerConnectionFactory; @@ -49,11 +49,11 @@ finally { } ``` -Notes: +::: info - Calling startPlayout without a prior initPlayout will throw an error. Always call initPlayout first. - If you only need the audio pipeline to be ready when remote audio arrives, you may delay playout initialization until after creating your RTCPeerConnection. +::: ---- ## Recording path (capture) @@ -85,13 +85,12 @@ adm.dispose(); factory.dispose(); ``` -Details: +::: info - Initialization order matters: call `initRecording()` before `startRecording()`. - The module exposes one virtual recording device; selection calls succeed with index 0. - Stereo can be enabled/disabled via the standard ADM methods; by default 1 channel is used. - If no AudioTransport is registered (no source), silence is injected to keep timings consistent. - ---- +::: ## When to use HeadlessAudioDeviceModule vs. dummy audio layer on AudioDeviceModule @@ -99,10 +98,8 @@ Details: - Using a standard `AudioDeviceModule` with `AudioLayer.kDummyAudio` disables actual audio I/O; the audio pipeline is not started for playout and sinks will typically not receive audio frame callbacks. Use this only when you intentionally do not want any audio delivery (e.g., video‑only or fully custom audio). Related guides: -- [Audio Device Selection](guide/audio/audio_devices.md) -- [Custom Audio Source](guide/audio/custom_audio_source.md) - ---- +- [Audio Device Selection](/guide/audio/audio-devices) +- [Custom Audio Source](/guide/audio/custom-audio-source) ## Limitations and notes - No real audio is played or captured; playout frames are pulled from the render pipeline and discarded, and capture frames are pulled from your source (or zeroed) and delivered into WebRTC. diff --git a/docs/build.md b/docs/guide/build.md similarity index 58% rename from docs/build.md rename to docs/guide/build.md index 3d23e1d6..ed6f3cf0 100644 --- a/docs/build.md +++ b/docs/guide/build.md @@ -4,20 +4,11 @@ To build the native code, be sure to install the prerequisite software (follow t **Note**: You don't have to install the Depot Tools, the build script will do that for you. - - - - - - - - - - - - - -
    LinuxUbuntu, other distros
    macOSXcode 9 or higher
    WindowsVisual Studio
    +| OS | Build Instructions | +| --------- | ----------------------------------------------------------------- | +| Linux | [Ubuntu][build-linux-ubuntu], [other distros][build-linux-other] | +| macOS | Xcode 9 or higher | +| Windows | [Visual Studio][build-windows] | Assuming you have all the prerequisites installed for your OS, run: @@ -34,3 +25,7 @@ On the first run, the WebRTC source tree will be loaded into the `//w | webrtc.branch | The WebRTC branch to checkout. | branch-heads/7204 | | webrtc.src.dir | The absolute checkout path for the WebRTC source tree. | /\/webrtc | | webrtc.install.dir | The install path for the compiled WebRTC library. Is also used to link against a pre-compiled WebRTC library to reduce build time. | /\/webrtc/build | + +[build-linux-ubuntu]: https://chromium.googlesource.com/chromium/src/+/master/docs/linux/build_instructions.md#system-requirements +[build-linux-other]: https://chromium.googlesource.com/chromium/src/+/master/docs/linux/build_instructions.md#Notes-for-other-distros +[build-windows]: https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#visual-studio \ No newline at end of file diff --git a/docs/guide/data/data_channels.md b/docs/guide/data/data-channels.md similarity index 99% rename from docs/guide/data/data_channels.md rename to docs/guide/data/data-channels.md index a4a18b26..e11e8ebe 100644 --- a/docs/guide/data/data_channels.md +++ b/docs/guide/data/data-channels.md @@ -273,7 +273,7 @@ dataChannel.dispose(); ## Best Practices -1. **Error Handling**: Always wrap `send` calls in try-catch blocks as they can throw exceptions if the buffer is full or the channel is not in the OPEN state. +1. **Error Handling**: Always wrap `send` calls in try-catch blocks as they can throw exceptions if the buffer is full or the channel is not in the `OPEN` state. 2. **Buffer Management**: Monitor the buffered amount to avoid overwhelming the channel. If `getBufferedAmount()` returns a large value, consider pausing sending until it decreases. @@ -294,8 +294,6 @@ dataChannel.dispose(); - `false` for UTF-8 text data - `true` for binary data ---- - ## Conclusion WebRTC data channels provide a powerful way to establish peer-to-peer communication for transferring arbitrary data between clients. diff --git a/docs/examples.md b/docs/guide/examples.md similarity index 89% rename from docs/examples.md rename to docs/guide/examples.md index a4e5f86d..86ee625c 100644 --- a/docs/examples.md +++ b/docs/guide/examples.md @@ -7,7 +7,7 @@ This section provides an overview of the example applications included in the `w The [`PeerConnectionExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/PeerConnectionExample.java) demonstrates how to set up a peer connection with audio and video tracks to be able to send and receive media. **Key features demonstrated:** -- Creating a PeerConnectionFactory +- Creating a `PeerConnectionFactory` - Creating audio and video tracks - Setting up a peer connection - Adding tracks to the peer connection for sending media @@ -32,7 +32,7 @@ This example is useful for applications that need to receive media streams from The [`CodecListExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/CodecListExample.java) demonstrates how to list all supported codecs with the WebRTC peer-connection-factory. **Key features demonstrated:** -- Creating a PeerConnectionFactory +- Creating a `PeerConnectionFactory` - Getting the supported codecs for both sending and receiving audio and video - Displaying detailed information about each codec @@ -43,10 +43,10 @@ This example is useful for understanding what codecs are available on the curren The [`DesktopVideoExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/DesktopVideoExample.java) demonstrates how to set up a peer connection with a desktop video source for screen or window capture. **Key features demonstrated:** -- Creating a PeerConnectionFactory +- Creating a `PeerConnectionFactory` - Getting available desktop sources (screens and windows) -- Creating a VideoDesktopSource for capturing screen or window content -- Configuring the VideoDesktopSource properties +- Creating a `VideoDesktopSource` for capturing screen or window content +- Configuring the `VideoDesktopSource` properties - Creating a video track with the desktop source - Setting up a peer connection @@ -66,13 +66,15 @@ The [`WebClientExample`](https://github.com/devopvoid/webrtc-java/blob/master/we This example is valuable for applications that need to establish WebRTC connections between Java clients and web browsers, enabling cross-platform real-time communication with audio and video. -**Note:** Before running the WebClientExample, you need to start the WebServer first. The WebServer provides the signaling service required for WebRTC connection establishment: +::: info +Before running the `WebClientExample`, you need to start the `WebServer` first. The `WebServer` provides the signaling service required for WebRTC connection establishment: +::: ```bash mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.web.server.WebServer" ``` -And the browser client is reachable at this url: https://localhost:8443 +And the browser client is reachable at this url: `https://localhost:8443` ## Running the Examples diff --git a/docs/quickstart.md b/docs/guide/get-started.md similarity index 80% rename from docs/quickstart.md rename to docs/guide/get-started.md index 6c5ed3f6..2fb828a7 100644 --- a/docs/quickstart.md +++ b/docs/guide/get-started.md @@ -1,36 +1,16 @@ -# Quickstart Guide +# Get Started This guide will help you get started with webrtc-java quickly. We'll cover installation, basic setup, and simple examples to demonstrate core functionality. ## Supported Platforms Maven Central artifacts contain native libraries that can be loaded on the following platforms: - - - - - - - - - - - - - - - - - - - - - - - - - -
    x64armarm64
    Linux✔ armeabi-v7a✔ arm64-v8a
    macOS-
    Windows--
    +| | x64 | arm | arm64 | +|-----------|---------|---------|---------| +| Linux | ✔ | ✔ | ✔ | +| macOS | ✔ | - | ✔ | +| Windows | ✔ | - | - | + ## Installation @@ -42,19 +22,22 @@ Add the following dependency to your `pom.xml`: dev.onvoid.webrtc webrtc-java - {{VERSION}} + {{ VERSION }} ``` -Note about native library classifiers: -- The webrtc-java artifact will automatically add the dependency for the native library artifact matching the currently running operating system and architecture at runtime. -- If you encounter issues loading the native library "webrtc-java", you can declare the platform-specific classifier explicitly. For example, for Windows 64-Bit: +::: info +**Native library classifiers** + +The webrtc-java artifact will automatically add the dependency for the native library artifact matching the currently running operating system and architecture at runtime. + +If you encounter issues loading the native library "webrtc-java", you can declare the platform-specific classifier explicitly. For example, for Windows 64-Bit: ```xml dev.onvoid.webrtc webrtc-java - {{VERSION}} + {{ VERSION }} windows-x86_64 ``` @@ -66,6 +49,7 @@ Complete list of available classifiers: - linux-x86_64 - linux-aarch64 - linux-aarch32 +::: #### Using SNAPSHOT Versions @@ -75,7 +59,7 @@ If you want to use the latest development version, you can use a SNAPSHOT releas dev.onvoid.webrtc webrtc-java - {{VERSION_SNAPSHOT}} + {{ VERSION_SNAPSHOT }} ``` @@ -101,19 +85,19 @@ To use SNAPSHOT versions, you need to add the following repository configuration Add the following to your `build.gradle`: -```gradle -implementation "dev.onvoid.webrtc:webrtc-java:{{VERSION}}" +```groovy +implementation "dev.onvoid.webrtc:webrtc-java:{{ VERSION }}" ``` For specific platforms, add the appropriate classifier: -```gradle -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "windows-x86_64" -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "macos-x86_64" -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "macos-aarch64" -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "linux-x86_64" -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "linux-aarch64" -implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{VERSION}}", classifier: "linux-aarch32" +```groovy +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "windows-x86_64" +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "macos-x86_64" +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "macos-aarch64" +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "linux-x86_64" +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "linux-aarch64" +implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "{{ VERSION }}", classifier: "linux-aarch32" ``` ## Basic Setup @@ -267,7 +251,9 @@ AudioTrackSource audioSource = factory.createAudioSource(audioOptions); AudioTrack audioTrack = factory.createAudioTrack("audio0", audioSource); ``` -> Audio devices can be specified via the `AudioDeviceModule`, see the guide on [Audio Devices](guide/audio/audio_devices) for more details. +::: info +Audio devices can be specified via the `AudioDeviceModule`, see the guide on [Audio Devices][audio-devices] for more details. +::: ### Adding Tracks to Peer Connection @@ -305,9 +291,16 @@ factory.dispose(); Now that you have a basic understanding of webrtc-java, you can explore more advanced features: -- [Data Channels](guide/data/data_channels) - Learn more about data channels -- [RTC Stats](guide/monitoring/rtc_stats) - Monitor connection quality -- [Desktop Capture](guide/video/desktop_capture) - Share screens and windows -- [All Guides](guide/overview) - Complete list of guides +- [Data Channels][data-channels] - Learn more about data channels +- [RTC Stats][rtc-stats] - Monitor connection quality +- [Desktop Capture][desktop-capture] - Share screens and windows +- [All Guides][overview] - Complete list of guides + +For a complete API reference, check the [JavaDoc][javadoc]. -For a complete API reference, check the [JavaDoc](https://javadoc.io/doc/dev.onvoid.webrtc/webrtc-java/latest/index.html). \ No newline at end of file +[audio-devices]: /guide/audio/audio-devices +[data-channels]: /guide/data/data-channels +[rtc-stats]: /guide/monitoring/rtc-stats +[desktop-capture]: /guide/video/desktop-capture +[overview]: /guide/ +[javadoc]: https://javadoc.io/doc/dev.onvoid.webrtc/webrtc-java/latest/index.html \ No newline at end of file diff --git a/docs/guide/index.md b/docs/guide/index.md new file mode 100644 index 00000000..6e1b913e --- /dev/null +++ b/docs/guide/index.md @@ -0,0 +1,40 @@ +# Guide Overview + +This section provides detailed guides for various features of the webrtc-java library. + +## Media Basics + +- [Media Devices](/guide/media/media-devices) - Working with audio and video devices +- [Bitrate and Framerate Constraints](/guide/media/constraints) - Controlling media quality +- [Send-only and Receive-only](/guide/media/directionality) - Configure transceiver directions (send-only, receive-only or inactive) + +## Audio + +- [Audio Device Selection](/guide/audio/audio-devices) - Selecting and configuring audio devices +- [Audio Processing](/guide/audio/audio-processing) - Voice processing components +- [Custom Audio Source](/guide/audio/custom-audio-source) - Using custom audio sources with WebRTC +- [Headless Audio](/guide/audio/headless-audio) - Playout pull without touching real OS audio devices +- [DTMF Sender](/guide/audio/dtmf-sender) - Sending DTMF tones in a call + +## Video + +- [Camera Capture](/guide/video/camera-capture) - Capturing video from cameras +- [Desktop Capture](/guide/video/desktop-capture) - Capturing and sharing screens and windows +- [Custom Video Source](/guide/video/custom-video-source) - Using custom video sources with WebRTC + +## Data Communication + +- [Data Channels](/guide/data/data-channels) - Sending and receiving arbitrary data between peers + +## Networking and ICE + +- [Port Allocator Config](/guide/networking/port-allocator-config) - Restrict ICE port ranges and control candidate gathering behavior + +## Monitoring and Debugging + +- [RTC Stats](/guide/monitoring/rtc-stats) - Monitoring connection quality and performance +- [Logging](/guide/monitoring/logging) - Configuring and using the logging system + +## Additional Resources + +For a complete API reference, check the [JavaDoc](https://javadoc.io/doc/dev.onvoid.webrtc/webrtc-java/latest/index.html). \ No newline at end of file diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md new file mode 100644 index 00000000..1065968a --- /dev/null +++ b/docs/guide/introduction.md @@ -0,0 +1,24 @@ +# Introduction + +webrtc-java is a Java wrapper for the [WebRTC Native API](https://webrtc.github.io/webrtc-org/native-code/native-apis), providing similar functionality to the [W3C JavaScript API](https://w3c.github.io/webrtc-pc). It allows Java developers to build real-time communication applications for desktop platforms without having to work directly with native code. + +The library provides a comprehensive set of Java classes that map to the WebRTC C++ API, making it possible to establish peer-to-peer connections, transmit audio and video, share screens, and exchange arbitrary data between applications. + +## Overview + +For more detailed information, check out the documentation: + +- [Get Started](/guide/get-started) - Get up and running quickly with webrtc-java +- [Guides](/guide/) - Comprehensive documentation on using the library +- [Examples](/guide/examples) - Sample code demonstrating various features +- [Build Notes](/guide/build) - Instructions for building the library from source + +## License + +Copyright (c) 2019 Alex Andres + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/docs/guide/media/constraints.md b/docs/guide/media/constraints.md index 8437c5de..fb3b32e8 100644 --- a/docs/guide/media/constraints.md +++ b/docs/guide/media/constraints.md @@ -1,4 +1,4 @@ -# Bitrate and Framerate Constraints +# Media Constraints This guide explains how to set bitrate and framerate constraints for MediaStreamTrack of RTCRtpSender. It covers: @@ -109,9 +109,9 @@ if (parameters.encodings != null && !parameters.encodings.isEmpty()) { The `scaleResolutionDownBy` parameter specifies how much to scale down the video in each dimension. For example, a value of 2.0 means the video will be scaled down by a factor of 2 in both width and height, resulting in a video that is 1/4 the size of the original. -> Note that these constraints are applied without requiring SDP renegotiation, making them suitable for dynamic adaptation to changing network conditions. - ---- +::: info +Note that these constraints are applied without requiring SDP renegotiation, making them suitable for dynamic adaptation to changing network conditions. +::: ## Conclusion diff --git a/docs/guide/media/send_receive_direction.md b/docs/guide/media/directionality.md similarity index 78% rename from docs/guide/media/send_receive_direction.md rename to docs/guide/media/directionality.md index 6a61b774..e6e73c26 100644 --- a/docs/guide/media/send_receive_direction.md +++ b/docs/guide/media/directionality.md @@ -1,4 +1,4 @@ -# Send-only and Receive-only Media +# Media Directionality This guide explains how to configure WebRTC media to be receive-only or send-only using the webrtc-java API. It also shows the equivalent configuration on the JavaScript (browser) side. You will learn how to: @@ -10,9 +10,9 @@ This guide explains how to configure WebRTC media to be receive-only or send-onl The examples build upon the RTCRtpTransceiver API, which is the recommended way to control directionality in modern WebRTC. Related API: -- dev.onvoid.webrtc.RTCRtpTransceiver -- dev.onvoid.webrtc.RTCRtpTransceiverInit -- dev.onvoid.webrtc.RTCRtpTransceiverDirection +- `dev.onvoid.webrtc.RTCRtpTransceiver` +- `dev.onvoid.webrtc.RTCRtpTransceiverInit` +- `dev.onvoid.webrtc.RTCRtpTransceiverDirection` References in the repository: - Tests: [RTCPeerConnectionTests.java](https://github.com/devopvoid/webrtc-java/blob/main/webrtc/src/test/java/dev/onvoid/webrtc/RTCPeerConnectionTests.java) @@ -20,16 +20,16 @@ References in the repository: ## Concepts overview -- SEND_RECV: Both sending and receiving are active (default when you add a track). -- SEND_ONLY: Only sending is negotiated; you won’t receive media on this transceiver. -- RECV_ONLY: Only receiving is negotiated; you won’t send media on this transceiver. -- INACTIVE: Neither sending nor receiving on this transceiver. +- `SEND_RECV`: Both sending and receiving are active (default when you add a track). +- `SEND_ONLY`: Only sending is negotiated; you won’t receive media on this transceiver. +- `RECV_ONLY`: Only receiving is negotiated; you won’t send media on this transceiver. +- `INACTIVE`: Neither sending nor receiving on this transceiver. These map to the SDP attributes a=sendrecv, a=sendonly, a=recvonly, a=inactive. ## Receive-only example -Use a transceiver with direction RECV_ONLY to indicate that you only want to receive media for a given kind (audio or video). You can optionally pass a dummy local track or omit sending entirely by not attaching a sending track. +Use a transceiver with direction `RECV_ONLY` to indicate that you only want to receive media for a given kind (audio or video). You can optionally pass a dummy local track or omit sending entirely by not attaching a sending track. ```java import dev.onvoid.webrtc.*; @@ -60,9 +60,10 @@ if (track instanceof dev.onvoid.webrtc.media.video.VideoTrack vTrack) { } ``` -Notes: +::: info - This pattern is used in the WhepExample included in the repository. - When you create the offer, the SDP will contain a=recvonly for that m= section. +::: ## Send-only example @@ -118,20 +119,22 @@ pc.createOffer(opts, new CreateSessionDescriptionObserver() { }); ``` -> Tip: You can also control sending without renegotiation by replacing the sender’s track or disabling it via MediaStreamTrack.setEnabled(false). However, the negotiated direction in SDP remains the same until you renegotiate. +::: tip +You can also control sending without renegotiation by replacing the sender’s track or disabling it via `MediaStreamTrack.setEnabled(false)`. However, the negotiated direction in SDP remains the same until you renegotiate. +::: ## Common patterns and tips -- If you only need to receive a stream from a server (e.g., WHEP), use RECV_ONLY and avoid capturing local devices. This simplifies permissions and reduces CPU usage. -- To temporarily stop sending without renegotiation, you can disable the sender’s track: sender.getTrack().setEnabled(false). -- Use INACTIVE when neither sending nor receiving should occur on a transceiver, but you want to keep it for future use. +- If you only need to receive a stream from a server (e.g., WHEP), use `RECV_ONLY` and avoid capturing local devices. This simplifies permissions and reduces CPU usage. +- To temporarily stop sending without renegotiation, you can disable the sender’s track: `sender.getTrack().setEnabled(false)`. +- Use `INACTIVE` when neither sending nor receiving should occur on a transceiver, but you want to keep it for future use. - Direction changes typically require a new offer/answer exchange. ## Troubleshooting -- No remote media arriving in RECV_ONLY mode: +- No remote media arriving in `RECV_ONLY` mode: - Ensure the remote endpoint actually sends media on that m= section. - - Verify codecs overlap (see CodecListExample in examples). + - Verify codecs overlap (see `CodecListExample` in examples). - Check network/firewall and ICE connectivity. - Permissions prompts appear even in receive-only mode: - Avoid creating real capture devices if you don’t need to send. You can add a transceiver with a dummy track. diff --git a/docs/guide/media/media_devices.md b/docs/guide/media/media-devices.md similarity index 98% rename from docs/guide/media/media_devices.md rename to docs/guide/media/media-devices.md index 834e1326..ae3613d2 100644 --- a/docs/guide/media/media_devices.md +++ b/docs/guide/media/media-devices.md @@ -12,7 +12,9 @@ Cameras and microphones play a key role in WebRTC. In a more complex application The `MediaDevices` class provides methods to query all available media devices connected to the system. -> Query devices on worker threads, not on UI or render threads. +::: info +Query devices on worker threads, not on UI or render threads. +::: ### Audio Capture Devices (Microphones) @@ -165,8 +167,6 @@ MediaDevices.addDeviceChangeListener(listener); MediaDevices.removeDeviceChangeListener(listener); ``` ---- - ## Conclusion This guide has provided a comprehensive overview of working with media devices in the WebRTC library. diff --git a/docs/guide/monitoring/logging.md b/docs/guide/monitoring/logging.md index de0eac3a..24852d25 100644 --- a/docs/guide/monitoring/logging.md +++ b/docs/guide/monitoring/logging.md @@ -131,7 +131,5 @@ Logging.addLogSink(Logging.Severity.VERBOSE, new Slf4jLogSink()); This approach allows you to integrate WebRTC's native logging with your application's existing logging infrastructure. ---- - ## Conclusion By using the provided methods and custom log sinks, you can effectively capture, process, and manage log messages to aid in debugging and monitoring your application. Whether you choose to use the native logging capabilities or integrate with existing logging frameworks, the WebRTC logging system is designed to be adaptable to your needs. \ No newline at end of file diff --git a/docs/guide/monitoring/rtc_stats.md b/docs/guide/monitoring/rtc-stats.md similarity index 99% rename from docs/guide/monitoring/rtc_stats.md rename to docs/guide/monitoring/rtc-stats.md index 46e97b9d..9a687da1 100644 --- a/docs/guide/monitoring/rtc_stats.md +++ b/docs/guide/monitoring/rtc-stats.md @@ -390,8 +390,6 @@ Different stat types have different attributes. Here are some common attributes Remember that the available attributes may vary depending on the state of the connection. ---- - ## Conclusion WebRTC statistics provide essential insights into the performance and health of your real-time communications. By leveraging the RTC Stats API in webrtc-java, you can monitor connection quality, diagnose issues, and optimize your application's performance. The ability to collect detailed metrics on packets, jitter, latency, and more allows you to make data-driven decisions. \ No newline at end of file diff --git a/docs/guide/networking/port_allocator_config.md b/docs/guide/networking/port-allocator-config.md similarity index 100% rename from docs/guide/networking/port_allocator_config.md rename to docs/guide/networking/port-allocator-config.md diff --git a/docs/guide/overview.md b/docs/guide/overview.md deleted file mode 100644 index 49146d8b..00000000 --- a/docs/guide/overview.md +++ /dev/null @@ -1,52 +0,0 @@ -# Guides - -This section provides detailed guides for various features of the webrtc-java library. - -## Media Basics - -- [Media Devices](guide/media/media_devices.md) - Working with audio and video devices -- [Bitrate and Framerate Constraints](guide/media/constraints.md) - Controlling media quality -- [Send-only and Receive-only](guide/media/send_receive_direction.md) - Configure transceiver directions (send-only, receive-only or inactive) - -## Audio - -- [Audio Device Selection](guide/audio/audio_devices.md) - Selecting and configuring audio devices -- [Audio Processing](guide/audio/audio_processing.md) - Voice processing components -- [Custom Audio Source](guide/audio/custom_audio_source.md) - Using custom audio sources with WebRTC -- [Headless Audio](guide/audio/headless_audio_device_module.md) - Playout pull without touching real OS audio devices -- [DTMF Sender](guide/audio/dtmf_sender.md) - Sending DTMF tones in a call - -## Video - -- [Camera Capture](guide/video/camera_capture.md) - Capturing video from cameras -- [Desktop Capture](guide/video/desktop_capture.md) - Capturing and sharing screens and windows -- [Custom Video Source](guide/video/custom_video_source.md) - Using custom video sources with WebRTC - -## Data Communication - -- [Data Channels](guide/data/data_channels.md) - Sending and receiving arbitrary data between peers - -## Networking and ICE - -- [Port Allocator Config](guide/networking/port_allocator_config.md) - Restrict ICE port ranges and control candidate gathering behavior - -## Monitoring and Debugging - -- [RTC Stats](guide/monitoring/rtc_stats.md) - Monitoring connection quality and performance -- [Logging](guide/monitoring/logging.md) - Configuring and using the logging system - -## Utilities - -- [Audio Converter](guide/utilities/audio_converter.md) - Resample and remix 10 ms PCM frames between different rates and channel layouts -- [Audio Recorder](guide/utilities/audio_recorder.md) - Capture microphone input and receive 10 ms PCM frames via an AudioSink -- [Audio Player](guide/utilities/audio_player.md) - Play PCM audio to an output device by supplying frames via an AudioSource -- [Video Buffer Converter](guide/utilities/video_buffer_converter.md) - Convert between I420 and common FourCC pixel formats (e.g., RGBA, NV12) -- [Video Capture](guide/utilities/video_capturer.md) - Control a camera device, configure capabilities, and deliver frames to a sink -- [Screen Capture](guide/utilities/screen_capturer.md) - Enumerate and capture full desktop screens/monitors -- [Window Capture](guide/utilities/window_capturer.md) - Enumerate and capture individual application windows -- [Voice Activity Detector](guide/utilities/voice_activity_detector.md) - Detect speech activity in PCM audio streams -- [Power Management](guide/utilities/power_management.md) - Prevent the display from sleeping during desktop capture or presentations - -## Additional Resources - -For a complete API reference, check the [JavaDoc](https://javadoc.io/doc/dev.onvoid.webrtc/webrtc-java/latest/index.html). \ No newline at end of file diff --git a/docs/guide/video/camera_capture.md b/docs/guide/video/camera-capture.md similarity index 98% rename from docs/guide/video/camera_capture.md rename to docs/guide/video/camera-capture.md index 8b5b22e3..d3ea7726 100644 --- a/docs/guide/video/camera_capture.md +++ b/docs/guide/video/camera-capture.md @@ -1,4 +1,4 @@ -# Camera Video Track +# Camera Video This guide focuses on setting up a peer connection with camera video capture capabilities, which allows you to capture and stream video from connected cameras in your WebRTC connection. @@ -7,7 +7,7 @@ This guide focuses on setting up a peer connection with camera video capture cap To enable camera video capture, you need to: 1. List available video devices (cameras) -2. Create and configure a VideoDeviceSource +2. Create and configure a `VideoDeviceSource` 3. Create a video track with the camera source 4. Add the track to your peer connection @@ -210,7 +210,7 @@ When processing video frames, consider these important points: For converting I420 frames to UI-friendly pixel formats (e.g., RGBA) and other pixel format conversions, use the `VideoBufferConverter` utility. -- See: [Video Buffer Converter](guide/utilities/video_buffer_converter.md) +- See: [Video Buffer Converter](/tools/video/video-buffer-converter) ### Scaling Video Frames @@ -292,8 +292,6 @@ When implementing camera capture in your application, consider these best practi 6. **Handle device changes**: Implement device change listeners to handle cameras being connected or disconnected. 7. **Consider privacy concerns**: In some applications, you may need to request permission to use the camera. ---- - ## Conclusion This guide has demonstrated how to set up a WebRTC peer connection with camera video capture capabilities and how to receive and process both local and remote video frames. diff --git a/docs/guide/video/custom_video_source.md b/docs/guide/video/custom-video-source.md similarity index 100% rename from docs/guide/video/custom_video_source.md rename to docs/guide/video/custom-video-source.md diff --git a/docs/guide/video/desktop_capture.md b/docs/guide/video/desktop-capture.md similarity index 98% rename from docs/guide/video/desktop_capture.md rename to docs/guide/video/desktop-capture.md index 9e04d42c..6f4e86bb 100644 --- a/docs/guide/video/desktop_capture.md +++ b/docs/guide/video/desktop-capture.md @@ -1,4 +1,4 @@ -# Desktop Video Track +# Desktop Video This guide focuses on setting up a peer connection with desktop video capture capabilities, which allows you to capture and stream content from your screens or application windows in your WebRTC connection. @@ -7,7 +7,7 @@ This guide focuses on setting up a peer connection with desktop video capture ca To enable desktop video capture, you need to: 1. List available desktop sources (screens and windows) -2. Create and configure a VideoDesktopSource +2. Create and configure a `VideoDesktopSource` 3. Create a video track with the desktop source 4. Add the track to your peer connection @@ -157,8 +157,6 @@ screenCapturer.dispose(); windowCapturer.dispose(); ``` ---- - ## Conclusion This guide has demonstrated how to set up a WebRTC peer connection with desktop video capture capabilities. diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index fada26c5..00000000 --- a/docs/index.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - webrtc-java - Java native interface for WebRTC - - - - - - - - -
    - - - - - - - - - - - diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..443f50ed --- /dev/null +++ b/docs/index.md @@ -0,0 +1,36 @@ +--- +layout: home + +hero: + name: "webrtc-java" + text: "Java native interface for WebRTC" + tagline: Build real‑time audio, video, and data with a clean Java API. + actions: + - theme: brand + text: Get Started + link: /guide/get-started + - theme: alt + text: What is webrtc-java? + link: /guide/introduction + - theme: alt + text: Sponsor this project + link: https://buymeacoffee.com/devopvoid + image: + src: /logo.png + alt: webrtc-java + +features: + - title: Cross-platform + details: Use the same Java API across Windows, macOS, and Linux with prebuilt native bindings. Ideal for desktop apps and server‑side media services. + - title: Native performance + details: Thin JNI layer with minimal overhead, delivering near-native performance with minimal context switching between Java and native code. + - title: Audio and video streaming + details: Audio and video capture from cameras and microphones devices, with support for custom media sources for flexible streaming solutions. + - title: Screen Sharing + details: Share application windows or the full desktop with minimal setup; integrate screen capture streams like any other media stream. + - title: Data Channels + details: Reliable and unordered/ordered SCTP data channels for arbitrary messaging, file transfer, and app signaling. + - title: Statistics API for monitoring + details: Access WebRTC stats (bitrate, packet loss, jitter, RTT, frame rate, CPU) to monitor and optimize media quality. +--- + diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..d0c34231 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,2261 @@ +{ + "name": "webrtc-java", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webrtc-java", + "devDependencies": { + "@types/node": "^24.3.1", + "vitepress": "^2.0.0-alpha.12" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "4.0.0-beta.8", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.0.0-beta.8.tgz", + "integrity": "sha512-/ZlyvZCjIJM4aaOYoJpVNHPJckX7J5KIbt6IWjnZXvo0QAUI1aH976vKEJUC9olgUbE3LWafB8yuX4qoqahIQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "4.0.0-beta.8", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-4.0.0-beta.8.tgz", + "integrity": "sha512-elgqPYpykRQr5MlfqoO8U2uC3BcPgjUQhzmHt/H4lSzP7khJ9Jpv/cCB4tiZreXb6GkdRgWr5csiItNq6jjnhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.50", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.50.tgz", + "integrity": "sha512-Z2ggRwKYEBB9eYAEi4NqEgIzyLhu0Buh4+KGzMPD6+xG7mk52wZJwLT/glDPtfslV503VtJbqzWqBUGkCMKOFA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz", + "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz", + "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz", + "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz", + "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz", + "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz", + "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz", + "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz", + "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz", + "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz", + "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz", + "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz", + "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz", + "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz", + "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz", + "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz", + "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz", + "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz", + "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz", + "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz", + "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz", + "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.12.2.tgz", + "integrity": "sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.12.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.12.2.tgz", + "integrity": "sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.12.2", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.12.2.tgz", + "integrity": "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.12.2", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.12.2.tgz", + "integrity": "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.12.2" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.12.2.tgz", + "integrity": "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.12.2" + } + }, + "node_modules/@shikijs/transformers": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.12.2.tgz", + "integrity": "sha512-+z1aMq4N5RoNGY8i7qnTYmG2MBYzFmwkm/yOd6cjEI7OVzcldVvzQCfxU1YbIVgsyB0xHVc2jFe1JhgoXyUoSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.12.2", + "@shikijs/types": "3.12.2" + } + }, + "node_modules/@shikijs/types": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.12.2.tgz", + "integrity": "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.0.1.tgz", + "integrity": "sha512-YBvjfpM7LEp5+b7ZDm4+mFrC+TgGjUmN8ff9lZcbHQ1MKhmftT/urCTZP0y1j26YQWr25l9TPaEbNLbILRiGoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.0.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.1.tgz", + "integrity": "sha512-7kiPhgTKNtNeXltEHnJJjIDlndlJP4P+UJvCw54uVHNDlI6JzwrSiRmW4cxKTug2wDbc/dkGaMnlZghcwV+aWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.1", + "birpc": "^2.5.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.1.tgz", + "integrity": "sha512-PqtWqPPRpMwZ9FjTzyugb5KeV9kmg2C3hjxZHwjl0lijT4QIJDd0z6AWcnbM9w2nayjDymyTt0+sbdTv3pVeNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.9.0.tgz", + "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.9.0", + "@vueuse/shared": "13.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/integrations": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-13.9.0.tgz", + "integrity": "sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "13.9.0", + "@vueuse/shared": "13.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7 || ^8", + "vue": "^3.5.0" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.9.0.tgz", + "integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.9.0.tgz", + "integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/focus-trap": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", + "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz", + "integrity": "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", + "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", + "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz", + "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.0", + "@rollup/rollup-android-arm64": "4.50.0", + "@rollup/rollup-darwin-arm64": "4.50.0", + "@rollup/rollup-darwin-x64": "4.50.0", + "@rollup/rollup-freebsd-arm64": "4.50.0", + "@rollup/rollup-freebsd-x64": "4.50.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", + "@rollup/rollup-linux-arm-musleabihf": "4.50.0", + "@rollup/rollup-linux-arm64-gnu": "4.50.0", + "@rollup/rollup-linux-arm64-musl": "4.50.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", + "@rollup/rollup-linux-ppc64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-musl": "4.50.0", + "@rollup/rollup-linux-s390x-gnu": "4.50.0", + "@rollup/rollup-linux-x64-gnu": "4.50.0", + "@rollup/rollup-linux-x64-musl": "4.50.0", + "@rollup/rollup-openharmony-arm64": "4.50.0", + "@rollup/rollup-win32-arm64-msvc": "4.50.0", + "@rollup/rollup-win32-ia32-msvc": "4.50.0", + "@rollup/rollup-win32-x64-msvc": "4.50.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/shiki": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.12.2.tgz", + "integrity": "sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.12.2", + "@shikijs/engine-javascript": "3.12.2", + "@shikijs/engine-oniguruma": "3.12.2", + "@shikijs/langs": "3.12.2", + "@shikijs/themes": "3.12.2", + "@shikijs/types": "3.12.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "2.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-2.0.0-alpha.12.tgz", + "integrity": "sha512-yZwCwRRepcpN5QeAhwSnEJxS3I6zJcVixqL1dnm6km4cnriLpQyy2sXQDsE5Ti3pxGPbhU51nTMwI+XC1KNnJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "^4.0.0-beta.7", + "@docsearch/js": "^4.0.0-beta.7", + "@iconify-json/simple-icons": "^1.2.47", + "@shikijs/core": "^3.9.2", + "@shikijs/transformers": "^3.9.2", + "@shikijs/types": "^3.9.2", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/devtools-api": "^8.0.0", + "@vue/shared": "^3.5.18", + "@vueuse/core": "^13.6.0", + "@vueuse/integrations": "^13.6.0", + "focus-trap": "^7.6.5", + "mark.js": "8.11.1", + "minisearch": "^7.1.2", + "shiki": "^3.9.2", + "vite": "^7.1.2", + "vue": "^3.5.18" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "oxc-minify": "^0.82.1", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "oxc-minify": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..62ddf810 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,12 @@ +{ + "name": "webrtc-java-docs", + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "preview": "vitepress preview" + }, + "devDependencies": { + "@types/node": "^24.3.1", + "vitepress": "^2.0.0-alpha.12" + } +} diff --git a/docs/privacy_policy.md b/docs/privacy_policy.md deleted file mode 100644 index 8b8ee46f..00000000 --- a/docs/privacy_policy.md +++ /dev/null @@ -1,155 +0,0 @@ -# Privacy Policy - -**Last updated: July 2025** - -## 1. Introduction - -This Privacy Policy explains how jrtc.dev collects, uses, and protects your personal information when you visit our website jrtc.dev. - -We are committed to protecting your privacy and complying with applicable data protection laws, including the General Data Protection Regulation (GDPR). - -## 2. Data Controller - -Alex Andres -Email: [contact@jrtc.dev](mailto:contact@jrtc.dev) -Physical address: Available upon request - -## 3. Information We Collect - -### 3.1 Analytics Data -We use Umami Cloud, a privacy-focused web analytics service, to understand how visitors interact with our website. Umami Cloud collects the following information on our behalf: - -- **Page views**: URLs of pages you visit on our site -- **Referrer information**: The website that directed you to our site -- **Browser information**: Type and version of your web browser -- **Device information**: Type of device (desktop, mobile, tablet) and screen resolution -- **Operating system**: Your device's operating system -- **Geographic location**: Country and region (derived from IP address, but IP addresses are not stored) -- **Session data**: Duration of your visit and pages viewed during your session - -### 3.2 Technical Information -Our website is hosted on GitHub Pages. GitHub's servers may automatically collect: -- IP addresses (in server logs, processed by GitHub) -- Technical request data for hosting purposes - -### 3.3 Information You Provide -If you contact us through forms or email, we may collect: -- Name -- Email address -- Message content -- Any other information you voluntarily provide - -## 4. How We Use Your Information - -We use the collected information for the following purposes: - -- **Website Analytics**: To understand website usage patterns and improve user experience -- **Technical Maintenance**: To ensure website security and proper functioning -- **Communication**: To respond to your inquiries and provide customer support -- **Legal Compliance**: To comply with applicable laws and regulations - -## 5. Legal Basis for Processing (GDPR) - -Under GDPR, we process your personal data based on the following legal grounds: - -- **Legitimate Interest** (Article 6(1)(f)): For analytics and website improvement -- **Consent** (Article 6(1)(a)): When you voluntarily provide information through contact forms -- **Legal Obligation** (Article 6(1)(c)): When required by law - -## 6. Data Sharing and Transfers - -### 6.1 Third-Party Services -We use: -- **GitHub Pages**: For website hosting (GitHub, Inc., United States) -- **Umami Cloud**: For privacy-focused web analytics (Umami Software, Inc.) - -Umami Cloud processes analytics data on our behalf as a data processor. Unlike traditional analytics providers like Google Analytics, Umami is designed with privacy as a core principle. GitHub Pages may process technical hosting data as described in their privacy policy. - -### 6.2 Data Transfers -Our website is hosted on GitHub Pages (provided by GitHub, Inc., located in the United States). Analytics data is collected and processed by Umami Cloud, which may store data in various locations globally as part of their cloud infrastructure. - -Both GitHub and Umami provide appropriate safeguards for international data transfers. You can review their privacy practices at: -- GitHub: https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement -- Umami: https://umami.is/privacy - -### 6.3 Legal Disclosure -We may disclose your information if required by law, court order, or to protect our rights and safety. - -## 7. Data Retention - -- **Analytics Data**: Processed by Umami Cloud and retained according to their data retention policies (typically 6 months) -- **Contact Information**: Retained for as long as necessary to respond to your inquiry -- **Technical Logs**: GitHub Pages logs retained according to GitHub's policies - -## 8. Your Rights Under GDPR - -If you are located in the EEA, you have the following rights: - -**Right of Access (Article 15)** -You can request information about the personal data we hold about you. - -**Right to Rectification (Article 16)** -You can request correction of inaccurate or incomplete personal data. - -**Right to Erasure (Article 17)** -You can request deletion of your personal data under certain circumstances. - -**Right to Restrict Processing (Article 18)** -You can request limitation of processing your personal data. - -**Right to Data Portability (Article 20)** -You can request your data in a structured, machine-readable format. - -**Right to Object (Article 21)** -You can object to processing based on legitimate interests. - -**Right to Withdraw Consent** -Where processing is based on consent, you can withdraw it at any time. - -**Right to Lodge a Complaint** -You can file a complaint with your local data protection authority. - -## 9. Exercising Your Rights - -To exercise any of your rights, please contact us at: -Email: [contact@jrtc.dev](mailto:contact@jrtc.dev) -Physical address: Available upon request - -We will respond to your request within 30 days. - -## 10. Cookies and Tracking - -### 10.1 Umami Cloud Analytics -**Important**: Umami Cloud does not use cookies. Instead, it uses cookieless tracking methods: -- **Tracking Method**: Privacy-focused analytics without persistent identifiers -- **Purpose**: Session and page view tracking for website analytics -- **Data Storage**: Processed by Umami Cloud with no persistent cookies -- **Privacy Benefits**: No cross-site tracking, no persistent user identification, GDPR-compliant by design - -### 10.2 Website Cookies -Our website itself does not set any cookies. All analytics are performed using Umami's cookieless tracking technology. - -### 10.3 Third-Party Cookies -Since our site is hosted on GitHub Pages, GitHub may use cookies for their platform functionality. Please refer to GitHub's privacy policy for details about any cookies they may set. - -## 11. Changes to This Policy - -We may update this Privacy Policy periodically. Changes will be posted on this page with an updated "Last updated" date. Significant changes will be communicated via website notice. - -## 12. Contact Information - -For privacy-related questions or concerns: - -**General Inquiries**: -Email: [contact@jrtc.dev](mailto:contact@jrtc.dev) -Physical address: Available upon request - -## 13. Supervisory Authority - -If you are located in the EEA and have concerns about our data processing, you may contact your local data protection authority: - -- **EU Data Protection Authorities**: https://edpb.europa.eu/about-edpb/about-edpb/members_en - ---- - -This policy is designed to be transparent about our data practices while ensuring compliance with GDPR and other applicable privacy laws. \ No newline at end of file diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d48cf0eb499c9d5181340e2df2377a0b38769c7a GIT binary patch literal 10117 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 literal 0 HcmV?d00001 diff --git a/docs/public/favicon-96x96.png b/docs/public/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..9b043bd22b90a4af8d9e25dbdd91f0f5334a8d98 GIT binary patch literal 5478 zcmV-s6`AUZP)^`L(&yuI>YyHDdw4a%vjLq&_Dv+fMHfB6ljfjore12k z2&u?XG=|jVO*Wfy(b!Qhlu~J8{AVfEia_BPF69fsktgYlQ1WbB z=>+F$-Nx9$eu*OShx3ZABXq;4UQt;^cX5? zi>qtkD6;1kOLH4~+Tt*h@SFPvf+Tj^grDT3SEbe<7e#GhBPtcXBaQr@29M(Nlf;RK z(LIlsi&?8$vYO`d;4q8WQHDsAxec_V4H@rSo#zpndmf?bMs=KPimymqWi{xKSvq5B z(wY)zfkq%z8=OxHaT29!FGD#MH;1IVxXL;*(-fy8oS*I()ScvB8|SG#pPK(%Q4Qe& z8U4rcc*zyjUP)N&DA>JbjOy-cu5O3dQ)Nr1CQ4pgf3L?gE2$zj7gZ6PtJ_L?R(6)` z)-zxB88|==K6)q$P8fk>PZ=eTKjTz+(mAJN)aWxY>fAGN(m7|~gfmZ-M~^&S`m(ct zOyf~I7kc0->ls06rj9#+e)nGa^7zwFk(W-sQcnKuZJ2!5t+?hlH)G;$H)6uA*JJ!G zlWD@QCZq5sh1a6+<`9m*d`YFox65HPF^l7pO1tkK>SuiL=b$(T0V;c zjR61>&50qZ13s$A$3~z9p``|i%;I2h0AW^6jvO}P80Y4xcRAPJbB7o->_|=`m`t#a z;hA}eD^pcf6{MnNQUh+P*&)yrV1g8TC1xi>Wg?kOv-qjK$slYyCod1DU2v|v;?^lN zxZp@ZNfJ*;S3{`UTM66YElLYA|f&F{-LE1`N38jWomy0T8li8S= zu>t&xzJMxjMktW64Rt4b#E{b|PhNP%<@V??7m<(8a%e!6PD2inq#6<>Y}oRFWor&ZA_N-OZnGt=?=+aJVZ z_dJTv|2Yq}HMMc_N;;NQy}v4W1Odu?hNx169z0mwdcM%=$botIAj^`ue9m{v?;(2~ zewgetpr1VU$@Y*@D$0e+ZQ*VN;)j~8Ixhx5_c z(3q&?qU_whQ~K-t)FS!ATxKJO1UVpPw;^WbWXpbo2g;GBk8;i*dy%~2rmN)*cioKZ z8LzwJMojwkbu{VL$rv+fJmq!HBUil=rVhV8E}p*(HFf-9%9jI#(S{EzSFAx*RZW6U zgdlmcWkWGqE`Jz7m^iky8e63M#C1x7J$m(&6K=f9nfBBp^7aSsB%X`Wmz_r^o^}!) zHGCKiJaRDg9n_Ed^gA5A`t+u}E}ht@De@5A@2|(c{aj>UHV}t(^)~kMJAOzq@vr3O zZCY1MvH1(ct||3(b9R7$Q_UU#u%lK$3ft^IWRSdL+HWxOw2>kwH=DCWQlO*~Ku%6J zI(P2|_?qti$UM7KXLRk>#b{;Csx@|Pbsf9mD;#G?*u7479{ImiTI1m!`?bR4-E zs&K7@xt;Pb^q2z5$<@C`YJ)Nm9oxaqAAUqx$#(2>o$VwD#||HiyYIRJx7|7hci%Z4 zQ?9)L`T2Y_v8}k;BM=B+`Ijry_n!dkf>S? zd4c@iJ>?MvLpb`(ua(Q!;(w;UhnHS>3vbT&02SrCLPlk1$ji+}|GvF&Xzv~z9>*CX zqiGf*RFrIYHm%!CP`Q8rH6}$au0e(*f-FpfUJ`JiMTR-{(J>vjn^M z`_bU|u&rzl7Js?QOGV3TY;44`#mljG&pt{e-&ACxHPLzu5seK9vWZpz1f+>FiM+5Z zoN)5-L9?~|qW7w67?^Ml-%{yaT*)$-NKIlMqJQ(Dp;Rs^BPl#K>bb@loiEMhJ4UvoLz}N$8rN z50Tp$efsvnslON=VkZ2RF%*s-dw1`1K6-Di^Wi@}wYc~cABTR;X41-=ch&`{s#EdKoKz`E6&a^h`w>e5AyI{ifQX}@ZaAjHBiMh?Z4 zE6>4=*NnzB6VAlI{=HoS@zTtajC}2=*vU7*`*P{x6+(gtDd$2+U6yNPe$;w!0Ave$ zGY7^^P!3%P*PPOuckR*58FJJ>v3|{_2L7tRXFE0_&PM-1edQ6u1``8Ek$A~Cl4S|> zJ^TR;}Js#)AsV6{#oyQWbuz4R@7!#M+ghYpFBe5 z=H)8G(1DSm6ON@+57t z-UZP%C?h(w*o2OrKqFXLKIC=k)G+(qPpq1nx-6Yd;q%GCM-D_3C^}0zNxavaRMc0~ z>(>_BZ_k*8uNJKk0h@PTLX0XGz9+PF#Y>Lr6MiT!4qVw$^=S$tVa65{XfcF};s;9| zDgOYl!HiHKD?2-|deu7Y+_78zYbPMVo&fI(nm_kTX$OKo#v}%g1p+oa<4=f36Q@Zg zoCi@@$33c@06nDC09KWaa2}FUzR3wRfLN_1P(7S9cKme>KYsPaH-B;*`7Zm;_^PR{ zMQyEmj3tTN5D(mR_goXZ)X>lZ4Zx1#qMeOo-77(Fa1+p4$o9(^r|c@Ncs#dL_AT7> z50D{LR@H)^IcTkz^>$UWDdZDoU@0>*p7X<|2X>WieBegu;6DH=XHs=tgLw2M_acR`6r%F`*;7i_qTlae#?!32^?E@RE4CK-u}Invd-VY zgyzMEG;(Q@1%ulkFEv+M4G8IzEZx-bG(VWfOW;KiZDm9dL7`DKA~mc5(U+|B1lcGo z-B3TZqM|6~7xSl{c;)2RU!9e=Yv&#taZp)V<9z?!27C4Q8yn+T(WT=!*j}+qt%-f1 zeb8KHGY4K!y=Wm3}Z0DZ>j2eC;ngnE#Rm2l`uIM&#Jg{w=~`yVMO z{vChsR1|;<`WHNYy#xC5mM!JO{{Hr->KNgAI zKO6R+rvtucNll z%Mpmswg^Q12^hhHWz;b;)VaNV!+ncNH{A1l+1h)jmaf0=w$iot-d?)y-Umw8GHuiE z=I`5Dw9jZD%s6bs9}l%Gx}S(The^R<*RH+3-9PT*TPwJX{>jP7iPAiX%f>@y7k_#`FFIxknjj(>E zd~(_0ou25gwT>XP2Aor5Nm$o{s2d}U2re=aNLWt6(L)F^AcL#oO=Zr`U3)DMvs)4Q zn1!bguz%=*Z!bNgy{Lz6@7$jRork0a6X@Bihh12BZXiFu8~-8`8*yEoUoKw!ErFPx z)D{c1ri|LH9ER@da1F@G%DxUH@)%)4%ja`WJh7l*;=~K&$WcdU3H9{=nqc?tN@vcT zrE<&G9|&Xt#A&91l!k0dw!!|P2Qx=6-}+QRSAz&9?A7a##>v-S6c{`9?5qI;`}jn- z`uw^nH*c=6-+5000FKNkl zHfQwy`Ez{$F%%%cg+!M60pB_8)Z_d&+;|!F>)$7Lz!$PRv)8f(XG(`zn~Ob-kp86=fea^^CdIcR&x$#x1jAA;-dB4Nqy zJ-P%63I+r^cgf3MuwbQl>+QMnyNneR{~U1TW_uUeoNNec`In?^7Ue4afR5ur%LcwK-?@&Ax zql)t@;?^7!F$R5udC-HIqnB@0Dv^^9Qpv?)W55oc=c*Yxe%ds|+K0wx+B78EjJ}?F zRM~^osVi0lbc)n{bU2|kGPuUJqjO{>q2$SV2 zK?a3uJ6%3FRGg}?YD|VO16wjP+nU2~l4~W@KZWG5(%Iskvgv;khNnFho4WJWm_djGM!ZT%5EZdyaohgbiU@qKL&p{!HAYaPFEE(T?7|6T^+alfpi3c zp?v9--IC}jK6CklnJQXKVH&TbNj`NWg|nX`GRhX`rMoyY9l8B!WnWENYCCirKn^Iw zJ20vbq-+byCxED~T6-W8d3+Ov_045AbI|@!@%7bjf#?xN^*=VjV-vQ+13AV&#TDEu z9+;s+H$a8$^NVNrdGHrN)FQH_)u_%@HKu@klp&;>#u1c)-H=T#VKP||Ou*VER>z=k zUNLch*Td;iThcTs;UJidN+?Z~N*)YqY-9AOvImP}?I72hcP2KJFTUbwY0;&;8&-j+ zkr8QO!V$r#6E~tQQ0-RefuqnfIwG}f!Gwn^DHxYDS*&U+u+3)Y3IX4=WmJGfAS-SMPt< zByd1Bz?FA#kc&qHD!*RPb(xb<1K)i3s6EkRFiS#r$|ld}W8 z0nxn5=Z`NdnO``gv}(ZJHaSy-?TjT!1)-T6Z%$Z-_$w~5yyP&cG({!~ z0p8_kd+xyow0+HEJRWR7OgY+qaj*exU-KA`2OAJmj>JW4Q~nPC0RR6SJad@<000I_ cL_t&o03i-!8!v|GZ~y=R07*qoM6N<$f^=DhzyJUM literal 0 HcmV?d00001 diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bec53e9c165f2bbb82a9a80db68753d79b0d877c GIT binary patch literal 15086 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 USbwl%%)tG}hBtuE0u1N(zruHS2LJ#7 literal 0 HcmV?d00001 diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 00000000..aeb21ad9 --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/public/logo.png b/docs/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e86ad46bf1161b1b796cb88730b3aac448fa2e4 GIT binary patch literal 36847 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 T0SoV7BO;#5Kg)fp|Ka}tB<*eo literal 0 HcmV?d00001 diff --git a/docs/public/site.webmanifest b/docs/public/site.webmanifest new file mode 100644 index 00000000..6b8bf46c --- /dev/null +++ b/docs/public/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "JRTC", + "short_name": "JRTC", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#1e0959", + "background_color": "#1e0959", + "display": "standalone" +} \ No newline at end of file diff --git a/docs/public/web-app-manifest-192x192.png b/docs/public/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..acf34436792fababbcb715698d32bfcfe862c3e5 GIT binary patch literal 8989 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¬u)N9TTIs0!pIu3 zoB(zz+gK9!Y5Dg}BGY5sn=#6=tHGexz{%=nI}~2}x4X z)18bsA}*FJbGH?vY=yQ(;{?zavw~vzJj`F2l%Oj+(T!!-fr;UyLjko>pT18RzI!-! zAaaiNNu?bE`5X-SHA*L^$5uxrW0U0)=vI2j6+zBZ&5}2YFPTRXgCqfp!0@%p;8&R} zyNE{;f&{r8g%$h=za$CZf~kbIWfQIf89nU)r$VrEd~zNb!T?&9BmrF9;yC5(*a?{b z#ga@UQxd{ZypO~J;TB;Z2FDl^z%i7j^!}*i3-1ebK)UkpCMXe_J%}!1624{2c}Y;J05G?I~8bElGedJi@2Ks(lNL?!Dy3 z5mrW7Ckm-fIq{Kzb*hiOj?hH1nRCy;oRb6y4Bd#1@|UK!EeGT;PfPH{K!%eH32>+S zxa&s1rOTmh0;mWyjDlwbY6fG;>5uB^fU#v6V7@9X){Z5TP;#<27_9ESM5cQyyB4Cg zWnF0(7E6|eg%azkiaTowyG(`l%72+z56J%t##q( zui93^{D57*r6XK z2@s`EdCdKjzz0?0q>6Il+Ux#OyiLqC+Qj^X1~w%D#A(E<&%sxp<7yfC+75sV${ zVDQRNzJTSG!fU{KYDY(TYA15pHlmH(19MIiATV@(I`WPymx0>26zc8gfsP#*OK*@W znqRQj4`$xm3VZLZhqp`;U^pHT9??yl5}~`DQ=%m>*1W(c$GgEx0;=bVz_Hlsijd1e zWqYAxi7^3mG`|cwF$t8c67AUrwcWBuy@uM+Yrmjv}brsZxSD6rtVaUD1 z>KA|wuK`<^0c}hc`F#YrV#o?pH*c<`UvD=I`K5g!Nvuo{5G7HLv2U;WyAj5kmw;_6 zpftVtCB)_yPXcg2Fr|&vk1*^u?-{ZTs6NPBr`X#H@VSE8%mmoL>C(Et@ruEuko$?Z z7c)^70n48OmMtX7H)%PiSCP^)g&TY7qV_OpIRygZoYBK~i6;T{#=tjdt{q}ccnXu` zkhf3eUV>8($Y{^e7@R7pzI{Mn7tq_utH~hosv=LO!=_%!hODx_0008INklWm z>~jT4j|TB1fF8ESr-k+juf2ki`l{Ia=n{YfQ>0cT%o79xB2XNf(Ir4}ngvvJtaWpQ ztk`Q~4Hz%>2E-@21n{DC(qjMj%Om(U@p=#*BT(fHqrbr*|bN6iEU&`l=F8sW=@I z#Dlslk^~qv9*sH?RNbUsDF{?gnuHfnkt9IDNkyOKLVgrZqfQhlS^c}!V|{o56;A?` zE4D`AH0ngTG)v@afX(YUqoA#tBe87i?Ge7n@qV@7l528$=I=#tBf`bfvW3 zF$Y$$vc>x`tD&E`=)ur90ZMa+m$4%aUbNiZY^}6!c) zajO}Lybu#068#dU2?CKKkR(8)@FvW4M<7W6-941(7bybaCcs#pOh)Ragn3j5gqr{X z@5G}rF5z1N2sjR!_LX6UTDS>N0eDBEqAOE-N4j>c_6ClTY+vb)sCx^6Hdq39<1PFX zG7+$Mq*H{Om$1!CbN@dEL8)VRVbeP9)ZWtD=zZ30AXKp_8^N_p$) ztyL7R*Mz5uRrz$O6#Q)yAWj4P`O07G@`ZqZ$<=F_e=OqaVVeMPDIjvxvqX$h00000NkvXXu0mjf Dy4nIL literal 0 HcmV?d00001 diff --git a/docs/public/web-app-manifest-512x512.png b/docs/public/web-app-manifest-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..503da02909ccdd347693ba502cc370db5e7c7c46 GIT binary patch literal 23100 zcmYIv1yoeu*Y?a1(k&e$je;QEjMCi#(v5U?3?U(@NT(puAxPKIC@I}t(j^_=MgPC| zTh3at&b{}XeRe!M&OQ+;O48UEzrW zJ;Ymsj*73Kgo+;$CxOcF$whBZF3lx#Ps^~pU9PcemTUBL(Y0afh|*TsRm-B5{-5&g z%*@V^911%NP9uu{{)KB%$iO3zW#E5*Pzz)b27KzBuu<3^e8<#{9tHt^WoRm(y7ta! zPDUg1CMFMhFaZ=ya2Nq6k_SVHl4bwzQ)Z9}v15b$!zc839uWOIb&#vm(*3_l=<%LG zh(O;k_%ay&ejhFr=m)&8OV`%=-wSlW3p2=Ri%Fx5MBE_{1VQNVTo(fnFEHRptgrmL z5BlCwf?g{>Ebs&mt^6P#g!W{v=1mHSasSR!XAb^BOufp}>)i2oCsVLS*u4*0>a00wnJdtE;u z%Rq%7=~$LMu|7Mn?0jq@kVwj5Mjhc&{ZL&kocgXx_7v96M+<{PppcheFsTWbT!&1v zmq~i!LTQjP*uurHx1z;RU=A7=)WEb~&>T@|3LU-o74&8wp};p4BTzp6TM@2=>|Of~ zlI`)_H|c+}qg4bZ9a+wE`Nz#W!mWGySILuPUyidN(8pvgf4K%cD?>XKYdUAg*VA14 zEky*%Tro5LAOEun1IKDqKwm66l>bwQN@QjbEyWBo|JT(V9iqCCM+DHoI=rnuWej-B z)?rl~`e}dxn;DXh$O9ipU(b1v*ID;&kebCTP2ude9qf+&k`>Pl*$aA!0 zCXgO~m*Rh`>?2>DWI?jUUfrbsw;35=GeIx&bjd;PCcQ1teO++NqRX9W%tFs^oHWqv72l_uS z!SG2g@jP7OAlVdHqW^Rm3a&!0tIdQUM4s8p{%@Bbp1{C(DJmoQf0qrcBb4b0z<|42 zIq3oevFL|6T(NCO%73Cqii7^BfsjEdnh%pcu&xz(ggpZS^|P1@evrX; zIs|&T`;%V_Pz=zXxCGLJk!J@$gxBZAa+w`IL;&ljsH}=U@E3ufWQTABI3M~bZUYcQ zhy>hbf4@zgwcaw}S7OSZ1UQb2Z(kA+ge8P>cqCG^OU=6pGBaZeM�F*L@=gbczGr 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