Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
/*******************************************************************************
* Copyright 2009-2024 Jörg Müller
*
* 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.
******************************************************************************/
#include "PipeWireDevice.h"
#include <spa/param/audio/format-utils.h>
#include "Exception.h"
#include "PipeWireLibrary.h"
#include "devices/DeviceManager.h"
#include "devices/IDeviceFactory.h"
AUD_NAMESPACE_BEGIN
void PipeWireDevice::handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error)
{
PipeWireDevice* device = (PipeWireDevice*) device_ptr;
// fprintf(stderr, "stream state: \"%s\"\n", pw_stream_state_as_string(state));
if(state == PW_STREAM_STATE_PAUSED)
{
AUD_pw_stream_flush(device->m_stream, false);
}
}
void PipeWireDevice::mixAudioBuffer(void* device_ptr)
{
PipeWireDevice* device = (PipeWireDevice*) device_ptr;
pw_buffer* pw_buf = AUD_pw_stream_dequeue_buffer(device->m_stream);
if(!pw_buf)
{
/* Couldn't get any buffer from PipeWire...*/
return;
}
/* We compute this here as the tick is not guaranteed to be up to date
* until the "process" callback is triggered.
*/
if(device->m_getSynchronizerStartTime)
{
pw_time tm;
AUD_pw_stream_get_time_n(device->m_stream, &tm, sizeof(tm));
device->m_synchronizerStartTime = tm.ticks;
device->m_getSynchronizerStartTime = false;
}
spa_data& spa_data = pw_buf->buffer->datas[0];
spa_chunk* chunk = spa_data.chunk;
chunk->offset = 0;
chunk->stride = AUD_DEVICE_SAMPLE_SIZE(device->m_specs);
int n_frames = spa_data.maxsize / chunk->stride;
if(pw_buf->requested)
{
n_frames = SPA_MIN(pw_buf->requested, n_frames);
}
size_t readsamples = device->getRingBuffer().getReadSize() / chunk->stride;
if(readsamples < n_frames)
n_frames = readsamples;
chunk->size = n_frames * chunk->stride;
device->getRingBuffer().read(reinterpret_cast<data_t*>(spa_data.data), chunk->size);
device->notifyMixingThread();
AUD_pw_stream_queue_buffer(device->m_stream, pw_buf);
}
void PipeWireDevice::preMixingWork(bool playing)
{
if(!playing)
{
if((getRingBuffer().getReadSize() == 0) && m_active)
{
AUD_pw_thread_loop_lock(m_thread);
AUD_pw_stream_set_active(m_stream, false);
AUD_pw_thread_loop_unlock(m_thread);
m_active = false;
}
}
}
void PipeWireDevice::playing(bool playing)
{
std::lock_guard<ILockable> lock(*this);
MixingThreadDevice::playing(playing);
if(playing)
{
AUD_pw_thread_loop_lock(m_thread);
AUD_pw_stream_set_active(m_stream, playing);
AUD_pw_thread_loop_unlock(m_thread);
m_active = true;
}
}
PipeWireDevice::PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize)
{
if(specs.channels == CHANNELS_INVALID)
specs.channels = CHANNELS_STEREO;
if(specs.format == FORMAT_INVALID)
specs.format = FORMAT_FLOAT32;
if(specs.rate == RATE_INVALID)
specs.rate = RATE_48000;
m_specs = specs;
spa_audio_format format = SPA_AUDIO_FORMAT_F32;
switch(m_specs.format)
{
case FORMAT_U8:
format = SPA_AUDIO_FORMAT_U8;
break;
case FORMAT_S16:
format = SPA_AUDIO_FORMAT_S16;
break;
case FORMAT_S24:
format = SPA_AUDIO_FORMAT_S24;
break;
case FORMAT_S32:
format = SPA_AUDIO_FORMAT_S32;
break;
case FORMAT_FLOAT32:
format = SPA_AUDIO_FORMAT_F32;
break;
case FORMAT_FLOAT64:
format = SPA_AUDIO_FORMAT_F64;
break;
default:
break;
}
AUD_pw_init(nullptr, nullptr);
m_thread = AUD_pw_thread_loop_new(name.c_str(), nullptr);
if(!m_thread)
{
AUD_THROW(DeviceException, "Could not create PipeWire thread.");
}
m_events = std::make_unique<pw_stream_events>();
m_events->version = PW_VERSION_STREAM_EVENTS;
m_events->state_changed = PipeWireDevice::handleStateChanged;
m_events->process = PipeWireDevice::mixAudioBuffer;
pw_properties* stream_props = AUD_pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Production", NULL);
/* Set the requested sample rate and latency. */
AUD_pw_properties_setf(stream_props, PW_KEY_NODE_RATE, "1/%u", uint(m_specs.rate));
AUD_pw_properties_setf(stream_props, PW_KEY_NODE_LATENCY, "%u/%u", buffersize, uint(m_specs.rate));
m_stream = AUD_pw_stream_new_simple(AUD_pw_thread_loop_get_loop(m_thread), name.c_str(), stream_props, m_events.get(), this);
if(!m_stream)
{
AUD_pw_thread_loop_destroy(m_thread);
AUD_THROW(DeviceException, "Could not create PipeWire stream.");
}
spa_audio_info_raw info{};
info.channels = m_specs.channels;
info.format = format;
info.rate = m_specs.rate;
uint8_t buffer[1024];
spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
const spa_pod* param = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
AUD_pw_stream_connect(m_stream, PW_DIRECTION_OUTPUT, PW_ID_ANY,
static_cast<pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_RT_PROCESS), &param, 1);
AUD_pw_thread_loop_start(m_thread);
create();
startMixingThread(buffersize * 2 * AUD_DEVICE_SAMPLE_SIZE(m_specs));
}
PipeWireDevice::~PipeWireDevice()
{
stopMixingThread();
/* Ensure that we are not playing back anything anymore. */
destroy();
/* Destruct all PipeWire data. */
AUD_pw_thread_loop_stop(m_thread);
AUD_pw_stream_destroy(m_stream);
AUD_pw_thread_loop_destroy(m_thread);
AUD_pw_deinit();
}
void PipeWireDevice::seekSynchronizer(double time)
{
/* Update start time here as we might update the seek position while playing back. */
m_getSynchronizerStartTime = true;
m_synchronizerStartPosition = time;
SoftwareDevice::seekSynchronizer(time);
}
double PipeWireDevice::getSynchronizerPosition()
{
if(!isSynchronizerPlaying() || m_getSynchronizerStartTime)
{
return m_synchronizerStartPosition;
}
pw_time tm;
AUD_pw_stream_get_time_n(m_stream, &tm, sizeof(tm));
uint64_t now = AUD_pw_stream_get_nsec(m_stream);
int64_t diff = now - tm.now;
/* Elapsed time since the last sample was queued. */
int64_t elapsed = (tm.rate.denom * diff) / (tm.rate.num * SPA_NSEC_PER_SEC);
/* Calculate the elapsed time in seconds from the last seek position. */
double elapsed_time = (tm.ticks - m_synchronizerStartTime + elapsed) * tm.rate.num / double(tm.rate.denom);
return elapsed_time + m_synchronizerStartPosition;
}
void PipeWireDevice::playSynchronizer()
{
/* Make sure that our start time is up to date. */
m_getSynchronizerStartTime = true;
SoftwareDevice::playSynchronizer();
}
void PipeWireDevice::stopSynchronizer()
{
m_synchronizerStartPosition = getSynchronizerPosition();
SoftwareDevice::stopSynchronizer();
}
class PipeWireDeviceFactory : public IDeviceFactory
{
private:
DeviceSpecs m_specs;
int m_buffersize;
std::string m_name;
public:
PipeWireDeviceFactory() : m_buffersize(AUD_DEFAULT_BUFFER_SIZE)
{
m_specs.format = FORMAT_S16;
m_specs.channels = CHANNELS_STEREO;
m_specs.rate = RATE_48000;
}
virtual std::shared_ptr<IDevice> openDevice()
{
return std::shared_ptr<IDevice>(new PipeWireDevice(m_name, m_specs, m_buffersize));
}
virtual int getPriority()
{
return 1 << 16;
}
virtual void setSpecs(DeviceSpecs specs)
{
m_specs = specs;
}
virtual void setBufferSize(int buffersize)
{
m_buffersize = buffersize;
}
virtual void setName(const std::string& name)
{
m_name = name;
}
};
void PipeWireDevice::registerPlugin()
{
if(loadPipeWire())
DeviceManager::registerDevice("PipeWire", std::shared_ptr<IDeviceFactory>(new PipeWireDeviceFactory));
}
#ifdef PIPEWIRE_PLUGIN
extern "C" AUD_PLUGIN_API void registerPlugin()
{
PipeWireDevice::registerPlugin();
}
extern "C" AUD_PLUGIN_API const char* getName()
{
return "Pipewire";
}
#endif
AUD_NAMESPACE_END

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright 2009-2024 Jörg Müller
*
* 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.
******************************************************************************/
#pragma once
#ifdef PIPEWIRE_PLUGIN
#define AUD_BUILD_PLUGIN
#endif
/**
* @file PipeWireDevice.h
* @ingroup plugin
* The PipeWireDevice class.
*/
#include <pipewire/pipewire.h>
#include "devices/MixingThreadDevice.h"
AUD_NAMESPACE_BEGIN
/**
* This device plays back through PipeWire, the simple direct media layer.
*/
class AUD_PLUGIN_API PipeWireDevice : public MixingThreadDevice
{
private:
pw_stream* m_stream;
pw_thread_loop* m_thread;
std::unique_ptr<pw_stream_events> m_events;
bool m_active{false};
/// Synchronizer.
bool m_getSynchronizerStartTime{false};
int64_t m_synchronizerStartTime{0};
double m_synchronizerStartPosition{0.0};
AUD_LOCAL static void handleStateChanged(void* device_ptr, enum pw_stream_state old, enum pw_stream_state state, const char* error);
/**
* Mixes the next bytes into the buffer.
* \param data The PipeWire device.
*/
AUD_LOCAL static void mixAudioBuffer(void* device_ptr);
// delete copy constructor and operator=
PipeWireDevice(const PipeWireDevice&) = delete;
PipeWireDevice& operator=(const PipeWireDevice&) = delete;
protected:
void preMixingWork(bool playing);
virtual void playing(bool playing);
public:
/**
* Opens the PipeWire audio device for playback.
* \param specs The wanted audio specification.
* \param buffersize The size of the internal buffer.
* \note The specification really used for opening the device may differ.
* \exception Exception Thrown if the audio device cannot be opened.
*/
PipeWireDevice(const std::string& name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
/**
* Closes the PipeWire audio device.
*/
virtual ~PipeWireDevice();
virtual void seekSynchronizer(double time);
virtual double getSynchronizerPosition();
virtual void playSynchronizer();
virtual void stopSynchronizer();
/**
* Registers this plugin.
*/
static void registerPlugin();
};
AUD_NAMESPACE_END

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright 2009-2024 Jörg Müller
*
* 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.
******************************************************************************/
#define PIPEWIRE_LIBRARY_IMPLEMENTATION
#include <string>
#include <array>
#include "PipeWireLibrary.h"
#ifdef DYNLOAD_PIPEWIRE
#include "plugin/PluginManager.h"
#endif
AUD_NAMESPACE_BEGIN
bool loadPipeWire()
{
#ifdef DYNLOAD_PIPEWIRE
std::array<const std::string, 2> names = {"libpipewire-0.3.so", "libpipewire-0.3.so.0"};
void* handle = nullptr;
for(auto& name : names)
{
handle = PluginManager::openLibrary(name);
if(handle)
break;
}
if (!handle)
return false;
#define PIPEWIRE_SYMBOL(sym) AUD_##sym = reinterpret_cast<decltype(&sym)>(PluginManager::lookupLibrary(handle, #sym))
#else
#define PIPEWIRE_SYMBOL(sym) AUD_##sym = &sym
#endif
#include "PipeWireSymbols.h"
#undef PIPEWIRE_SYMBOL
return AUD_pw_check_library_version != nullptr && AUD_pw_check_library_version(1, 1, 0);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright 2009-2024 Jörg Müller
*
* 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.
******************************************************************************/
#pragma once
#ifdef PIPEWIRE_PLUGIN
#define AUD_BUILD_PLUGIN
#endif
/**
* @file PipeWireLibrary.h
* @ingroup plugin
*/
#include "Audaspace.h"
#include <pipewire/pipewire.h>
#include <pipewire/stream.h>
AUD_NAMESPACE_BEGIN
#ifdef PIPEWIRE_LIBRARY_IMPLEMENTATION
#define PIPEWIRE_SYMBOL(sym) decltype(&sym) AUD_##sym
#else
#define PIPEWIRE_SYMBOL(sym) extern decltype(&sym) AUD_##sym
#endif
#include "PipeWireSymbols.h"
#undef PIPEWIRE_SYMBOL
bool loadPipeWire();
AUD_NAMESPACE_END

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright 2009-2024 Jörg Müller
*
* 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.
******************************************************************************/
PIPEWIRE_SYMBOL(pw_init);
PIPEWIRE_SYMBOL(pw_deinit);
PIPEWIRE_SYMBOL(pw_properties_new);
PIPEWIRE_SYMBOL(pw_properties_setf);
PIPEWIRE_SYMBOL(pw_stream_connect);
PIPEWIRE_SYMBOL(pw_stream_destroy);
PIPEWIRE_SYMBOL(pw_stream_get_nsec);
PIPEWIRE_SYMBOL(pw_stream_get_time_n);
PIPEWIRE_SYMBOL(pw_stream_new_simple);
PIPEWIRE_SYMBOL(pw_stream_queue_buffer);
PIPEWIRE_SYMBOL(pw_stream_dequeue_buffer);
PIPEWIRE_SYMBOL(pw_stream_set_active);
PIPEWIRE_SYMBOL(pw_stream_flush);
PIPEWIRE_SYMBOL(pw_thread_loop_destroy);
PIPEWIRE_SYMBOL(pw_thread_loop_get_loop);
PIPEWIRE_SYMBOL(pw_thread_loop_lock);
PIPEWIRE_SYMBOL(pw_thread_loop_unlock);
PIPEWIRE_SYMBOL(pw_thread_loop_new);
PIPEWIRE_SYMBOL(pw_thread_loop_start);
PIPEWIRE_SYMBOL(pw_thread_loop_stop);
PIPEWIRE_SYMBOL(pw_check_library_version);