2022-10-29 18:16:14 -05:00
/*
* Copyright ( c ) 2022 , Gregory Bertilson < zaggy1024 @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/Format.h>
# include <LibCore/Timer.h>
2022-11-09 19:47:56 -06:00
# include <LibVideo/Containers/Matroska/MatroskaDemuxer.h>
2022-10-29 18:16:14 -05:00
# include <LibVideo/VP9/Decoder.h>
# include "PlaybackManager.h"
namespace Video {
2023-02-06 01:24:33 -06:00
# define TRY_OR_FATAL_ERROR(expression) \
( { \
2023-02-09 13:27:43 -05:00
auto & & _fatal_expression = ( expression ) ; \
2023-02-06 01:24:33 -06:00
if ( _fatal_expression . is_error ( ) ) { \
dispatch_fatal_error ( _fatal_expression . release_error ( ) ) ; \
return ; \
} \
static_assert ( ! : : AK : : Detail : : IsLvalueReference < decltype ( _fatal_expression . release_value ( ) ) > , \
" Do not return a reference from a fallible expression " ) ; \
_fatal_expression . release_value ( ) ; \
} )
2023-04-25 09:02:24 -04:00
DecoderErrorOr < NonnullOwnPtr < PlaybackManager > > PlaybackManager : : from_file ( StringView filename )
2022-10-29 18:16:14 -05:00
{
2023-04-08 21:22:15 -04:00
auto demuxer = TRY ( Matroska : : MatroskaDemuxer : : from_file ( filename ) ) ;
2023-04-25 09:02:24 -04:00
return create_with_demuxer ( move ( demuxer ) ) ;
2023-04-08 21:22:15 -04:00
}
2023-05-11 12:25:52 +01:00
DecoderErrorOr < NonnullOwnPtr < PlaybackManager > > PlaybackManager : : from_mapped_file ( NonnullRefPtr < Core : : MappedFile > mapped_file )
{
auto demuxer = TRY ( Matroska : : MatroskaDemuxer : : from_mapped_file ( move ( mapped_file ) ) ) ;
return create_with_demuxer ( move ( demuxer ) ) ;
}
2023-04-25 09:02:24 -04:00
DecoderErrorOr < NonnullOwnPtr < PlaybackManager > > PlaybackManager : : from_data ( ReadonlyBytes data )
2023-04-08 21:22:15 -04:00
{
auto demuxer = TRY ( Matroska : : MatroskaDemuxer : : from_data ( data ) ) ;
2023-04-25 09:02:24 -04:00
return create_with_demuxer ( move ( demuxer ) ) ;
2023-04-08 21:22:15 -04:00
}
2023-04-25 09:02:24 -04:00
DecoderErrorOr < NonnullOwnPtr < PlaybackManager > > PlaybackManager : : create_with_demuxer ( NonnullOwnPtr < Demuxer > demuxer )
2023-04-08 21:22:15 -04:00
{
2022-11-11 17:14:27 -06:00
auto video_tracks = TRY ( demuxer - > get_tracks_for_type ( TrackType : : Video ) ) ;
2022-10-29 18:16:14 -05:00
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 ( ) ) ;
2023-04-25 09:02:24 -04:00
return make < PlaybackManager > ( demuxer , track , make < VP9 : : Decoder > ( ) ) ;
2022-10-29 18:16:14 -05:00
}
2023-04-25 09:02:24 -04:00
PlaybackManager : : PlaybackManager ( NonnullOwnPtr < Demuxer > & demuxer , Track video_track , NonnullOwnPtr < VideoDecoder > & & decoder )
2023-04-08 19:18:38 -04:00
: m_demuxer ( move ( demuxer ) )
2022-10-29 18:16:14 -05:00
, m_selected_video_track ( video_track )
, m_decoder ( move ( decoder ) )
, m_frame_queue ( make < VideoFrameQueue > ( ) )
2023-04-12 00:42:14 -05:00
, m_playback_handler ( make < SeekingStateHandler > ( * this , false , Time : : zero ( ) , SeekMode : : Fast ) )
2022-10-29 18:16:14 -05:00
{
2023-04-25 09:02:24 -04:00
m_present_timer = Core : : Timer : : create_single_shot ( 0 , [ & ] { timer_callback ( ) ; } ) . release_value_but_fixme_should_propagate_errors ( ) ;
m_decode_timer = Core : : Timer : : create_single_shot ( 0 , [ & ] { on_decode_timer ( ) ; } ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-04-08 19:18:38 -04:00
2023-02-06 01:24:33 -06:00
TRY_OR_FATAL_ERROR ( m_playback_handler - > on_enter ( ) ) ;
2022-10-29 18:16:14 -05:00
}
2023-04-25 09:02:24 -04:00
PlaybackManager : : ~ PlaybackManager ( ) = default ;
2022-10-29 18:16:14 -05:00
void PlaybackManager : : resume_playback ( )
{
2023-02-06 01:24:33 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Resuming playback. " ) ;
TRY_OR_FATAL_ERROR ( m_playback_handler - > play ( ) ) ;
2022-10-29 18:16:14 -05:00
}
void PlaybackManager : : pause_playback ( )
{
2023-02-06 01:24:33 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Pausing playback. " ) ;
if ( ! m_playback_handler - > is_playing ( ) )
warnln ( " Cannot pause. " ) ;
TRY_OR_FATAL_ERROR ( m_playback_handler - > pause ( ) ) ;
2022-10-29 18:16:14 -05:00
}
Time PlaybackManager : : current_playback_time ( )
{
2023-02-06 01:24:33 -06:00
return m_playback_handler - > current_time ( ) ;
2022-10-29 18:16:14 -05:00
}
Time PlaybackManager : : duration ( )
{
2022-11-11 17:14:27 -06:00
auto duration_result = m_demuxer - > duration ( ) ;
if ( duration_result . is_error ( ) )
2023-02-12 00:44:16 -06:00
dispatch_decoder_error ( duration_result . release_error ( ) ) ;
2022-11-11 17:14:27 -06:00
return duration_result . release_value ( ) ;
2022-10-29 18:16:14 -05:00
}
2023-02-06 01:24:33 -06:00
void PlaybackManager : : dispatch_fatal_error ( Error error )
2022-11-09 06:52:37 -06:00
{
2023-02-06 01:24:33 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Encountered fatal error: {} " , error . string_literal ( ) ) ;
// FIXME: For threading, this will have to use a pre-allocated event to send to the main loop
// to be able to gracefully handle OOM.
2023-04-08 19:18:38 -04:00
if ( on_fatal_playback_error )
on_fatal_playback_error ( move ( error ) ) ;
2023-02-06 01:24:33 -06:00
}
2022-11-27 00:45:14 -06:00
2023-02-12 00:44:16 -06:00
void PlaybackManager : : dispatch_decoder_error ( DecoderError error )
2023-02-06 01:24:33 -06:00
{
2022-11-09 06:52:37 -06:00
switch ( error . category ( ) ) {
case DecoderErrorCategory : : EndOfStream :
2022-11-11 22:04:38 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " {} " , error . string_literal ( ) ) ;
2023-02-06 01:24:33 -06:00
TRY_OR_FATAL_ERROR ( m_playback_handler - > stop ( ) ) ;
2022-11-09 06:52:37 -06:00
break ;
default :
2022-11-11 22:04:38 -06:00
dbgln ( " Playback error encountered: {} " , error . string_literal ( ) ) ;
2023-02-06 01:24:33 -06:00
TRY_OR_FATAL_ERROR ( m_playback_handler - > stop ( ) ) ;
2023-04-08 19:18:38 -04:00
if ( on_decoder_error )
on_decoder_error ( move ( error ) ) ;
2022-11-09 06:52:37 -06:00
break ;
}
}
2023-02-12 00:44:16 -06:00
void PlaybackManager : : dispatch_new_frame ( RefPtr < Gfx : : Bitmap > frame )
{
2023-04-08 19:18:38 -04:00
if ( on_video_frame )
on_video_frame ( move ( frame ) ) ;
2023-02-12 00:44:16 -06:00
}
bool PlaybackManager : : dispatch_frame_queue_item ( FrameQueueItem & & item )
{
if ( item . is_error ( ) ) {
dispatch_decoder_error ( item . release_error ( ) ) ;
return true ;
}
2023-04-11 23:42:47 -05:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Sent frame for presentation with timestamp {}ms " , item . timestamp ( ) . to_milliseconds ( ) ) ;
2023-02-12 00:44:16 -06:00
dispatch_new_frame ( item . bitmap ( ) ) ;
return false ;
}
2023-02-12 02:25:25 -06:00
void PlaybackManager : : dispatch_state_change ( )
{
2023-04-08 19:18:38 -04:00
if ( on_playback_state_change )
on_playback_state_change ( ) ;
2023-02-12 02:25:25 -06:00
}
2023-02-06 01:24:33 -06:00
void PlaybackManager : : timer_callback ( )
2022-11-14 00:54:21 -06:00
{
2023-02-06 01:24:33 -06:00
TRY_OR_FATAL_ERROR ( m_playback_handler - > on_timer_callback ( ) ) ;
2022-11-14 00:54:21 -06:00
}
2023-02-06 04:11:32 -06:00
void PlaybackManager : : seek_to_timestamp ( Time target_timestamp , SeekMode seek_mode )
2022-10-29 18:16:14 -05:00
{
2023-02-06 04:11:32 -06:00
TRY_OR_FATAL_ERROR ( m_playback_handler - > seek ( target_timestamp , seek_mode ) ) ;
2022-10-29 18:16:14 -05:00
}
2023-02-06 01:25:02 -06:00
Optional < Time > PlaybackManager : : seek_demuxer_to_most_recent_keyframe ( Time timestamp , Optional < Time > earliest_available_sample )
2022-10-29 18:16:14 -05:00
{
2022-11-11 22:26:19 -06:00
// FIXME: When the demuxer is getting samples off the main thread in the future, this needs to
// mutex so that seeking can't happen while that thread is getting a sample.
2023-02-06 01:25:02 -06:00
auto result = m_demuxer - > seek_to_most_recent_keyframe ( m_selected_video_track , timestamp , move ( earliest_available_sample ) ) ;
2022-11-09 06:52:37 -06:00
if ( result . is_error ( ) )
2023-02-12 00:44:16 -06:00
dispatch_decoder_error ( result . release_error ( ) ) ;
2023-02-06 01:24:33 -06:00
return result . release_value ( ) ;
2022-10-29 18:16:14 -05:00
}
2022-11-11 22:26:19 -06:00
void PlaybackManager : : restart_playback ( )
{
seek_to_timestamp ( Time : : zero ( ) ) ;
}
2023-02-06 01:24:33 -06:00
void PlaybackManager : : start_timer ( int milliseconds )
{
m_present_timer - > start ( milliseconds ) ;
}
2022-10-29 18:16:14 -05:00
bool PlaybackManager : : decode_and_queue_one_sample ( )
{
2022-11-14 00:48:31 -06:00
if ( m_frame_queue - > size ( ) > = FRAME_BUFFER_COUNT ) {
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Frame queue is full, stopping " ) ;
2022-10-29 18:16:14 -05:00
return false ;
2022-11-14 00:48:31 -06:00
}
2022-10-29 18:16:14 -05:00
# if PLAYBACK_MANAGER_DEBUG
auto start_time = Time : : now_monotonic ( ) ;
# endif
2023-02-12 00:50:30 -06:00
auto enqueue_error = [ & ] ( DecoderError & & error , Time timestamp ) {
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Enqueued decoder error: {} " , error . string_literal ( ) ) ;
m_frame_queue - > enqueue ( FrameQueueItem : : error_marker ( move ( error ) , timestamp ) ) ;
} ;
# define TRY_OR_ENQUEUE_ERROR(expression, timestamp) \
( { \
auto & & _temporary_result = ( expression ) ; \
if ( _temporary_result . is_error ( ) ) { \
enqueue_error ( _temporary_result . release_error ( ) , ( timestamp ) ) ; \
return false ; \
} \
static_assert ( ! : : AK : : Detail : : IsLvalueReference < decltype ( _temporary_result . release_value ( ) ) > , \
" Do not return a reference from a fallible expression " ) ; \
_temporary_result . release_value ( ) ; \
2022-11-09 06:52:37 -06:00
} )
2023-02-12 00:50:30 -06:00
auto frame_sample = TRY_OR_ENQUEUE_ERROR ( m_demuxer - > get_next_video_sample_for_track ( m_selected_video_track ) , Time : : min ( ) ) ;
2022-11-03 19:18:38 -05:00
OwnPtr < VideoFrame > decoded_frame = nullptr ;
while ( ! decoded_frame ) {
2023-02-12 00:50:30 -06:00
TRY_OR_ENQUEUE_ERROR ( m_decoder - > receive_sample ( frame_sample - > data ( ) ) , frame_sample - > timestamp ( ) ) ;
2022-11-03 19:18:38 -05:00
while ( true ) {
auto frame_result = m_decoder - > get_decoded_frame ( ) ;
if ( frame_result . is_error ( ) ) {
if ( frame_result . error ( ) . category ( ) = = DecoderErrorCategory : : NeedsMoreInput )
break ;
2022-10-29 18:16:14 -05:00
2023-02-12 00:50:30 -06:00
enqueue_error ( frame_result . release_error ( ) , frame_sample - > timestamp ( ) ) ;
2022-11-03 19:18:38 -05:00
return false ;
}
decoded_frame = frame_result . release_value ( ) ;
VERIFY ( decoded_frame ) ;
}
}
2022-10-29 18:16:14 -05:00
auto & cicp = decoded_frame - > cicp ( ) ;
cicp . adopt_specified_values ( frame_sample - > container_cicp ( ) ) ;
2023-02-08 14:16:37 -05:00
cicp . default_code_points_if_unspecified ( { ColorPrimaries : : BT709 , TransferCharacteristics : : BT709 , MatrixCoefficients : : BT709 , VideoFullRangeFlag : : Studio } ) ;
2022-11-08 03:24:30 -06:00
// BT.601, BT.709 and BT.2020 have a similar transfer function to sRGB, so other applications
// (Chromium, VLC) forgo transfer characteristics conversion. We will emulate that behavior by
// handling those as sRGB instead, which causes no transfer function change in the output,
// unless display color management is later implemented.
switch ( cicp . transfer_characteristics ( ) ) {
case TransferCharacteristics : : BT601 :
case TransferCharacteristics : : BT709 :
case TransferCharacteristics : : BT2020BitDepth10 :
case TransferCharacteristics : : BT2020BitDepth12 :
cicp . set_transfer_characteristics ( TransferCharacteristics : : SRGB ) ;
break ;
default :
break ;
}
2022-10-29 18:16:14 -05:00
2023-02-12 00:50:30 -06:00
auto bitmap = TRY_OR_ENQUEUE_ERROR ( decoded_frame - > to_bitmap ( ) , frame_sample - > timestamp ( ) ) ;
2022-11-09 06:52:37 -06:00
m_frame_queue - > enqueue ( FrameQueueItem : : frame ( bitmap , frame_sample - > timestamp ( ) ) ) ;
2022-10-29 18:16:14 -05:00
# if PLAYBACK_MANAGER_DEBUG
auto end_time = Time : : now_monotonic ( ) ;
2023-02-06 01:24:33 -06:00
dbgln ( " Decoding sample at {}ms took {}ms, queue contains {} items " , frame_sample - > timestamp ( ) . to_milliseconds ( ) , ( end_time - start_time ) . to_milliseconds ( ) , m_frame_queue - > size ( ) ) ;
2022-10-29 18:16:14 -05:00
# endif
return true ;
}
void PlaybackManager : : on_decode_timer ( )
{
2023-02-06 01:24:33 -06:00
if ( ! decode_and_queue_one_sample ( ) ) {
// Note: When threading is implemented, this must be dispatched via an event loop.
TRY_OR_FATAL_ERROR ( m_playback_handler - > on_buffer_filled ( ) ) ;
2022-10-29 18:16:14 -05:00
return ;
}
// Continually decode until buffering is complete
2023-02-06 01:24:33 -06:00
m_decode_timer - > start ( 0 ) ;
}
Time PlaybackManager : : PlaybackStateHandler : : current_time ( ) const
{
return m_manager . m_last_present_in_media_time ;
}
ErrorOr < void > PlaybackManager : : PlaybackStateHandler : : seek ( Time target_timestamp , SeekMode seek_mode )
{
return replace_handler_and_delete_this < SeekingStateHandler > ( is_playing ( ) , target_timestamp , seek_mode ) ;
2022-10-29 18:16:14 -05:00
}
2023-02-06 01:24:33 -06:00
ErrorOr < void > PlaybackManager : : PlaybackStateHandler : : stop ( )
{
return replace_handler_and_delete_this < StoppedStateHandler > ( ) ;
}
template < class T , class . . . Args >
ErrorOr < void > PlaybackManager : : PlaybackStateHandler : : replace_handler_and_delete_this ( Args . . . args )
{
auto temp_handler = TRY ( adopt_nonnull_own_or_enomem < PlaybackStateHandler > ( new ( nothrow ) T ( m_manager , args . . . ) ) ) ;
m_manager . m_playback_handler . swap ( temp_handler ) ;
# if PLAYBACK_MANAGER_DEBUG
m_has_exited = true ;
dbgln ( " Changing state from {} to {} " , temp_handler - > name ( ) , m_manager . m_playback_handler - > name ( ) ) ;
# endif
TRY ( m_manager . m_playback_handler - > on_enter ( ) ) ;
2023-04-12 00:21:43 -05:00
m_manager . dispatch_state_change ( ) ;
2023-02-06 01:24:33 -06:00
return { } ;
}
PlaybackManager & PlaybackManager : : PlaybackStateHandler : : manager ( ) const
{
# if PLAYBACK_MANAGER_DEBUG
VERIFY ( ! m_has_exited ) ;
# endif
return m_manager ;
}
2023-02-12 02:36:06 -06:00
class PlaybackManager : : ResumingStateHandler : public PlaybackManager : : PlaybackStateHandler {
2023-02-12 00:48:27 -06:00
public :
2023-02-12 02:36:06 -06:00
ResumingStateHandler ( PlaybackManager & manager , bool playing )
2023-02-12 00:48:27 -06:00
: PlaybackStateHandler ( manager )
, m_playing ( playing )
{
}
2023-02-12 02:36:06 -06:00
~ ResumingStateHandler ( ) override = default ;
protected :
ErrorOr < void > assume_next_state ( )
{
if ( ! m_playing )
return replace_handler_and_delete_this < PausedStateHandler > ( ) ;
return replace_handler_and_delete_this < PlayingStateHandler > ( ) ;
}
ErrorOr < void > play ( ) override
{
m_playing = true ;
manager ( ) . dispatch_state_change ( ) ;
return { } ;
}
2023-04-12 01:01:29 -05:00
bool is_playing ( ) const override { return m_playing ; }
2023-02-12 02:36:06 -06:00
ErrorOr < void > pause ( ) override
{
m_playing = false ;
manager ( ) . dispatch_state_change ( ) ;
return { } ;
}
2023-04-09 09:39:48 -04:00
bool m_playing { false } ;
2023-02-12 02:36:06 -06:00
} ;
2023-02-06 01:24:33 -06:00
class PlaybackManager : : PlayingStateHandler : public PlaybackManager : : PlaybackStateHandler {
public :
PlayingStateHandler ( PlaybackManager & manager )
: PlaybackStateHandler ( manager )
{
}
~ PlayingStateHandler ( ) override = default ;
private :
ErrorOr < void > on_enter ( ) override
{
m_last_present_in_real_time = Time : : now_monotonic ( ) ;
return on_timer_callback ( ) ;
}
StringView name ( ) override { return " Playing " sv ; }
2023-04-12 01:01:29 -05:00
bool is_playing ( ) const override { return true ; } ;
PlaybackState get_state ( ) const override { return PlaybackState : : Playing ; }
2023-02-06 01:24:33 -06:00
ErrorOr < void > pause ( ) override
{
manager ( ) . m_last_present_in_media_time = current_time ( ) ;
return replace_handler_and_delete_this < PausedStateHandler > ( ) ;
}
ErrorOr < void > buffer ( ) override
{
manager ( ) . m_last_present_in_media_time = current_time ( ) ;
return replace_handler_and_delete_this < BufferingStateHandler > ( true ) ;
}
Time current_time ( ) const override
{
return manager ( ) . m_last_present_in_media_time + ( Time : : now_monotonic ( ) - m_last_present_in_real_time ) ;
}
ErrorOr < void > on_timer_callback ( ) override
{
2023-02-12 02:00:59 -06:00
auto set_presentation_timer = [ & ] ( ) {
auto frame_time_ms = ( manager ( ) . m_next_frame - > timestamp ( ) - current_time ( ) ) . to_milliseconds ( ) ;
VERIFY ( frame_time_ms < = NumericLimits < int > : : max ( ) ) ;
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Time until next frame is {}ms " , frame_time_ms ) ;
manager ( ) . start_timer ( max ( static_cast < int > ( frame_time_ms ) , 0 ) ) ;
} ;
2023-02-12 00:48:27 -06:00
if ( manager ( ) . m_next_frame . has_value ( ) & & current_time ( ) < manager ( ) . m_next_frame - > timestamp ( ) ) {
2023-02-12 02:00:59 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Current time {}ms is too early to present the next frame at {}ms, delaying " , current_time ( ) . to_milliseconds ( ) , manager ( ) . m_next_frame - > timestamp ( ) . to_milliseconds ( ) ) ;
set_presentation_timer ( ) ;
return { } ;
}
2023-02-06 01:24:33 -06:00
Optional < FrameQueueItem > future_frame_item ;
bool should_present_frame = false ;
// Skip frames until we find a frame past the current playback time, and keep the one that precedes it to display.
while ( ! manager ( ) . m_frame_queue - > is_empty ( ) ) {
future_frame_item . emplace ( manager ( ) . m_frame_queue - > dequeue ( ) ) ;
manager ( ) . m_decode_timer - > start ( 0 ) ;
2023-02-12 00:50:30 -06:00
if ( future_frame_item - > timestamp ( ) > = current_time ( ) | | future_frame_item - > timestamp ( ) = = FrameQueueItem : : no_timestamp ) {
2023-02-06 01:24:33 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Should present frame, future {} is error or after {}ms " , future_frame_item - > debug_string ( ) , current_time ( ) . to_milliseconds ( ) ) ;
should_present_frame = true ;
break ;
}
if ( manager ( ) . m_next_frame . has_value ( ) ) {
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " At {}ms: Dropped {} in favor of {} " , current_time ( ) . to_milliseconds ( ) , manager ( ) . m_next_frame - > debug_string ( ) , future_frame_item - > debug_string ( ) ) ;
manager ( ) . m_skipped_frames + + ;
}
manager ( ) . m_next_frame . emplace ( future_frame_item . release_value ( ) ) ;
}
// If we don't have both of these items, we can't present, since we need to set a timer for
// the next frame. Check if we need to buffer based on the current state.
if ( ! manager ( ) . m_next_frame . has_value ( ) | | ! future_frame_item . has_value ( ) ) {
# if PLAYBACK_MANAGER_DEBUG
StringBuilder debug_string_builder ;
debug_string_builder . append ( " We don't have " sv ) ;
if ( ! manager ( ) . m_next_frame . has_value ( ) ) {
debug_string_builder . append ( " a frame to present " sv ) ;
if ( ! future_frame_item . has_value ( ) )
debug_string_builder . append ( " or a future frame " sv ) ;
} else {
debug_string_builder . append ( " a future frame " sv ) ;
}
debug_string_builder . append ( " , checking for error and buffering " sv ) ;
dbgln_if ( PLAYBACK_MANAGER_DEBUG , debug_string_builder . to_deprecated_string ( ) ) ;
# endif
if ( future_frame_item . has_value ( ) ) {
if ( future_frame_item - > is_error ( ) ) {
2023-02-12 00:44:16 -06:00
manager ( ) . dispatch_decoder_error ( future_frame_item . release_value ( ) . release_error ( ) ) ;
2023-02-06 01:24:33 -06:00
return { } ;
}
manager ( ) . m_next_frame . emplace ( future_frame_item . release_value ( ) ) ;
}
TRY ( buffer ( ) ) ;
return { } ;
}
// If we have a frame, send it for presentation.
if ( should_present_frame ) {
auto now = Time : : now_monotonic ( ) ;
manager ( ) . m_last_present_in_media_time + = now - m_last_present_in_real_time ;
m_last_present_in_real_time = now ;
2023-02-12 00:44:16 -06:00
if ( manager ( ) . dispatch_frame_queue_item ( manager ( ) . m_next_frame . release_value ( ) ) )
return { } ;
2023-02-06 01:24:33 -06:00
}
// Now that we've presented the current frame, we can throw whatever error is next in queue.
// This way, we always display a frame before the stream ends, and should also show any frames
// we already had when a real error occurs.
if ( future_frame_item - > is_error ( ) ) {
2023-02-12 00:44:16 -06:00
manager ( ) . dispatch_decoder_error ( future_frame_item . release_value ( ) . release_error ( ) ) ;
2023-02-06 01:24:33 -06:00
return { } ;
}
// The future frame item becomes the next one to present.
manager ( ) . m_next_frame . emplace ( future_frame_item . release_value ( ) ) ;
2023-02-12 02:00:59 -06:00
set_presentation_timer ( ) ;
2023-02-06 01:24:33 -06:00
return { } ;
}
Time m_last_present_in_real_time = Time : : zero ( ) ;
} ;
class PlaybackManager : : PausedStateHandler : public PlaybackManager : : PlaybackStateHandler {
public :
PausedStateHandler ( PlaybackManager & manager )
: PlaybackStateHandler ( manager )
{
}
~ PausedStateHandler ( ) override = default ;
private :
StringView name ( ) override { return " Paused " sv ; }
ErrorOr < void > play ( ) override
{
return replace_handler_and_delete_this < PlayingStateHandler > ( ) ;
}
2023-04-12 01:01:29 -05:00
bool is_playing ( ) const override { return false ; } ;
PlaybackState get_state ( ) const override { return PlaybackState : : Paused ; }
2023-02-06 01:24:33 -06:00
} ;
2023-02-12 02:36:06 -06:00
class PlaybackManager : : BufferingStateHandler : public PlaybackManager : : ResumingStateHandler {
using PlaybackManager : : ResumingStateHandler : : ResumingStateHandler ;
2023-02-06 01:24:33 -06:00
ErrorOr < void > on_enter ( ) override
{
manager ( ) . m_decode_timer - > start ( 0 ) ;
return { } ;
}
StringView name ( ) override { return " Buffering " sv ; }
ErrorOr < void > on_buffer_filled ( ) override
{
2023-02-12 02:36:06 -06:00
return assume_next_state ( ) ;
2023-02-06 01:24:33 -06:00
}
2023-04-12 01:01:29 -05:00
PlaybackState get_state ( ) const override { return PlaybackState : : Buffering ; }
2023-02-06 01:24:33 -06:00
} ;
2023-02-12 02:36:06 -06:00
class PlaybackManager : : SeekingStateHandler : public PlaybackManager : : ResumingStateHandler {
2023-02-06 01:24:33 -06:00
public :
SeekingStateHandler ( PlaybackManager & manager , bool playing , Time target_timestamp , SeekMode seek_mode )
2023-02-12 02:36:06 -06:00
: ResumingStateHandler ( manager , playing )
2023-02-06 01:24:33 -06:00
, m_target_timestamp ( target_timestamp )
, m_seek_mode ( seek_mode )
{
}
~ SeekingStateHandler ( ) override = default ;
private :
ErrorOr < void > on_enter ( ) override
{
2023-02-06 01:25:02 -06:00
auto earliest_available_sample = manager ( ) . m_last_present_in_media_time ;
2023-02-12 00:50:30 -06:00
if ( manager ( ) . m_next_frame . has_value ( ) & & manager ( ) . m_next_frame - > timestamp ( ) ! = FrameQueueItem : : no_timestamp ) {
2023-02-06 01:25:02 -06:00
earliest_available_sample = min ( earliest_available_sample , manager ( ) . m_next_frame - > timestamp ( ) ) ;
}
auto keyframe_timestamp = manager ( ) . seek_demuxer_to_most_recent_keyframe ( m_target_timestamp , earliest_available_sample ) ;
# if PLAYBACK_MANAGER_DEBUG
auto seek_mode_name = m_seek_mode = = SeekMode : : Accurate ? " Accurate " sv : " Fast " sv ;
if ( keyframe_timestamp . has_value ( ) ) {
dbgln ( " {} seeking to timestamp target {}ms, selected keyframe at {}ms " , seek_mode_name , m_target_timestamp . to_milliseconds ( ) , keyframe_timestamp - > to_milliseconds ( ) ) ;
} else {
2023-04-11 23:42:47 -05:00
dbgln ( " {} seeking to timestamp target {}ms, demuxer kept its iterator position after {}ms " , seek_mode_name , m_target_timestamp . to_milliseconds ( ) , earliest_available_sample . to_milliseconds ( ) ) ;
2023-02-06 01:25:02 -06:00
}
# endif
2023-02-06 01:24:33 -06:00
if ( m_seek_mode = = SeekMode : : Fast ) {
2023-04-12 00:24:05 -05:00
m_target_timestamp = keyframe_timestamp . value_or ( manager ( ) . m_last_present_in_media_time ) ;
2023-02-06 01:24:33 -06:00
}
2023-02-06 01:25:02 -06:00
if ( keyframe_timestamp . has_value ( ) ) {
2023-04-11 23:42:47 -05:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Keyframe is nearer to the target than the current frames, clearing queue " ) ;
2023-02-06 01:24:33 -06:00
manager ( ) . m_frame_queue - > clear ( ) ;
manager ( ) . m_next_frame . clear ( ) ;
2023-04-12 00:24:05 -05:00
manager ( ) . m_last_present_in_media_time = keyframe_timestamp . value ( ) ;
2023-02-06 01:25:02 -06:00
} else if ( m_target_timestamp > = manager ( ) . m_last_present_in_media_time & & manager ( ) . m_next_frame . has_value ( ) & & manager ( ) . m_next_frame . value ( ) . timestamp ( ) > m_target_timestamp ) {
2023-04-11 23:42:47 -05:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Target timestamp is between the last presented frame and the next frame, exiting seek at {}ms " , m_target_timestamp . to_milliseconds ( ) ) ;
2023-02-06 01:24:33 -06:00
manager ( ) . m_last_present_in_media_time = m_target_timestamp ;
2023-02-12 02:36:06 -06:00
return assume_next_state ( ) ;
2023-02-06 01:24:33 -06:00
}
return skip_samples_until_timestamp ( ) ;
}
ErrorOr < void > skip_samples_until_timestamp ( )
{
while ( ! manager ( ) . m_frame_queue - > is_empty ( ) ) {
auto item = manager ( ) . m_frame_queue - > dequeue ( ) ;
manager ( ) . m_decode_timer - > start ( 0 ) ;
2023-02-12 00:50:30 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Dequeuing frame at {}ms and comparing to seek target {}ms " , item . timestamp ( ) . to_milliseconds ( ) , m_target_timestamp . to_milliseconds ( ) ) ;
2023-04-12 00:24:05 -05:00
if ( manager ( ) . m_next_frame . has_value ( ) & & ( item . timestamp ( ) > m_target_timestamp | | item . timestamp ( ) = = FrameQueueItem : : no_timestamp ) ) {
// If the frame we're presenting is later than the target timestamp, skip the timestamp forward to it.
if ( manager ( ) . m_next_frame - > timestamp ( ) > m_target_timestamp ) {
manager ( ) . m_last_present_in_media_time = manager ( ) . m_next_frame - > timestamp ( ) ;
} else {
2023-02-12 00:50:30 -06:00
manager ( ) . m_last_present_in_media_time = m_target_timestamp ;
2023-02-06 01:24:33 -06:00
}
2023-02-12 00:50:30 -06:00
2023-04-12 00:24:05 -05:00
if ( manager ( ) . dispatch_frame_queue_item ( manager ( ) . m_next_frame . release_value ( ) ) )
return { } ;
2023-02-06 01:24:33 -06:00
manager ( ) . m_next_frame . emplace ( item ) ;
2023-02-12 00:50:30 -06:00
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Exiting seek to {} state at {}ms " , m_playing ? " Playing " : " Paused " , manager ( ) . m_last_present_in_media_time . to_milliseconds ( ) ) ;
2023-02-12 02:36:06 -06:00
return assume_next_state ( ) ;
2023-02-12 00:50:30 -06:00
}
manager ( ) . m_next_frame . emplace ( item ) ;
2023-02-06 01:24:33 -06:00
}
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Frame queue is empty while seeking, waiting for buffer fill. " ) ;
manager ( ) . m_decode_timer - > start ( 0 ) ;
return { } ;
}
StringView name ( ) override { return " Seeking " sv ; }
ErrorOr < void > seek ( Time target_timestamp , SeekMode seek_mode ) override
{
m_target_timestamp = target_timestamp ;
m_seek_mode = seek_mode ;
return on_enter ( ) ;
}
Time current_time ( ) const override
{
return m_target_timestamp ;
}
// We won't need this override when threaded, the queue can pause us in on_enter().
ErrorOr < void > on_buffer_filled ( ) override
{
dbgln_if ( PLAYBACK_MANAGER_DEBUG , " Buffer filled while seeking, dequeuing until timestamp. " ) ;
return skip_samples_until_timestamp ( ) ;
}
2023-04-12 01:01:29 -05:00
PlaybackState get_state ( ) const override { return PlaybackState : : Seeking ; }
2023-02-06 01:24:33 -06:00
Time m_target_timestamp { Time : : zero ( ) } ;
SeekMode m_seek_mode { SeekMode : : Accurate } ;
} ;
class PlaybackManager : : StoppedStateHandler : public PlaybackManager : : PlaybackStateHandler {
public :
StoppedStateHandler ( PlaybackManager & manager )
: PlaybackStateHandler ( manager )
{
}
~ StoppedStateHandler ( ) override = default ;
private :
ErrorOr < void > on_enter ( ) override
{
return { } ;
}
StringView name ( ) override { return " Stopped " sv ; }
ErrorOr < void > play ( ) override
{
2023-02-06 01:25:02 -06:00
manager ( ) . m_next_frame . clear ( ) ;
manager ( ) . m_frame_queue - > clear ( ) ;
auto start_timestamp = manager ( ) . seek_demuxer_to_most_recent_keyframe ( Time : : zero ( ) ) ;
VERIFY ( start_timestamp . has_value ( ) ) ;
manager ( ) . m_last_present_in_media_time = start_timestamp . release_value ( ) ;
2023-04-12 00:42:14 -05:00
return replace_handler_and_delete_this < SeekingStateHandler > ( true , Time : : zero ( ) , SeekMode : : Fast ) ;
2023-02-06 01:24:33 -06:00
}
2023-04-12 01:01:29 -05:00
bool is_playing ( ) const override { return false ; } ;
PlaybackState get_state ( ) const override { return PlaybackState : : Stopped ; }
2023-02-06 01:24:33 -06:00
} ;
2022-10-29 18:16:14 -05:00
}