From c1a34ac4ce0cbbadf06f2fdb446c093808410412 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 1 Dec 2021 23:35:56 +0000 Subject: [PATCH] Added support for generating compressed video. Uses simgear::VideoEncoder(). Added video-start and video-stop commands. We allow specification of container, codec, quality, speed and bitrate. src/Main/fg_commands.cxx Added video-start and video-stop commands. src/Viewer/viewmgr.cxx src/Viewer/viewmgr.hxx Added video_start() and video_stop(). --- src/Main/fg_commands.cxx | 44 +++++++++++ src/Viewer/viewmgr.cxx | 158 ++++++++++++++++++++++++++++++++++++++- src/Viewer/viewmgr.hxx | 25 +++++++ 3 files changed, 224 insertions(+), 3 deletions(-) diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx index 4b47d5cab..86d1a0fb4 100644 --- a/src/Main/fg_commands.cxx +++ b/src/Main/fg_commands.cxx @@ -429,6 +429,47 @@ do_view_new (const SGPropertyNode * arg, SGPropertyNode * root) return true; } +/** + * Built-in command: video-start. + * + * If arg->name exists, we use it as the leafname of the generated video, + * appending '.'+{/sim/video/container} if it doesn't contain '.' already. + * + * Otherwise we use: + * fgvideo-{/sim/aircraft}-YYMMDD-HHMMSS.{/sim/video/container} + * + * The video file is generated in directory {/sim/paths/screenshot-dir}. + * + * We also create a convenience link in the same directory called + * fgvideo-{/sim/aircraft}. (where is the same suffix as the + * recording file) that points to the video file. + */ +static bool +do_video_start (const SGPropertyNode * arg, SGPropertyNode * root) +{ + auto view_mgr = globals->get_subsystem(); + if (!view_mgr) return false; + view_mgr->video_start( + arg->getStringValue("name"), + arg->getStringValue("codec"), + arg->getDoubleValue("quality", -1), + arg->getDoubleValue("speed", -1), + arg->getIntValue("bitrate", 0) + ); + return true; +} + +/** + * Built-in command: video-stop. + */ +static bool +do_video_stop (const SGPropertyNode * arg, SGPropertyNode * root) +{ + auto view_mgr = globals->get_subsystem(); + if (!view_mgr) return false; + view_mgr->video_stop(); + return true; +} /** * Built-in command: toggle a bool property value. @@ -1053,6 +1094,9 @@ static struct { { "profiler-start", do_profiler_start }, { "profiler-stop", do_profiler_stop }, + + { "video-start", do_video_start }, + { "video-stop", do_video_stop }, { 0, 0 } // zero-terminated }; diff --git a/src/Viewer/viewmgr.cxx b/src/Viewer/viewmgr.cxx index 51a2e4769..9d0a6b429 100644 --- a/src/Viewer/viewmgr.cxx +++ b/src/Viewer/viewmgr.cxx @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include
#include "view.hxx" @@ -164,6 +166,21 @@ FGViewMgr::unbind () SviewClear(); } +static void videoEncodingPopup(const std::string& message, int delay) +{ + SGPropertyNode_ptr args(new SGPropertyNode); + args->setStringValue("label", message); + args->setIntValue("delay", (delay) ? delay : 15); + SG_LOG(SG_GENERAL, SG_ALERT, message); + globals->get_commands()->execute("show-message", args); +} + +static void videoEncodingError(const std::string& message) +{ + globals->get_props()->setIntValue("/sim/video/error", 1); + videoEncodingPopup(message, 15); +} + void FGViewMgr::update (double dt) { @@ -172,10 +189,10 @@ FGViewMgr::update (double dt) return; } - // Update the current view - currentView->update(dt); + // Update the current view + currentView->update(dt); -// update the camera now + // update the camera now osg::ref_ptr cameraGroup = flightgear::CameraGroup::getDefault(); if (cameraGroup) { cameraGroup->setCameraParameters(currentView->get_v_fov(), @@ -185,6 +202,27 @@ FGViewMgr::update (double dt) } SviewUpdate(dt); + + if (_video_encoder) + { + flightgear::CameraGroup* camera_group = flightgear::CameraGroup::getDefault(); + + for (auto& camera_info : camera_group->getCameras()) + { + if (camera_info->flags & flightgear::CameraInfo::GUI) continue; + osg::GraphicsContext* gc = camera_info->compositor->getGraphicsContext(); + try + { + _video_encoder->encode(dt, gc); + } + catch (std::exception& e) + { + videoEncodingError(e.what()); + _video_encoder.reset(); + } + break; + } + } std::string callsign = globals->get_props()->getStringValue("/sim/log-multiplayer-callsign"); if (callsign != "") @@ -328,6 +366,120 @@ FGViewMgr::add_view( flightgear::View * v ) v->init(); } +void FGViewMgr::video_start( + const std::string& name_in, + const std::string& codec_in, + double quality, + double speed, + int bitrate + ) +{ + SG_LOG(SG_GENERAL, SG_ALERT, "FGViewMgr::video_start():" + << " name_in=" << name_in + << " codec_in=" << codec_in + << " quality=" << quality + << " speed=" << speed + << " bitrate=" << bitrate + ); + globals->get_props()->setIntValue("/sim/video/error", 0); + std::string name = name_in; + std::string codec = codec_in; + + std::string name_link = std::string("fgvideo-") + fgGetString("/sim/aircraft"); + + if (name == "") + { + /* Use a default name containing current date and time. */ + time_t calendar_time = time(NULL); + struct tm* local_tm = localtime(&calendar_time); + char buffer[256]; + strftime(buffer, sizeof(buffer), "-%Y%m%d-%H%M%S", local_tm); + name = name_link + buffer; + } + size_t dot = name.find("."); + if (dot == std::string::npos) + { + /* No suffix specified. We need one because it determines the video + container format. */ + std::string container = fgGetString("/sim/video/container", "mpeg"); + name += "." + container; + name_link += "." + container; + } + else + { + // Give link the same suffix. + name_link += name.substr(dot); + } + SGPath path = SGPath(fgGetString("/sim/video/directory")); + SGPath path_link = path; + path.append(name); + path_link.append(name_link); + path_link.remove(); + bool ok = path_link.makeLink(path.file()); + if (!ok) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to create link " + << path_link.c_str() << " => " << path.file() + ); + } + + if (codec == "") codec = fgGetString("/sim/video/codec"); + if (quality == -1) quality = fgGetDouble("/sim/video/quality"); + if (speed == -1) speed = fgGetDouble("/sim/video/speed"); + if (bitrate == 0) bitrate = fgGetInt("/sim/video/bitrate"); + + std::string warning; + if (quality != -1 && (quality < 0 || quality > 1)) + { + warning += "Ignoring quality=" + std::to_string(quality) + " because should be -1 or in range 0-1.\n"; + quality = -1; + } + if (speed != -1 && (speed < 0 || speed > 1)) + { + warning += "Ignoring speed=" + std::to_string(speed) + " because should be -1 or in range 0-1.\n"; + speed = -1; + } + if (bitrate < 0) + { + warning += "Ignoring bitrate=" + std::to_string(bitrate) + " because should be >= 0.\n"; + bitrate = 0; + } + if (warning != "") + { + videoEncodingPopup(warning, 10); + } + + SG_LOG(SG_SYSTEMS, SG_ALERT, "Starting video encoding." + << " codec=" << codec + << " quality=" << quality + << " speed=" << speed + << " bitrate=" << bitrate + << " path=" << path + << " path_link=" << path_link + ); + try + { + _video_encoder.reset(new simgear::VideoEncoder(path.str(), codec, quality, speed, bitrate)); + } + catch (std::exception& e) + { + videoEncodingError(e.what()); + } +} + +void FGViewMgr::video_stop() +{ + if (_video_encoder) + { + _video_encoder.reset(); + videoEncodingPopup("Video encoding stopped", 5); + } + else + { + videoEncodingPopup("[Video encoding already stopped]", 5); + } +} + int FGViewMgr::getCurrentViewIndex() const { return _current; diff --git a/src/Viewer/viewmgr.hxx b/src/Viewer/viewmgr.hxx index da6c55d82..464bdfb83 100644 --- a/src/Viewer/viewmgr.hxx +++ b/src/Viewer/viewmgr.hxx @@ -31,6 +31,8 @@ #include #include #include +#include + // forward decls namespace flightgear @@ -92,6 +94,27 @@ public: void add_view( flightgear::View * v ); + // Start video encoding to . + // + // If is "" we generate a name containing current date and time. + // + // If is "" we use /sim/video/codec. + // + // If quality is -1 we use /sim/video/quality; similarly for speed. If + // bitrate is 0 we use /sim/video/bitrate. + // + // We show popup warning if values are out of range - quality and speed + // must be -1 or 0-1, bitrate must be >= 0. + // + void video_start( + const std::string& name, + const std::string& codec, + double quality, + double speed, + int bitrate + ); + void video_stop(); + private: simgear::TiedPropertyList _tiedProperties; @@ -104,6 +127,8 @@ private: viewer_list views; int _current = 0; + + std::unique_ptr _video_encoder; }; #endif // _VIEWMGR_HXX