mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Implement appending and demuxing WebM MSE segments
The segments are parsed for the SourceBufferProcessor by the WebMByteStreamParser. It parses the initialization segment to update its internal set of tracks, then SourceBufferProcessor/SourceBuffer set them up for playback. When a media segment is received, it also parses as much of it as is available, returning all the coded frames found so far. SourceBufferProcessor then tells TrackBufferDemuxer to remove any overlapping frames and insert the new ones. TrackBufferDemuxer implements the Demuxer interface in terms of the coded frame store maintained by the SourceBufferProcessor. It returns the frames in decode order when requested by a data provider. When a is needed, it finds the keyframe prior to the target timestamp, and checks that there are no gaps in data up to the target timestamp. If there are any gaps, it blocks until the gaps are gone.
This commit is contained in:
committed by
Gregory Bertilson
parent
c4612110f5
commit
51c3f7c41e
Notes:
github-actions[bot]
2026-04-01 07:56:02 +00:00
Author: https://github.com/Zaggy1024 Commit: https://github.com/LadybirdBrowser/ladybird/commit/51c3f7c41e7 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8655 Reviewed-by: https://github.com/tcl3
@@ -1182,7 +1182,7 @@ void HTMLMediaElement::load_url_resource(URL::URL const& url_record, Function<vo
|
||||
failure_callback(move(error_message));
|
||||
};
|
||||
|
||||
set_up_playback_manager();
|
||||
set_up_playback_manager_for_remote();
|
||||
|
||||
load_remote_resource(EntireResource {});
|
||||
}
|
||||
@@ -1424,6 +1424,7 @@ void HTMLMediaElement::load_local_resource(MediaProviderObject const& media_prov
|
||||
// 3. Set [[port to main]] null.
|
||||
|
||||
media_source->set_assigned_to_media_element({}, *this);
|
||||
set_up_playback_manager_for_local();
|
||||
|
||||
// 4. Set the readyState attribute to "open".
|
||||
// 5. Queue a task to fire an event named sourceopen at the MediaSource.
|
||||
@@ -1520,6 +1521,11 @@ void HTMLMediaElement::restart_fetch_at_offset(u64 offset)
|
||||
|
||||
void HTMLMediaElement::set_audio_track_enabled(Badge<AudioTrack>, GC::Ptr<HTML::AudioTrack> audio_track, bool enabled)
|
||||
{
|
||||
VERIFY(m_playback_manager);
|
||||
|
||||
if (!m_playback_manager->audio_tracks().contains_slow(audio_track->track_in_playback_manager()))
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
m_playback_manager->enable_an_audio_track(audio_track->track_in_playback_manager());
|
||||
else
|
||||
@@ -1535,6 +1541,11 @@ Painting::ExternalContentSource& HTMLMediaElement::ensure_external_content_sourc
|
||||
|
||||
void HTMLMediaElement::set_selected_video_track(Badge<VideoTrack>, GC::Ptr<HTML::VideoTrack> video_track)
|
||||
{
|
||||
VERIFY(m_playback_manager);
|
||||
|
||||
if (video_track && !m_playback_manager->video_tracks().contains_slow(video_track->track_in_playback_manager()))
|
||||
return;
|
||||
|
||||
if (m_selected_video_track) {
|
||||
VERIFY(m_selected_video_track_sink);
|
||||
m_playback_manager->remove_the_displaying_video_sink_for_track(m_selected_video_track->track_in_playback_manager());
|
||||
@@ -1579,7 +1590,7 @@ void HTMLMediaElement::on_audio_track_added(Media::Track const& track)
|
||||
auto audio_track = realm.create<AudioTrack>(realm, *this, track);
|
||||
|
||||
// 2. Update the media element's audioTracks attribute's AudioTrackList object with the new AudioTrack object.
|
||||
m_audio_tracks->add_track({}, audio_track);
|
||||
m_audio_tracks->add_track(audio_track);
|
||||
|
||||
// 3. Let enable be unknown.
|
||||
auto enable = TriState::Unknown;
|
||||
@@ -1621,7 +1632,7 @@ void HTMLMediaElement::on_video_track_added(Media::Track const& track)
|
||||
auto video_track = realm.create<VideoTrack>(realm, *this, track);
|
||||
|
||||
// 2. Update the media element's videoTracks attribute's VideoTrackList object with the new VideoTrack object.
|
||||
m_video_tracks->add_track({}, *video_track);
|
||||
m_video_tracks->add_track(video_track);
|
||||
|
||||
// 3. Let enable be unknown.
|
||||
auto enable = TriState::Unknown;
|
||||
@@ -1737,7 +1748,7 @@ void HTMLMediaElement::on_metadata_parsed()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list
|
||||
void HTMLMediaElement::set_up_playback_manager()
|
||||
void HTMLMediaElement::set_up_playback_manager_for_remote()
|
||||
{
|
||||
m_playback_manager = Media::PlaybackManager::create();
|
||||
|
||||
@@ -1797,6 +1808,63 @@ void HTMLMediaElement::set_up_playback_manager()
|
||||
});
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list
|
||||
void HTMLMediaElement::set_up_playback_manager_for_local()
|
||||
{
|
||||
m_playback_manager = Media::PlaybackManager::create();
|
||||
|
||||
m_has_enabled_preferred_audio_track = false;
|
||||
m_has_selected_preferred_video_track = false;
|
||||
|
||||
// NB: The spec is unclear on whether the following media resource track conditions should trigger multiple
|
||||
// times on one media resource, but it is implied to be possible by the start of the "Media elements"
|
||||
// section, where it says that a "media resource can have multiple audio and video tracks."
|
||||
// https://html.spec.whatwg.org/multipage/media.html#media-elements
|
||||
// Therefore, we enumerate all the available tracks into our VideoTrackList and AudioTrackList.
|
||||
|
||||
// AD-HOC: Enable the tracks in PlaybackManager if MediaSource already enabled them in the DOM.
|
||||
// Note that we do not want to call on_(audio/video)_track_added() here, since the MSE spec takes care
|
||||
// of setting up the tracks.
|
||||
m_playback_manager->on_track_added = GC::weak_callback(*this, [](auto& self, auto& track) {
|
||||
if (track.type() == Media::TrackType::Audio) {
|
||||
self.m_audio_tracks->for_each_track([&](auto& element_track) {
|
||||
if (!element_track.enabled())
|
||||
return IterationDecision::Continue;
|
||||
if (element_track.track_in_playback_manager() == track) {
|
||||
self.m_playback_manager->enable_an_audio_track(track);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
} else if (track.type() == Media::TrackType::Video) {
|
||||
self.m_video_tracks->for_each_track([&](auto& element_track) {
|
||||
if (!element_track.selected())
|
||||
return IterationDecision::Continue;
|
||||
if (element_track.track_in_playback_manager() == track) {
|
||||
self.m_selected_video_track = element_track;
|
||||
self.m_selected_video_track_sink = self.m_playback_manager->get_or_create_the_displaying_video_sink_for_track(track);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// -> Once enough of the media data has been fetched to determine the duration of the media resource, its dimensions, and other metadata
|
||||
m_playback_manager->on_metadata_parsed = GC::weak_callback(*this, [](auto& self) {
|
||||
self.on_metadata_parsed();
|
||||
});
|
||||
|
||||
// -> If the media data is corrupted
|
||||
m_playback_manager->on_error = GC::weak_callback(*this, [](auto& self, Media::DecoderError&& error) {
|
||||
self.set_decoder_error(MUST(String::from_utf8(error.description())));
|
||||
});
|
||||
|
||||
m_playback_manager->on_playback_state_change = GC::weak_callback(*this, [](auto& self) {
|
||||
self.on_playback_manager_state_change();
|
||||
});
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list
|
||||
void HTMLMediaElement::process_media_data(FetchingStatus fetching_status)
|
||||
{
|
||||
@@ -1893,8 +1961,8 @@ void HTMLMediaElement::forget_media_resource_specific_tracks()
|
||||
// of text tracks all the media-resource-specific text tracks, then empty the media element's audioTracks attribute's AudioTrackList object, then
|
||||
// empty the media element's videoTracks attribute's VideoTrackList object. No events (in particular, no removetrack events) are fired as part of
|
||||
// this; the error and emptied events, fired by the algorithms that invoke this one, can be used instead.
|
||||
m_audio_tracks->remove_all_tracks({});
|
||||
m_video_tracks->remove_all_tracks({});
|
||||
m_audio_tracks->remove_all_tracks();
|
||||
m_video_tracks->remove_all_tracks();
|
||||
m_playback_manager.clear();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user