Map to simulation time with frame stepping instead of video stretching. Remove resolution and duration as properties

This commit is contained in:
Ylva Selling
2023-02-08 17:22:18 -05:00
parent 7e8962efa0
commit e67fe569ae
2 changed files with 48 additions and 130 deletions

View File

@@ -84,28 +84,10 @@ namespace {
"Go to start in video"
};
constexpr openspace::properties::Property::PropertyInfo DurationInfo = {
"Duration",
"Duration",
"Duration of video, in seconds"
};
constexpr openspace::properties::Property::PropertyInfo ResolutionInfo = {
"Resolution",
"Resolution",
"Resolution of video, in pixels"
};
struct [[codegen::Dictionary(VideoTileProvider)]] Parameters {
// [[codegen::verbatim(FileInfo.description)]]
std::filesystem::path file;
// [[codegen::verbatim(ResolutionInfo.description)]]
glm::ivec2 resolution;
// [[codegen::verbatim(DurationInfo.description)]]
std::optional<double> duration;
// [[codegen::verbatim(StartTimeInfo.description)]]
std::optional<std::string> startTime [[codegen::datetime()]];
@@ -198,15 +180,12 @@ VideoTileProvider::VideoTileProvider(const ghoul::Dictionary& dictionary)
: _play(PlayInfo)
, _pause(PauseInfo)
, _goToStart(GoToStartInfo)
, _videoDuration(DurationInfo, 0.0)
, _videoResolution(ResolutionInfo, glm::ivec2(0))
{
ZoneScoped
const Parameters p = codegen::bake<Parameters>(dictionary);
_videoFile = p.file;
_videoResolution = p.resolution;
if (p.animationMode.has_value()) {
switch (*p.animationMode) {
@@ -236,14 +215,14 @@ VideoTileProvider::VideoTileProvider(const ghoul::Dictionary& dictionary)
" end time");
return;
}
if (!p.duration.has_value()) {
LERROR("Video tile layer tried to map to simulation time but duration");
return;
}
_videoDuration = *p.duration;
//_videoDuration = *p.duration;
_startJ200Time = Time::convertTime(*p.startTime);
_endJ200Time = Time::convertTime(*p.endTime);
ghoul_assert(_endJ200Time > _startJ200Time, "Invalid times for video");
global::timeManager->addTimeJumpCallback([this]() {
seekToTime(correctVideoPlaybackTime());
});
}
global::callback::postSyncPreDraw->emplace_back([this]() {
@@ -331,7 +310,6 @@ void VideoTileProvider::pause() {
MPV_FORMAT_FLAG,
&pause
);
_isPaused = true;
if (!checkMpvError(result)) {
LWARNING("Error when pausing video");
}
@@ -346,7 +324,6 @@ void VideoTileProvider::play() {
MPV_FORMAT_FLAG,
&pause
);
_isPaused = false;
if (!checkMpvError(result)) {
LWARNING("Error when playing video");
}
@@ -402,7 +379,7 @@ void VideoTileProvider::initializeMpv() {
// https://mpv.io/manual/master/#options-video-timing-offset
setPropertyStringMpv("video-timing-offset", "0");
setPropertyStringMpv("load-stats-overlay", "");
//setPropertyStringMpv("load-stats-overlay", "");
//mpv_set_property_string(_mpvHandle, "script-opts", "autoload-disabled=yes");
@@ -453,7 +430,7 @@ void VideoTileProvider::initializeMpv() {
}
//Create FBO to render video into
createFBO(_videoResolution.value().x, _videoResolution.value().y);
createFBO(_videoResolution.x, _videoResolution.y);
//Observe video parameters
observePropertyMpv("video-params", MPV_FORMAT_NODE, LibmpvPropertyKey::Params);
@@ -466,8 +443,7 @@ void VideoTileProvider::initializeMpv() {
observePropertyMpv("fps", MPV_FORMAT_DOUBLE, LibmpvPropertyKey::Fps);
if (_animationMode == AnimationMode::MapToSimulationTime) {
updateStretchingOfTime();
pauseVideoIfOutsideValidTime();
pause();
}
_isInitialized = true;
@@ -493,73 +469,40 @@ double VideoTileProvider::correctVideoPlaybackTime() const {
return percentage * _videoDuration;
}
void VideoTileProvider::pauseVideoIfOutsideValidTime() {
if (!isWithingStartEndTime() && !_isPaused) {
pause();
}
else if (isWithingStartEndTime() && _isPaused) {
play();
}
}
void VideoTileProvider::seekToTime(double time) {
bool isPlaying = !_isPaused;
bool seekIsDifferent = abs(time - _currentVideoTime) > SeekThreshold;
if (seekIsDifferent) {
if (seekIsDifferent && !_isSeeking) {
// Pause while seeking
pause();
// Seek
std::string timeString = std::to_string(time);
const char* params = timeString.c_str();
const char* args[] = { "seek", params, "absolute", NULL };
int result = mpv_command_async(
_mpvHandle,
static_cast<uint64_t>(LibmpvPropertyKey::Command),
args
);
if (!checkMpvError(result)) {
LINFO("Seek resulted in invalid operation");
}
// Play if video was playing before seek
if (isPlaying) {
play();
}
}
}
void VideoTileProvider::updateStretchingOfTime() {
double deltaTime = global::timeManager->deltaTime();
// If were going backwards in time, we don't need to play the video forwards
if (deltaTime < 0) {
pause();
}
else {
double stretchedTime = (_endJ200Time - _startJ200Time) / deltaTime; // seconds
if (stretchedTime > 0.0) {
int result = mpv_set_property_async(
_mpvHandle,
static_cast<uint64_t>(LibmpvPropertyKey::Speed),
"speed",
MPV_FORMAT_DOUBLE,
&stretchedTime
);
if (!checkMpvError(result)) {
LWARNING("Error when pausing video");
}
}
const char* cmd[] = { "seek", params, "absolute", NULL };
commandAsyncMpv(cmd, LibmpvPropertyKey::Seek);
_isSeeking = true;
}
}
void VideoTileProvider::renderMpv() {
if (_animationMode == AnimationMode::MapToSimulationTime) {
pauseVideoIfOutsideValidTime();
// If we are in valid times, step frames accordingly
if (isWithingStartEndTime()) {
double now = global::timeManager->time().j2000Seconds();
double deltaTime = now - _timeAtLastRender;
if (deltaTime > _frameDuration) {
// Stepping forwards
stepFrameForward();
_timeAtLastRender = now;
}
else if (deltaTime < -_frameDuration) {
// Stepping backwards
stepFrameBackward();
_timeAtLastRender = now;
}
}
// Make sure we are at the correct time
double time = correctVideoPlaybackTime();
bool shouldSeek = abs(time - _currentVideoTime) > SeekThreshold;
if (shouldSeek) {
seekToTime(time);
}
@@ -567,7 +510,6 @@ void VideoTileProvider::renderMpv() {
handleMpvEvents();
if (_wakeup) {
if ((mpv_render_context_update(_mpvRenderContext) & MPV_RENDER_UPDATE_FRAME)) {
// See render_gl.h on what OpenGL environment mpv expects, and other API
@@ -576,8 +518,8 @@ void VideoTileProvider::renderMpv() {
int fboInt = static_cast<int>(_fbo);
mpv_opengl_fbo mpfbo{
fboInt ,
_videoResolution.value().x,
_videoResolution.value().y, 0
_videoResolution.x,
_videoResolution.y, 0
};
int flip_y{ 1 };
@@ -728,9 +670,7 @@ void VideoTileProvider::handleMpvProperties(mpv_event* event) {
}
_videoDuration = *duration;
if (_animationMode == AnimationMode::MapToSimulationTime) {
updateStretchingOfTime();
}
_frameDuration = _fps * ((_endJ200Time - _startJ200Time) /_videoDuration);
LINFO(fmt::format("Duration: {}", *duration));
break;
@@ -766,25 +706,16 @@ void VideoTileProvider::handleMpvProperties(mpv_event* event) {
break;
}
if (*height == _videoResolution.value().y) {
if (*height == _videoResolution.y) {
break;
}
LINFO(fmt::format("New height: {}", *height));
if (*height > 0) {
if (_videoResolution.value().x > 0 && _fbo > 0) {
resizeFBO(_videoResolution.value().x, *height);
}
else {
glm::ivec2 newValue = _videoResolution.value();
newValue.y = *height;
_videoResolution = newValue;
}
}
else {
LERROR("Could not find height of video");
if (*height > 0 && _videoResolution.x > 0 && _fbo > 0) {
resizeFBO(_videoResolution.x, *height);
}
break;
}
case LibmpvPropertyKey::Width: {
@@ -801,25 +732,16 @@ void VideoTileProvider::handleMpvProperties(mpv_event* event) {
break;
}
if (*width == _videoResolution.value().y) {
if (*width == _videoResolution.y) {
break;
}
LINFO(fmt::format("New width: {}", *width));
if (*width > 0) {
if (_videoResolution.value().y > 0 && _fbo > 0) {
resizeFBO(*width, _videoResolution.value().y);
}
else {
glm::ivec2 newValue = _videoResolution.value();
newValue.x = *width;
_videoResolution = newValue;
}
}
else {
LERROR("Could not find width of video");
if (*width > 0 && _videoResolution.y > 0 && _fbo > 0) {
resizeFBO(*width, _videoResolution.y);
}
break;
}
case LibmpvPropertyKey::Meta: {
@@ -849,7 +771,7 @@ void VideoTileProvider::handleMpvProperties(mpv_event* event) {
}
case LibmpvPropertyKey::Params: {
if (!event->data) {
LERROR("Could not find video parameters");
LINFO("Could not find video parameters");
break;
}
@@ -1045,8 +967,8 @@ void VideoTileProvider::createFBO(int width, int height) {
// Render video frame to texture
_mpvFbo = mpv_opengl_fbo{
static_cast<int>(_fbo),
_videoResolution.value().x,
_videoResolution.value().y,
_videoResolution.x,
_videoResolution.y,
0
};
}
@@ -1054,7 +976,7 @@ void VideoTileProvider::createFBO(int width, int height) {
void VideoTileProvider::resizeFBO(int width, int height) {
LINFO(fmt::format("Resizing FBO with width: {} and height: {}", width, height));
if (width == _videoResolution.value().x && height == _videoResolution.value().y) {
if (width == _videoResolution.x && height == _videoResolution.y) {
return;
}

View File

@@ -64,14 +64,11 @@ public:
static documentation::Documentation Documentation();
private:
// Time for 1 frame when 24 fps (1.0 / 24.0). Chose 24 fps as this is the lowest
// probably fps we'll encounter
static constexpr double SeekThreshold = 0.0417;
// Threshold where we are officially out of sync
static constexpr double SeekThreshold = 1.0;
properties::TriggerProperty _play;
properties::TriggerProperty _pause;
properties::TriggerProperty _goToStart;
properties::DoubleProperty _videoDuration;
properties::IVec2Property _videoResolution;
// libmpv property keys
enum class LibmpvPropertyKey : uint64_t {
@@ -84,7 +81,6 @@ private:
Command,
Seek,
Width,
Speed,
Fps,
Pause
};
@@ -100,8 +96,6 @@ private:
// Map to simulation time functions
double correctVideoPlaybackTime() const;
bool isWithingStartEndTime() const;
void pauseVideoIfOutsideValidTime();
void updateStretchingOfTime();
// Libmpv
void initializeMpv(); // Called first time in postSyncPreDraw
@@ -128,10 +122,13 @@ private:
double _currentVideoTime = 0.0;
double _frameDuration = 0.0;
double _fps = 0.04166666667; //1/24
double _timeAtLastRender = 0.0;
bool _hasReachedEnd = false;
bool _tileIsReady = false;
bool _isInitialized = false;
bool _isSeeking = false;
double _videoDuration = 0.0;
glm::ivec2 _videoResolution = glm::ivec2(2048, 1024);
// libmpv
mpv_handle* _mpvHandle = nullptr;
@@ -141,7 +138,6 @@ private:
GLuint _fbo = 0;
int _wakeup = 0;
bool _didRender = false;
bool _isPaused = false;
// Cache for rendering the same frame
std::map<TileIndex::TileHashKey, Tile> _tileCache;