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;
|
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.
|
* Built-in command: toggle a bool property value.
|
||||||
|
@ -1054,6 +1095,9 @@ static struct {
|
||||||
{ "profiler-start", do_profiler_start },
|
{ "profiler-start", do_profiler_start },
|
||||||
{ "profiler-stop", do_profiler_stop },
|
{ "profiler-stop", do_profiler_stop },
|
||||||
|
|
||||||
|
{ "video-start", do_video_start },
|
||||||
|
{ "video-stop", do_video_stop },
|
||||||
|
|
||||||
{ 0, 0 } // zero-terminated
|
{ 0, 0 } // zero-terminated
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/scene/util/OsgMath.hxx>
|
#include <simgear/scene/util/OsgMath.hxx>
|
||||||
#include <simgear/props/props_io.hxx>
|
#include <simgear/props/props_io.hxx>
|
||||||
|
#include <simgear/screen/video-encoder.hxx>
|
||||||
|
#include <simgear/structure/commands.hxx>
|
||||||
|
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include "view.hxx"
|
#include "view.hxx"
|
||||||
|
@ -164,6 +166,21 @@ FGViewMgr::unbind ()
|
||||||
SviewClear();
|
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
|
void
|
||||||
FGViewMgr::update (double dt)
|
FGViewMgr::update (double dt)
|
||||||
{
|
{
|
||||||
|
@ -172,10 +189,10 @@ FGViewMgr::update (double dt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the current view
|
// Update the current view
|
||||||
currentView->update(dt);
|
currentView->update(dt);
|
||||||
|
|
||||||
// update the camera now
|
// update the camera now
|
||||||
osg::ref_ptr<flightgear::CameraGroup> cameraGroup = flightgear::CameraGroup::getDefault();
|
osg::ref_ptr<flightgear::CameraGroup> cameraGroup = flightgear::CameraGroup::getDefault();
|
||||||
if (cameraGroup) {
|
if (cameraGroup) {
|
||||||
cameraGroup->setCameraParameters(currentView->get_v_fov(),
|
cameraGroup->setCameraParameters(currentView->get_v_fov(),
|
||||||
|
@ -186,6 +203,27 @@ FGViewMgr::update (double dt)
|
||||||
|
|
||||||
SviewUpdate(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");
|
std::string callsign = globals->get_props()->getStringValue("/sim/log-multiplayer-callsign");
|
||||||
if (callsign != "")
|
if (callsign != "")
|
||||||
{
|
{
|
||||||
|
@ -328,6 +366,120 @@ FGViewMgr::add_view( flightgear::View * v )
|
||||||
v->init();
|
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
|
int FGViewMgr::getCurrentViewIndex() const
|
||||||
{
|
{
|
||||||
return _current;
|
return _current;
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
#include <simgear/props/tiedpropertylist.hxx>
|
#include <simgear/props/tiedpropertylist.hxx>
|
||||||
#include <simgear/math/SGMath.hxx>
|
#include <simgear/math/SGMath.hxx>
|
||||||
|
#include <simgear/screen/video-encoder.hxx>
|
||||||
|
|
||||||
|
|
||||||
// forward decls
|
// forward decls
|
||||||
namespace flightgear
|
namespace flightgear
|
||||||
|
@ -92,6 +94,27 @@ public:
|
||||||
|
|
||||||
void add_view( flightgear::View * v );
|
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:
|
private:
|
||||||
simgear::TiedPropertyList _tiedProperties;
|
simgear::TiedPropertyList _tiedProperties;
|
||||||
|
|
||||||
|
@ -104,6 +127,8 @@ private:
|
||||||
viewer_list views;
|
viewer_list views;
|
||||||
|
|
||||||
int _current = 0;
|
int _current = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<simgear::VideoEncoder> _video_encoder;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _VIEWMGR_HXX
|
#endif // _VIEWMGR_HXX
|
||||||
|
|
Loading…
Reference in a new issue