1
0
Fork 0
flightgear/docs-mini/README-recordings.md
Julian Smith 31ec727872 Added record/replay of extra properties, with specific support for window size/position and view settings.
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.
2021-02-13 11:38:52 +00:00

133 lines
7.1 KiB
Markdown

# Flightgear recordings
Recording files generally have a `.fgtape` suffix.
As of 2020-12-22, there are three kinds of recordings:
* Normal
* Continuous
* Recovery
Normal recordings are compressed and contain frames at varying intervals with more recent frames being closer together in time. They are generated from the in-memory recording that Flightgear always maintains. They may contain multiplayer information.
Continuous recordings are uncompressed and written directly to a file while Flightgear runs, giving high resolution with near unlimited recording time. They may contain multiplayer information. As of 2020-12-23 they may contain information about extra properties, allowing replay of views and main window position/size.
Recovery recordings are essentially single-frame Continuous recordings. When enabled, Flightgear creates them periodically to allow recovery of a session if Flightgear crashes.
## Properties
* `/sim/replay/tape-directory` - where to save recordings.
* `/sim/replay/record-multiplayer` - if true, we include multiplayer information in Normal and Continuous recordings.
* Normal recordings:
* `/sim/replay/buffer/high-res-time` - period for high resolution recording.
* `/sim/replay/buffer/medium-res-time` - period for medium resolution.
* `/sim/replay/buffer/low-res-time` - period for low resolution.
* `/sim/replay/buffer/medium-res-sample-dt` - sample period for medium resolution.
* `/sim/replay/buffer/low-res-sample-dt` - sample period for low resolution.
* Continuous recordings:
* `/sim/replay/record-continuous` - if true, do continuous record to file.
* `/sim/replay/record-extra-properties` - if true, we include selected properties in recordings.
* Recovery recordings:
* `/sim/replay/record-recovery-period` - if non-zero, we update recovery recording in specified interval.
## Code
The code that creates recordings is not particularly clean or easy to work with.
It consists mainly of two source files:
* `src/Aircraft/flightrecorder.cxx`
* `src/Aircraft/replay.cxx`
Despite their names these files are both involved with record and replay. `src/Aircraft/flightrecorder.cxx` is lower-level; it takes care of setting up data for each frame when recording (see `FGFlightRecorder::capture()`) and reading data when replaying (see `FGFlightRecorder::replay()`).
`src/Aircraft/replay.cxx` is complicated and does various things. It maintains 3 in-memory buffers containing recording information at different temporal resolutions so that Flightgear can store any session in memory. For example only the most recent 60s is recorded at full frame rate.
## File format
### Normal recordings
* Header:
* A zero-terminated magic string: `FlightGear Flight Recorder Tape` (variable `FlightRecorderFileMagic`).
* A Meta property tree containing a `meta` node with various child nodes.
* A Config property tree containing information about what signals will be contained in each frame.
Each signal is a property; signals are used for the main recorded information such as position and orientation of the user's aircraft, and positions of flight surfaces, gear up/down etc. Aircraft may define their own customised set of signals.
The Meta and Config property trees are each written as `<length:64><text>` where `<text>` is a text representation of the properties. `<text>` is terminated with a zero which is included in the `<length:64>` field.
* A series of frames, each containg the data in a `FGReplayData` instance, looking like:
* Frame time as a binary double.
* Signals information as described in the header's `Config` property tree, represented as a 64-bit length followed by binary data.
All data after the header is in a single gzipped stream.
### Continuous recordings
* Header:
* A zero-terminated magic string: `FlightGear Flight Recorder Tape` (variable `FlightRecorderFileMagic`).
* A property tree represented as a `uint32` length followed by zero-terminated text. This contains:
* A `meta` node with various child nodes.
* `data[]` nodes describing the data items in each frame in the order in which they occur. Supported values are:
* `signals` - core information about the user's aircraft.
* `multiplayer` - information about multiplayer aircraft.
* `extra-properties` - information about extra properties.
* A `signals` node containing layout information for signals, in the same format as for Normal recordings.
The header is written by `FGReplay::continuousWriteHeader()`.
* A series of frames, each containg the data in a `FGReplayData` instance, looking like:
* Frame time as a binary double.
* A list of ordered `<length:32><data>` items as described by the `data[]` nodes in the header. This format allows Flightgear to skip data items that it doesn't understand if loading a recording that was created by a newer version.
* For `signals`, `<data>` is binary data for the core aircraft properties.
* For `multiplayer`, `<data>` is a list of `<length:16><packet>` items where `<packet>` is a multiplayer packet as received from the network.
* For `extra-properties`, `<data>` is a list of property changes, each one being:
* `<length:16><path><length:16><value>` - property <path> has changed to `<value>`.
Removal of a property is encoded as `<0:16><length:16><path>`.
Continuous recordings do not use compression, in order to simplify random access when replaying.
## Replay of Continuous recordings
When a Continuous recording is loaded, `FGReplay::loadTape()` first steps through the entire file, building up a mapping in memory from frame times to offsets within the file, so that we can support the user jumping forwards and backwards in the recording.
If the recording contains extra properties, we also build a cache of the locations of frames that have a non-empty extra-properties item, again to support jumping around in time.
## Multiplayer
### Recording while replaying:
If the user replays part of their history while we are saving to a Continuous recording, and the Continuous recording includes multiplayer information, then we carry on receiving multiplayer information from the network and writing it to the Continuous recording. The user's aircraft is recorded as being stationary for the period when the user was replaying.
### Replaying
When replaying a recording that contains multiplayer information, only recorded multiplayer information is displayed to the user.
Chat messages are included in multiplayer recordings. When replaying, we attempt to hide recorded chat messages and only show live chat messages, but this is a little flakey, especially when jumping around in history.
### Details
The way that Multiplayer data is handled is a little complex. When recording, we build up a buffer of multiplayer packets that we have received since the last time that we wrote out a frame of data, then write them all out in the next frame we write.
When replaying, `FGFlightRecorder::replay()` sends replayed multiplayer messages into the low-level multiplayer packet-receiving code by calling `FGMultiplayMgr::pushMessageHistory()`.