jianshdaim
player.cpp
// Copyright 2018, Bosch Software Innovations GmbH.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rosbag2_transport/player.hpp"
#include "rosbag2_transport/record_h264_codec.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
#include <queue>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <unistd.h>
#include "rcl/graph.h"
#include "rclcpp/rclcpp.hpp"
#include "rcutils/time.h"
#include "rosbag2_cpp/clocks/time_controller_clock.hpp"
#include "rosbag2_cpp/reader.hpp"
#include "rosbag2_cpp/typesupport_helpers.hpp"
#include "rosbag2_storage/storage_filter.hpp"
#include "qos.hpp"
namespace
{
/**
* Trivial std::unique_lock wrapper providing constructor that allows Clang Thread Safety Analysis.
* The std::unique_lock does not have these annotations.
*/
class RCPPUTILS_TSA_SCOPED_CAPABILITY TSAUniqueLock : public std::unique_lock<std::mutex>
{
public:
explicit TSAUniqueLock(std::mutex & mu) RCPPUTILS_TSA_ACQUIRE(mu)
: std::unique_lock<std::mutex>(mu)
{}
~TSAUniqueLock() RCPPUTILS_TSA_RELEASE() {}
};
/**
* Determine which QoS to offer for a topic.
* The priority of the profile selected is:
* 1. The override specified in play_options (if one exists for the topic).
* 2. A profile automatically adapted to the recorded QoS profiles of publishers on the topic.
*
* \param topic_name The full name of the topic, with namespace (ex. /arm/joint_status).
* \param topic_qos_profile_overrides A map of topic to QoS profile overrides.
* @return The QoS profile to be used for subscribing.
*/
rclcpp::QoS publisher_qos_for_topic(
const rosbag2_storage::TopicMetadata & topic,
const std::unordered_map<std::string, rclcpp::QoS> & topic_qos_profile_overrides,
const rclcpp::Logger & logger)
{
using rosbag2_transport::Rosbag2QoS;
auto qos_it = topic_qos_profile_overrides.find(topic.name);
if (qos_it != topic_qos_profile_overrides.end()) {
RCLCPP_INFO_STREAM(
logger,
"Overriding QoS profile for topic " << topic.name);
return Rosbag2QoS{qos_it->second};
} else if (topic.offered_qos_profiles.empty()) {
return Rosbag2QoS{};
}
const auto profiles_yaml = YAML::Load(topic.offered_qos_profiles);
const auto offered_qos_profiles = profiles_yaml.as<std::vector<Rosbag2QoS>>();
return Rosbag2QoS::adapt_offer_to_recorded_offers(topic.name, offered_qos_profiles);
}
} // namespace
namespace rosbag2_transport
{
int topic_n = 0;
int special_topic_num = 0;
int pub_topic_num = 0;
bool comp_min_element(std::pair<uint8_t, rosbag2_storage::SerializedBagMessageSharedPtr> el1,
std::pair<uint8_t, rosbag2_storage::SerializedBagMessageSharedPtr> el2)
{
if(el1.second == nullptr) {
return false;
}
if(el2.second == nullptr) {
return true;
}
return el1.second->time_stamp < el2.second->time_stamp;
}
Player::Player(const std::string & node_name, const rclcpp::NodeOptions & node_options)
: rclcpp::Node(node_name, node_options)
{
// TODO(karsten1987): Use this constructor later with parameter parsing.
// The reader, storage_options as well as play_options can be loaded via parameter.
// That way, the player can be used as a simple component in a component manager.
throw rclcpp::exceptions::UnimplementedError();
}
Player::Player(
const rosbag2_storage::StorageOptions & storage_options,
const rosbag2_transport::PlayOptions & play_options,
const std::string & node_name,
const rclcpp::NodeOptions & node_options)
: Player(std::make_unique<rosbag2_cpp::Reader>(),
storage_options, play_options,
node_name, node_options)
{}
Player::Player(
std::unique_ptr<rosbag2_cpp::Reader> reader,
const rosbag2_storage::StorageOptions & storage_options,
const rosbag2_transport::PlayOptions & play_options,
const std::string & node_name,
const rclcpp::NodeOptions & node_options)
: Player(std::move(reader),
// only call KeyboardHandler when using default keyboard handler implementation
std::shared_ptr<KeyboardHandler>(new KeyboardHandler()),
storage_options, play_options,
node_name, node_options)
{}
Player::Player(
std::unique_ptr<rosbag2_cpp::Reader> reader,
std::shared_ptr<KeyboardHandler> keyboard_handler,
const rosbag2_storage::StorageOptions & storage_options,
const rosbag2_transport::PlayOptions & play_options,
const std::string & node_name,
const rclcpp::NodeOptions & node_options)
: rclcpp::Node(
node_name,
rclcpp::NodeOptions(node_options).arguments(play_options.topic_remapping_options)),
storage_options_(storage_options),
play_options_(play_options),
keyboard_handler_(keyboard_handler)
{
{
std::lock_guard<std::mutex> lk(reader_mutex_);
reader_ = std::move(reader);
// keep reader open until player is destroyed
reader_->open(storage_options_, {"", rmw_get_serialization_format()});
auto metadata = reader_->get_metadata();
starting_time_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
metadata.starting_time.time_since_epoch()).count();
// If a non-default (positive) starting time offset is provided in PlayOptions,
// then add the offset to the starting time obtained from reader metadata
if (play_options_.start_offset < 0) {
RCLCPP_WARN_STREAM(
get_logger(),
"Invalid start offset value: " <<
RCUTILS_NS_TO_S(static_cast<double>(play_options_.start_offset)) <<
". Negative start offset ignored.");
} else {
starting_time_ += play_options_.start_offset;
}
clock_ = std::make_unique<rosbag2_cpp::TimeControllerClock>(
starting_time_, std::chrono::steady_clock::now,
std::chrono::milliseconds{100}, play_options_.start_paused);
set_rate(play_options_.rate);
topic_qos_profile_overrides_ = play_options_.topic_qos_profile_overrides;
prepare_publishers();
}
srv_pause_ = create_service<rosbag2_interfaces::srv::Pause>(
"~/pause",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::Pause::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::Pause::Response>/* response */)
{
pause();
});
srv_resume_ = create_service<rosbag2_interfaces::srv::Resume>(
"~/resume",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::Resume::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::Resume::Response>/* response */)
{
resume();
});
srv_toggle_paused_ = create_service<rosbag2_interfaces::srv::TogglePaused>(
"~/toggle_paused",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::TogglePaused::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::TogglePaused::Response>/* response */)
{
toggle_paused();
});
srv_is_paused_ = create_service<rosbag2_interfaces::srv::IsPaused>(
"~/is_paused",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::IsPaused::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::IsPaused::Response> response)
{
response->paused = is_paused();
});
srv_get_rate_ = create_service<rosbag2_interfaces::srv::GetRate>(
"~/get_rate",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::GetRate::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::GetRate::Response> response)
{
response->rate = get_rate();
});
srv_set_rate_ = create_service<rosbag2_interfaces::srv::SetRate>(
"~/set_rate",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::SetRate::Request> request,
const std::shared_ptr<rosbag2_interfaces::srv::SetRate::Response> response)
{
response->success = set_rate(request->rate);
});
srv_play_next_ = create_service<rosbag2_interfaces::srv::PlayNext>(
"~/play_next",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::PlayNext::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::PlayNext::Response> response)
{
response->success = play_next();
});
add_keyboard_callbacks();
}
Player::Player(
rosbag2_storage::BagMetadata & metadata,
std::vector<std::string>& bag_files,
std::shared_ptr<KeyboardHandler> keyboard_handler,
const rosbag2_storage::StorageOptions & storage_options,
const rosbag2_transport::PlayOptions & play_options,
const std::string & node_name,
const rclcpp::NodeOptions & node_options)
: rclcpp::Node(
node_name,
rclcpp::NodeOptions(node_options).arguments(play_options.topic_remapping_options)),
md_(metadata),
storage_options_(storage_options),
play_options_(play_options),
keyboard_handler_(keyboard_handler)
{
// new_bags_.reserve(256);
for(size_t i = 0; i < bag_files.size(); ++i) {
initial_bags_.push_back(bag_files[i]);
}
all_bags_.swap(bag_files);
{
std::lock_guard<std::mutex> lk(reader_mutex_);
starting_time_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
md_.starting_time.time_since_epoch()).count();
if (play_options_.start_offset < 0) {
RCLCPP_WARN_STREAM(
get_logger(),
"Invalid start offset value: " <<
RCUTILS_NS_TO_S(static_cast<double>(play_options_.start_offset)) <<
". Negative start offset ignored.");
} else {
starting_time_ += play_options_.start_offset;
}
clock_ = std::make_unique<rosbag2_cpp::TimeControllerClock>(
starting_time_, std::chrono::steady_clock::now,
std::chrono::milliseconds{100}, play_options_.start_paused);
set_rate(play_options_.rate);
topic_qos_profile_overrides_ = play_options_.topic_qos_profile_overrides;
prepare_metaddata_publishers();
}
create_addbag_subscriber();
srv_pause_ = create_service<rosbag2_interfaces::srv::Pause>(
"~/pause",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::Pause::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::Pause::Response>/* response */)
{
pause();
});
srv_resume_ = create_service<rosbag2_interfaces::srv::Resume>(
"~/resume",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::Resume::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::Resume::Response>/* response */)
{
resume();
});
srv_toggle_paused_ = create_service<rosbag2_interfaces::srv::TogglePaused>(
"~/toggle_paused",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::TogglePaused::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::TogglePaused::Response>/* response */)
{
toggle_paused();
});
srv_is_paused_ = create_service<rosbag2_interfaces::srv::IsPaused>(
"~/is_paused",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::IsPaused::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::IsPaused::Response> response)
{
response->paused = is_paused();
});
srv_get_rate_ = create_service<rosbag2_interfaces::srv::GetRate>(
"~/get_rate",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::GetRate::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::GetRate::Response> response)
{
response->rate = get_rate();
});
srv_set_rate_ = create_service<rosbag2_interfaces::srv::SetRate>(
"~/set_rate",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::SetRate::Request> request,
const std::shared_ptr<rosbag2_interfaces::srv::SetRate::Response> response)
{
response->success = set_rate(request->rate);
});
srv_play_next_ = create_service<rosbag2_interfaces::srv::PlayNext>(
"~/play_next",
[this](
const std::shared_ptr<rmw_request_id_t>/* request_header */,
const std::shared_ptr<rosbag2_interfaces::srv::PlayNext::Request>/* request */,
const std::shared_ptr<rosbag2_interfaces::srv::PlayNext::Response> response)
{
response->success = play_next();
});
add_keyboard_callbacks();
}
void Player::prepare_metaddata_publishers()
{
rosbag2_storage::StorageFilter storage_filter;
storage_filter.topics = play_options_.topics_to_filter;
// reader_->set_filter(storage_filter);
// Create /clock publisher
if (play_options_.clock_publish_frequency > 0.f) {
const auto publish_period = std::chrono::nanoseconds(
static_cast<uint64_t>(RCUTILS_S_TO_NS(1) / play_options_.clock_publish_frequency));
// NOTE: PlayerClock does not own this publisher because rosbag2_cpp
// should not own transport-based functionality
clock_publisher_ = this->create_publisher<rosgraph_msgs::msg::Clock>(
"/clock", rclcpp::ClockQoS());
clock_publish_timer_ = this->create_wall_timer(
publish_period, [this]() {
auto msg = rosgraph_msgs::msg::Clock();
msg.clock = rclcpp::Time(clock_->now());
clock_publisher_->publish(msg);
});
}
// Create topic publishers
// auto topics = reader_->get_all_topics_and_types();
std::vector<rosbag2_storage::TopicMetadata> topics_metadat;
for (const auto & topic_information : md_.topics_with_message_count) {
topics_metadat.push_back(topic_information.topic_metadata);
}
for (const auto & topic : topics_metadat) {
if (publishers_.find(topic.name) != publishers_.end()) {
continue;
}
// filter topics to add publishers if necessary
auto & filter_topics = storage_filter.topics;
if (!filter_topics.empty()) {
auto iter = std::find(filter_topics.begin(), filter_topics.end(), topic.name);
if (iter == filter_topics.end()) {
continue;
}
}
auto topic_qos = publisher_qos_for_topic(
topic, topic_qos_profile_overrides_,
this->get_logger());
try {
publishers_.insert(
std::make_pair(
topic.name, this->create_generic_publisher(topic.name, topic.type, topic_qos)));
} catch (const std::runtime_error & e) {
// using a warning log seems better than adding a new option
// to ignore some unknown message type library
RCLCPP_WARN(
this->get_logger(),
"Ignoring a topic '%s', reason: %s.", topic.name.c_str(), e.what());
}
}
}
void Player::create_add_topic_publisher(rosbag2_storage::BagMetadata& metadata)
{
std::vector<rosbag2_storage::TopicMetadata> topics_metadat;
for (const auto & topic_information : metadata.topics_with_message_count) {
topics_metadat.push_back(topic_information.topic_metadata);
}
rosbag2_storage::StorageFilter storage_filter;
storage_filter.topics = play_options_.topics_to_filter;
for (const auto & topic : topics_metadat) {
if (publishers_.find(topic.name) != publishers_.end()) {
continue;
}
// filter topics to add publishers if necessary
auto & filter_topics = storage_filter.topics;
if (!filter_topics.empty()) {
auto iter = std::find(filter_topics.begin(), filter_topics.end(), topic.name);
if (iter == filter_topics.end()) {
continue;
}
}
auto topic_qos = publisher_qos_for_topic(
topic, topic_qos_profile_overrides_,
this->get_logger());
try {
publishers_.insert(
std::make_pair(
topic.name, this->create_generic_publisher(topic.name, topic.type, topic_qos)));
} catch (const std::runtime_error & e) {
// using a warning log seems better than adding a new option
// to ignore some unknown message type library
RCLCPP_WARN(
this->get_logger(),
"Ignoring a topic '%s', reason: %s.", topic.name.c_str(), e.what());
}
}
}
void Player::create_addbag_subscriber() {
addbag_subscriber_ = this->create_subscription<std_msgs::msg::String>(
"/rosbag2_player/add_db3",
10,
std::bind(&Player::addtopic_callback, this, std::placeholders::_1)
);
}
void Player::addtopic_callback(const std_msgs::msg::String::SharedPtr msg)
{
std::cout << "Enter in Player::addtopic_callback" << std::endl;
if(all_bags_.end() != std::find(all_bags_.begin(), all_bags_.end(), msg->data)) {
std::cout << "received New bag file: " << msg->data.c_str() << ", ignore " << std::endl;
return;
}
std::cout << "New bag file: " << msg->data.c_str() << std::endl;
all_bags_.push_back(msg->data);
rosbag2_storage::StorageOptions storage_options;
storage_options.uri = msg->data;
storage_options.storage_id="sqlite3";
auto reader = ReaderWriterFactory::make_reader(storage_options);
reader->open(storage_options);
{
std::lock_guard<std::mutex> lc(bag_mutex_);
int index = new_bag_index_.load();
new_bag_info bag_info(reader, storage_options.uri);
new_bags_.emplace_back(std::move(bag_info));
}
auto bag_topics_and_types = new_bags_[new_bag_index_].reader_->get_all_topics_and_types();
for (const auto & topic_metadata : bag_topics_and_types) {
// const std::string & topic_name = topic_metadata.name;
if(close_writer != nullptr) {
close_writer->add_topic_with_info(topic_metadata);
}
// filtered_outputs.try_emplace(topic_name);
// filtered_outputs[topic_name].push_back(writer.get());
}
rosbag2_storage::BagMetadata metadata = new_bags_[new_bag_index_].reader_->get_metadata();
create_add_topic_publisher(metadata);
++new_bag_index_;
new_bag_.store(true);
bag_cv_.notify_one();
}
void Player::delete_played_bag(std::string bag_file)
{
if (unlink(bag_file.c_str()) == 0) {
std::cout << "File " << bag_file << " deleted successfully\n";
} else {
if (errno == ENOENT) {
std::cout << "File " << bag_file << " does not exist\n";
} else {
std::cout << "File " << bag_file << " Error deleting file\n";
}
}
}
int new_bag_number = 0;
void Player::write_new_bag_to_cache(rosbag2_cpp::Writer* writer)
{
std::shared_ptr<rosbag2_storage::SerializedBagMessage> bag_msg;
std::cout << "Enter in write_new_bag_to_cache, new_bag_index_ is: " << new_bag_index_.load() << std::endl;
for(size_t index = 0; index < new_bag_index_.load(); ++index) {
if(new_bags_[index].is_writer) {
continue;
}
while(new_bags_[index].reader_->has_next())
{
++new_bag_number;
++topic_n;
bag_msg = new_bags_[index].reader_->read_next();
writer->write(bag_msg);
}
new_bags_[index].is_writer = true;
// delete_played_bag(new_bags_[index].bag_uri);
}
std::cout << "leave write_new_bag_to_cache, add new bag messages: " << new_bag_number << std::endl;
}
Player::~Player()
{
// remove callbacks on key_codes to prevent race conditions
// Note: keyboard_handler handles locks between removing & executing callbacks
for (auto cb_handle : keyboard_callbacks_) {
keyboard_handler_->delete_key_press_callback(cb_handle);
}
// closes reader
std::lock_guard<std::mutex> lk(reader_mutex_);
if (reader_) {
reader_->close();
}
}
rosbag2_cpp::Reader * Player::release_reader()
{
reader_->close();
return reader_.release();
}
const std::chrono::milliseconds
Player::queue_read_wait_period_ = std::chrono::milliseconds(100);
bool Player::is_storage_completely_loaded() const
{
if (storage_loading_future_.valid() &&
storage_loading_future_.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
storage_loading_future_.get();
}
return !storage_loading_future_.valid();
}
std::unordered_map<std::string, std::vector<rosbag2_cpp::Writer *>>
Player::get_topic_and_writer(
const std::vector<std::unique_ptr<rosbag2_cpp::Reader>> & input_bags,
const std::vector<std::pair<std::unique_ptr<rosbag2_cpp::Writer>, rosbag2_transport::RecordOptions>> & output_bags)
{
std::unordered_map<std::string, std::vector<rosbag2_cpp::Writer *>> filtered_outputs;
for (const auto & [writer, record_options] : output_bags) {
for (const auto & input_bag : input_bags) {
auto bag_topics_and_types = input_bag->get_all_topics_and_types();
for (const auto & topic_metadata : bag_topics_and_types) {
const std::string & topic_name = topic_metadata.name;
writer->add_topic_with_info(topic_metadata);
filtered_outputs.try_emplace(topic_name);
filtered_outputs[topic_name].push_back(writer.get());
}
}
}
return filtered_outputs;
}
void Player::perform_rewrite_parallel(
const std::vector<std::unique_ptr<rosbag2_cpp::Reader>> & input_bags,
const std::vector<std::pair<std::unique_ptr<rosbag2_cpp::Writer>, rosbag2_transport::RecordOptions>> & output_bags
)
{
if (input_bags.empty() || output_bags.empty()) {
throw std::runtime_error("Must provide at least one input and one output bag to rewrite.");
}
close_writer = output_bags[0].first.get();
std::unordered_map<std::string, std::vector<rosbag2_cpp::Writer*>> topic_outputs = get_topic_and_writer(input_bags, output_bags);
std::vector<std::shared_ptr<rosbag2_storage::SerializedBagMessage>> next_messages;
next_messages.resize(input_bags.size(), nullptr);
std::shared_ptr<rosbag2_storage::SerializedBagMessage> next_msg;
uint8_t has_message = 0;
int8_t bags_size = input_bags.size() - 1;
uint8_t size_bit = 0;
while (bags_size >= 0) {
size_bit |= 1 << bags_size;
--bags_size;
}
int temp = 0;
std::map<uint8_t, rosbag2_storage::SerializedBagMessageSharedPtr> next_bags_msgs;
for(size_t index = 0; index < input_bags.size(); ++index) {
next_bags_msgs.insert({index, nullptr});
}
while (has_message < size_bit)
{
for (size_t i = 0; i < input_bags.size(); i++)
{
if(next_bags_msgs.at(i) == nullptr)
{
if(input_bags.at(i)->has_next()) {
next_bags_msgs.at(i) = input_bags.at(i)->read_next();
}
else {
temp = 1 << i;
has_message |= temp;
}
}
}
auto it = std::min_element(next_bags_msgs.begin(), next_bags_msgs.end(), comp_min_element);
next_msg = it->second;
if(next_msg == nullptr) {
continue;
}
auto topic_writers = topic_outputs.find(next_msg->topic_name);
if (topic_writers != topic_outputs.end()) {
// if(next_msg->topic_name == "/driver/radar_right_front_object")
// std::cout << "time stamp is: " << next_msg->time_stamp << ", topic is: next_msg->topic_name: " << next_msg->topic_name << std::endl;
close_writer->write(next_msg);
++topic_n;
// std::cout << "writer the " << topic_n << " topic: " << next_msg->topic_name << std::endl;
// for (auto writer : topic_writers->second) {
// }
// close_writer = topic_writers->second[0];
}
it->second = nullptr;
}
// for(auto i : initial_bags_) {
// // delete_played_bag(i);
// }
write_new_bag_to_cache(close_writer);
// while (true)
// {
// // std::cout << "is_waiting_ ..." << std::endl;
// is_waiting_.store(true);
// std::unique_lock<std::mutex> lc(bag_mutex_);
// bag_cv_.wait(lc, [this](){
// return new_bag_.load() || !is_in_play_;
// });
// if(!is_in_play_) {
// break;
// }
// size_t index = new_bag_index_.load() - 1;
// while(new_bags_[index].reader_->has_next())
// {
// next_msg = new_bags_[index].reader_->read_next();
// close_writer->write(next_msg);
// new_bags_[index].is_writer = true;
// }
// new_bag_.store(false);
// }
// close_writer->close_cache_consumer();
std::cout << "end of writer topic, number is: " << topic_n << std::endl;
}
// void Player::play(bool h264_enable)
void Player::play()
{
std::cout << "Enter in Player::play, start play\n";
is_in_play_ = true;
float delay;
if (play_options_.delay >= 0.0) {
delay = play_options_.delay;
} else {
RCLCPP_WARN(
this->get_logger(),
"Invalid delay value: %f. Delay is disabled.",
play_options_.delay);
delay = 0.0;
}
RCLCPP_INFO_STREAM(get_logger(), "Start to play...");
try {
do {
if (delay > 0.0) {
RCLCPP_INFO_STREAM(this->get_logger(), "Sleep " << delay << " sec");
std::chrono::duration<float> duration(delay);
std::this_thread::sleep_for(duration);
}
reader_->open(storage_options_, {"", rmw_get_serialization_format()});
const auto starting_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
reader_->get_metadata().starting_time.time_since_epoch()).count();
clock_->jump(starting_time);
storage_loading_future_ = std::async(
std::launch::async,
[this]() {load_storage_content();});
// is_h264_decode_enabled_.store(h264_enable);
wait_for_filled_queue();
play_messages_from_queue();
reader_->close();
} while (rclcpp::ok() && play_options_.loop);
} catch (std::runtime_error & e) {
RCLCPP_ERROR(this->get_logger(), "Failed to play: %s", e.what());
}
is_in_play_ = false;
}
using namespace std::chrono_literals;
void Player::play_with_cache(std::vector<std::unique_ptr<rosbag2_cpp::Reader>>& input_bags,
std::vector<std::pair<std::unique_ptr<rosbag2_cpp::Writer>, rosbag2_transport::RecordOptions>>& output_bags)
{
std::cout << "Enter in Player::play_with_cache\n";
is_in_play_ = true;
float delay;
if (play_options_.delay >= 0.0) {
delay = play_options_.delay;
} else {
RCLCPP_WARN(
this->get_logger(),
"Invalid delay value: %f. Delay is disabled.",
play_options_.delay);
delay = 0.0;
}
RCLCPP_INFO_STREAM(get_logger(), "Start to play...");
try {
do {
if (delay > 0.0) {
RCLCPP_INFO_STREAM(this->get_logger(), "Sleep " << delay << " sec");
std::chrono::duration<float> duration(delay);
std::this_thread::sleep_for(duration);
}
const auto starting_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
md_.starting_time.time_since_epoch()).count();
clock_->jump(starting_time);
storage_loading_future_ = std::async(std::launch::async,
[this, &input_bags, &output_bags] () {
perform_rewrite_parallel(input_bags, output_bags);
}
);
wait_for_filled_queue();
play_messages_from_buffer();
} while (rclcpp::ok() && play_options_.loop);
} catch (std::runtime_error & e) {
RCLCPP_ERROR(this->get_logger(), "Failed to play: %s", e.what());
}
is_in_play_ = false;
new_bag_.store(true);
bag_cv_.notify_one();
// std::cout << "End of play_with_cache, pub_topic_num is: " << pub_topic_num << std::endl;
}
void Player::pause()
{
clock_->pause();
RCLCPP_INFO_STREAM(get_logger(), "Pausing play. Press space to continue ...");
}
void Player::resume()
{
clock_->resume();
RCLCPP_INFO_STREAM(get_logger(), "Resuming play. Press space to pause ...");
}
void Player::toggle_paused()
{
is_paused() ? resume() : pause();
}
bool Player::is_paused() const
{
return clock_->is_paused();
}
double Player::get_rate() const
{
return clock_->get_rate();
}
bool Player::set_rate(double rate)
{
bool ok = clock_->set_rate(rate);
if (ok) {
RCLCPP_INFO_STREAM(get_logger(), "Set rate to " << rate);
} else {
RCLCPP_WARN_STREAM(get_logger(), "Failed to set rate to invalid value " << rate);
}
return ok;
}
rosbag2_storage::SerializedBagMessageSharedPtr * Player::peek_next_message_from_queue()
{
rosbag2_storage::SerializedBagMessageSharedPtr * message_ptr = message_queue_.peek();
if (message_ptr == nullptr && !is_storage_completely_loaded() && rclcpp::ok()) {
RCLCPP_WARN(
this->get_logger(),
"Message queue starved. Messages will be delayed. Consider "
"increasing the --read-ahead-queue-size option.");
while (message_ptr == nullptr && !is_storage_completely_loaded() && rclcpp::ok()) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
message_ptr = message_queue_.peek();
}
}
return message_ptr;
}
rosbag2_storage::SerializedBagMessageSharedPtr * Player::peek_next_message_from_buffer()
{
rosbag2_storage::SerializedBagMessageSharedPtr * message_ptr = message_queue_.peek();
if (message_ptr == nullptr && !is_waiting_.load() && rclcpp::ok()) {
RCLCPP_WARN(
this->get_logger(),
"Message queue starved. Messages will be delayed. Consider "
"increasing the --read-ahead-queue-size option.");
while (message_ptr == nullptr && !is_waiting_.load() && rclcpp::ok()) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
message_ptr = message_queue_.peek();
}
}
return message_ptr;
}
bool Player::play_next()
{
// Temporary take over playback from play_messages_from_queue()
std::lock_guard<std::mutex> lk(skip_message_in_main_play_loop_mutex_);
if (!clock_->is_paused() || !is_in_play_) {
return false;
}
skip_message_in_main_play_loop_ = true;
rosbag2_storage::SerializedBagMessageSharedPtr * message_ptr = peek_next_message_from_queue();
bool next_message_published = false;
while (message_ptr != nullptr && !next_message_published) {
{
rosbag2_storage::SerializedBagMessageSharedPtr message = *message_ptr;
next_message_published = publish_message(message);
clock_->jump(message->time_stamp);
}
message_queue_.pop();
message_ptr = peek_next_message_from_queue();
}
return next_message_published;
}
void Player::wait_for_filled_queue() const
{
while (
message_queue_.size_approx() < play_options_.read_ahead_queue_size &&
!is_storage_completely_loaded() && rclcpp::ok())
{
std::this_thread::sleep_for(queue_read_wait_period_);
}
}
void Player::load_storage_content()
{
auto queue_lower_boundary =
static_cast<size_t>(play_options_.read_ahead_queue_size * read_ahead_lower_bound_percentage_);
auto queue_upper_boundary = play_options_.read_ahead_queue_size;
while (reader_->has_next() && rclcpp::ok()) {
if (message_queue_.size_approx() < queue_lower_boundary) {
enqueue_up_to_boundary(queue_upper_boundary);
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
}
void Player::load_message(rosbag2_storage::SerializedBagMessageSharedPtr message)
{
// std::cout << "Enter in load_message\n";
auto queue_lower_boundary =
static_cast<size_t>(play_options_.read_ahead_queue_size * read_ahead_lower_bound_percentage_);
size_t size = message_queue_.size_approx();
if (rclcpp::ok()) {
if(message_queue_.size_approx() > play_options_.read_ahead_queue_size * 2) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
message_queue_.enqueue(message);
}
std::cout << "add message to queue: " << ++message_count_ << ", message size is: " << size+1 << std::endl;
}
void Player::enqueue_up_to_boundary(uint64_t boundary)
{
rosbag2_storage::SerializedBagMessageSharedPtr message;
for (size_t i = message_queue_.size_approx(); i < boundary; i++) {
if (!reader_->has_next()) {
break;
}
message = reader_->read_next();
message_queue_.enqueue(message);
}
}
bool Player::is_key_frame(const std::vector<uint8_t>& data)
{
if(data.size() < 5){
return false;
}
int32_t nal_type = data.at(4) & 0x1F;
if(data.at(0) == 0 && data.at(1) == 0 && data.at(2) == 0 && data.at(3) == 1){
if(nal_type == 5 || nal_type == 7 || nal_type == 8){
RCLCPP_INFO(rclcpp::get_logger("rosbag2_transport"), "Found I frame at 4!!!!");
return true;
}
}
if(data.at(0) == 0 && data.at(1) == 0 && data.at(2) == 1){
nal_type = data.at(3) & 0x1f;
if(nal_type == 5 || nal_type == 7 || nal_type == 8){
RCLCPP_INFO(rclcpp::get_logger("rosbag2_transport"), "Found I frame at 3!!!!");
return true;
}
}
return false;
}
void Player::play_messages_from_queue()
{
playing_messages_from_queue_ = true;
// Note: We need to use message_queue_.peek() instead of message_queue_.try_dequeue(message)
// to support play_next() API logic.
rosbag2_storage::SerializedBagMessageSharedPtr * message_ptr = peek_next_message_from_queue();
while (message_ptr != nullptr && rclcpp::ok()) {
{
rosbag2_storage::SerializedBagMessageSharedPtr message = *message_ptr;
// Do not move on until sleep_until returns true
// It will always sleep, so this is not a tight busy loop on pause
while (rclcpp::ok() && !clock_->sleep_until(message->time_stamp))
{
}
if (rclcpp::ok()) {
{
std::lock_guard<std::mutex> lk(skip_message_in_main_play_loop_mutex_);
if (skip_message_in_main_play_loop_) {
skip_message_in_main_play_loop_ = false;
message_ptr = peek_next_message_from_queue();
continue;
}
}
// std::cout << "play_messages_from_queue..."<<std::endl;
//h264 feature: decode h264 data from storage before playing
std::shared_ptr<sensor_msgs::msg::Image> ros_msg = std::make_shared<sensor_msgs::msg::Image>();
rclcpp::Serialization<sensor_msgs::msg::Image> ser;
// if(is_h264_decode_enabled_.load()
// && message->topic_name.find("/driver/camera_") != std::string::npos
// && message->topic_name.find("_image") != std::string::npos)
// {
// auto serialize_msg = rclcpp::SerializedMessage(*message->serialized_data);
// ser.deserialize_message(&serialize_msg, ros_msg.get());
// // static bool iframe_found = false;
// // if(!iframe_found && is_key_frame(ros_msg->data)){
// // iframe_found = true;
// // }
// // if(!iframe_found){
// // RCLCPP_INFO(rclcpp::get_logger("rosbag2_transport"), "waiting for I frame..........!!!!, data size: %d", ros_msg->data.size());
// // message_queue_.pop();
// // message_ptr = peek_next_message_from_queue();
// // continue;
// // }
// if(RecordCodecH264::decode(message->topic_name, ros_msg)){
// ser.serialize_message(ros_msg.get(), &serialize_msg);
// *message->serialized_data = serialize_msg.release_rcl_serialized_message();
// }
// else{
// RCLCPP_ERROR(rclcpp::get_logger("rosbag2_transport"), "Failed to decode h264 image of topic: %s", message->topic_name);
// message_queue_.pop();
// message_ptr = peek_next_message_from_queue();
// continue;
// }
// }
publish_message(message);
}
message_queue_.pop();
message_ptr = peek_next_message_from_queue();
}
}
playing_messages_from_queue_ = false;
}
int publish_count = 0;
void Player::play_messages_from_buffer()
{
std::cout << "Player::play_messages_from_buffer" << std::endl;
playing_messages_from_queue_ = true;
// Note: We need to use message_queue_.peek() instead of message_queue_.try_dequeue(message)
// to support play_next() API logic.
rosbag2_storage::SerializedBagMessageSharedPtr * message_ptr = peek_next_message_from_buffer();
while (message_ptr != nullptr && rclcpp::ok()) {
{
rosbag2_storage::SerializedBagMessageSharedPtr message = *message_ptr;
// Do not move on until sleep_until returns true
// It will always sleep, so this is not a tight busy loop on pause
while (rclcpp::ok() && !clock_->sleep_until(message->time_stamp))
{
}
if (rclcpp::ok()) {
{
std::lock_guard<std::mutex> lk(skip_message_in_main_play_loop_mutex_);
if (skip_message_in_main_play_loop_) {
skip_message_in_main_play_loop_ = false;
message_ptr = peek_next_message_from_buffer();
continue;
}
}
std::shared_ptr<sensor_msgs::msg::Image> ros_msg = std::make_shared<sensor_msgs::msg::Image>();
rclcpp::Serialization<sensor_msgs::msg::Image> ser;
std::cout << "***** publish_message *****" << std::endl;
publish_message(message);
}
// std::cout << "----get next message_queue_ msg----pop "<<std::endl;
message_queue_.pop();
auto size = message_queue_.size_approx();
std::cout << "----message_queue_ size---- "<<message_queue_.size_approx()<<std::endl;
if(size > 0){
message_ptr = peek_next_message_from_buffer();
}else{
break;
}
// message_ptr = peek_next_message_from_buffer();
// std::cout << "----get next message_queue_ msg---- "<<std::endl;
}
}
playing_messages_from_queue_ = false;
std::cout << "publish_count is: " << publish_count << std::endl;
}
void Player::prepare_publishers()
{
rosbag2_storage::StorageFilter storage_filter;
storage_filter.topics = play_options_.topics_to_filter;
reader_->set_filter(storage_filter);
// Create /clock publisher
if (play_options_.clock_publish_frequency > 0.f) {
const auto publish_period = std::chrono::nanoseconds(
static_cast<uint64_t>(RCUTILS_S_TO_NS(1) / play_options_.clock_publish_frequency));
// NOTE: PlayerClock does not own this publisher because rosbag2_cpp
// should not own transport-based functionality
clock_publisher_ = this->create_publisher<rosgraph_msgs::msg::Clock>(
"/clock", rclcpp::ClockQoS());
clock_publish_timer_ = this->create_wall_timer(
publish_period, [this]() {
auto msg = rosgraph_msgs::msg::Clock();
msg.clock = rclcpp::Time(clock_->now());
clock_publisher_->publish(msg);
});
}
// Create topic publishers
auto topics = reader_->get_all_topics_and_types();
for (const auto & topic : topics) {
if (publishers_.find(topic.name) != publishers_.end()) {
continue;
}
// filter topics to add publishers if necessary
auto & filter_topics = storage_filter.topics;
if (!filter_topics.empty()) {
auto iter = std::find(filter_topics.begin(), filter_topics.end(), topic.name);
if (iter == filter_topics.end()) {
continue;
}
}
auto topic_qos = publisher_qos_for_topic(
topic, topic_qos_profile_overrides_,
this->get_logger());
try {
publishers_.insert(
std::make_pair(
topic.name, this->create_generic_publisher(topic.name, topic.type, topic_qos)));
} catch (const std::runtime_error & e) {
// using a warning log seems better than adding a new option
// to ignore some unknown message type library
RCLCPP_WARN(
this->get_logger(),
"Ignoring a topic '%s', reason: %s.", topic.name.c_str(), e.what());
}
}
}
bool Player::publish_message(rosbag2_storage::SerializedBagMessageSharedPtr message)
{
bool message_published = false;
auto publisher_iter = publishers_.find(message->topic_name);
if (publisher_iter != publishers_.end()) {
if(publisher_iter->second == nullptr){
std::cout << "null publisher ptr..." <<std::endl;
}else
{
std::cout << "start publish, topic name is: " << message->topic_name << ", topic count is: " << ++publish_count << std::endl;
// ++publish_count;
publisher_iter->second->publish(rclcpp::SerializedMessage(*message->serialized_data));
message_published = true;
}
}
return message_published;
}
void Player::add_key_callback(
KeyboardHandler::KeyCode key,
const std::function<void()> & cb,
const std::string & op_name)
{
std::string key_str = enum_key_code_to_str(key);
if (key == KeyboardHandler::KeyCode::UNKNOWN) {
RCLCPP_ERROR_STREAM(
get_logger(),
"Invalid key binding " << key_str << " for " << op_name);
throw std::invalid_argument("Invalid key binding.");
}
keyboard_callbacks_.push_back(
keyboard_handler_->add_key_press_callback(
[cb](KeyboardHandler::KeyCode /*key_code*/,
KeyboardHandler::KeyModifiers /*key_modifiers*/) {cb();},
key));
// show instructions
RCLCPP_INFO_STREAM(
get_logger(),
"Press " << key_str << " for " << op_name);
}
void Player::add_keyboard_callbacks()
{
// skip if disabled
if (play_options_.disable_keyboard_controls) {
return;
}
RCLCPP_INFO_STREAM(get_logger(), "Adding keyboard callbacks.");
// check keybindings
add_key_callback(
play_options_.pause_resume_toggle_key,
[this]() {toggle_paused();},
"Pause/Resume"
);
add_key_callback(
play_options_.play_next_key,
[this]() {play_next();},
"Play Next Message"
);
add_key_callback(
play_options_.increase_rate_key,
[this]() {set_rate(get_rate() * 1.1);},
"Increase Rate 10%"
);
add_key_callback(
play_options_.decrease_rate_key,
[this]() {set_rate(get_rate() * 0.9);},
"Decrease Rate 10%"
);
}
} // namespace rosbag2_transport
更多推荐


所有评论(0)