mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibMedia: Move overlapping audio block correction to the data provider
This prevents PlaybackManager's seek while enabling an audio track from causing the AudioMixingSink to push audio blocks forward unnecessarily. Previously, the seek would cause the initial block or blocks to repeat from the perspective of AudioMixingSink, so it would think that it needs to shift the first block after the seek forward by a few samples. By moving this to the AudioDataProvider, we can clear the last sample index every time the decoder is flushed, ensuring that the block shifting always makes sense. By doing this in AudioMixingSink instead of the Decoder implementations, we avoid having to duplicate this shifting logic across multiple implementations. This also fixes an issue where multiple audio blocks occupying the same timestamp would be skipped while seeking, causing a significant break in audio.
This commit is contained in:
parent
7e325d64f5
commit
ae5e200dfc
Notes:
github-actions[bot]
2025-11-17 15:53:04 +00:00
Author: https://github.com/Zaggy1024
Commit: ae5e200dfc
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6786
Reviewed-by: https://github.com/gmta ✅
5 changed files with 38 additions and 21 deletions
|
|
@ -17,7 +17,8 @@ public:
|
|||
|
||||
u32 sample_rate() const { return m_sample_rate; }
|
||||
u8 channel_count() const { return m_channel_count; }
|
||||
AK::Duration start_timestamp() const { return m_start_timestamp; }
|
||||
AK::Duration timestamp() const { return m_timestamp; }
|
||||
i64 timestamp_in_samples() const { return m_timestamp_in_samples; }
|
||||
Data& data() { return m_data; }
|
||||
Data const& data() const { return m_data; }
|
||||
|
||||
|
|
@ -25,20 +26,27 @@ public:
|
|||
{
|
||||
m_sample_rate = 0;
|
||||
m_channel_count = 0;
|
||||
m_start_timestamp = AK::Duration::zero();
|
||||
m_timestamp_in_samples = 0;
|
||||
m_data = Data();
|
||||
}
|
||||
template<typename Callback>
|
||||
void emplace(u32 sample_rate, u8 channel_count, AK::Duration start_timestamp, Callback data_callback)
|
||||
void emplace(u32 sample_rate, u8 channel_count, AK::Duration timestamp, Callback data_callback)
|
||||
{
|
||||
VERIFY(sample_rate != 0);
|
||||
VERIFY(channel_count != 0);
|
||||
VERIFY(m_data.is_empty());
|
||||
m_sample_rate = sample_rate;
|
||||
m_channel_count = channel_count;
|
||||
m_start_timestamp = start_timestamp;
|
||||
m_timestamp = timestamp;
|
||||
m_timestamp_in_samples = timestamp.to_time_units(1, sample_rate);
|
||||
data_callback(m_data);
|
||||
}
|
||||
void set_timestamp_in_samples(i64 timestamp_in_samples)
|
||||
{
|
||||
VERIFY(!is_empty());
|
||||
m_timestamp_in_samples = timestamp_in_samples;
|
||||
m_timestamp = AK::Duration::from_time_units(timestamp_in_samples, 1, m_sample_rate);
|
||||
}
|
||||
bool is_empty() const
|
||||
{
|
||||
return m_sample_rate == 0;
|
||||
|
|
@ -55,7 +63,8 @@ public:
|
|||
private:
|
||||
u32 m_sample_rate { 0 };
|
||||
u8 m_channel_count { 0 };
|
||||
AK::Duration m_start_timestamp;
|
||||
AK::Duration m_timestamp;
|
||||
i64 m_timestamp_in_samples { 0 };
|
||||
Data m_data;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,21 @@ bool AudioDataProvider::ThreadData::should_thread_exit() const
|
|||
return m_exit;
|
||||
}
|
||||
|
||||
void AudioDataProvider::ThreadData::flush_decoder()
|
||||
{
|
||||
m_decoder->flush();
|
||||
m_last_sample = NumericLimits<i64>::min();
|
||||
}
|
||||
|
||||
DecoderErrorOr<void> AudioDataProvider::ThreadData::retrieve_next_block(AudioBlock& block)
|
||||
{
|
||||
TRY(m_decoder->write_next_block(block));
|
||||
if (block.timestamp_in_samples() < m_last_sample)
|
||||
block.set_timestamp_in_samples(m_last_sample);
|
||||
m_last_sample = block.timestamp_in_samples() + static_cast<i64>(block.sample_count());
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void AudioDataProvider::ThreadData::process_seek_on_main_thread(u32 seek_id, T&& function)
|
||||
{
|
||||
|
|
@ -166,7 +181,7 @@ bool AudioDataProvider::ThreadData::handle_seek()
|
|||
auto demuxer_seek_result = demuxer_seek_result_or_error.value_or(DemuxerSeekResult::MovedPosition);
|
||||
|
||||
if (demuxer_seek_result == DemuxerSeekResult::MovedPosition)
|
||||
m_decoder->flush();
|
||||
flush_decoder();
|
||||
|
||||
auto new_seek_id = seek_id;
|
||||
AudioBlock last_block;
|
||||
|
|
@ -191,7 +206,7 @@ bool AudioDataProvider::ThreadData::handle_seek()
|
|||
|
||||
while (new_seek_id == seek_id) {
|
||||
AudioBlock current_block;
|
||||
auto block_result = m_decoder->write_next_block(current_block);
|
||||
auto block_result = retrieve_next_block(current_block);
|
||||
if (block_result.is_error()) {
|
||||
if (block_result.error().category() == DecoderErrorCategory::EndOfStream) {
|
||||
resolve_seek(seek_id);
|
||||
|
|
@ -205,7 +220,7 @@ bool AudioDataProvider::ThreadData::handle_seek()
|
|||
return true;
|
||||
}
|
||||
|
||||
if (current_block.start_timestamp() > timestamp) {
|
||||
if (current_block.timestamp() > timestamp) {
|
||||
auto locker = take_lock();
|
||||
m_queue.clear();
|
||||
|
||||
|
|
@ -291,7 +306,7 @@ void AudioDataProvider::ThreadData::push_data_and_decode_a_block()
|
|||
}
|
||||
|
||||
auto block = AudioBlock();
|
||||
auto timestamp_result = m_decoder->write_next_block(block);
|
||||
auto timestamp_result = retrieve_next_block(block);
|
||||
if (timestamp_result.is_error()) {
|
||||
if (timestamp_result.error().category() == DecoderErrorCategory::NeedsMoreInput)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ private:
|
|||
void set_error_handler(ErrorHandler&&);
|
||||
|
||||
bool should_thread_exit() const;
|
||||
void flush_decoder();
|
||||
DecoderErrorOr<void> retrieve_next_block(AudioBlock&);
|
||||
bool handle_seek();
|
||||
template<typename T>
|
||||
void process_seek_on_main_thread(u32 seek_id, T&&);
|
||||
|
|
@ -80,6 +82,7 @@ private:
|
|||
NonnullRefPtr<MutexedDemuxer> m_demuxer;
|
||||
Track m_track;
|
||||
NonnullOwnPtr<AudioDecoder> m_decoder;
|
||||
i64 m_last_sample { NumericLimits<i64>::min() };
|
||||
|
||||
size_t m_queue_max_size { 8 };
|
||||
AudioQueue m_queue;
|
||||
|
|
|
|||
|
|
@ -111,14 +111,7 @@ void AudioMixingSink::create_playback_stream(u32 sample_rate, u32 channel_count)
|
|||
if (new_block.is_empty())
|
||||
return false;
|
||||
|
||||
auto new_block_first_sample_offset = new_block.start_timestamp().to_time_units(1, sample_rate);
|
||||
if (!track_data.current_block.is_empty() && track_data.current_block.sample_rate() == sample_rate && track_data.current_block.channel_count() == channel_count) {
|
||||
auto current_block_end = track_data.current_block_first_sample_offset + static_cast<i64>(track_data.current_block.sample_count());
|
||||
new_block_first_sample_offset = max(new_block_first_sample_offset, current_block_end);
|
||||
}
|
||||
|
||||
track_data.current_block = move(new_block);
|
||||
track_data.current_block_first_sample_offset = new_block_first_sample_offset;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -137,7 +130,7 @@ void AudioMixingSink::create_playback_stream(u32 sample_rate, u32 channel_count)
|
|||
continue;
|
||||
}
|
||||
|
||||
auto first_sample_offset = track_data.current_block_first_sample_offset;
|
||||
auto first_sample_offset = current_block.timestamp_in_samples();
|
||||
if (first_sample_offset >= samples_end)
|
||||
break;
|
||||
|
||||
|
|
@ -279,10 +272,8 @@ void AudioMixingSink::set_time(AK::Duration time)
|
|||
self->m_next_sample_to_write = time.to_time_units(1, self->m_playback_stream_sample_rate);
|
||||
}
|
||||
|
||||
for (auto& [track, track_data] : self->m_track_mixing_datas) {
|
||||
for (auto& [track, track_data] : self->m_track_mixing_datas)
|
||||
track_data.current_block.clear();
|
||||
track_data.current_block_first_sample_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->m_playing)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ private:
|
|||
|
||||
NonnullRefPtr<AudioDataProvider> provider;
|
||||
AudioBlock current_block;
|
||||
i64 current_block_first_sample_offset { NumericLimits<i64>::min() };
|
||||
};
|
||||
|
||||
void deferred_create_playback_stream(Track const& track);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue