1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 19:57:45 +00:00

Ladybird+LibWeb+WebContent: Create a platform plugin for playing audio

This creates (and installs upon WebContent startup) a platform plugin to
play audio data.

On Serenity, we use AudioServer to play audio over IPC. Unfortunately,
AudioServer is currently coupled with Serenity's audio devices, and thus
cannot be used in Ladybird on Lagom. Instead, we use a Qt audio device
to play the audio, which requires the Qt multimedia package.

While we use Qt to play the audio, note that we can still use LibAudio
to decode the audio data and retrieve samples - we simply send Qt the
raw PCM signals.
This commit is contained in:
Timothy Flynn 2023-06-12 13:44:10 -04:00 committed by Andreas Kling
parent ee48d7514f
commit a34e369252
15 changed files with 317 additions and 9 deletions

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "AudioCodecPluginLadybird.h"
#include <AK/Endian.h>
#include <AK/MemoryStream.h>
#include <LibAudio/Sample.h>
#include <QAudioFormat>
#include <QAudioSink>
#include <QBuffer>
#include <QMediaDevices>
namespace Ladybird {
ErrorOr<NonnullOwnPtr<AudioCodecPluginLadybird>> AudioCodecPluginLadybird::create()
{
auto devices = TRY(adopt_nonnull_own_or_enomem(new (nothrow) QMediaDevices()));
auto const& device_info = devices->defaultAudioOutput();
auto format = device_info.preferredFormat();
format.setSampleFormat(QAudioFormat::Int16);
format.setChannelCount(2);
if (!device_info.isFormatSupported(format))
return Error::from_string_literal("Audio device format not supported");
auto audio_output = TRY(adopt_nonnull_own_or_enomem(new (nothrow) QAudioSink(device_info, format)));
return adopt_nonnull_own_or_enomem(new (nothrow) AudioCodecPluginLadybird(move(devices), move(audio_output)));
}
AudioCodecPluginLadybird::AudioCodecPluginLadybird(NonnullOwnPtr<QMediaDevices> devices, NonnullOwnPtr<QAudioSink> audio_output)
: m_devices(move(devices))
, m_audio_output(move(audio_output))
, m_io_device(m_audio_output->start())
{
}
AudioCodecPluginLadybird::~AudioCodecPluginLadybird() = default;
size_t AudioCodecPluginLadybird::device_sample_rate()
{
return m_audio_output->format().sampleRate();
}
void AudioCodecPluginLadybird::enqueue_samples(FixedArray<Audio::Sample> samples)
{
QByteArray buffer;
buffer.resize(samples.size() * 2 * sizeof(u16));
FixedMemoryStream stream { Bytes { buffer.data(), static_cast<size_t>(buffer.size()) } };
for (auto& sample : samples) {
LittleEndian<i16> pcm;
pcm = static_cast<i16>(sample.left * NumericLimits<i16>::max());
MUST(stream.write_value(pcm));
pcm = static_cast<i16>(sample.right * NumericLimits<i16>::max());
MUST(stream.write_value(pcm));
}
m_io_device->write(buffer.data(), buffer.size());
}
size_t AudioCodecPluginLadybird::remaining_samples() const
{
return 0;
}
void AudioCodecPluginLadybird::resume_playback()
{
m_audio_output->resume();
}
void AudioCodecPluginLadybird::pause_playback()
{
m_audio_output->suspend();
}
void AudioCodecPluginLadybird::playback_ended()
{
m_audio_output->suspend();
}
}

View file

@ -0,0 +1,42 @@
/*
* 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 <LibAudio/Forward.h>
#include <LibWeb/Platform/AudioCodecPlugin.h>
class QAudioSink;
class QIODevice;
class QMediaDevices;
namespace Ladybird {
class AudioCodecPluginLadybird final : public Web::Platform::AudioCodecPlugin {
public:
static ErrorOr<NonnullOwnPtr<AudioCodecPluginLadybird>> create();
virtual ~AudioCodecPluginLadybird() override;
virtual size_t device_sample_rate() override;
virtual void enqueue_samples(FixedArray<Audio::Sample>) override;
virtual size_t remaining_samples() const override;
virtual void resume_playback() override;
virtual void pause_playback() override;
virtual void playback_ended() override;
private:
AudioCodecPluginLadybird(NonnullOwnPtr<QMediaDevices>, NonnullOwnPtr<QAudioSink>);
NonnullOwnPtr<QMediaDevices> m_devices;
NonnullOwnPtr<QAudioSink> m_audio_output;
QIODevice* m_io_device { nullptr };
};
}

View file

@ -73,7 +73,7 @@ add_compile_options(-Wno-user-defined-literals)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network Svg)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network Svg Multimedia)
set(BROWSER_SOURCE_DIR ${SERENITY_SOURCE_DIR}/Userland/Applications/Browser/)

View file

@ -6,6 +6,7 @@ set(WEBCONTENT_SOURCES
${WEBCONTENT_SOURCE_DIR}/PageHost.cpp
${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp
${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.cpp
../AudioCodecPluginLadybird.cpp
../EventLoopImplementationQt.cpp
../FontPluginQt.cpp
../ImageCodecPluginLadybird.cpp
@ -25,4 +26,4 @@ qt_add_executable(WebContent ${WEBCONTENT_SOURCES})
target_include_directories(WebContent PRIVATE ${SERENITY_SOURCE_DIR}/Userland/Services/)
target_include_directories(WebContent PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(WebContent PRIVATE Qt::Core Qt::Gui Qt::Network LibCore LibFileSystem LibGfx LibIPC LibJS LibMain LibWeb LibWebSocket)
target_link_libraries(WebContent PRIVATE Qt::Core Qt::Gui Qt::Network Qt::Multimedia LibAudio LibCore LibFileSystem LibGfx LibIPC LibJS LibMain LibWeb LibWebSocket)

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "../AudioCodecPluginLadybird.h"
#include "../EventLoopImplementationQt.h"
#include "../FontPluginQt.h"
#include "../ImageCodecPluginLadybird.h"
@ -57,6 +58,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPluginLadybird);
Web::Platform::AudioCodecPlugin::install_creation_hook([] {
return Ladybird::AudioCodecPluginLadybird::create();
});
Web::ResourceLoader::initialize(RequestManagerQt::create());
Web::WebSockets::WebSocketClientManager::initialize(Ladybird::WebSocketClientManagerLadybird::create());