This is by Lars Toenning <dev@ltoenning.de>, Roman Ludwicki <romek21@op.pl> and
SDeAstis <salvatore.deastis@gmail.com>, in 2021 Hackathon.
Also cope with removal of SGPropertyNode::getName() - use getNameString()
instead.
If specified we start video encoding when replay starts, and stop video
encoding when we reach recording end.
This is supported with --load-tape on the command line and from the 'Load tape'
dialogue.
src/Aircraft/continuous.cxx
src/Aircraft/continuous.hxx
If stopping replay with auto create video, then stop video encoding and
restore original /sim/time/fixed-dt if we changed it.
src/Aircraft/replay-internal.cxx
src/Aircraft/replay-internal.hxx
When starting replay, call view_mgr->video_start() and set
/sim/time/fixed-dt if specified.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
loadTape(): added bool create_video and double fixed_dt args.
src/Main/options.cxx
Added new command-line options --load-tape-create-video and
--load-tape-fixed-dt=<double> to allow auto create video and fixed-dt when
replaying.
The main FGReplay subsystem class now contains just the public API for
record/replay, plus a pointer to instance of new ReplayInternal implementation
class. Also moved some Continuous-specific code into new source file.
src/Aircraft/CMakeLists.txt
Updated for new files.
src/Aircraft/continuous.cxx
src/Aircraft/continuous.hxx
New, code specific to Continuous record/replay.
src/Aircraft/flightrecorder.hxx
Minor changes.
src/Aircraft/replay-internal.cxx
src/Aircraft/replay-internal.hxx
New, contains implementation of record/replay.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
Modified to be just concerned with public record/replay API. The
implementation now defers to the ReplayInternal instance. Added support for
auto create-video when loading Continuous recording.
src/Main/fg_init.cxx
resetStatisticsProperties() is now in FGReplay, not FGReplayData - the
latter is not in the replay.hxx header any more.
scripts/python/performance_replay.py
New, script for replaying a recording in Flightgear and gathering
statistics.
scripts/python/recordreplay.py
Minor change.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
If /sim/replay/log-frame-times is true, populate
/sim/replay/log-frame-times/dt[] with frame times while replaying.
With Normal recordings, if we are recording multiplayer aircraft, we continue
appending to the in-memory recording while replaying, so we need to stop when
we reach the original end of recorded frames.
New properies so recordreplay can know extent of current in-memory normal
recording:
sim/replay/record-normal-begin
sim/replay/record-normal-end
This is required since scripts/python/recordreplay.py improved checking of
recording length when replaying.
scripts/python/recordreplay.py
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
src/Aircraft/replay.*:
If /sim/replay/record-continuous-compression is true, we compress each
frame's data as a separate raw zlib stream.
Requires latest simgear's simgear/io/iostreams/zlibstream.cxx for
decompression with ZLibCompressionFormat::ZLIB_RAW. Haven't figured out how
to extend simgear's code to provide a compressing ostream so for now we
have our own local compression code.
We open popup and set sim/replay/replay-error=true if we fail to read
compressed data.
scripts/python/recordreplay.py:
Added test of compressed continuous recordings.
docs-mini/README-recordings.md:
Added information about compressed format.
Some spacecraft send out-of-range engine numbers from the JSBsim side.
Should probably be fixed in JSBsim itself but let’s check here to
keep compat with existing FDMs.
FGReplay was using time t, AIMultiplayer was using t+dt. The fix is to make
FGReplay also use t+dt.
scripts/python/recordreplay.py
Make --test-motion-mp test for this problem - we check that user and mp
aircraft are a constant distance apart when replaying.
src/AIModel/AIMultiplayer.cxx
Create properties showing distance moved since start by user and mp
aircraft, which can then be tested by recordreplay.py.
src/Aircraft/replay.cxx
Increment current_time by dt before calling replay(current_time), to
ensure that replay() sees the time as is later used by AIMultiplayer (via
/sim/replay/time).
Also, when moving MP packets around, medium_term buffer can become empty. Have
added checks for empty short, medium and long term buffers when moving packets.
Setting sim/replay/log-raw-speed logs raw speed to
/sim/replay/log-raw-speed-values/value[].
'scripts/python/recordreplay.py --test-motion' uses this to test for a bug
where the user aircraft simtime and signals information don't quite match in
continuous recordings.
The end time is updated in background when replaying from url, so we must not
set /sim/replay/end-time elsewhere. So we now only set /sim/replay/end-time in
FGReplay::get_end_time() if recording is not Continuous.
FGFlightRecorder::replay():
Fixed behaviour when going back in time while replaying - we were not
replaying initial property changes correctly because we deliberately
don't load signals data for some frames when we are only interested in
multiplayer or extra properties.
Removed incorrect code that tried to avoid replaying the same frame twice
in succession.
FGReplay::update(double dt):
Improved how we figure out which frames we need to replay just for multiplayer
or extra property changes.
FGReplay::makeTapePath():
New fn containing the code for converting tape name into path by prepending
tape dir and appending .fgtape as necessary. Called when loading tape at
startup or at runtime.
Cached some more property nodes in SGPropertyNode_ptr's.
Don't recordCHAT_MSG_ID messages; unfortunately it looks like most (all?)
chat message text is received as part of POS_DATA_ID messages tied to
sim/multiplay/chat, so this doesn't actually avoid recording chat messages.
Need to use simgear::HTTP::FileRequestRef shared pointer, otherwise
HTTPFileRequest will delete it after the transfer.
Also changed callback to a lambda to match reinstated simgear API.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
Recording of extra properties is only supported in Continuous recordings.
Modified Continuous recording format to allow future forwards
compatibility. See docs-mini/README-recordings.md for details. This breaks
compatibility with previously-generated Continuous recordings, but this only
affects next.
To reduce overhead we record all extra property values in the first frame and
then later frames contain only extra property changes. When replaying, if the
user jumps backwards we replay all extra property changes since the start of
the recording. Similarly if the user jumps forwards, we replay any intervening
extra property changes.
Recording extra properties:
This is enabled by:
/sim/replay/record-extra-properties
The extra properties that are recorded are identified by the property
paths in the values of /sim/replay/record-extra-properties-paths/path[]
properties. We record the entire tree for each specified path.
Recording of main window position size:
We have specific support for record and replay of main window position/size.
This is enabled by:
/sim/replay/record-main-window
Recording of main window view:
We have specific support for recording the view type and direction/zoom
settings.
This is enabled by:
/sim/replay/record-main-view
We record the /sim/current-view/ property tree, excluding some subtrees
that continuously vary but are not required for replaying of the view.
When replaying, we allow separate control of what extra property changes are
replayed, with:
/sim/replay/replay-extra-property-changes
/sim/replay/replay-extra-property-removal
/sim/replay/replay-main-window-position
/sim/replay/replay-main-window-size
/sim/replay/replay-main-view
We work around some problems caused by the use of tied properties when
replaying changes to view-number.
Window position issue:
When replaying window position and size changes, things get a little tricky
because osgViewer::GraphicsWindow::setWindowRectangle() takes a position
for the entire window, but osgGA::GUIEventAdapter::RESIZE events contain
the position of the interior of the window; for example the y values will
differ by the height of the window's titlebar. This can cause windows to
move progressively further down each time they are positioned or resized.
There doesn't seem to be a way of finding the size of a window's
furniture directly. So instead this commit adds a new method
osgGA::GUIEventAdapter::setWindowRectangleInteriorWithCorrection() which
wraps osgViewer::GraphicsWindow::setWindowRectangle(). We listen for the
following osgGA::GUIEventAdapter::RESIZE event and calculate corrections
for x and y position that are used for subsequent calls.
docs-mini/README-recordings.md:
Updated to document new Continuous format.
scripts/python/recordreplay.py:
New script to test various aspects of record/replay.
Other:
We now create convenience softlink to most recent continuous recording, using
SGPath::makeLink(). Note that SGPath::makeLink() currently does nothing on
Windows.
Changed format of Continuous recordings to contain a single property tree
in header. This is much simpler than having separate Config and Meta trees.
Continuous replay:
If the user replays a continuous recording from file and then end replay
with the 'My controls' or 'End replay' buttons, we now forget about the
continuous recording. This enables the usual in-memory record/replay to be
used subsequently. Also added a '-continuous' suffix to continuous fgtape
filenames.
New recovery system:
If /sim/replay/recovery-period is set and greater than zero, we
periodically save a single-item continuous recording to a recovery file
called <aircraft-type>-recovery.fgtape (with no date or time in the name),
doing so in such a way as to ensure that there is always a valid recovery
file even if flightgear crashes. One can then resume the flight from the
most recently-saved point by loading this from within flightgear or with
the --load-tape=... option.
Also did a fair amount of refactoring and tried to clarify the different
property nodes that we embed within recordings.
This background recording keeps the user aircraft stationary while recording
all movements of multiplayer aircraft. Allows one to look at replay without,
for example, disturbing continuous record to file.
This allows full-fidelity recordings of arbitrary length (limited only by disc
space) to be created and replayed. These recordings always contain multiplayer
information, regardless of /sim/replay/multiplayer.
When an uncompressed recording is loaded, it is not copied into memory. Instead
we extract individual frame information as required when replaying.
Recording to file is activated by setting /sim/replay/record-continuous to
true. We use the same filename pattern as ordinary fgtapes. The file format
is similar except for being uncompressed (so that replaying can seek within
the recording; gzip etc don't seem to support seeking within the uncompressed
stream.)
Fgtape:
If multipayer replay is active we write multiplayer packet information to
fgtape files. We also add /sim/replay/multiplayer to the properties written
to fgtape file so at load time we knows whether to expect multiplayer
packets.
New builds of fg can load and replay old recordings.
It is expected (but has not been tested) that old builds of fg will be able
to read new fgtape recordings where /sim/replay/multiplayer was unset or
false (currently the default).
Old builds of fg will not be able to load fgtape recordings that include
multiplayer data.
Avoid incorrect removal of multiplayer aircraft when replaying:
We now clear multiplayer motion history when we jump forwards or backwards
during replay. This ensures that multiplayer code doesn't remove
multiplayer aircraft because of inconsistent-looking time stamps.
We preserve some multiplay information when purging recorded information
to save space. This ensures that when replaying we get frequent enough
packets to avoid the multiplayer code thinking that multiplayer aircraft
have disappeared.
At the moment this only works for live replays - we don't (yet) write the
multiplayer information to fgtape files.
Enable with:
--prop:bool:/sim/replay/multiplayer=true
This works by copying all raw multiplayer packets into a buffer in
FGMultiplayMgr. Each time it is called, FGFlightRecorder::capture() moves all
the available packet from this buffer into its FGReplayData. Thus we store
roughly syncronised multiplayer packets along with the user aircraft's detailed
replay properties.
When replaying, FGFlightRecorder pushes packets into a buffer in
FGMultiplayMgr, which are used instead of live multiplayer packets. [Actually
when replaying FGMultiplayMgr still reads live packets in order to handle live
chat messages, and ignores chat messages from FGFlightRecorder.]
Requires corresponding SimGear change. With this last round, we work
with FGData on non-Latin1 path on Windows. (Final fixes are for
Nasal io.readfile, which needed io.stat to work, and SGSky allowing
correct SGPath use)