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:
parent
69344c29c0
commit
c1a34ac4ce
3 changed files with 224 additions and 3 deletions
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue