diff --git a/Engine/media/video/video_core.cpp b/Engine/media/video/video_core.cpp index 27afa5378ec..d51e7efef63 100644 --- a/Engine/media/video/video_core.cpp +++ b/Engine/media/video/video_core.cpp @@ -114,7 +114,8 @@ int video_core_slot_init(std::unique_ptr in, auto player = create_video_player(ext_hint); if (!player) return -1; - if (!player->Open(std::move(in), name, params.Flags, params.TargetSize, params.TargetColorDepth)) + if (!player->Open(std::move(in), name, + params.Flags, params.TargetSize, params.TargetColorDepth, params.FPS)) return -1; return video_core_slot_init(std::move(player)); } diff --git a/Engine/media/video/videoplayer.cpp b/Engine/media/video/videoplayer.cpp index 8bd85746a3f..16674729e1f 100644 --- a/Engine/media/video/videoplayer.cpp +++ b/Engine/media/video/videoplayer.cpp @@ -38,7 +38,7 @@ HError VideoPlayer::Open(std::unique_ptr data_stream, HError VideoPlayer::Open(std::unique_ptr data_stream, const String &name, int flags, - const Size &target_sz, int target_depth) + const Size &target_sz, int target_depth, int target_fps) { // We request a target depth from decoder, but it may ignore our request, // so we have to check actual "native" frame's depth after @@ -48,6 +48,7 @@ HError VideoPlayer::Open(std::unique_ptr data_stream, _name = name; _flags = flags; + _targetFPS = target_fps > 0 ? target_fps : _frameRate; // Start the audio stream if (HasAudio()) { @@ -63,7 +64,8 @@ HError VideoPlayer::Open(std::unique_ptr data_stream, SetTargetFrame(target_sz); } - _frameTime = 1000 / _frameRate; + // TODO: actually support dynamic FPS, need to adjust audio speed + _frameTime = 1000 / _targetFPS; return HError::None(); } @@ -128,13 +130,15 @@ void VideoPlayer::Play() switch (_playState) { - case PlayStatePaused: Resume(); /* fall-through */ - case PlayStateInitial: _playState = PlayStatePlaying; break; - default: break; // TODO: support rewind/replay from stop/finished state? + case PlayStateInitial: + case PlayStatePaused: + if (_audioOut) + _audioOut->Play(); + _playState = PlayStatePlaying; + break; + default: + break; // TODO: support rewind/replay from stop/finished state? } - - if (_audioOut) - _audioOut->Play(); } void VideoPlayer::Pause() @@ -146,15 +150,6 @@ void VideoPlayer::Pause() _playState = PlayStatePaused; } -void VideoPlayer::Resume() -{ - if (_playState != PlayStatePaused) return; - - if (_audioOut) - _audioOut->Resume(); - _playState = PlayStatePlaying; -} - void VideoPlayer::Seek(float pos_ms) { // TODO @@ -254,10 +249,14 @@ void VideoPlayer::BufferAudio() bool VideoPlayer::ProcessVideo() { + const auto now = AGS_Clock::now(); + const auto duration = + std::chrono::duration_cast(now - _firstFrameTime).count(); // If has got a ready video frame, then test whether it's time to drop it if (_videoReadyFrame) { - if (true /* test timestamp! */) + // TODO: get frame's timestamp if available from decoder? + if (duration >= _framesPlayed * _frameTime) { _videoReadyFrame = nullptr; } @@ -267,10 +266,21 @@ bool VideoPlayer::ProcessVideo() // a new one from queue if (!_videoReadyFrame && _videoFrameQueue.size() > 0) { - if (true /* test timestamp! */) + // TODO: get frame's timestamp if available from decoder? + if (_framesPlayed == 0u || + duration >= _framesPlayed * _frameTime) { _videoReadyFrame = std::move(_videoFrameQueue.front()); _videoFrameQueue.pop_front(); + + _lastFrameTime = now; + // First frame: save timestamp + if (_framesPlayed == 0u) + { + _firstFrameTime = _lastFrameTime; + } + + _framesPlayed++; } } diff --git a/Engine/media/video/videoplayer.h b/Engine/media/video/videoplayer.h index e04d5af2396..a6831738fea 100644 --- a/Engine/media/video/videoplayer.h +++ b/Engine/media/video/videoplayer.h @@ -26,6 +26,7 @@ #include #include #include +#include "ac/timer.h" #include "gfx/bitmap.h" #include "media/audio/audiodefines.h" #include "media/audio/openalsource.h" @@ -59,7 +60,7 @@ class VideoPlayer const String &name, int flags); Common::HError Open(std::unique_ptr data_stream, const String &name, int flags, - const Size &target_sz, int target_depth); + const Size &target_sz, int target_depth = 0, int target_fps = 0); // Tells if the videoplayer object is valid and ready to render virtual bool IsValid() { return false; } bool HasVideo() const { return (_flags & kVideo_EnableVideo) != 0; } @@ -126,9 +127,6 @@ class VideoPlayer uint32_t _frameRate = 0u; private: - // Resumes after pausing - void Resume(); - // Read and queue video frames void BufferVideo(); // Read and queue audio frames @@ -146,17 +144,23 @@ class VideoPlayer // Output video frame's color depth and size Size _targetSize; int _targetDepth = 0; + int _targetFPS = 0; size_t _videoQueueMax = 5u; size_t _audioQueueMax = 0u; // we don't have a real queue atm // Playback state - uint32_t _frameTime = 0u; // frame duration in ms PlaybackState _playState = PlayStateInitial; + size_t _framesPlayed = 0u; + AGS_Clock::time_point _firstFrameTime; // time when the first frame was ready + AGS_Clock::time_point _lastFrameTime; // time when the previous frame was ready + // TODO: implement timing freeze/update on pause/resume + AGS_Clock::time_point _pauseTime; // time when the playback was paused // Audio // Audio queue (single frame for now, because output buffers too) SoundBuffer _audioFrame{}; // Audio output object std::unique_ptr _audioOut; // Video + uint32_t _frameTime = 0u; // frame duration in ms // Helper buffer for retrieving video frames of different size/depth; // should match "native" video frame size and color depth std::unique_ptr _vframeBuf;