ladybird/Libraries/LibMedia/Containers/MP3Navigator.h
Zaggy1024 16f5a3405f LibMedia: Implement buffered ranges for MP3
A new MP3Navigator class is added, which determines timestamps for byte
positions by resyncing to a frame and then interpolating between known
points on either side. The known points start out as the first frame's
position in the file at timestamp 0, and EOF at the timestamp for
FFmpeg's file duration estimate. New buffered ranges are interpolated
between those two points, but also between the end of a prior range and
the start of the next.

Since MP3 can have variable bitrate without declaring it in the file
header, we have to allow buffered ranges to shift forward as new data
arrives to make room for underestimated durations. This is done for all
ranges following the first that has been appended to, keeping the start
of the current range consistent, so that subsequent seeks within that
range remain consistent.

Seeking is also implemented within the navigator to ensure that the
byte<->timestamp mapping is consistent and the buffered ranges begin
exactly where the seek landed.
2026-05-28 10:30:20 -05:00

47 lines
1.4 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/Time.h>
#include <AK/Vector.h>
#include <LibMedia/Containers/ContainerNavigator.h>
#include <LibSync/Mutex.h>
namespace Media {
class MP3Navigator final : public ContainerNavigator {
public:
MP3Navigator(NonnullRefPtr<MediaStream> stream, size_t first_frame_position, AK::Duration total_duration);
struct CachedRange {
size_t byte_start { 0 };
AK::Duration time_start { AK::Duration::zero() };
size_t last_scanned_byte { 0 };
u64 duration_in_ticks { 0 };
};
TimeRanges buffered_time_ranges(Vector<MediaStream::ByteRange> const& byte_ranges) const override;
DecoderErrorOr<SeekResult> seek_to_timestamp(AK::Duration timestamp) const override;
private:
void update_cached_ranges(Vector<MediaStream::ByteRange> const& byte_ranges, MediaStreamCursor&) const;
void reproject_cached_range_times() const;
NonnullRefPtr<MediaStream> m_stream;
size_t m_first_frame_position;
AK::Duration m_total_duration;
NonnullRefPtr<MediaStreamCursor> m_buffered_range_scanning_cursor;
NonnullRefPtr<MediaStreamCursor> m_seek_range_scanning_cursor;
NonnullRefPtr<MediaStreamCursor> m_seek_cursor;
mutable Sync::Mutex m_mutex;
mutable Vector<CachedRange> m_cached_ranges;
};
}