LibMedia: Flush the video decoder when seeking

This allows H.264 videos to seek correctly.
This commit is contained in:
Zaggy1024 2024-06-19 22:36:27 -05:00 committed by Andrew Kaster
commit 8848ee775b
Notes: sideshowbarker 2024-07-17 02:38:39 +09:00
4 changed files with 46 additions and 38 deletions

View file

@ -82,6 +82,8 @@ enum class VideoFullRangeFlag : u8 {
// https://en.wikipedia.org/wiki/Coding-independent_code_points // https://en.wikipedia.org/wiki/Coding-independent_code_points
struct CodingIndependentCodePoints { struct CodingIndependentCodePoints {
public: public:
constexpr CodingIndependentCodePoints() = default;
constexpr CodingIndependentCodePoints(ColorPrimaries color_primaries, TransferCharacteristics transfer_characteristics, MatrixCoefficients matrix_coefficients, VideoFullRangeFlag video_full_range_flag) constexpr CodingIndependentCodePoints(ColorPrimaries color_primaries, TransferCharacteristics transfer_characteristics, MatrixCoefficients matrix_coefficients, VideoFullRangeFlag video_full_range_flag)
: m_color_primaries(color_primaries) : m_color_primaries(color_primaries)
, m_transfer_characteristics(transfer_characteristics) , m_transfer_characteristics(transfer_characteristics)
@ -124,10 +126,10 @@ public:
} }
private: private:
ColorPrimaries m_color_primaries; ColorPrimaries m_color_primaries = ColorPrimaries::BT709;
TransferCharacteristics m_transfer_characteristics; TransferCharacteristics m_transfer_characteristics = TransferCharacteristics::BT709;
MatrixCoefficients m_matrix_coefficients; MatrixCoefficients m_matrix_coefficients = MatrixCoefficients::BT709;
VideoFullRangeFlag m_video_full_range_flag; VideoFullRangeFlag m_video_full_range_flag = VideoFullRangeFlag::Full;
}; };
constexpr StringView color_primaries_to_string(ColorPrimaries color_primaries) constexpr StringView color_primaries_to_string(ColorPrimaries color_primaries)

View file

