1
0
Fork 0

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().
This commit is contained in:
Julian Smith 2021-12-01 23:35:56 +00:00
parent 69344c29c0
commit c1a34ac4ce
3 changed files with 224 additions and 3 deletions

View file

@ -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}.<suffix> (where <suffix> 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<FGViewMgr>();
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<FGViewMgr>();
if (!view_mgr) return false;
view_mgr->video_stop();
return true;
}
/**
* Built-in command: toggle a bool property value.
@ -1054,6 +1095,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
};

View file

@ -31,6 +31,8 @@
#include <simgear/compiler.h>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/screen/video-encoder.hxx>
#include <simgear/structure/commands.hxx>
#include <Main/fg_props.hxx>
#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<flightgear::CameraGroup> cameraGroup = flightgear::CameraGroup::getDefault();
if (cameraGroup) {
cameraGroup->setCameraParameters(currentView->get_v_fov(),
@ -186,6 +203,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;

View file

@ -31,6 +31,8 @@
#include <simgear/props/props.hxx>
#include <simgear/props/tiedpropertylist.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/screen/video-encoder.hxx>
// forward decls
namespace flightgear
@ -92,6 +94,27 @@ public:
void add_view( flightgear::View * v );
// Start video encoding to <path>.
//
// If <name> is "" we generate a name containing current date and time.
//
// If <codec> 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<simgear::VideoEncoder> _video_encoder;
};
#endif // _VIEWMGR_HXX