diff --git a/include/cinder/app/AppBase.h b/include/cinder/app/AppBase.h index 65116caf56..892e9bcfe6 100644 --- a/include/cinder/app/AppBase.h +++ b/include/cinder/app/AppBase.h @@ -331,9 +331,25 @@ class AppBase { virtual void setFullScreen( bool fullScreen, const FullScreenOptions &options = FullScreenOptions() ) { getWindow()->setFullScreen( fullScreen, options ); } //! Returns the number of seconds which have elapsed since application launch - double getElapsedSeconds() const { return mTimer.getSeconds(); } + //double getElapsedSeconds() const { return mTimer.getSeconds(); } + double getTrueElapsedSeconds() const { return mTimer.getSeconds(); } + double getElapsedSeconds() const { + return mDoPlaybackForCapture ? mFrameCount / mCaptureFrameRate : mTimer.getSeconds(); + } //! Returns the number of animation frames which have elapsed since application launch uint32_t getElapsedFrames() const { return mFrameCount; } + + void setPlaybackForCapture ( bool doPlaybackForCapture ) { + mDoPlaybackForCapture = doPlaybackForCapture; + mFpsLastSampleTime = getElapsedSeconds(); + mFpsLastSampleFrame = mFrameCount; + } + float getCaptureFrameRate() const { return mCaptureFrameRate; } + void setCaptureFrameRate( float frameRate ) { mCaptureFrameRate = frameRate; } + + float getPlaybackFrameRate() const { + return mDoPlaybackForCapture ? mCaptureFrameRate : getFrameRate(); + } //! Returns whether the app is registered to receive multiTouch events from the operating system, configurable via Settings at startup. Disabled by default on desktop platforms, enabled on mobile. bool isMultiTouchEnabled() const { return mMultiTouchEnabled; } @@ -438,6 +454,9 @@ class AppBase { double mFpsSampleInterval; bool mMultiTouchEnabled, mHighDensityDisplayEnabled; RendererRef mDefaultRenderer; + + bool mDoPlaybackForCapture; + float mCaptureFrameRate; std::vector mCommandLineArgs; std::shared_ptr mTimeline; @@ -495,6 +514,8 @@ inline Area getWindowBounds() { return AppBase::get()->getWindowBounds(); } inline float getWindowContentScale() { return AppBase::get()->getWindowContentScale(); } //! Returns the maximum frame-rate the active App will attempt to maintain. inline float getFrameRate() { return AppBase::get()->getFrameRate(); } +//! Returns the current frame rate setting - either real-time or capture mode +inline float getPlaybackFrameRate() { return AppBase::get()->getPlaybackFrameRate(); } //! Sets the maximum frame-rate the active App will attempt to maintain. inline void setFrameRate( float frameRate ) { AppBase::get()->setFrameRate( frameRate ); } //! Returns whether the active App is in full-screen mode or not. diff --git a/include/cinder/qtime/QuickTimeImplAvf.h b/include/cinder/qtime/QuickTimeImplAvf.h index 5fe2b3186e..53c2b6dbef 100644 --- a/include/cinder/qtime/QuickTimeImplAvf.h +++ b/include/cinder/qtime/QuickTimeImplAvf.h @@ -127,9 +127,9 @@ class MovieBase { //! Sets whether the movie is set to loop during playback. If \a palindrome is true, the movie will "ping-pong" back and forth void setLoop( bool loop = true, bool palindrome = false ); //! Advances the movie by one frame (a single video sample). Ignores looping settings. - bool stepForward(); + bool stepForward( int frms = 1 ); //! Steps backward by one frame (a single video sample). Ignores looping settings. - bool stepBackward(); + bool stepBackward( int frms = 1 ); /** Sets the playback rate, which begins playback immediately for nonzero values. * 1.0 represents normal speed. Negative values indicate reverse playback and \c 0 stops. * diff --git a/src/cinder/TimelineItem.cpp b/src/cinder/TimelineItem.cpp index 55b352e0f6..8a9e630e69 100644 --- a/src/cinder/TimelineItem.cpp +++ b/src/cinder/TimelineItem.cpp @@ -115,7 +115,9 @@ void TimelineItem::stepTo( float newTime, bool reverse ) complete( true ); } } - else if( ( ! mLoop ) && ( ! mInfinite ) ) { // newTime >= endTime + // CB - I've fixed this bug before + //else if( ( ! mLoop ) && ( ! mInfinite ) ) { // newTime >= endTime + else if( ( ! mLoop ) && ( ! mInfinite ) && ( ! mPingPong ) ) { // newTime >= endTime if( ( ! mComplete ) && ( ! reverse ) ) { mComplete = true; mReverseComplete = false; diff --git a/src/cinder/app/AppBase.cpp b/src/cinder/app/AppBase.cpp index 4dc36c16c1..45c8d12d84 100644 --- a/src/cinder/app/AppBase.cpp +++ b/src/cinder/app/AppBase.cpp @@ -92,7 +92,8 @@ void AppBase::Settings::setShouldQuit( bool shouldQuit ) AppBase::AppBase() : mFrameCount( 0 ), mAverageFps( 0 ), mFpsSampleInterval( 1 ), mTimer( true ), mTimeline( Timeline::create() ), - mFpsLastSampleFrame( 0 ), mFpsLastSampleTime( 0 ) + mFpsLastSampleFrame( 0 ), mFpsLastSampleTime( 0 ), + mDoPlaybackForCapture( false ), mCaptureFrameRate( 30 ) { sInstance = this; @@ -155,6 +156,7 @@ void AppBase::privateSetup__() setup(); } +// this is the central update() call in the application void AppBase::privateUpdate__() { mFrameCount++; @@ -170,16 +172,22 @@ void AppBase::privateUpdate__() mSignalUpdate.emit(); - update(); + update(); // call ::update() + + // mTimer is used only here and in getElapsedSeconds() + // mFrameRate defaults to 60.0f, used in startAnimationTimer() to create a timer which fires at the frame rate + // mCaptureFrameRate defaults to 30.0f since that's what works best for quicktime videos + double now = getElapsedSeconds(); - mTimeline->stepTo( static_cast( getElapsedSeconds() ) ); + // update master timeline - not sure why this is done after update() + mTimeline->stepTo( static_cast( now ) ); - double now = mTimer.getSeconds(); + // update FPS statistics + // mFpsSampleInterval == 1.0 secs by default if( now > mFpsLastSampleTime + mFpsSampleInterval ) { //calculate average Fps over sample interval uint32_t framesPassed = mFrameCount - mFpsLastSampleFrame; mAverageFps = (float)(framesPassed / (now - mFpsLastSampleTime)); - mFpsLastSampleTime = now; mFpsLastSampleFrame = mFrameCount; } diff --git a/src/cinder/qtime/QuickTimeImplAvf.mm b/src/cinder/qtime/QuickTimeImplAvf.mm index d67922ec7e..74c8af6e1c 100644 --- a/src/cinder/qtime/QuickTimeImplAvf.mm +++ b/src/cinder/qtime/QuickTimeImplAvf.mm @@ -367,20 +367,21 @@ - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output mPalindrome = (loop? palindrome: false); } -bool MovieBase::stepForward() +bool MovieBase::stepForward( int frms /* = 1 */ ) { if( ! mPlayerItem ) return false; bool can_step_forwards = [mPlayerItem canStepForward]; if( can_step_forwards ) { - [mPlayerItem stepByCount:1]; + //[mPlayerItem stepByCount:1]; + [mPlayerItem stepByCount:frms]; } return can_step_forwards; } -bool MovieBase::stepBackward() +bool MovieBase::stepBackward( int frms /* = 1 */ ) { if( ! mPlayerItem) return false; @@ -388,7 +389,8 @@ - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output bool can_step_backwards = [mPlayerItem canStepBackward]; if (can_step_backwards) { - [mPlayerItem stepByCount:-1]; + //[mPlayerItem stepByCount:-1]; + [mPlayerItem stepByCount:-frms]; } return can_step_backwards;