123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /*
- * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Format.h>
- #include <LibCore/Timer.h>
- #include <LibVideo/MatroskaReader.h>
- #include <LibVideo/VP9/Decoder.h>
- #include "MatroskaDemuxer.h"
- #include "PlaybackManager.h"
- namespace Video {
- DecoderErrorOr<NonnullRefPtr<PlaybackManager>> PlaybackManager::from_file(Object* event_handler, StringView filename)
- {
- NonnullOwnPtr<Demuxer> demuxer = TRY(MatroskaDemuxer::from_file(filename));
- auto video_tracks = demuxer->get_tracks_for_type(TrackType::Video);
- if (video_tracks.is_empty())
- return DecoderError::with_description(DecoderErrorCategory::Invalid, "No video track is present"sv);
- auto track = video_tracks[0];
- dbgln_if(PLAYBACK_MANAGER_DEBUG, "Selecting video track number {}", track.identifier());
- NonnullOwnPtr<VideoDecoder> decoder = make<VP9::Decoder>();
- return PlaybackManager::construct(event_handler, demuxer, track, decoder);
- }
- PlaybackManager::PlaybackManager(Object* event_handler, NonnullOwnPtr<Demuxer>& demuxer, Track video_track, NonnullOwnPtr<VideoDecoder>& decoder)
- : Object(event_handler)
- , m_main_loop(Core::EventLoop::current())
- , m_demuxer(move(demuxer))
- , m_selected_video_track(video_track)
- , m_decoder(move(decoder))
- , m_frame_queue(make<VideoFrameQueue>())
- , m_present_timer(Core::Timer::construct())
- , m_decode_timer(Core::Timer::construct())
- {
- m_present_timer->set_single_shot(true);
- m_present_timer->set_interval(0);
- m_present_timer->on_timeout = [&] { update_presented_frame(); };
- m_decode_timer->set_single_shot(true);
- m_decode_timer->set_interval(0);
- m_decode_timer->on_timeout = [&] { on_decode_timer(); };
- }
- void PlaybackManager::set_playback_status(PlaybackStatus status)
- {
- if (status != m_status) {
- auto old_status = m_status;
- m_status = status;
- dbgln_if(PLAYBACK_MANAGER_DEBUG, "Set playback status from {} to {}", playback_status_to_string(old_status), playback_status_to_string(m_status));
- if (status == PlaybackStatus::Playing) {
- if (old_status == PlaybackStatus::Stopped || old_status == PlaybackStatus::Corrupted) {
- restart_playback();
- m_frame_queue->clear();
- m_skipped_frames = 0;
- }
- m_last_present_in_real_time = Time::now_monotonic();
- m_present_timer->start();
- } else {
- m_last_present_in_media_time = current_playback_time();
- m_last_present_in_real_time = Time::zero();
- m_present_timer->stop();
- }
- m_main_loop.post_event(*this, make<PlaybackStatusChangeEvent>(status, old_status));
- }
- }
- void PlaybackManager::event(Core::Event& event)
- {
- if (event.type() == DecoderErrorOccurred) {
- auto& error_event = static_cast<DecoderErrorEvent&>(event);
- VERIFY(error_event.error().category() != DecoderErrorCategory::EndOfStream);
- set_playback_status(PlaybackStatus::Corrupted);
- }
- // Allow events to bubble up in all cases.
- event.ignore();
- }
- void PlaybackManager::resume_playback()
- {
- set_playback_status(PlaybackStatus::Playing);
- }
- void PlaybackManager::pause_playback()
- {
- set_playback_status(PlaybackStatus::Paused);
- }
- bool PlaybackManager::prepare_next_frame()
- {
- if (m_next_frame.has_value())
- return true;
- if (m_frame_queue->is_empty())
- return false;
- auto frame_item = m_frame_queue->dequeue();
- m_next_frame.emplace(move(frame_item));
- m_decode_timer->start();
- return true;
- }
- Time PlaybackManager::current_playback_time()
- {
- if (is_playing())
- return m_last_present_in_media_time + (Time::now_monotonic() - m_last_present_in_real_time);
- return m_last_present_in_media_time;
- }
- Time PlaybackManager::duration()
- {
- return m_demuxer->duration();
- }
- void PlaybackManager::on_decoder_error(DecoderError error)
- {
- dbgln("Playback error encountered: {}", error.string_literal());
- switch (error.category()) {
- case DecoderErrorCategory::EndOfStream:
- set_playback_status(PlaybackStatus::Stopped);
- break;
- default:
- set_playback_status(PlaybackStatus::Corrupted);
- m_main_loop.post_event(*this, make<DecoderErrorEvent>(move(error)));
- break;
- }
- }
- void PlaybackManager::update_presented_frame()
- {
- bool out_of_queued_frames = false;
- Optional<FrameQueueItem> frame_item_to_display;
- while (true) {
- out_of_queued_frames = out_of_queued_frames || !prepare_next_frame();
- if (out_of_queued_frames)
- break;
- VERIFY(m_next_frame.has_value());
- if (m_next_frame->is_error() || m_next_frame->timestamp() > current_playback_time())
- break;
- if (frame_item_to_display.has_value()) {
- dbgln_if(PLAYBACK_MANAGER_DEBUG, "At {}ms: Dropped {} in favor of {}", current_playback_time().to_milliseconds(), frame_item_to_display->debug_string(), m_next_frame->debug_string());
- m_skipped_frames++;
- }
- frame_item_to_display = m_next_frame.release_value();
- }
- if (!out_of_queued_frames && frame_item_to_display.has_value()) {
- m_main_loop.post_event(*this, make<VideoFramePresentEvent>(frame_item_to_display->bitmap()));
- m_last_present_in_media_time = current_playback_time();
- m_last_present_in_real_time = Time::now_monotonic();
- frame_item_to_display.clear();
- }
- if (frame_item_to_display.has_value()) {
- VERIFY(!m_next_frame.has_value());
- m_next_frame = frame_item_to_display;
- dbgln_if(PLAYBACK_MANAGER_DEBUG, "Set next frame back to dequeued item {}", m_next_frame->debug_string());
- }
- if (!is_playing())
- return;
- if (!out_of_queued_frames) {
- if (m_next_frame->is_error()) {
- on_decoder_error(m_next_frame->release_error());
- m_next_frame.clear();
- return;
- }
- auto frame_time_ms = (m_next_frame->timestamp() - current_playback_time()).to_milliseconds();
- VERIFY(frame_time_ms <= NumericLimits<int>::max());
- dbgln_if(PLAYBACK_MANAGER_DEBUG, "Time until next frame is {}ms", frame_time_ms);
- m_present_timer->start(max(static_cast<int>(frame_time_ms), 0));
- return;
- }
- set_playback_status(PlaybackStatus::Buffering);
- m_decode_timer->start();
- }
- void PlaybackManager::restart_playback()
- {
- m_last_present_in_media_time = Time::zero();
- m_last_present_in_real_time = Time::zero();
- auto result = m_demuxer->seek_to_most_recent_keyframe(m_selected_video_track, 0);
- if (result.is_error())
- on_decoder_error(result.release_error());
- }
- bool PlaybackManager::decode_and_queue_one_sample()
- {
- if (m_frame_queue->size() >= FRAME_BUFFER_COUNT)
- return false;
- #if PLAYBACK_MANAGER_DEBUG
- auto start_time = Time::now_monotonic();
- #endif
- #define TRY_OR_ENQUEUE_ERROR(expression) \
- ({ \
- auto _temporary_result = ((expression)); \
- if (_temporary_result.is_error()) { \
- dbgln("Enqueued decoder error: {}", _temporary_result.error().string_literal()); \
- m_frame_queue->enqueue(FrameQueueItem::error_marker(_temporary_result.release_error())); \
- return false; \
- } \
- _temporary_result.release_value(); \
- })
- auto frame_sample = TRY_OR_ENQUEUE_ERROR(m_demuxer->get_next_video_sample_for_track(m_selected_video_track));
- TRY_OR_ENQUEUE_ERROR(m_decoder->receive_sample(frame_sample->data()));
- auto decoded_frame = TRY_OR_ENQUEUE_ERROR(m_decoder->get_decoded_frame());
- auto& cicp = decoded_frame->cicp();
- cicp.adopt_specified_values(frame_sample->container_cicp());
- cicp.default_code_points_if_unspecified({ Video::ColorPrimaries::BT709, Video::TransferCharacteristics::BT709, Video::MatrixCoefficients::BT709, Video::ColorRange::Studio });
- auto bitmap = TRY_OR_ENQUEUE_ERROR(decoded_frame->to_bitmap());
- m_frame_queue->enqueue(FrameQueueItem::frame(bitmap, frame_sample->timestamp()));
- #if PLAYBACK_MANAGER_DEBUG
- auto end_time = Time::now_monotonic();
- dbgln("Decoding took {}ms", (end_time - start_time).to_milliseconds());
- #endif
- return true;
- }
- void PlaybackManager::on_decode_timer()
- {
- if (!decode_and_queue_one_sample() && is_buffering()) {
- set_playback_status(PlaybackStatus::Playing);
- return;
- }
- // Continually decode until buffering is complete
- if (is_buffering())
- m_decode_timer->start();
- }
- }
|