mirror of
https://github.com/SerenityOS/serenity
synced 2026-04-25 17:15:42 +02:00
Ladybird: Remove the Qt Multimedia audio plugin
Now that ladybird was moved moved to its own repository, we don't need to keep unnecessary features like the Qt Multimedia audio backend. This means that PulseAudio is now required to have working audio in the Qt chrome. Ladybird only still exists in the SerenityOS repo to make testing LibWeb easier.
This commit is contained in:
@@ -7,12 +7,10 @@
|
||||
|
||||
Qt6 development packages and a C++23 capable compiler are required. g++-13 or clang-17 are required at a minimum for c++23 support.
|
||||
|
||||
NOTE: In all of the below lists of packages, the Qt6 multimedia package is not needed if your Linux system supports PulseAudio.
|
||||
|
||||
On Debian/Ubuntu required packages include, but are not limited to:
|
||||
|
||||
```
|
||||
sudo apt install build-essential cmake libgl1-mesa-dev ninja-build qt6-base-dev qt6-tools-dev-tools qt6-multimedia-dev ccache
|
||||
sudo apt install build-essential cmake libgl1-mesa-dev ninja-build qt6-base-dev qt6-tools-dev-tools ccache
|
||||
```
|
||||
|
||||
For Ubuntu 20.04 and above, ensure that the Qt6 Wayland packages are available:
|
||||
@@ -24,19 +22,19 @@ sudo apt install qt6-wayland
|
||||
On Arch Linux/Manjaro:
|
||||
|
||||
```
|
||||
sudo pacman -S --needed base-devel cmake libgl ninja qt6-base qt6-tools qt6-wayland qt6-multimedia ccache
|
||||
sudo pacman -S --needed base-devel cmake libgl ninja qt6-base qt6-tools qt6-wayland ccache
|
||||
```
|
||||
|
||||
On Fedora or derivatives:
|
||||
|
||||
```
|
||||
sudo dnf install cmake libglvnd-devel ninja-build qt6-qtbase-devel qt6-qttools-devel qt6-qtwayland-devel qt6-qtmultimedia-devel ccache
|
||||
sudo dnf install cmake libglvnd-devel ninja-build qt6-qtbase-devel qt6-qttools-devel qt6-qtwayland-devel ccache
|
||||
```
|
||||
|
||||
On openSUSE:
|
||||
|
||||
```
|
||||
sudo zypper install cmake libglvnd-devel ninja qt6-base-devel qt6-multimedia-devel qt6-tools-devel qt6-wayland-devel ccache
|
||||
sudo zypper install cmake libglvnd-devel ninja qt6-base-devel qt6-tools-devel qt6-wayland-devel ccache
|
||||
```
|
||||
|
||||
On NixOS or with Nix:
|
||||
@@ -83,7 +81,7 @@ pfexec pkg install cmake ninja clang-17 libglvnd qt6
|
||||
On Haiku:
|
||||
|
||||
```
|
||||
pkgman install cmake ninja cmd:python3 qt6_base_devel qt6_multimedia_devel qt6_tools_devel openal_devel
|
||||
pkgman install cmake ninja cmd:python3 qt6_base_devel qt6_tools_devel openal_devel
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "AudioCodecPluginQt.h"
|
||||
#include "AudioThread.h"
|
||||
#include <LibAudio/Loader.h>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioCodecPluginQt>> AudioCodecPluginQt::create(NonnullRefPtr<Audio::Loader> loader)
|
||||
{
|
||||
auto audio_thread = TRY(AudioThread::create(move(loader)));
|
||||
audio_thread->start();
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) AudioCodecPluginQt(move(audio_thread)));
|
||||
}
|
||||
|
||||
AudioCodecPluginQt::AudioCodecPluginQt(NonnullOwnPtr<AudioThread> audio_thread)
|
||||
: m_audio_thread(move(audio_thread))
|
||||
{
|
||||
connect(m_audio_thread, &AudioThread::playback_position_updated, this, [this](auto position) {
|
||||
if (on_playback_position_updated)
|
||||
on_playback_position_updated(position);
|
||||
});
|
||||
}
|
||||
|
||||
AudioCodecPluginQt::~AudioCodecPluginQt()
|
||||
{
|
||||
m_audio_thread->stop().release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::resume_playback()
|
||||
{
|
||||
m_audio_thread->queue_task({ AudioTask::Type::Play }).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::pause_playback()
|
||||
{
|
||||
m_audio_thread->queue_task({ AudioTask::Type::Pause }).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::set_volume(double volume)
|
||||
{
|
||||
|
||||
AudioTask task { AudioTask::Type::Volume };
|
||||
task.data = volume;
|
||||
|
||||
m_audio_thread->queue_task(move(task)).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::seek(double position)
|
||||
{
|
||||
AudioTask task { AudioTask::Type::Seek };
|
||||
task.data = position;
|
||||
|
||||
m_audio_thread->queue_task(move(task)).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
Duration AudioCodecPluginQt::duration()
|
||||
{
|
||||
return m_audio_thread->duration();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibAudio/Forward.h>
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
#include <QObject>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class AudioThread;
|
||||
|
||||
class AudioCodecPluginQt final
|
||||
: public QObject
|
||||
, public Web::Platform::AudioCodecPlugin {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<AudioCodecPluginQt>> create(NonnullRefPtr<Audio::Loader>);
|
||||
virtual ~AudioCodecPluginQt() override;
|
||||
|
||||
virtual void resume_playback() override;
|
||||
virtual void pause_playback() override;
|
||||
virtual void set_volume(double) override;
|
||||
virtual void seek(double) override;
|
||||
|
||||
virtual Duration duration() override;
|
||||
|
||||
private:
|
||||
explicit AudioCodecPluginQt(NonnullOwnPtr<AudioThread>);
|
||||
|
||||
NonnullOwnPtr<AudioThread> m_audio_thread;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "AudioThread.h"
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
struct AudioDevice {
|
||||
static AudioDevice create(Audio::Loader const& loader)
|
||||
{
|
||||
auto const& device_info = QMediaDevices::defaultAudioOutput();
|
||||
|
||||
auto format = device_info.preferredFormat();
|
||||
format.setSampleRate(static_cast<int>(loader.sample_rate()));
|
||||
format.setChannelCount(2);
|
||||
|
||||
auto audio_output = make<QAudioSink>(device_info, format);
|
||||
return AudioDevice { move(audio_output) };
|
||||
}
|
||||
|
||||
AudioDevice(AudioDevice&&) = default;
|
||||
|
||||
AudioDevice& operator=(AudioDevice&& device)
|
||||
{
|
||||
if (audio_output) {
|
||||
audio_output->stop();
|
||||
io_device = nullptr;
|
||||
}
|
||||
|
||||
swap(audio_output, device.audio_output);
|
||||
swap(io_device, device.io_device);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~AudioDevice()
|
||||
{
|
||||
if (audio_output)
|
||||
audio_output->stop();
|
||||
}
|
||||
|
||||
OwnPtr<QAudioSink> audio_output;
|
||||
QIODevice* io_device { nullptr };
|
||||
|
||||
private:
|
||||
explicit AudioDevice(NonnullOwnPtr<QAudioSink> output)
|
||||
: audio_output(move(output))
|
||||
{
|
||||
io_device = audio_output->start();
|
||||
}
|
||||
};
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioThread>> AudioThread::create(NonnullRefPtr<Audio::Loader> loader)
|
||||
{
|
||||
auto task_queue = TRY(AudioTaskQueue::create());
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) AudioThread(move(loader), move(task_queue)));
|
||||
}
|
||||
|
||||
ErrorOr<void> AudioThread::stop()
|
||||
{
|
||||
TRY(queue_task({ AudioTask::Type::Stop }));
|
||||
wait();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> AudioThread::queue_task(AudioTask task)
|
||||
{
|
||||
return m_task_queue.blocking_enqueue(move(task), []() {
|
||||
usleep(UPDATE_RATE_MS * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
AudioThread::AudioThread(NonnullRefPtr<Audio::Loader> loader, AudioTaskQueue task_queue)
|
||||
: m_loader(move(loader))
|
||||
, m_task_queue(move(task_queue))
|
||||
{
|
||||
auto duration = static_cast<double>(m_loader->total_samples()) / static_cast<double>(m_loader->sample_rate());
|
||||
m_duration = Duration::from_milliseconds(static_cast<i64>(duration * 1000.0));
|
||||
}
|
||||
|
||||
void AudioThread::run()
|
||||
{
|
||||
auto devices = make<QMediaDevices>();
|
||||
auto audio_device = AudioDevice::create(m_loader);
|
||||
|
||||
connect(devices, &QMediaDevices::audioOutputsChanged, this, [this]() {
|
||||
queue_task({ AudioTask::Type::RecreateAudioDevice }).release_value_but_fixme_should_propagate_errors();
|
||||
});
|
||||
|
||||
auto paused = Paused::Yes;
|
||||
|
||||
while (true) {
|
||||
auto& audio_output = audio_device.audio_output;
|
||||
auto* io_device = audio_device.io_device;
|
||||
|
||||
if (auto result = m_task_queue.dequeue(); result.is_error()) {
|
||||
VERIFY(result.error() == AudioTaskQueue::QueueStatus::Empty);
|
||||
} else {
|
||||
auto task = result.release_value();
|
||||
|
||||
switch (task.type) {
|
||||
case AudioTask::Type::Stop:
|
||||
return;
|
||||
|
||||
case AudioTask::Type::Play:
|
||||
audio_output->resume();
|
||||
paused = Paused::No;
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Pause:
|
||||
audio_output->suspend();
|
||||
paused = Paused::Yes;
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Seek:
|
||||
VERIFY(task.data.has_value());
|
||||
m_position = Web::Platform::AudioCodecPlugin::set_loader_position(m_loader, *task.data, m_duration);
|
||||
|
||||
if (paused == Paused::Yes)
|
||||
Q_EMIT playback_position_updated(m_position);
|
||||
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Volume:
|
||||
VERIFY(task.data.has_value());
|
||||
audio_output->setVolume(*task.data);
|
||||
break;
|
||||
|
||||
case AudioTask::Type::RecreateAudioDevice:
|
||||
audio_device = AudioDevice::create(m_loader);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (paused == Paused::No) {
|
||||
if (auto result = play_next_samples(*audio_output, *io_device); result.is_error()) {
|
||||
// FIXME: Propagate the error to the HTMLMediaElement.
|
||||
} else {
|
||||
Q_EMIT playback_position_updated(m_position);
|
||||
paused = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
usleep(UPDATE_RATE_MS * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<AudioThread::Paused> AudioThread::play_next_samples(QAudioSink& audio_output, QIODevice& io_device)
|
||||
{
|
||||
bool all_samples_loaded = m_loader->loaded_samples() >= m_loader->total_samples();
|
||||
|
||||
if (all_samples_loaded) {
|
||||
audio_output.suspend();
|
||||
(void)m_loader->reset();
|
||||
|
||||
m_position = m_duration;
|
||||
return Paused::Yes;
|
||||
}
|
||||
|
||||
auto bytes_available = audio_output.bytesFree();
|
||||
auto bytes_per_sample = audio_output.format().bytesPerSample();
|
||||
auto channel_count = audio_output.format().channelCount();
|
||||
auto samples_to_load = bytes_available / bytes_per_sample / channel_count;
|
||||
|
||||
auto samples = TRY(Web::Platform::AudioCodecPlugin::read_samples_from_loader(*m_loader, samples_to_load));
|
||||
enqueue_samples(audio_output, io_device, move(samples));
|
||||
|
||||
m_position = Web::Platform::AudioCodecPlugin::current_loader_position(m_loader);
|
||||
return Paused::No;
|
||||
}
|
||||
|
||||
void AudioThread::enqueue_samples(QAudioSink const& audio_output, QIODevice& io_device, FixedArray<Audio::Sample> samples)
|
||||
{
|
||||
auto buffer_size = samples.size() * audio_output.format().bytesPerSample() * audio_output.format().channelCount();
|
||||
|
||||
if (buffer_size > static_cast<size_t>(m_sample_buffer.size()))
|
||||
m_sample_buffer.resize(buffer_size);
|
||||
|
||||
FixedMemoryStream stream { Bytes { m_sample_buffer.data(), buffer_size } };
|
||||
|
||||
for (auto const& sample : samples) {
|
||||
switch (audio_output.format().sampleFormat()) {
|
||||
case QAudioFormat::UInt8:
|
||||
write_sample<u8>(stream, sample.left);
|
||||
write_sample<u8>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Int16:
|
||||
write_sample<i16>(stream, sample.left);
|
||||
write_sample<i16>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Int32:
|
||||
write_sample<i32>(stream, sample.left);
|
||||
write_sample<i32>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Float:
|
||||
write_sample<float>(stream, sample.left);
|
||||
write_sample<float>(stream, sample.right);
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
io_device.write(m_sample_buffer.data(), buffer_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibAudio/Loader.h>
|
||||
#include <LibAudio/Sample.h>
|
||||
#include <LibCore/SharedCircularQueue.h>
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioSink>
|
||||
#include <QByteArray>
|
||||
#include <QMediaDevices>
|
||||
#include <QThread>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
static constexpr u32 UPDATE_RATE_MS = 10;
|
||||
|
||||
struct AudioTask {
|
||||
enum class Type {
|
||||
Stop,
|
||||
Play,
|
||||
Pause,
|
||||
Seek,
|
||||
Volume,
|
||||
RecreateAudioDevice,
|
||||
};
|
||||
|
||||
Type type;
|
||||
Optional<double> data {};
|
||||
};
|
||||
|
||||
using AudioTaskQueue = Core::SharedSingleProducerCircularQueue<AudioTask>;
|
||||
|
||||
class AudioThread final : public QThread { // We have to use QThread, otherwise internal Qt media QTimer objects do not work.
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<AudioThread>> create(NonnullRefPtr<Audio::Loader> loader);
|
||||
|
||||
ErrorOr<void> stop();
|
||||
|
||||
Duration duration() const { return m_duration; }
|
||||
|
||||
ErrorOr<void> queue_task(AudioTask task);
|
||||
|
||||
Q_SIGNALS:
|
||||
void playback_position_updated(Duration);
|
||||
|
||||
private:
|
||||
AudioThread(NonnullRefPtr<Audio::Loader> loader, AudioTaskQueue task_queue);
|
||||
|
||||
enum class Paused {
|
||||
Yes,
|
||||
No,
|
||||
};
|
||||
|
||||
void run() override;
|
||||
|
||||
ErrorOr<Paused> play_next_samples(QAudioSink& audio_output, QIODevice& io_device);
|
||||
|
||||
void enqueue_samples(QAudioSink const& audio_output, QIODevice& io_device, FixedArray<Audio::Sample> samples);
|
||||
|
||||
template<typename T>
|
||||
void write_sample(FixedMemoryStream& stream, float sample)
|
||||
{
|
||||
// The values that need to be written to the stream vary depending on the output channel format, and isn't
|
||||
// particularly well documented. The value derivations performed below were adapted from a Qt example:
|
||||
// https://code.qt.io/cgit/qt/qtmultimedia.git/tree/examples/multimedia/audiooutput/audiooutput.cpp?h=6.4.2#n46
|
||||
LittleEndian<T> pcm;
|
||||
|
||||
if constexpr (IsSame<T, u8>)
|
||||
pcm = static_cast<u8>((sample + 1.0f) / 2 * NumericLimits<u8>::max());
|
||||
else if constexpr (IsSame<T, i16>)
|
||||
pcm = static_cast<i16>(sample * NumericLimits<i16>::max());
|
||||
else if constexpr (IsSame<T, i32>)
|
||||
pcm = static_cast<i32>(sample * NumericLimits<i32>::max());
|
||||
else if constexpr (IsSame<T, float>)
|
||||
pcm = sample;
|
||||
else
|
||||
static_assert(DependentFalse<T>);
|
||||
|
||||
MUST(stream.write_value(pcm));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Audio::Loader> m_loader;
|
||||
AudioTaskQueue m_task_queue;
|
||||
|
||||
QByteArray m_sample_buffer;
|
||||
|
||||
Duration m_duration;
|
||||
Duration m_position;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -28,18 +28,6 @@ if (ENABLE_QT)
|
||||
target_link_libraries(WebContent PRIVATE Qt::Core Qt::Network)
|
||||
target_compile_definitions(WebContent PRIVATE HAVE_QT=1)
|
||||
set_target_properties(WebContent PROPERTIES AUTOMOC ON)
|
||||
|
||||
if (NOT HAVE_PULSEAUDIO)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
|
||||
|
||||
target_sources(WebContent PRIVATE
|
||||
../Qt/AudioCodecPluginQt.cpp
|
||||
../Qt/AudioThread.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(WebContent PRIVATE Qt::Multimedia)
|
||||
target_compile_definitions(WebContent PRIVATE HAVE_QT_MULTIMEDIA=1)
|
||||
endif()
|
||||
else()
|
||||
set(LIB_TYPE STATIC)
|
||||
if (IOS)
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
# include <Ladybird/Qt/EventLoopImplementationQt.h>
|
||||
# include <Ladybird/Qt/RequestManagerQt.h>
|
||||
# include <QCoreApplication>
|
||||
|
||||
# if defined(HAVE_QT_MULTIMEDIA)
|
||||
# include <Ladybird/Qt/AudioCodecPluginQt.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(AK_OS_MACOS)
|
||||
@@ -82,9 +78,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin);
|
||||
|
||||
Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) {
|
||||
#if defined(HAVE_QT_MULTIMEDIA)
|
||||
return Ladybird::AudioCodecPluginQt::create(move(loader));
|
||||
#elif defined(AK_OS_MACOS) || defined(HAVE_PULSEAUDIO)
|
||||
#if defined(AK_OS_MACOS) || defined(HAVE_PULSEAUDIO)
|
||||
return Web::Platform::AudioCodecPluginAgnostic::create(move(loader));
|
||||
#else
|
||||
(void)loader;
|
||||
|
||||
@@ -12,7 +12,6 @@ mkShell.override { stdenv = gcc13Stdenv; } {
|
||||
pkg-config
|
||||
qt6.qtbase
|
||||
qt6.qtbase.dev
|
||||
qt6.qtmultimedia
|
||||
qt6.qttools
|
||||
qt6.qtwayland
|
||||
qt6.qtwayland.dev
|
||||
|
||||
@@ -3,20 +3,11 @@ import("//Ladybird/link_qt.gni")
|
||||
import("//Ladybird/moc_qt_objects.gni")
|
||||
import("//Meta/gn/build/libs/pulse/enable.gni")
|
||||
|
||||
enable_qt_multimedia = !enable_pulseaudio && current_os != "mac"
|
||||
|
||||
moc_qt_objects("generate_moc") {
|
||||
sources = [
|
||||
"//Ladybird/Qt/EventLoopImplementationQtEventTarget.h",
|
||||
"//Ladybird/Qt/RequestManagerQt.h",
|
||||
]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
sources += [
|
||||
"//Ladybird/Qt/AudioCodecPluginQt.cpp",
|
||||
"//Ladybird/Qt/AudioThread.cpp",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
link_qt("WebContent_qt") {
|
||||
@@ -24,10 +15,6 @@ link_qt("WebContent_qt") {
|
||||
"Core",
|
||||
"Network",
|
||||
]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
qt_components += [ "Multimedia" ]
|
||||
}
|
||||
}
|
||||
|
||||
executable("WebContent") {
|
||||
@@ -83,14 +70,6 @@ executable("WebContent") {
|
||||
"//Ladybird/Qt/WebSocketQt.cpp",
|
||||
]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
defines += [ "HAVE_QT_MULTIMEDIA" ]
|
||||
sources += [
|
||||
"//Ladybird/Qt/AudioCodecPluginQt.cpp",
|
||||
"//Ladybird/Qt/AudioThread.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
sources += get_target_outputs(":generate_moc")
|
||||
deps += [ ":generate_moc" ]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user