diff --git a/Libraries/LibMedia/CMakeLists.txt b/Libraries/LibMedia/CMakeLists.txt index f47c6205495..6d9b4c76e6d 100644 --- a/Libraries/LibMedia/CMakeLists.txt +++ b/Libraries/LibMedia/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES Containers/Matroska/Reader.cpp IncrementallyPopulatedStream.cpp PlaybackManager.cpp + PlaybackStates/StartingStateHandler.cpp PlaybackStates/PausedStateHandler.cpp PlaybackStates/PlaybackStateHandler.cpp PlaybackStates/ResumingStateHandler.cpp diff --git a/Libraries/LibMedia/PlaybackManager.cpp b/Libraries/LibMedia/PlaybackManager.cpp index 602df7d1f80..b2ac80c3b85 100644 --- a/Libraries/LibMedia/PlaybackManager.cpp +++ b/Libraries/LibMedia/PlaybackManager.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -146,7 +146,7 @@ DecoderErrorOr PlaybackManager::prepare_playback_from_demuxer(WeakPlayback NonnullOwnPtr PlaybackManager::create() { auto playback_manager = adopt_own(*new (nothrow) PlaybackManager()); - playback_manager->m_handler = make(*playback_manager, RESUMING_SUSPEND_TIMEOUT_MS); + playback_manager->m_handler = make(*playback_manager); playback_manager->m_handler->on_enter(); return playback_manager; } @@ -346,12 +346,12 @@ void PlaybackManager::enable_an_audio_track(Track const& track) { auto& track_data = get_audio_data_for_track(track); VERIFY(!track_data.enabled); + track_data.enabled = true; if (m_audio_sink) { VERIFY(m_audio_sink->provider(track) == nullptr); m_audio_sink->set_provider(track, track_data.provider); + m_tracks_still_buffering.set(track); } - track_data.enabled = true; - m_tracks_still_buffering.set(track); m_handler->on_track_enabled(track); } @@ -384,6 +384,11 @@ bool PlaybackManager::track_is_enabled(Track const& track) const return true; } +void PlaybackManager::start() +{ + m_handler->start(); +} + void PlaybackManager::play() { m_handler->play(); diff --git a/Libraries/LibMedia/PlaybackManager.h b/Libraries/LibMedia/PlaybackManager.h index 2025c619c65..f81b3f0cafa 100644 --- a/Libraries/LibMedia/PlaybackManager.h +++ b/Libraries/LibMedia/PlaybackManager.h @@ -78,6 +78,7 @@ public: bool track_is_enabled(Track const&) const; + void start(); void play(); void pause(); void seek(AK::Duration timestamp, SeekMode); diff --git a/Libraries/LibMedia/PlaybackStates/AvailableData.h b/Libraries/LibMedia/PlaybackStates/AvailableData.h index b4c6cb39bbb..7523318498a 100644 --- a/Libraries/LibMedia/PlaybackStates/AvailableData.h +++ b/Libraries/LibMedia/PlaybackStates/AvailableData.h @@ -11,6 +11,7 @@ namespace Media { enum class AvailableData : u8 { + None, Current, Future, }; diff --git a/Libraries/LibMedia/PlaybackStates/Forward.h b/Libraries/LibMedia/PlaybackStates/Forward.h index 57c39396d3e..e1636d8261b 100644 --- a/Libraries/LibMedia/PlaybackStates/Forward.h +++ b/Libraries/LibMedia/PlaybackStates/Forward.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Gregory Bertilson + * Copyright (c) 2025-present, the Ladybird developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,6 +9,7 @@ #include #define ENUMERATE_PLAYBACK_STATE_HANDLERS(X) \ + X(StartingStateHandler) \ X(BufferingStateHandler) \ X(PlaybackStateHandler) \ X(PlayingStateHandler) \ diff --git a/Libraries/LibMedia/PlaybackStates/PlaybackState.h b/Libraries/LibMedia/PlaybackStates/PlaybackState.h index 6e1602b6516..3cfefc36777 100644 --- a/Libraries/LibMedia/PlaybackStates/PlaybackState.h +++ b/Libraries/LibMedia/PlaybackStates/PlaybackState.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Gregory Bertilson + * Copyright (c) 2025-2026, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +11,7 @@ namespace Media { enum class PlaybackState : u8 { + Starting, Buffering, Playing, Paused, diff --git a/Libraries/LibMedia/PlaybackStates/PlaybackStateHandler.h b/Libraries/LibMedia/PlaybackStates/PlaybackStateHandler.h index 0eecb6e129f..7386617fcb0 100644 --- a/Libraries/LibMedia/PlaybackStates/PlaybackStateHandler.h +++ b/Libraries/LibMedia/PlaybackStates/PlaybackStateHandler.h @@ -25,6 +25,7 @@ public: virtual void on_enter() = 0; virtual void on_exit() = 0; + virtual void start() { } virtual void play() = 0; virtual void pause() = 0; virtual void seek(AK::Duration timestamp, SeekMode); diff --git a/Libraries/LibMedia/PlaybackStates/StartingStateHandler.cpp b/Libraries/LibMedia/PlaybackStates/StartingStateHandler.cpp new file mode 100644 index 00000000000..957b41bbbf1 --- /dev/null +++ b/Libraries/LibMedia/PlaybackStates/StartingStateHandler.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "StartingStateHandler.h" + +#include +#include + +namespace Media { + +void StartingStateHandler::start() +{ + m_started = true; + + if (manager().m_tracks_still_buffering.is_empty()) + resume(); +} + +void StartingStateHandler::exit_buffering() +{ + if (m_started) + resume(); +} + +} diff --git a/Libraries/LibMedia/PlaybackStates/StartingStateHandler.h b/Libraries/LibMedia/PlaybackStates/StartingStateHandler.h new file mode 100644 index 00000000000..230b7e3801b --- /dev/null +++ b/Libraries/LibMedia/PlaybackStates/StartingStateHandler.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Media { + +class StartingStateHandler final : public ResumingStateHandler { +public: + StartingStateHandler(PlaybackManager& manager) + : ResumingStateHandler(manager, false) + { + } + virtual ~StartingStateHandler() override = default; + + virtual void start() override; + + virtual PlaybackState state() override + { + return PlaybackState::Starting; + } + virtual AvailableData available_data() override + { + return AvailableData::None; + } + + virtual void enter_buffering() override { } + virtual void exit_buffering() override; + +private: + bool m_started { false }; +}; + +} diff --git a/Libraries/LibWeb/HTML/HTMLMediaElement.cpp b/Libraries/LibWeb/HTML/HTMLMediaElement.cpp index 6be7a496b5e..b1f3110ff1d 100644 --- a/Libraries/LibWeb/HTML/HTMLMediaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLMediaElement.cpp @@ -1743,7 +1743,9 @@ void HTMLMediaElement::on_metadata_parsed() }); } - // AD-HOC: If we've already got buffered data, we need to upgrade the readyState further than HAVE_METADATA. + // AD-HOC: Now that we've enabled one of each available track type, the playback manager can be started. If this + // causes the playback manager to exit the initial state, the ready state should change. + m_playback_manager->start(); update_ready_state(); } @@ -2127,7 +2129,8 @@ void HTMLMediaElement::update_ready_state() auto current_range = ranges.range_at_or_after(current_time); auto available_data = m_playback_manager->available_data(); - if (available_data == Media::AvailableData::Current && !current_range.has_value()) { + if (available_data == Media::AvailableData::None + || (available_data == Media::AvailableData::Current && !current_range.has_value())) { // 1. Set the HTMLMediaElement's readyState attribute to HAVE_METADATA. set_ready_state(ReadyState::HaveMetadata); // 2. Abort these steps. diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-after-decode-error.txt b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-after-decode-error.txt index bf994e7b403..00882314b67 100644 --- a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-after-decode-error.txt +++ b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-after-decode-error.txt @@ -1,15 +1,12 @@ loadstart: readyState=0 -durationchange: readyState=4 duration=0 -loadedmetadata: readyState=4 -loadeddata: readyState=4 -canplay: readyState=4 -canplaythrough: readyState=4 -error: readyState=4 code=3 +durationchange: readyState=1 duration=0 +loadedmetadata: readyState=1 +error: readyState=1 code=3 abort: readyState=0 emptied: readyState=0 loadstart: readyState=0 -durationchange: readyState=4 duration=12.043 -loadedmetadata: readyState=4 +durationchange: readyState=1 duration=12.043 +loadedmetadata: readyState=1 loadeddata: readyState=4 canplay: readyState=4 canplaythrough: readyState=4 diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-during-error.txt b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-during-error.txt index 078189ee60e..165a469e46e 100644 --- a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-during-error.txt +++ b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-load-during-error.txt @@ -5,8 +5,8 @@ Events: error handler: src setter returned: readyState=0 emptied: readyState=0 loadstart: readyState=0 - durationchange: readyState=4 duration=12.043 - loadedmetadata: readyState=4 + durationchange: readyState=1 duration=12.043 + loadedmetadata: readyState=1 loadeddata: readyState=4 canplay: readyState=4 canplaythrough: readyState=4 diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-readyState-progression.txt b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-readyState-progression.txt index c34064130dd..d5f7f42fab1 100644 --- a/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-readyState-progression.txt +++ b/Tests/LibWeb/Text/expected/HTML/HTMLMediaElement-readyState-progression.txt @@ -1,6 +1,6 @@ loadstart: readyState=0 -durationchange: readyState=4 duration=12.043 -loadedmetadata: readyState=4 +durationchange: readyState=1 duration=12.043 +loadedmetadata: readyState=1 loadeddata: readyState=4 canplay: readyState=4 canplaythrough: readyState=4