From 9f44fcbded3982b674c668859ec0d83a5a264588 Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Tue, 7 Oct 2025 12:07:59 -0500 Subject: [PATCH] Everywhere: Remove AudioCodecPlugin and Qt Multimedia These are no longer needed now that audio is played through PlaybackManager. --- .../features/ladybird/install-fedora.sh | 2 +- .../features/ladybird/install-ubuntu.sh | 2 +- Documentation/BuildInstructionsLadybird.md | 30 ++- Libraries/LibWeb/CMakeLists.txt | 2 - Libraries/LibWeb/Forward.h | 1 - .../LibWeb/Platform/AudioCodecPlugin.cpp | 62 ----- Libraries/LibWeb/Platform/AudioCodecPlugin.h | 46 ---- .../Platform/AudioCodecPluginAgnostic.cpp | 202 ----------------- .../Platform/AudioCodecPluginAgnostic.h | 42 ---- .../gn/secondary/Ladybird/WebContent/BUILD.gn | 21 -- .../Libraries/LibWeb/Platform/BUILD.gn | 2 - Services/WebContent/CMakeLists.txt | 17 +- Services/WebContent/main.cpp | 21 -- UI/Android/src/main/cpp/WebContentService.cpp | 5 - UI/Qt/AudioCodecPluginQt.cpp | 67 ------ UI/Qt/AudioCodecPluginQt.h | 43 ---- UI/Qt/AudioThread.cpp | 212 ------------------ UI/Qt/AudioThread.h | 105 --------- vcpkg.json | 8 - 19 files changed, 16 insertions(+), 874 deletions(-) delete mode 100644 Libraries/LibWeb/Platform/AudioCodecPlugin.cpp delete mode 100644 Libraries/LibWeb/Platform/AudioCodecPlugin.h delete mode 100644 Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.cpp delete mode 100644 Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.h delete mode 100644 UI/Qt/AudioCodecPluginQt.cpp delete mode 100644 UI/Qt/AudioCodecPluginQt.h delete mode 100644 UI/Qt/AudioThread.cpp delete mode 100644 UI/Qt/AudioThread.h diff --git a/.devcontainer/features/ladybird/install-fedora.sh b/.devcontainer/features/ladybird/install-fedora.sh index f6b7f2e067b..331c4cf875a 100644 --- a/.devcontainer/features/ladybird/install-fedora.sh +++ b/.devcontainer/features/ladybird/install-fedora.sh @@ -7,4 +7,4 @@ dnf install -y git gh # Ladybird dev dependencies dnf install -y autoconf-archive automake ccache cmake curl google-noto-sans-mono-fonts liberation-sans-fonts \ libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib qt6-qtbase-devel \ - qt6-qtmultimedia-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static + qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static diff --git a/.devcontainer/features/ladybird/install-ubuntu.sh b/.devcontainer/features/ladybird/install-ubuntu.sh index 1c19487fe11..c4e1867635c 100644 --- a/.devcontainer/features/ladybird/install-ubuntu.sh +++ b/.devcontainer/features/ladybird/install-ubuntu.sh @@ -24,7 +24,7 @@ install_llvm_key() { ### Install packages apt update -y -apt install -y lsb-release git python3 autoconf autoconf-archive automake build-essential cmake libdrm-dev libgl1-mesa-dev libtool nasm ninja-build pkg-config qt6-base-dev qt6-tools-dev-tools qt6-multimedia-dev qt6-wayland ccache fonts-liberation2 zip unzip curl tar +apt install -y lsb-release git python3 autoconf autoconf-archive automake build-essential cmake libdrm-dev libgl1-mesa-dev libtool nasm ninja-build pkg-config qt6-base-dev qt6-tools-dev-tools qt6-wayland ccache fonts-liberation2 zip unzip curl tar ### Ensure new enough host compiler is available VERSION="0.0.0" diff --git a/Documentation/BuildInstructionsLadybird.md b/Documentation/BuildInstructionsLadybird.md index 49a9c2bb37c..e7223a33aec 100644 --- a/Documentation/BuildInstructionsLadybird.md +++ b/Documentation/BuildInstructionsLadybird.md @@ -9,9 +9,6 @@ We currently use gcc-14 and clang-20 in our CI pipeline. If these versions are n CMake 3.25 or newer must be available in $PATH. -> [!NOTE] -> In all of the below lists of packages, the Qt6 multimedia package is not needed if your Linux system supports PulseAudio. - --- ### Debian/Ubuntu: @@ -67,34 +64,34 @@ sudo apt update && sudo apt install g++-14 libstdc++-14-dev #### Audio support: -- Recommendation: Install PulseAudio development package: +- Install PulseAudio development package: ```bash sudo apt install libpulse-dev ``` -- Alternative: Install Qt6's multimedia package: - -```bash -sudo apt install qt6-multimedia-dev -``` - ### Arch Linux/Manjaro: ``` -sudo pacman -S --needed autoconf-archive automake base-devel ccache cmake curl libgl nasm ninja qt6-base qt6-multimedia qt6-tools qt6-wayland ttf-liberation tar unzip zip +sudo pacman -S --needed autoconf-archive automake base-devel ccache cmake curl libgl nasm ninja qt6-base qt6-tools qt6-wayland ttf-liberation tar unzip zip +``` + +Optionally, install the PulseAudio headers for audio playback support: + +``` +sudo pacman -S libpulse ``` ### Fedora or derivatives: ``` -sudo dnf install autoconf-archive automake ccache cmake curl git libdrm-devel liberation-sans-fonts libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib perl-Time-Piece qt6-qtbase-devel qt6-qtmultimedia-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static +sudo dnf install autoconf-archive automake ccache cmake curl git libdrm-devel liberation-sans-fonts libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib perl-Time-Piece qt6-qtbase-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static ``` ### openSUSE: ``` -sudo zypper install autoconf-archive automake ccache cmake curl gcc14 gcc14-c++ git liberation-fonts libglvnd-devel libtool nasm ninja qt6-base-devel qt6-multimedia-devel qt6-tools-devel qt6-wayland-devel tar unzip zip +sudo zypper install autoconf-archive automake ccache cmake curl gcc14 gcc14-c++ git liberation-fonts libglvnd-devel libtool nasm ninja qt6-base-devel qt6-tools-devel qt6-wayland-devel tar unzip zip ``` If one or more of the base repository packages are flagged as having an out-of-date version during the build process, you may need add the `devel:tools:building` repository. For example, on Leap 15.6, the `autoconf` package might be version 2.69, whereas the `gperf` package requires 2.70 to build. @@ -119,11 +116,10 @@ Nothing to do. > sudo zypper install autoconf-2.72-80.d_t_b.1.noarch ``` -It is currently recommended to install the `libpulse-devel` package to avoid runtime dynamic linking issues. If issues persist you may need to remove the `qt6-multimedia-devel` package to avoid linking issues. +It is necessary to install the `libpulse-devel` package to enable audio playback: ``` sudo zypper install libpulse-devel -sudo zypper remove qt6-multimedia-devel ``` The build process requires at least python3.7; openSUSE Leap only features Python 3.6 as default, so it is recommendable to install the package `python312` and create a virtual environment (venv) in this case. @@ -141,7 +137,7 @@ This virtual environment can be created once and reused in future shell sessions ``` sudo xbps-install -Su # (optional) ensure packages are up to date to avoid "Transaction aborted due to unresolved dependencies." -sudo xbps-install -S git bash gcc python3 curl cmake zip unzip linux-headers make pkg-config autoconf automake autoconf-archive nasm MesaLib-devel ninja qt6-base-devel qt6-multimedia-devel qt6-tools-devel qt6-wayland-devel +sudo xbps-install -S git bash gcc python3 curl cmake zip unzip linux-headers make pkg-config autoconf automake autoconf-archive nasm MesaLib-devel ninja qt6-base-devel qt6-tools-devel qt6-wayland-devel ``` ### NixOS or with Nix: @@ -209,7 +205,7 @@ Or, download a version of Gradle >= 8.0.0, and run the ``gradlew`` program in `` ### FreeBSD ``` -pkg install autoconf-archive automake autoconf bash cmake curl gmake gn libdrm libtool libxcb libxkbcommon libX11 libXrender libXi nasm ninja patchelf pkgconf python3 qt6-base qt6-multimedia unzip zip +pkg install autoconf-archive automake autoconf bash cmake curl gmake gn libdrm libtool libxcb libxkbcommon libX11 libXrender libXi nasm ninja patchelf pkgconf python3 qt6-base unzip zip ``` ## Build steps diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 1226468171e..2299d81eb39 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -836,8 +836,6 @@ set(SOURCES PerformanceTimeline/PerformanceObserverEntryList.cpp PermissionsPolicy/AutoplayAllowlist.cpp PixelUnits.cpp - Platform/AudioCodecPlugin.cpp - Platform/AudioCodecPluginAgnostic.cpp Platform/EventLoopPlugin.cpp Platform/EventLoopPluginSerenity.cpp Platform/FontPlugin.cpp diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index b91bd95d8bd..a59eaf7fc3b 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -988,7 +988,6 @@ class AutoplayAllowlist; namespace Web::Platform { -class AudioCodecPlugin; class Timer; } diff --git a/Libraries/LibWeb/Platform/AudioCodecPlugin.cpp b/Libraries/LibWeb/Platform/AudioCodecPlugin.cpp deleted file mode 100644 index 49c36c3468f..00000000000 --- a/Libraries/LibWeb/Platform/AudioCodecPlugin.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Web::Platform { - -static AudioCodecPlugin::AudioCodecPluginCreator s_creation_hook; - -AudioCodecPlugin::AudioCodecPlugin() = default; -AudioCodecPlugin::~AudioCodecPlugin() = default; - -void AudioCodecPlugin::install_creation_hook(AudioCodecPluginCreator creation_hook) -{ - VERIFY(!s_creation_hook); - s_creation_hook = move(creation_hook); -} - -ErrorOr> AudioCodecPlugin::create(NonnullRefPtr loader) -{ - VERIFY(s_creation_hook); - return s_creation_hook(move(loader)); -} - -ErrorOr> AudioCodecPlugin::read_samples_from_loader(Audio::Loader& loader, size_t samples_to_load) -{ - auto buffer_or_error = loader.get_more_samples(samples_to_load); - if (buffer_or_error.is_error()) { - dbgln("Error while loading samples: {}", buffer_or_error.error()); - return Error::from_string_literal("Error while loading samples"); - } - - return buffer_or_error.release_value(); -} - -AK::Duration AudioCodecPlugin::set_loader_position(Audio::Loader& loader, double position, AK::Duration duration) -{ - if (loader.total_samples() == 0) - return current_loader_position(loader); - - auto duration_value = static_cast(duration.to_milliseconds()) / 1000.0; - position = position / duration_value * static_cast(loader.total_samples() - 1); - - loader.seek(static_cast(position)).release_value_but_fixme_should_propagate_errors(); - return current_loader_position(loader); -} - -AK::Duration AudioCodecPlugin::current_loader_position(Audio::Loader const& loader) -{ - auto samples_played = static_cast(loader.loaded_samples()); - auto sample_rate = static_cast(loader.sample_rate()); - - return AK::Duration::from_milliseconds(static_cast(samples_played / sample_rate * 1000.0)); -} - -} diff --git a/Libraries/LibWeb/Platform/AudioCodecPlugin.h b/Libraries/LibWeb/Platform/AudioCodecPlugin.h deleted file mode 100644 index 4863a904f14..00000000000 --- a/Libraries/LibWeb/Platform/AudioCodecPlugin.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Web::Platform { - -class WEB_API AudioCodecPlugin : public Weakable { -public: - using AudioCodecPluginCreator = Function>(NonnullRefPtr)>; - - static void install_creation_hook(AudioCodecPluginCreator); - static ErrorOr> create(NonnullRefPtr); - - virtual ~AudioCodecPlugin(); - - static ErrorOr> read_samples_from_loader(Audio::Loader&, size_t samples_to_load); - static AK::Duration set_loader_position(Audio::Loader&, double position, AK::Duration duration); - static AK::Duration current_loader_position(Audio::Loader const&); - - virtual void resume_playback() = 0; - virtual void pause_playback() = 0; - virtual void set_volume(double) = 0; - virtual void seek(double) = 0; - - virtual AK::Duration duration() = 0; - - Function on_playback_position_updated; - Function on_decoder_error; - -protected: - AudioCodecPlugin(); -}; - -} diff --git a/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.cpp b/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.cpp deleted file mode 100644 index 1631432efff..00000000000 --- a/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "AudioCodecPluginAgnostic.h" - -namespace Web::Platform { - -constexpr int update_interval = 50; - -static AK::Duration timestamp_from_samples(i64 samples, u32 sample_rate) -{ - return AK::Duration::from_milliseconds(samples * 1000 / sample_rate); -} - -static AK::Duration get_loader_timestamp(NonnullRefPtr const& loader) -{ - return timestamp_from_samples(loader->loaded_samples(), loader->sample_rate()); -} - -ErrorOr> AudioCodecPluginAgnostic::create(NonnullRefPtr const& loader) -{ - auto duration = timestamp_from_samples(loader->total_samples(), loader->sample_rate()); - - auto update_timer = Core::Timer::create(); - update_timer->set_interval(update_interval); - - auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AudioCodecPluginAgnostic(loader, duration, move(update_timer)))); - - constexpr u32 latency_ms = 100; - // FIXME: Audio loaders are hard-coded to output stereo audio. Once that changes, the channel count provided - // below should be retrieved from the audio loader instead of being hard-coded to 2. - RefPtr output = TRY(Audio::PlaybackStream::create( - Audio::OutputState::Suspended, loader->sample_rate(), /* channels = */ 2, latency_ms, - [&plugin = *plugin, loader](Bytes buffer, Audio::PcmSampleFormat format, size_t sample_count) -> ReadonlyBytes { - VERIFY(format == Audio::PcmSampleFormat::Float32); - - auto samples_result = loader->get_more_samples(sample_count); - if (samples_result.is_error()) { - dbgln("Error while loading samples: {}", samples_result.error()); - plugin.on_decoder_error(MUST(String::formatted("Decoding failure: {}", samples_result.error()))); - return buffer.trim(0); - } - - auto samples = samples_result.release_value(); - VERIFY(samples.size() <= sample_count); - - FixedMemoryStream writing_stream { buffer }; - - for (auto& sample : samples) { - MUST(writing_stream.write_value(sample.left)); - MUST(writing_stream.write_value(sample.right)); - } - - // FIXME: Check if we have loaded samples past the current known duration, and if so, update it - // and notify the media element. - return buffer.trim(writing_stream.offset()); - })); - - output->set_underrun_callback([&plugin = *plugin, loader, output]() { - auto new_device_time = output->total_time_played(); - auto new_media_time = timestamp_from_samples(loader->loaded_samples(), loader->sample_rate()); - plugin.m_main_thread_event_loop.deferred_invoke([&plugin, new_device_time, new_media_time]() { - plugin.m_last_resume_in_device_time = new_device_time; - plugin.m_last_resume_in_media_time = new_media_time; - }); - }); - - plugin->m_output = move(output); - - return plugin; -} - -AudioCodecPluginAgnostic::AudioCodecPluginAgnostic(NonnullRefPtr loader, AK::Duration duration, NonnullRefPtr update_timer) - : m_loader(move(loader)) - , m_duration(duration) - , m_main_thread_event_loop(Core::EventLoop::current()) - , m_update_timer(move(update_timer)) -{ - m_update_timer->on_timeout = [self = make_weak_ptr()]() { - if (self) - self->update_timestamp(); - }; -} - -void AudioCodecPluginAgnostic::resume_playback() -{ - m_paused = false; - m_output->resume() - ->when_resolved([self = make_weak_ptr()](AK::Duration new_device_time) { - if (!self) - return; - - self->m_main_thread_event_loop.deferred_invoke([self, new_device_time]() { - if (!self) - return; - - self->m_last_resume_in_device_time = new_device_time; - self->m_update_timer->start(); - }); - }) - .when_rejected([](Error&&) { - // FIXME: Propagate errors. - }); -} - -void AudioCodecPluginAgnostic::pause_playback() -{ - m_paused = true; - m_output->drain_buffer_and_suspend() - ->when_resolved([self = make_weak_ptr()]() { - if (!self) - return; - - auto new_media_time = timestamp_from_samples(self->m_loader->loaded_samples(), self->m_loader->sample_rate()); - auto new_device_time = self->m_output->total_time_played(); - - self->m_main_thread_event_loop.deferred_invoke([self, new_media_time, new_device_time]() { - if (!self) - return; - - self->m_last_resume_in_media_time = new_media_time; - self->m_last_resume_in_device_time = new_device_time; - self->m_update_timer->stop(); - self->update_timestamp(); - }); - }) - .when_rejected([](Error&&) { - // FIXME: Propagate errors. - }); -} - -void AudioCodecPluginAgnostic::set_volume(double volume) -{ - m_output->set_volume(volume)->when_rejected([](Error&&) { - // FIXME: Propagate errors. - }); -} - -void AudioCodecPluginAgnostic::seek(double position) -{ - m_output->discard_buffer_and_suspend() - ->when_resolved([self = make_weak_ptr(), position, was_paused = m_paused]() -> ErrorOr { - if (!self) - return {}; - - auto sample_position = static_cast(position * self->m_loader->sample_rate()); - auto seek_result = self->m_loader->seek(sample_position); - if (seek_result.is_error()) - return Error::from_string_literal("Seeking in audio loader failed"); - - auto new_media_time = get_loader_timestamp(self->m_loader); - auto new_device_time = self->m_output->total_time_played(); - - self->m_main_thread_event_loop.deferred_invoke([self, was_paused, new_device_time, new_media_time]() { - if (!self) - return; - - self->m_last_resume_in_device_time = new_device_time; - self->m_last_resume_in_media_time = new_media_time; - - if (was_paused) { - self->update_timestamp(); - } else { - self->m_output->resume()->when_rejected([](Error&&) { - // FIXME: Propagate errors. - }); - } - }); - - return {}; - }) - .when_rejected([](Error&&) { - // FIXME: Propagate errors. - }); -} - -AK::Duration AudioCodecPluginAgnostic::duration() -{ - return m_duration; -} - -void AudioCodecPluginAgnostic::update_timestamp() -{ - auto current_device_time_delta = m_output->total_time_played() - m_last_resume_in_device_time; - - auto current_media_time = m_last_resume_in_media_time + current_device_time_delta; - current_media_time = min(current_media_time, m_duration); - on_playback_position_updated(current_media_time); -} - -} diff --git a/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.h b/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.h deleted file mode 100644 index e4b0ec7e8bb..00000000000 --- a/Libraries/LibWeb/Platform/AudioCodecPluginAgnostic.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Web::Platform { - -class WEB_API AudioCodecPluginAgnostic final : public AudioCodecPlugin { -public: - static ErrorOr> create(NonnullRefPtr const&); - - virtual void resume_playback() override; - virtual void pause_playback() override; - virtual void set_volume(double) override; - virtual void seek(double) override; - - virtual AK::Duration duration() override; - -private: - explicit AudioCodecPluginAgnostic(NonnullRefPtr loader, AK::Duration, NonnullRefPtr update_timer); - - void update_timestamp(); - - NonnullRefPtr m_loader; - RefPtr m_output { nullptr }; - AK::Duration m_duration { AK::Duration::zero() }; - AK::Duration m_last_resume_in_media_time { AK::Duration::zero() }; - AK::Duration m_last_resume_in_device_time { AK::Duration::zero() }; - Core::EventLoop& m_main_thread_event_loop; - NonnullRefPtr m_update_timer; - bool m_paused { true }; -}; - -} diff --git a/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn b/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn index c9a5f0d3540..01f0ed8fc1c 100644 --- a/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn @@ -3,25 +3,12 @@ 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" ] - - if (enable_qt_multimedia) { - sources += [ - "//Ladybird/Qt/AudioCodecPluginQt.cpp", - "//Ladybird/Qt/AudioThread.cpp", - ] - } } link_qt("WebContent_qt") { qt_components = [ "Core" ] - - if (enable_qt_multimedia) { - qt_components += [ "Multimedia" ] - } } executable("WebContent") { @@ -75,14 +62,6 @@ executable("WebContent") { "//Ladybird/Qt/StringUtils.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" ] } diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/Platform/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/Platform/BUILD.gn index fe8ad5eab59..2e06a53f72f 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/Platform/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/Platform/BUILD.gn @@ -2,8 +2,6 @@ source_set("Platform") { configs += [ "//Userland/Libraries/LibWeb:configs" ] deps = [ "//Userland/Libraries/LibWeb:all_generated" ] sources = [ - "AudioCodecPlugin.cpp", - "AudioCodecPluginAgnostic.cpp", "EventLoopPlugin.cpp", "EventLoopPluginSerenity.cpp", "FontPlugin.cpp", diff --git a/Services/WebContent/CMakeLists.txt b/Services/WebContent/CMakeLists.txt index 1518d0ecf21..daa085f6bc9 100644 --- a/Services/WebContent/CMakeLists.txt +++ b/Services/WebContent/CMakeLists.txt @@ -34,22 +34,7 @@ target_link_libraries(webcontentservice PUBLIC LibCore LibCrypto LibFileSystem L target_link_libraries(webcontentservice PRIVATE OpenSSL::Crypto OpenSSL::SSL) target_link_libraries(webcontentservice PRIVATE SDL3::SDL3) -if (ENABLE_QT AND NOT DEFINED LADYBIRD_AUDIO_BACKEND) - find_package(Qt6 REQUIRED COMPONENTS Multimedia) - - qt_add_executable(WebContent main.cpp) - target_link_libraries(WebContent PRIVATE Qt::Core) - target_compile_definitions(WebContent PRIVATE HAVE_QT=1) - - target_sources(WebContent PRIVATE - ${LADYBIRD_SOURCE_DIR}/UI/Qt/AudioCodecPluginQt.cpp - ${LADYBIRD_SOURCE_DIR}/UI/Qt/AudioThread.cpp - ) - target_link_libraries(WebContent PRIVATE LibWebViewPlatform Qt::Multimedia) - target_compile_definitions(WebContent PRIVATE HAVE_QT_MULTIMEDIA=1) -else() - add_executable(WebContent main.cpp) -endif() +add_executable(WebContent main.cpp) target_link_libraries(WebContent PRIVATE webcontentservice LibURL) diff --git a/Services/WebContent/main.cpp b/Services/WebContent/main.cpp index 60c61a31956..02582b88a1e 100644 --- a/Services/WebContent/main.cpp +++ b/Services/WebContent/main.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -28,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -39,12 +37,6 @@ #include #include -#if defined(HAVE_QT_MULTIMEDIA) -# include -# include -# include -#endif - #if defined(AK_OS_MACOS) # include #endif @@ -69,25 +61,12 @@ ErrorOr ladybird_main(Main::Arguments arguments) return -1; } -#if defined(HAVE_QT_MULTIMEDIA) - QCoreApplication app(arguments.argc, arguments.argv); - - Core::EventLoopManager::install(*new WebView::EventLoopManagerQt); -#endif Core::EventLoop event_loop; WebView::platform_init(); Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity); - Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) { -#if defined(HAVE_QT_MULTIMEDIA) - return Ladybird::AudioCodecPluginQt::create(move(loader)); -#else - return Web::Platform::AudioCodecPluginAgnostic::create(move(loader)); -#endif - }); - StringView command_line {}; StringView executable_path {}; auto config_path = ByteString::formatted("{}/ladybird/default-config", WebView::s_ladybird_resource_root); diff --git a/UI/Android/src/main/cpp/WebContentService.cpp b/UI/Android/src/main/cpp/WebContentService.cpp index f90b4349dc4..21ad00111c1 100644 --- a/UI/Android/src/main/cpp/WebContentService.cpp +++ b/UI/Android/src/main/cpp/WebContentService.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -55,10 +54,6 @@ ErrorOr service_main(int ipc_socket) auto image_decoder_client = TRY(bind_image_decoder_service()); Web::Platform::ImageCodecPlugin::install(*new WebView::ImageCodecPlugin(move(image_decoder_client))); - Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) { - return Web::Platform::AudioCodecPluginAgnostic::create(move(loader)); - }); - Web::Bindings::initialize_main_thread_vm(Web::Bindings::AgentType::SimilarOriginWindow); auto request_server_client = TRY(bind_request_server_service()); diff --git a/UI/Qt/AudioCodecPluginQt.cpp b/UI/Qt/AudioCodecPluginQt.cpp deleted file mode 100644 index ca3d4b678c1..00000000000 --- a/UI/Qt/AudioCodecPluginQt.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Ladybird { - -ErrorOr> AudioCodecPluginQt::create(NonnullRefPtr 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 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(); -} - -AK::Duration AudioCodecPluginQt::duration() -{ - return m_audio_thread->duration(); -} - -} diff --git a/UI/Qt/AudioCodecPluginQt.h b/UI/Qt/AudioCodecPluginQt.h deleted file mode 100644 index e2b9e8e354b..00000000000 --- a/UI/Qt/AudioCodecPluginQt.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -namespace Ladybird { - -class AudioThread; - -class AudioCodecPluginQt final - : public QObject - , public Web::Platform::AudioCodecPlugin { - Q_OBJECT - -public: - static ErrorOr> create(NonnullRefPtr); - 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 AK::Duration duration() override; - -private: - explicit AudioCodecPluginQt(NonnullOwnPtr); - - NonnullOwnPtr m_audio_thread; -}; - -} diff --git a/UI/Qt/AudioThread.cpp b/UI/Qt/AudioThread.cpp deleted file mode 100644 index 5188fc3c1a0..00000000000 --- a/UI/Qt/AudioThread.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * Copyright (c) 2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -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(loader.sample_rate())); - format.setChannelCount(2); - - auto audio_output = make(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 audio_output; - QIODevice* io_device { nullptr }; - -private: - explicit AudioDevice(NonnullOwnPtr output) - : audio_output(move(output)) - { - io_device = audio_output->start(); - } -}; - -ErrorOr> AudioThread::create(NonnullRefPtr loader) -{ - auto task_queue = TRY(AudioTaskQueue::create()); - return adopt_nonnull_own_or_enomem(new (nothrow) AudioThread(move(loader), move(task_queue))); -} - -ErrorOr AudioThread::stop() -{ - TRY(queue_task({ AudioTask::Type::Stop })); - wait(); - - return {}; -} - -ErrorOr AudioThread::queue_task(AudioTask task) -{ - return m_task_queue.blocking_enqueue(move(task), []() { - usleep(UPDATE_RATE_MS * 1000); - }); -} - -AudioThread::AudioThread(NonnullRefPtr loader, AudioTaskQueue task_queue) - : m_loader(move(loader)) - , m_task_queue(move(task_queue)) -{ - auto duration = static_cast(m_loader->total_samples()) / static_cast(m_loader->sample_rate()); - m_duration = AK::Duration::from_milliseconds(static_cast(duration * 1000.0)); -} - -void AudioThread::run() -{ - auto devices = make(); - 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::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 samples) -{ - auto buffer_size = samples.size() * audio_output.format().bytesPerSample() * audio_output.format().channelCount(); - - if (buffer_size > static_cast(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(stream, sample.left); - write_sample(stream, sample.right); - break; - case QAudioFormat::Int16: - write_sample(stream, sample.left); - write_sample(stream, sample.right); - break; - case QAudioFormat::Int32: - write_sample(stream, sample.left); - write_sample(stream, sample.right); - break; - case QAudioFormat::Float: - write_sample(stream, sample.left); - write_sample(stream, sample.right); - break; - default: - VERIFY_NOT_REACHED(); - } - } - - io_device.write(m_sample_buffer.data(), buffer_size); -} - -} diff --git a/UI/Qt/AudioThread.h b/UI/Qt/AudioThread.h deleted file mode 100644 index e11a840d1f4..00000000000 --- a/UI/Qt/AudioThread.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2023, Tim Flynn - * Copyright (c) 2023, Andrew Kaster - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Ladybird { - -static constexpr u32 UPDATE_RATE_MS = 10; - -struct AudioTask { - enum class Type { - Stop, - Play, - Pause, - Seek, - Volume, - RecreateAudioDevice, - }; - - Type type; - Optional data {}; -}; - -using AudioTaskQueue = Core::SharedSingleProducerCircularQueue; - -class AudioThread final : public QThread { // We have to use QThread, otherwise internal Qt media QTimer objects do not work. - Q_OBJECT - -public: - static ErrorOr> create(NonnullRefPtr loader); - - ErrorOr stop(); - - AK::Duration duration() const { return m_duration; } - - ErrorOr queue_task(AudioTask task); - -Q_SIGNALS: - void playback_position_updated(AK::Duration); - -private: - AudioThread(NonnullRefPtr loader, AudioTaskQueue task_queue); - - enum class Paused { - Yes, - No, - }; - - void run() override; - - ErrorOr play_next_samples(QAudioSink& audio_output, QIODevice& io_device); - - void enqueue_samples(QAudioSink const& audio_output, QIODevice& io_device, FixedArray samples); - - template - 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 pcm; - - if constexpr (IsSame) - pcm = static_cast((sample + 1.0f) / 2 * NumericLimits::max()); - else if constexpr (IsSame) - pcm = static_cast(sample * NumericLimits::max()); - else if constexpr (IsSame) - pcm = static_cast(sample * NumericLimits::max()); - else if constexpr (IsSame) - pcm = sample; - else - static_assert(DependentFalse); - - MUST(stream.write_value(pcm)); - } - - NonnullRefPtr m_loader; - AudioTaskQueue m_task_queue; - - QByteArray m_sample_buffer; - - AK::Duration m_duration; - AK::Duration m_position; -}; - -} diff --git a/vcpkg.json b/vcpkg.json index 282a62c32c4..81b2d9ef6fb 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -154,10 +154,6 @@ "zstd" ] }, - { - "name": "qtmultimedia", - "platform": "windows | freebsd" - }, { "name": "pthread", "platform": "windows" @@ -284,10 +280,6 @@ "name": "qtbase", "version": "6.8.3#2" }, - { - "name": "qtmultimedia", - "version": "6.8.3#0" - }, { "name": "sdl3", "version": "3.2.22#0"