LibMedia: Implement a generic MediaTimeProvider for video-only timing

This time provider can later be swapped out for the AudioMixingSink
when it implements the MediaTimeProvider interface, so that frame
timing can be driven by audio when it is present.
This commit is contained in:
Zaggy1024 2025-09-25 15:15:13 -05:00 committed by Jelle Raaijmakers
parent 3d0b8cc30c
commit 3ebaa0cd3f
Notes: github-actions[bot] 2025-10-28 00:34:38 +00:00
7 changed files with 92 additions and 23 deletions

View file

@ -12,6 +12,7 @@ set(SOURCES
Containers/Matroska/Reader.cpp
PlaybackManager.cpp
Providers/AudioDataProvider.cpp
Providers/GenericTimeProvider.cpp
Providers/VideoDataProvider.cpp
Sinks/AudioMixingSink.cpp
Sinks/DisplayingVideoSink.cpp

View file

@ -8,6 +8,7 @@
#include <LibMedia/FFmpeg/FFmpegDemuxer.h>
#include <LibMedia/MutexedDemuxer.h>
#include <LibMedia/Providers/AudioDataProvider.h>
#include <LibMedia/Providers/GenericTimeProvider.h>
#include <LibMedia/Providers/VideoDataProvider.h>
#include <LibMedia/Sinks/AudioMixingSink.h>
#include <LibMedia/Sinks/DisplayingVideoSink.h>
@ -72,22 +73,26 @@ DecoderErrorOr<NonnullRefPtr<PlaybackManager>> PlaybackManager::try_create(Reado
if (!supported_audio_tracks.is_empty())
audio_sink = DECODER_TRY_ALLOC(AudioMixingSink::try_create());
auto playback_manager = DECODER_TRY_ALLOC(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackManager(demuxer, weak_playback_manager, move(supported_video_tracks), move(supported_video_track_datas), audio_sink, move(supported_audio_tracks), move(supported_audio_track_datas))));
// Create the time provider.
auto time_provider = DECODER_TRY_ALLOC(try_make_ref_counted<GenericTimeProvider>());
auto playback_manager = DECODER_TRY_ALLOC(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackManager(demuxer, weak_playback_manager, time_provider, move(supported_video_tracks), move(supported_video_track_datas), audio_sink, move(supported_audio_tracks), move(supported_audio_track_datas))));
weak_playback_manager->m_manager = playback_manager;
playback_manager->set_up_error_handlers();
return playback_manager;
}
PlaybackManager::PlaybackManager(NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<WeakPlaybackManager> const& weak_wrapper, VideoTracks&& video_tracks, VideoTrackDatas&& video_track_datas, RefPtr<AudioMixingSink> const& audio_sink, AudioTracks&& audio_tracks, AudioTrackDatas&& audio_track_datas)
PlaybackManager::PlaybackManager(NonnullRefPtr<MutexedDemuxer> const& demuxer, NonnullRefPtr<WeakPlaybackManager> const& weak_wrapper, NonnullRefPtr<MediaTimeProvider> const& time_provider, VideoTracks&& video_tracks, VideoTrackDatas&& video_track_datas, RefPtr<AudioMixingSink> const& audio_sink, AudioTracks&& audio_tracks, AudioTrackDatas&& audio_track_datas)
: m_demuxer(demuxer)
, m_weak_wrapper(weak_wrapper)
, m_time_provider(time_provider)
, m_video_tracks(video_tracks)
, m_video_track_datas(video_track_datas)
, m_audio_sink(audio_sink)
, m_audio_tracks(audio_tracks)
, m_audio_track_datas(audio_track_datas)
, m_real_time_base(MonotonicTime::now())
{
m_time_provider->resume();
}
PlaybackManager::~PlaybackManager()
@ -125,11 +130,6 @@ void PlaybackManager::dispatch_error(DecoderError&& error)
on_error(move(error));
}
AK::Duration PlaybackManager::current_time() const
{
return MonotonicTime::now() - m_real_time_base;
}
AK::Duration PlaybackManager::duration() const
{
return m_demuxer->total_duration().value_or(AK::Duration::zero());
@ -165,9 +165,9 @@ NonnullRefPtr<DisplayingVideoSink> PlaybackManager::get_or_create_the_displaying
{
auto& track_data = get_video_data_for_track(track);
if (track_data.display == nullptr) {
track_data.display = MUST(Media::DisplayingVideoSink::try_create(m_weak_wrapper));
track_data.display = MUST(Media::DisplayingVideoSink::try_create(m_time_provider));
track_data.display->set_provider(track, track_data.provider);
track_data.provider->seek(current_time());
track_data.provider->seek(m_time_provider->current_time());
}
VERIFY(track_data.display->provider(track) == track_data.provider);

View file

@ -39,7 +39,7 @@ public:
static DecoderErrorOr<NonnullRefPtr<PlaybackManager>> try_create(ReadonlyBytes data);
~PlaybackManager();
AK::Duration current_time() const;
AK::Duration current_time() const { return m_time_provider->current_time(); }
AK::Duration duration() const;
VideoTracks const& video_tracks() const { return m_video_tracks; }
@ -63,7 +63,7 @@ public:
Function<void(DecoderError&&)> on_error;
private:
class WeakPlaybackManager final : public MediaTimeProvider {
class WeakPlaybackManager : public AtomicRefCounted<WeakPlaybackManager> {
friend class PlaybackManager;
public:
@ -75,14 +75,6 @@ private:
return m_manager;
}
virtual AK::Duration current_time() const override
{
Threading::MutexLocker locker { m_mutex };
if (m_manager)
return m_manager->current_time();
return AK::Duration::zero();
}
private:
void revoke()
{
@ -107,7 +99,7 @@ private:
};
using AudioTrackDatas = Vector<AudioTrackData, EXPECTED_AUDIO_TRACK_COUNT>;
PlaybackManager(NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<WeakPlaybackManager> const&, VideoTracks&&, VideoTrackDatas&&, RefPtr<AudioMixingSink> const&, AudioTracks&&, AudioTrackDatas&&);
PlaybackManager(NonnullRefPtr<MutexedDemuxer> const&, NonnullRefPtr<WeakPlaybackManager> const&, NonnullRefPtr<MediaTimeProvider> const&, VideoTracks&&, VideoTrackDatas&&, RefPtr<AudioMixingSink> const&, AudioTracks&&, AudioTrackDatas&&);
void set_up_error_handlers();
void dispatch_error(DecoderError&&);
@ -119,6 +111,8 @@ private:
NonnullRefPtr<WeakPlaybackManager> m_weak_wrapper;
NonnullRefPtr<MediaTimeProvider> m_time_provider;
VideoTracks m_video_tracks;
VideoTrackDatas m_video_track_datas;
@ -126,8 +120,6 @@ private:
AudioTracks m_audio_tracks;
AudioTrackDatas m_audio_track_datas;
MonotonicTime m_real_time_base;
bool m_is_in_error_state { false };
};

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "GenericTimeProvider.h"
namespace Media {
GenericTimeProvider::GenericTimeProvider() = default;
GenericTimeProvider::~GenericTimeProvider() = default;
AK::Duration GenericTimeProvider::current_time() const
{
auto time = m_media_time;
if (m_monotonic_time_on_resume.has_value())
time += MonotonicTime::now() - m_monotonic_time_on_resume.value();
return time;
}
void GenericTimeProvider::resume()
{
m_monotonic_time_on_resume.emplace(MonotonicTime::now());
}
void GenericTimeProvider::pause()
{
if (!m_monotonic_time_on_resume.has_value())
return;
m_media_time = current_time();
m_monotonic_time_on_resume = {};
}
void GenericTimeProvider::set_time(AK::Duration time)
{
if (m_monotonic_time_on_resume.has_value())
m_monotonic_time_on_resume.emplace(MonotonicTime::now());
m_media_time = time;
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibMedia/Providers/MediaTimeProvider.h>
namespace Media {
class GenericTimeProvider final : public MediaTimeProvider {
public:
GenericTimeProvider();
virtual ~GenericTimeProvider() override;
virtual AK::Duration current_time() const override;
virtual void resume() override;
virtual void pause() override;
virtual void set_time(AK::Duration) override;
private:
Optional<MonotonicTime> m_monotonic_time_on_resume;
AK::Duration m_media_time;
};
}

View file

@ -16,6 +16,9 @@ public:
virtual ~MediaTimeProvider() = default;
virtual AK::Duration current_time() const = 0;
virtual void resume() = 0;
virtual void pause() = 0;
virtual void set_time(AK::Duration) = 0;
};
}