/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2025 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #ifndef __OPENSPACE_CORE___SESSIONRECORDINGHANDLER___H__ #define __OPENSPACE_CORE___SESSIONRECORDINGHANDLER___H__ #include #include #include #include namespace openspace::interaction { class SessionRecordingHandler : public properties::PropertyOwner { public: enum class SessionState { Idle = 0, Recording, Playback, PlaybackPaused }; using CallbackHandle = int; using StateChangeCallback = std::function; SessionRecordingHandler(); ~SessionRecordingHandler() override = default; /** * This is called with every rendered frame. If in recording state, the camera state * will be saved to the recording file (if its state has changed since last). If in * playback state, the next keyframe will be used (if it is time to do so). */ void preSynchronization(double dt); /** * If enabled, calling this function will render information about the session * recording that is currently taking place to the screen. */ void render() const; /** * Fixed delta time set by user for use during saving of frame during playback mode. */ double fixedDeltaTimeDuringFrameOutput() const; /** * Returns the number of microseconds that have elapsed since playback started, if * playback is set to be in the mode where a screenshot is captured with every * rendered frame (enableTakeScreenShotDuringPlayback() is used to enable this mode). * At the start of playback, this timer is set to the current steady_clock value. * However, during playback it is incremented by the fixed framerate of the playback * rather than the actual clock value (as in normal operation). * * \return Number of microseconds elapsed since playback started in terms of the * number of rendered frames multiplied by the fixed time increment per frame */ std::chrono::steady_clock::time_point currentPlaybackInterpolationTime() const; /** * Returns the simulated application time. This simulated application time is only * used when playback is set to be in the mode where a screenshot is captured with * every rendered frame (enableTakeScreenShotDuringPlayback() is used to enable this * mode). At the start of playback, this timer is set to the value of the current * applicationTime function provided by the window delegate (used during normal mode * or playback). However, during playback it is incremented by the fixed framerate of * the playback rather than the actual clock value. * * \return Application time in seconds, for use in playback-with-frames mode */ double currentApplicationInterpolationTime() const; /** * Starts a recording session, which will save data to the provided filename according * to the data format specified, and will continue until recording is stopped using * stopRecording() method. */ void startRecording(); /** * Used to stop a recording in progress. If open, the recording file will be closed, * and all keyframes deleted from memory. * \param filename File saved with recorded keyframes */ void stopRecording(const std::filesystem::path& filename, DataMode dataMode, bool overwrite = false); /** * Used to check if a session recording is in progress. * * \return `true` if recording is in progress */ bool isRecording() const; /** * Starts a playback session, which can run in one of three different time modes. * * \param timeline The session recording timeline that should be played back * \param loop If true then the file will playback in loop mode, continuously looping * back to the beginning until it is manually stopped * \param shouldWaitForFinishedTiles If true, the playback will wait for tiles to be * finished before progressing to the next frame. This value is only used when * `enableTakeScreenShotDuringPlayback` was called before. Otherwise this value * will be ignored * \param saveScreenshotFps If this value is specified, screenshots will be taken at * the provided framerate. If the value is not specified, no screenshots will * be taken */ void startPlayback(SessionRecording timeline, bool loop, bool shouldWaitForFinishedTiles, std::optional saveScreenshotFps); /** * Used to stop a playback in progress. If open, the playback file will be closed, and * all keyframes deleted from memory. */ void stopPlayback(); /** * Returns playback pause status. * * \return `true` if playback is paused */ bool isPlaybackPaused() const; /** * Pauses a playback session. This does both the normal pause functionality of setting * simulation delta time to zero, and pausing the progression through the timeline. * * \param pause If `true`, then will set playback timeline progression to zero */ void setPlaybackPause(bool pause); /** * Enables that rendered frames should be saved during playback. * * \param fps Number of frames per second. */ //void enableTakeScreenShotDuringPlayback(int fps); /** * Used to disable that renderings are saved during playback. */ //void disableTakeScreenShotDuringPlayback(); /** * Used to check if a session playback is in progress. * * \return `true` if playback is in progress */ bool isPlayingBack() const; void seek(double recordingTime); /** * Is saving frames during playback. */ bool isSavingFramesDuringPlayback() const; bool shouldWaitForTileLoading() const; /** * Used to obtain the state of idle/recording/playback. * * \return int value of state as defined by struct SessionState */ SessionState state() const; /** * Used to trigger a save of a script to the recording file, but only if a recording * is currently in progress. * * \param script String of the Lua command to be saved */ void saveScriptKeyframeToTimeline(std::string script); /** * \return The Lua library that contains all Lua functions available to affect the * interaction */ static openspace::scripting::LuaLibrary luaLibrary(); /** * Used to request a callback for notification of playback state change. * * \param cb Function handle for callback * \return CallbackHandle value of callback number */ CallbackHandle addStateChangeCallback(StateChangeCallback cb); /** * Removes the callback for notification of playback state change. * * \param handle Function handle for the callback */ void removeStateChangeCallback(CallbackHandle handle); /** * Provides list of available playback files. * * \return Vector of filenames in recordings dir */ std::vector playbackList() const; /** * Since session recordings only record changes, the initial conditions aren't * preserved when a playback starts. This function is called whenever a property value * is set and a recording is in progress. Before the set happens, this function will * read the current value of the property and store it so that when the recording is * finished, the initial state will be added as a set property command at the * beginning of the recording file, to be applied when playback starts. * * \param prop The property being set */ void savePropertyBaseline(properties::Property& prop); private: void tickPlayback(double dt); void tickRecording(double dt); void setupPlayback(double startTime); void cleanUpTimelinesAndKeyframes(); void checkIfScriptUsesScenegraphNode(std::string_view script) const; properties::BoolProperty _renderPlaybackInformation; properties::BoolProperty _ignoreRecordedScale; properties::BoolProperty _addModelMatrixinAscii; struct { double elapsedTime = 0.0; bool isLooping = false; bool playbackPausedWithDeltaTimePause = false; bool waitForLoading = false; struct { bool enabled = false; double deltaTime = 1.0 / 30.0; std::chrono::steady_clock::time_point currentRecordedTime; double currentApplicationTime = 0.0; } saveScreenshots; } _playback; struct { double elapsedTime = 0.0; } _recording; SessionState _state = SessionState::Idle; SessionState _lastState = SessionState::Idle; SessionRecording _timeline; std::vector::const_iterator _currentEntry = _timeline.entries.end(); std::unordered_map _savePropertiesBaseline; std::vector _loadedNodes; int _nextCallbackHandle = 0; std::vector> _stateChangeCallbacks; }; } // namespace openspace::interaction #endif // __OPENSPACE_CORE___SESSIONRECORDINGHANDLER___H__