ros2 / rosbag2

Apache License 2.0
285 stars 251 forks source link

Support replaying multiple bags #1848

Open christophebedard opened 2 weeks ago

christophebedard commented 2 weeks ago

This adds support for replaying multiple bags with ros2 bag play and the underlying classes (rosbag2_transport::Player/rosbag2_py::Player).

To replay multiple bags:

$ ros2 bag play -i bag1 -i bag2 -i bag3 [storage_id]

Messages are played in order, based on their recv_timestamp (recorder reception timestamp). rosbag2_transport::Player uses a simple implementation of a priority queue to select the "next" message from all input bags. This does assume that the messages in each bag are ordered by recv_timestamp.

Note that the priority queue implementation does require locking, but the "ordering" it does is very simple given the aforementioned ordering assumption. However, I did make it consider the trivial case where there's only 1 underlying queue (i.e., input bag), in which case there is no need to track priorities and thus no need for locking. Furthermore, this priority queue does not support actually ordering messages, e.g., to re-order an input bag's messages by send_timestamp.

Other notes:

  1. Like ros2 bag convert, I added the -i, --input option that takes a bag URI and an optional explicit storage ID. This option can be used multiple times for multiple bags.
  2. I kept the bag_path positional argument (but made it optional) because it is quite nice to be able to just do ros2 bag play my-bag. However, I deprecated the storage ID option (--storage); users will have to use -i, --input to specify an explicit storage ID for an input bag.
  3. I had to change some APIs (mainly constructors) to support N input bags. I was planning to deprecate constructors that only take 1 input bag/storage options, but given the number of layers/classes involved, it became too annoying. We can consider deprecating some of them in another PR.
  4. I did break rosbag2_transport::Player::get_storage_options() to make it return a std::vector<rosbag2_storage::StorageOptions>. This is apparently only used in tests. I also broke rosbag2_py::Player::play_impl(), but it apparently only protected (and not private) for testing purposes.