mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-04 18:23:39 +00:00
LibWeb: Begin detecting the end of an HTMLMediaElement media resource
This commit is contained in:
parent
33047b38ec
commit
4797f07883
Notes:
sideshowbarker
2024-07-17 04:32:07 +09:00
Author: https://github.com/trflynn89
Commit: 4797f07883
Pull-request: https://github.com/SerenityOS/serenity/pull/18281
Reviewed-by: https://github.com/linusg
4 changed files with 90 additions and 0 deletions
|
@ -171,6 +171,11 @@ void HTMLMediaElement::set_current_playback_position(double playback_position)
|
||||||
m_official_playback_position = m_current_playback_position;
|
m_official_playback_position = m_current_playback_position;
|
||||||
|
|
||||||
time_marches_on();
|
time_marches_on();
|
||||||
|
|
||||||
|
// NOTE: Invoking the following steps is not listed in the spec. Rather, the spec just describes the scenario in
|
||||||
|
// which these steps should be invoked, which is when we've reached the end of the media playback.
|
||||||
|
if (m_current_playback_position == m_duration)
|
||||||
|
reached_end_of_media_playback().release_value_but_fixme_should_propagate_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#dom-media-duration
|
// https://html.spec.whatwg.org/multipage/media.html#dom-media-duration
|
||||||
|
@ -184,6 +189,16 @@ double HTMLMediaElement::duration() const
|
||||||
return m_duration;
|
return m_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#dom-media-ended
|
||||||
|
bool HTMLMediaElement::ended() const
|
||||||
|
{
|
||||||
|
// The ended attribute must return true if, the last time the event loop reached step 1, the media element had ended
|
||||||
|
// playback and the direction of playback was forwards, and false otherwise.
|
||||||
|
// FIXME: Add a hook into EventLoop::process() to be notified when step 1 is reached.
|
||||||
|
// FIXME: Detect playback direction.
|
||||||
|
return has_ended_playback();
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/media.html#durationChange
|
// https://html.spec.whatwg.org/multipage/media.html#durationChange
|
||||||
void HTMLMediaElement::set_duration(double duration)
|
void HTMLMediaElement::set_duration(double duration)
|
||||||
{
|
{
|
||||||
|
@ -1047,6 +1062,71 @@ void HTMLMediaElement::set_paused(bool paused)
|
||||||
on_paused();
|
on_paused();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#ended-playback
|
||||||
|
bool HTMLMediaElement::has_ended_playback() const
|
||||||
|
{
|
||||||
|
// A media element is said to have ended playback when:
|
||||||
|
|
||||||
|
// The element's readyState attribute is HAVE_METADATA or greater, and
|
||||||
|
if (m_ready_state < ReadyState::HaveMetadata)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Either:
|
||||||
|
if (
|
||||||
|
// The current playback position is the end of the media resource, and
|
||||||
|
m_current_playback_position == m_duration &&
|
||||||
|
|
||||||
|
// FIXME: The direction of playback is forwards, and
|
||||||
|
|
||||||
|
// The media element does not have a loop attribute specified.
|
||||||
|
!has_attribute(HTML::AttributeNames::loop)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Or:
|
||||||
|
// The current playback position is the earliest possible position, and
|
||||||
|
// The direction of playback is backwards.
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/media.html#reaches-the-end
|
||||||
|
WebIDL::ExceptionOr<void> HTMLMediaElement::reached_end_of_media_playback()
|
||||||
|
{
|
||||||
|
// 1. If the media element has a loop attribute specified, then seek to the earliest possible position of the media resource and return.
|
||||||
|
if (has_attribute(HTML::AttributeNames::loop)) {
|
||||||
|
// FIXME: Seek to the beginning of the media resource.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. As defined above, the ended IDL attribute starts returning true once the event loop returns to step 1.
|
||||||
|
|
||||||
|
// 3. Queue a media element task given the media element and the following steps:
|
||||||
|
queue_a_media_element_task([this]() mutable {
|
||||||
|
// 1. Fire an event named timeupdate at the media element.
|
||||||
|
dispatch_time_update_event().release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
// 2. If the media element has ended playback, the direction of playback is forwards, and paused is false, then:
|
||||||
|
// FIXME: Detect playback direction.
|
||||||
|
if (has_ended_playback() && !paused()) {
|
||||||
|
// 1. Set the paused attribute to true.
|
||||||
|
set_paused(true);
|
||||||
|
|
||||||
|
// 2. Fire an event named pause at the media element.
|
||||||
|
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::pause).release_value_but_fixme_should_propagate_errors());
|
||||||
|
|
||||||
|
// 3. Take pending play promises and reject pending play promises with the result and an "AbortError" DOMException.
|
||||||
|
auto promises = take_pending_play_promises();
|
||||||
|
reject_pending_play_promises<WebIDL::AbortError>(promises, "Media playback has ended"_fly_string.release_value_but_fixme_should_propagate_errors());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Fire an event named ended at the media element.
|
||||||
|
dispatch_event(TRY(DOM::Event::create(realm(), HTML::EventNames::ended)));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> HTMLMediaElement::dispatch_time_update_event()
|
WebIDL::ExceptionOr<void> HTMLMediaElement::dispatch_time_update_event()
|
||||||
{
|
{
|
||||||
ScopeGuard guard { [this] { m_running_time_update_event_handler = false; } };
|
ScopeGuard guard { [this] { m_running_time_update_event_handler = false; } };
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
|
|
||||||
double duration() const;
|
double duration() const;
|
||||||
bool paused() const { return m_paused; }
|
bool paused() const { return m_paused; }
|
||||||
|
bool ended() const;
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> play();
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> play();
|
||||||
WebIDL::ExceptionOr<void> pause();
|
WebIDL::ExceptionOr<void> pause();
|
||||||
|
|
||||||
|
@ -93,6 +94,9 @@ private:
|
||||||
void set_paused(bool);
|
void set_paused(bool);
|
||||||
void set_duration(double);
|
void set_duration(double);
|
||||||
|
|
||||||
|
bool has_ended_playback() const;
|
||||||
|
WebIDL::ExceptionOr<void> reached_end_of_media_playback();
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> dispatch_time_update_event();
|
WebIDL::ExceptionOr<void> dispatch_time_update_event();
|
||||||
|
|
||||||
enum class TimeMarchesOnReason {
|
enum class TimeMarchesOnReason {
|
||||||
|
|
|
@ -33,6 +33,7 @@ interface HTMLMediaElement : HTMLElement {
|
||||||
attribute double currentTime;
|
attribute double currentTime;
|
||||||
readonly attribute unrestricted double duration;
|
readonly attribute unrestricted double duration;
|
||||||
readonly attribute boolean paused;
|
readonly attribute boolean paused;
|
||||||
|
readonly attribute boolean ended;
|
||||||
[Reflect, CEReactions] attribute boolean autoplay;
|
[Reflect, CEReactions] attribute boolean autoplay;
|
||||||
[Reflect, CEReactions] attribute boolean loop;
|
[Reflect, CEReactions] attribute boolean loop;
|
||||||
Promise<undefined> play();
|
Promise<undefined> play();
|
||||||
|
|
|
@ -37,6 +37,11 @@ VideoTrack::VideoTrack(JS::Realm& realm, JS::NonnullGCPtr<HTMLMediaElement> medi
|
||||||
m_media_element->set_current_playback_position(playback_position_ms / 1000.0);
|
m_media_element->set_current_playback_position(playback_position_ms / 1000.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
m_playback_manager->on_end_of_stream = [this]() {
|
||||||
|
auto playback_position_ms = static_cast<double>(duration().to_milliseconds());
|
||||||
|
m_media_element->set_current_playback_position(playback_position_ms / 1000.0);
|
||||||
|
};
|
||||||
|
|
||||||
m_playback_manager->on_decoder_error = [](auto) {
|
m_playback_manager->on_decoder_error = [](auto) {
|
||||||
// FIXME: Propagate this error to HTMLMediaElement's error attribute.
|
// FIXME: Propagate this error to HTMLMediaElement's error attribute.
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue