Skip to content

Commit 3d940f7

Browse files
committed
OnComplete signals don't fire on windows on looping videos. Apparently this is by design according to the underlying spec but it means it has to be emulated by watching for indicative state changes leading up to a loop.
1 parent 6ea7278 commit 3d940f7

File tree

5 files changed

+42
-1
lines changed

5 files changed

+42
-1
lines changed

samples/SimplePlayback/src/SimplePlaybackApp.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void SimplePlaybackApp::loadDefaultVideo()
7070
{
7171
auto fmt = AX::Video::MediaPlayer::Format().HardwareAccelerated( _hardwareAccelerated );
7272
_player = AX::Video::MediaPlayer::Create( CINDER_PATH "/samples/QuickTimeBasic/assets/bbb.mp4", fmt );
73+
_player->SetLoop ( true );
7374
connectSignals();
7475
_player->Play();
7576
}

src/AX-MediaPlayer.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ namespace AX
154154
return _impl->SeekToPercentage ( std::clamp ( normalizedTime, 0.0f, 1.0f ), approximate );
155155
}
156156

157+
void MediaPlayer::FrameStep ( int delta )
158+
{
159+
return _impl->FrameStep ( delta );
160+
}
161+
157162
bool MediaPlayer::IsComplete ( ) const
158163
{
159164
return _impl->IsComplete ( );

src/AX-MediaPlayer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ namespace AX::Video
122122
void SeekToSeconds ( float seconds, bool approximate = false );
123123
void SeekToPercentage ( float normalizedTime, bool approximate = false );
124124

125+
void FrameStep ( int delta );
126+
125127
float GetPositionInSeconds ( ) const;
126128
float GetDurationInSeconds ( ) const;
127129

src/msw/AX-MediaPlayerMSWImpl.cxx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,29 @@ namespace AX::Video
391391
case MF_MEDIA_ENGINE_EVENT_SEEKING:
392392
{
393393
_owner.OnSeekStart.emit();
394+
_timeInSecondsAtStartOfSeek = _owner.GetPositionInSeconds ( );
394395
break;
395396
}
396397

397398
case MF_MEDIA_ENGINE_EVENT_SEEKED:
398399
{
399400
_owner.OnSeekEnd.emit();
401+
if ( _owner.IsLooping ( ) )
402+
{
403+
// @NOTE(andrew): Since the complete event is not fired on looping videos,
404+
// the best metric I was able to rely on was an EVENT_SEEKING with current time 0
405+
// followed by an EVENT_SEEKED, also at time 0. My crude hack is to try and keep
406+
// track of the current time when these are fired and if they're both zero
407+
// (or close to it) then to assume a loop has taken place. Not perfect, but
408+
// since the MFMediaEngine follows the HTML5 video spec, you can take it up
409+
// with the brains trust at the W3C ;)
410+
411+
auto now = _owner.GetPositionInSeconds ( );
412+
if ( now < 0.05f && ( ( now - _timeInSecondsAtStartOfSeek ) < 0.01f ) )
413+
{
414+
_owner.OnComplete.emit ( );
415+
}
416+
}
400417
break;
401418
}
402419

@@ -410,7 +427,7 @@ namespace AX::Video
410427
{
411428
_owner.OnBufferingEnd.emit();
412429
break;
413-
}
430+
}
414431

415432
case MF_MEDIA_ENGINE_EVENT_ERROR:
416433
{
@@ -607,6 +624,14 @@ namespace AX::Video
607624
}
608625
}
609626

627+
void MediaPlayer::Impl::FrameStep ( int delta )
628+
{
629+
if ( _mediaEngineEx )
630+
{
631+
_mediaEngineEx->FrameStep ( delta > 0 ? true : false );
632+
}
633+
}
634+
610635
bool MediaPlayer::Impl::IsComplete ( ) const
611636
{
612637
if ( _mediaEngine )

src/msw/AX-MediaPlayerMSWImpl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ namespace AX::Video
112112
float GetPositionInSeconds ( ) const;
113113
float GetDurationInSeconds ( ) const { return _duration; }
114114

115+
void FrameStep ( int delta );
116+
115117
bool CheckNewFrame ( ) const { return _hasNewFrame.load ( ); }
116118
const ci::Surface8uRef & GetSurface ( ) const;
117119
MediaPlayer::FrameLeaseRef GetTexture ( ) const;
@@ -141,6 +143,12 @@ namespace AX::Video
141143
ComPtr<IMFMediaEngineEx> _mediaEngineEx{ nullptr };
142144
mutable std::atomic_bool _hasNewFrame{ false };
143145
std::mutex _eventMutex;
146+
147+
// This is to try and determine if a loop has occurred
148+
// since there's no loop event and it's indistinguishable
149+
// from a regular user loop
150+
float _timeInSecondsAtStartOfSeek{ 0.0f };
151+
144152
struct Event
145153
{
146154
DWORD eventId{ 0 };

0 commit comments

Comments
 (0)