@ -92,7 +92,7 @@ Duration PlaybackManager::current_playback_time()
Duration PlaybackManager::duration() Duration PlaybackManager::duration()
{ {
auto duration_result = ({ auto duration_result = ({
auto demuxer_locker = Threading::MutexLocker(m_demuxer_mutex); auto demuxer_locker = Threading::MutexLocker(m_decoder_mutex);
m_demuxer->duration(); m_demuxer->duration();
}); });
if (duration_result.is_error()) { if (duration_result.is_error()) {
@ -168,7 +168,10 @@ void PlaybackManager::seek_to_timestamp(Duration target_timestamp, SeekMode seek
DecoderErrorOr<Optional<Duration>> PlaybackManager::seek_demuxer_to_most_recent_keyframe(Duration timestamp, Optional<Duration> earliest_available_sample) DecoderErrorOr<Optional<Duration>> PlaybackManager::seek_demuxer_to_most_recent_keyframe(Duration timestamp, Optional<Duration> earliest_available_sample)
{ {
return m_demuxer->seek_to_most_recent_keyframe(m_selected_video_track, timestamp, move(earliest_available_sample)); auto seeked_timestamp = TRY(m_demuxer->seek_to_most_recent_keyframe(m_selected_video_track, timestamp, move(earliest_available_sample)));
if (seeked_timestamp.has_value())
m_decoder->flush();
return seeked_timestamp;
} }
Optional<FrameQueueItem> PlaybackManager::dequeue_one_frame() Optional<FrameQueueItem> PlaybackManager::dequeue_one_frame()
@ -202,17 +205,20 @@ void PlaybackManager::decode_and_queue_one_sample()
FrameQueueItem item_to_enqueue; FrameQueueItem item_to_enqueue;
while (item_to_enqueue.is_empty()) { while (item_to_enqueue.is_empty()) {
OwnPtr<VideoFrame> decoded_frame = nullptr;
CodingIndependentCodePoints container_cicp;
{
Threading::MutexLocker decoder_locker(m_decoder_mutex);
// Get a sample to decode. // Get a sample to decode.
auto sample_result = [&]() { auto sample_result = m_demuxer->get_next_sample_for_track(m_selected_video_track);
// FIXME: Implement and use a class to enforce that this field is accessed through a mutex (like Kernel::MutexProtected).
Threading::MutexLocker demuxer_locker(m_demuxer_mutex);
return m_demuxer->get_next_sample_for_track(m_selected_video_track);
}();
if (sample_result.is_error()) { if (sample_result.is_error()) {
item_to_enqueue = FrameQueueItem::error_marker(sample_result.release_error(), FrameQueueItem::no_timestamp); item_to_enqueue = FrameQueueItem::error_marker(sample_result.release_error(), FrameQueueItem::no_timestamp);
break; break;
} }
auto sample = sample_result.release_value(); auto sample = sample_result.release_value();
container_cicp = sample.auxiliary_data().get<Video::VideoSampleData>().container_cicp();
// Submit the sample to the decoder. // Submit the sample to the decoder.
auto decode_result = m_decoder->receive_sample(sample.timestamp(), sample.data()); auto decode_result = m_decoder->receive_sample(sample.timestamp(), sample.data());
@ -222,7 +228,6 @@ void PlaybackManager::decode_and_queue_one_sample()
} }
// Retrieve the last available frame to present. // Retrieve the last available frame to present.
OwnPtr<VideoFrame> decoded_frame = nullptr;
while (true) { while (true) {
auto frame_result = m_decoder->get_decoded_frame(); auto frame_result = m_decoder->get_decoded_frame();
@ -237,11 +242,12 @@ void PlaybackManager::decode_and_queue_one_sample()
decoded_frame = frame_result.release_value(); decoded_frame = frame_result.release_value();
} }
}
// Convert the frame for display. // Convert the frame for display.
if (decoded_frame != nullptr) { if (decoded_frame != nullptr) {
auto& cicp = decoded_frame->cicp(); auto& cicp = decoded_frame->cicp();
cicp.adopt_specified_values(sample.auxiliary_data().get<Video::VideoSampleData>().container_cicp()); cicp.adopt_specified_values(container_cicp);
cicp.default_code_points_if_unspecified({ ColorPrimaries::BT709, TransferCharacteristics::BT709, MatrixCoefficients::BT709, VideoFullRangeFlag::Studio }); cicp.default_code_points_if_unspecified({ ColorPrimaries::BT709, TransferCharacteristics::BT709, MatrixCoefficients::BT709, VideoFullRangeFlag::Studio });
// BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, so other applications // BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, so other applications
@ -570,7 +576,7 @@ private:
} }
{ {
Threading::MutexLocker demuxer_locker(manager().m_demuxer_mutex); Threading::MutexLocker demuxer_locker(manager().m_decoder_mutex);
auto demuxer_seek_result = manager().seek_demuxer_to_most_recent_keyframe(m_target_timestamp, earliest_available_sample); auto demuxer_seek_result = manager().seek_demuxer_to_most_recent_keyframe(m_target_timestamp, earliest_available_sample);
if (demuxer_seek_result.is_error()) { if (demuxer_seek_result.is_error()) {

View file

@ -176,7 +176,7 @@ private:
Duration m_last_present_in_media_time = Duration::zero(); Duration m_last_present_in_media_time = Duration::zero();
NonnullOwnPtr<Demuxer> m_demuxer; NonnullOwnPtr<Demuxer> m_demuxer;
Threading::Mutex m_demuxer_mutex; Threading::Mutex m_decoder_mutex;
Track m_selected_video_track; Track m_selected_video_track;
VideoFrameQueue m_frame_queue; VideoFrameQueue m_frame_queue;