diff --git a/src/ChargeVideo.hpp b/src/ChargeVideo.hpp index 99c3856..89096a7 100644 --- a/src/ChargeVideo.hpp +++ b/src/ChargeVideo.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -57,8 +58,12 @@ private: class Video { public: + // Enums & Flags + enum class State { Playing, Finished, Paused, Idle }; + enum class Flags : uint32_t { None = 0, Looping = 1 << 0 }; + Video(std::string path, ChargeAudio::Engine *audioEngine = nullptr, - bool ShouldVideoLoop = true, float BufferSizeInSeconds = 1.0f); + Flags videoFlags = Flags::None, float bufferSizeInSeconds = 1.0f); ~Video(); // Manual Control @@ -67,19 +72,19 @@ public: // Automatic play void Play(); void Pause(); - void StopLooping(); - void StartLooping(); + void SwitchLooping(); void Restart(); + // Info + const double GetDuration(); + const double GetPlaybackTime(); + const Vector2i GetDimensions(); + State GetState(); + Flags GetFlags(); + // Frame and buffer GL::Texture2D CurrentFrame; - float BufferLenghtInSeconds = 1; - bool isVideoLooping = true, isVideoOver = false, isVideoPaused = false; - - // SAR and Scaling - Vector2i Dimensions{0, 0}; - // Audio ChargeAudio::SoundContainer Sound; @@ -92,7 +97,11 @@ private: _ffmpeg::AVStream *videoStream, *audioStream; struct _ffmpeg::SwsContext *swsCtx = NULL; // Visual struct _ffmpeg::SwrContext *swrCtx = NULL; // Audio + + // State int8_t videoStreamNum = -1, audioStreamNum = -1; + State videoState; + Flags videoFlags; uint16_t ID = 0; // Time specific @@ -112,9 +121,11 @@ private: // SAR / Sizing uint32_t scaleFactor = 1; + Vector2i Dimensions{0, 0}; // Frame handling bool frameSet = false; + float bufferLenghtInSeconds; // Methods void continueVideo(); @@ -132,5 +143,23 @@ private: void loadTexture(ImageView2D image); Image2D loadImage(Containers::Array data); }; + +inline Video::Flags operator|(Video::Flags x, Video::Flags y) { + return static_cast( + static_cast>(x) | + static_cast>(y)); +} + +inline Video::Flags operator&(Video::Flags x, Video::Flags y) { + return static_cast( + static_cast>(x) & + static_cast>(y)); +} + +inline Video::Flags operator^(Video::Flags x, Video::Flags y) { + return static_cast( + static_cast>(x) ^ + static_cast>(y)); +} } // namespace ChargeVideo #endif diff --git a/src/Video.cpp b/src/Video.cpp index de0d53a..7bd7e6d 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -24,11 +24,13 @@ using namespace _ffmpeg; #include // ================== Video Construct/Destruct ================== -// ShouldVideoLoop default is true -Video::Video(std::string path, ChargeAudio::Engine *engine, - bool ShouldVideoLoop, float BufferSizeInSeconds) - : BufferLenghtInSeconds(BufferSizeInSeconds), - isVideoLooping(ShouldVideoLoop), audioEngine(engine) { +Video::Video(std::string path, ChargeAudio::Engine *engine, Flags videoF, + float bufferS) + : audioEngine(engine) { + // Have to do it here since ordering of init in the header class + bufferLenghtInSeconds = bufferS; + videoFlags = videoF; + // Context to hold our data ctx = avformat_alloc_context(); if (!ctx) { @@ -102,7 +104,7 @@ Video::Video(std::string path, ChargeAudio::Engine *engine, Sound = audioEngine->CreateSound(10); } - bufferMaxFrames = av_q2d(videoStream->avg_frame_rate) * BufferLenghtInSeconds; + bufferMaxFrames = av_q2d(videoStream->avg_frame_rate) * bufferLenghtInSeconds; timeBase = av_q2d(videoStream->time_base); } @@ -125,8 +127,7 @@ void Video::Play() { if (audioStreamNum != -1) { Sound->Play(); } - isVideoPaused = false; - isVideoOver = false; + videoState = State::Playing; } void Video::Pause() { @@ -138,7 +139,7 @@ void Video::Pause() { Sound->Pause(); } ID = 0; - isVideoPaused = true; + videoState = State::Paused; } void Video::Restart() { @@ -148,18 +149,24 @@ void Video::Restart() { restartVideo(); } -void Video::StopLooping() { isVideoLooping = false; } - -void Video::StartLooping() { isVideoLooping = true; } +const double Video::GetDuration() { return timeBase * videoStream->duration; } +const double Video::GetPlaybackTime() { return clock; } +const Vector2i Video::GetDimensions() { return Dimensions; } +Video::State Video::GetState() { return videoState; } +Video::Flags Video::GetFlags() { return videoFlags; } +void Video::SwitchLooping() { videoFlags = videoFlags ^ Flags::Looping; } // ================== Private Video Controls ================== void Video::continueVideo() { + bool finishedDecoding = currentFrameNumber >= videoStream->nb_frames - 2, + bufferEmpty = frameBuffer.empty(), + isNotLooping = (videoFlags & Flags::Looping) != Flags::Looping; // Looping handling - if (currentFrameNumber >= videoStream->nb_frames - 2) { - if (!isVideoLooping) { - isVideoOver = true; - Pause(); // Here we did that (check comment below) - return; // We remove what we are returning TO + if (finishedDecoding && bufferEmpty) { + if (isNotLooping) { + Pause(); + videoState = State::Finished; + return; } restartVideo(); } @@ -175,19 +182,19 @@ void Video::continueVideo() { // Load frame auto nextFrame = frameBuffer.begin(); - if (frameBuffer.size() > 0 && nextFrame->first <= clock) { + if (!bufferEmpty && nextFrame->first <= clock) { loadTexture(nextFrame->second); frameBuffer.erase(nextFrame); } - if (frameBuffer.size() < bufferMaxFrames) { + if (!finishedDecoding && frameBuffer.size() < bufferMaxFrames) { auto frameData = loadNextFrame(); frameBuffer.insert_or_assign(frameData.first, loadImage(std::move(frameData.second))); } if (audioStreamNum != -1 && - Sound->GetState() != ChargeAudio::Sound::SoundState::Playing) + Sound->GetState() != ChargeAudio::Sound::State::Playing) Sound->Play(); }