blob: ad2124896106fe046b1271997f6fe457b0887b4b [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media_perception/output_manager.h"
#include <functional>
#include <base/logging.h>
#include <dbus/object_proxy.h>
#include "media_perception/frame_perception.pb.h"
#include "media_perception/hotword_detection.pb.h"
#include "media_perception/huddly.pb.h"
#include "media_perception/occupancy_trigger.pb.h"
#include "media_perception/presence_perception.pb.h"
#include "media_perception/proto_mojom_conversion.h"
#include "media_perception/serialized_proto.h"
namespace mri {
namespace {
// To avoid passing a lambda as a base::Closure.
void OnConnectionClosedOrError(const std::string& interface_type) {
LOG(INFO) << "Got closed connection: " << interface_type;
}
} // namespace
OutputManager::~OutputManager() {
if (thread_.IsRunning()) {
base::WaitableEvent destruction_complete(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&OutputManager::DestructDbus, base::Unretained(this),
&destruction_complete));
destruction_complete.Wait();
}
}
void OutputManager::DestructDbus(base::WaitableEvent* destruction_complete) {
dbus_connection_.reset();
destruction_complete->Signal();
}
OutputManager::OutputManager(
const std::string& configuration_name,
std::shared_ptr<Rtanalytics> rtanalytics,
const PerceptionInterfaces& interfaces,
chromeos::media_perception::mojom::PerceptionInterfacesPtr* interfaces_ptr)
: thread_("OutputManager Dbus Thread") {
// Save the configuration name in case we need to reference it later.
configuration_name_ = configuration_name;
rtanalytics_ = rtanalytics;
for (const PerceptionInterface& interface : interfaces.interface()) {
// Frame perception interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_FRAME_PERCEPTION) {
(*interfaces_ptr)->frame_perception_handler_request =
frame_perception_handler_.BindNewPipeAndPassReceiver();
frame_perception_handler_.set_disconnect_handler(
base::Bind(&OnConnectionClosedOrError, "INTERFACE_FRAME_PERCEPTION"));
// Frame perception outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() ==
PipelineOutputType::OUTPUT_FRAME_PERCEPTION) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleFramePerception, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Hotword detection interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_HOTWORD_DETECTION) {
(*interfaces_ptr)->hotword_detection_handler_request =
hotword_detection_handler_.BindNewPipeAndPassReceiver();
hotword_detection_handler_.set_disconnect_handler(base::Bind(
&OnConnectionClosedOrError, "INTERFACE_HOTWORD_DETECTION"));
// Hotword detection outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() ==
PipelineOutputType::OUTPUT_HOTWORD_DETECTION) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleHotwordDetection, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Presence perception interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_PRESENCE_PERCEPTION) {
(*interfaces_ptr)->presence_perception_handler_request =
presence_perception_handler_.BindNewPipeAndPassReceiver();
presence_perception_handler_.set_disconnect_handler(base::Bind(
&OnConnectionClosedOrError, "INTERFACE_PRESENCE_PERCEPTION"));
// Presence perception outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() ==
PipelineOutputType::OUTPUT_PRESENCE_PERCEPTION) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandlePresencePerception, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Occupancy trigger interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_OCCUPANCY_TRIGGER) {
(*interfaces_ptr)->occupancy_trigger_handler_request =
occupancy_trigger_handler_.BindNewPipeAndPassReceiver();
occupancy_trigger_handler_.set_disconnect_handler(base::Bind(
&OnConnectionClosedOrError, "INTERFACE_OCCUPANCY_TRIGGER"));
// Occpancy trigger outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() ==
PipelineOutputType::OUTPUT_OCCUPANCY_TRIGGER) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleOccupancyTrigger, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Appearances interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_APPEARANCES) {
(*interfaces_ptr)->appearances_handler_request =
appearances_handler_.BindNewPipeAndPassReceiver();
appearances_handler_.set_disconnect_handler(
base::Bind(&OnConnectionClosedOrError, "APPEARANCES"));
// Appearances outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() == PipelineOutputType::OUTPUT_APPEARANCES) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleAppearances, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// One touch Autozoom interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_ONE_TOUCH_AUTOZOOM) {
(*interfaces_ptr)->one_touch_autozoom_handler_request =
one_touch_autozoom_handler_.BindNewPipeAndPassReceiver();
one_touch_autozoom_handler_.set_disconnect_handler(
base::Bind(&OnConnectionClosedOrError, "ONE_TOUCH_AUTOZOOM"));
// One touch Autozoom outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() == PipelineOutputType::OUTPUT_SMART_FRAMING) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleSmartFraming, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Software Autozoom interface setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_SOFTWARE_AUTOZOOM) {
(*interfaces_ptr)->software_autozoom_handler_request =
software_autozoom_handler_.BindNewPipeAndPassReceiver();
software_autozoom_handler_.set_disconnect_handler(
base::Bind(&OnConnectionClosedOrError, "SOFTWARE_AUTOZOOM"));
// Software Autozoom outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() == PipelineOutputType::OUTPUT_SMART_FRAMING) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleSmartFraming, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
}
}
}
continue;
}
// Falcon Autozoom outputs setup.
if (interface.interface_type() ==
PerceptionInterfaceType::INTERFACE_FALCON_AUTOZOOM) {
// Falcon Autozoom outputs setup.
for (const PipelineOutput& output : interface.output()) {
if (output.output_type() ==
PipelineOutputType::OUTPUT_INDEXED_TRANSITIONS) {
SerializedSuccessStatus serialized_status =
rtanalytics->SetPipelineOutputHandler(
configuration_name, output.stream_name(),
std::bind(&OutputManager::HandleIndexedTransitions, this,
std::placeholders::_1));
SuccessStatus status =
Serialized<SuccessStatus>(serialized_status).Deserialize();
if (!status.success()) {
LOG(ERROR) << "Failed to set output handler for "
<< configuration_name << " with output "
<< output.stream_name();
continue;
}
if (!thread_.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0))) {
LOG(ERROR) << "Failed to create dbus thread.";
continue;
}
dbus_connection_ = std::make_unique<brillo::DBusConnection>();
thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&OutputManager::InitializeDbus,
base::Unretained(this)));
}
}
continue;
}
}
}
void OutputManager::InitializeDbus() {
bus_ = dbus_connection_->Connect();
if (bus_ == nullptr) {
LOG(FATAL) << "Unable to connect to Dbus from OutputManager.";
}
dbus_proxy_ = bus_->GetObjectProxy(
"org.chromium.IpPeripheralService",
dbus::ObjectPath("/org/chromium/IpPeripheralService"));
}
void OutputManager::HandleFramePerception(const std::vector<uint8_t>& bytes) {
if (!frame_perception_handler_.is_bound()) {
LOG(WARNING) << "Got frame perception output but handler ptr is not bound.";
return;
}
if (frame_perception_handler_.get() == nullptr) {
LOG(ERROR) << "Handler ptr is null.";
return;
}
FramePerception frame_perception =
Serialized<FramePerception>(bytes).Deserialize();
frame_perception_handler_->OnFramePerception(
chromeos::media_perception::mojom::ToMojom(frame_perception));
}
void OutputManager::HandleHotwordDetection(const std::vector<uint8_t>& bytes) {
if (!hotword_detection_handler_.is_bound()) {
LOG(WARNING)
<< "Got hotword detection output but handler ptr is not bound.";
return;
}
if (hotword_detection_handler_.get() == nullptr) {
LOG(ERROR) << "Handler ptr is null.";
return;
}
HotwordDetection hotword_detection =
Serialized<HotwordDetection>(bytes).Deserialize();
hotword_detection_handler_->OnHotwordDetection(
chromeos::media_perception::mojom::ToMojom(hotword_detection));
}
void OutputManager::HandlePresencePerception(
const std::vector<uint8_t>& bytes) {
if (!presence_perception_handler_.is_bound()) {
LOG(WARNING)
<< "Got presence perception output but handler ptr is not bound.";
return;
}
if (presence_perception_handler_.get() == nullptr) {
LOG(ERROR) << "Handler ptr is null.";
return;
}
PresencePerception presence_perception =
Serialized<PresencePerception>(bytes).Deserialize();
presence_perception_handler_->OnPresencePerception(
chromeos::media_perception::mojom::ToMojom(presence_perception));
}
void OutputManager::HandleOccupancyTrigger(const std::vector<uint8_t>& bytes) {
if (!occupancy_trigger_handler_.is_bound()) {
LOG(WARNING)
<< "Got occupancy trigger output but handler ptr is not bound.";
return;
}
if (occupancy_trigger_handler_.get() == nullptr) {
LOG(ERROR) << "Handler ptr is null.";
return;
}
OccupancyTrigger occupancy_trigger =
Serialized<OccupancyTrigger>(bytes).Deserialize();
occupancy_trigger_handler_->OnOccupancyTrigger(
chromeos::media_perception::mojom::ToMojom(occupancy_trigger));
}
void OutputManager::HandleAppearances(const std::vector<uint8_t>& bytes) {
if (!appearances_handler_.is_bound()) {
LOG(WARNING) << "Got appearances but handler ptr is not bound.";
return;
}
if (appearances_handler_.get() == nullptr) {
LOG(ERROR) << "Handler ptr is null.";
return;
}
appearances_handler_->OnAppearances(bytes);
}
void OutputManager::HandleSmartFraming(const std::vector<uint8_t>& bytes) {
if (one_touch_autozoom_handler_.is_bound() &&
one_touch_autozoom_handler_.get() != nullptr) {
one_touch_autozoom_handler_->OnSmartFraming(bytes);
} else if (software_autozoom_handler_.is_bound() &&
software_autozoom_handler_.get() != nullptr) {
software_autozoom_handler_->OnSmartFraming(bytes);
} else {
LOG(WARNING) << "Got smart framing but handler ptr is not bound.";
}
}
void OutputManager::HandleIndexedTransitions(
const std::vector<uint8_t>& bytes) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&OutputManager::HandleIndexedTransitionsOnDbusThread,
base::Unretained(this), bytes));
}
void OutputManager::HandleIndexedTransitionsOnDbusThread(
const std::vector<uint8_t>& bytes) {
DCHECK(thread_.task_runner()->BelongsToCurrentThread());
std::string falcon_ip = rtanalytics_->GetFalconIp(configuration_name_);
std::size_t found = falcon_ip.find_last_of(".");
if (found == -1) {
LOG(ERROR) << "Device id is not an IP address.";
return;
}
falcon_ip = falcon_ip.substr(0, found);
// Send indexed transitions bytes over D-bus to the IP peripheral service.
if (bytes.size() == 0) {
dbus::MethodCall method_call("org.chromium.IpPeripheralService.FalconGrpc",
"ResetPTZTransition");
dbus::MessageWriter writer(&method_call);
writer.AppendString(falcon_ip);
dbus_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce([](dbus::Response* response) {}));
} else {
dbus::MethodCall method_call("org.chromium.IpPeripheralService.FalconGrpc",
"DoPTZTransition");
dbus::MessageWriter writer(&method_call);
writer.AppendString(falcon_ip);
writer.AppendArrayOfBytes(bytes.data(), bytes.size());
dbus_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OutputManager::HandleFalconPtzTransitionResponse,
weak_ptr_factory_.GetWeakPtr()));
}
}
void OutputManager::HandleFalconPtzTransitionResponse(
dbus::Response* response) {
std::vector<uint8_t> serialized_response;
if (response == nullptr) {
LOG(ERROR) << "HandleFalconPtzTransitionResponse ignoring dbus nullptr.";
huddly::IndexedTransitionsResponse itr;
itr.set_chosen_starting_index(-1);
serialized_response =
Serialized<huddly::IndexedTransitionsResponse>(itr).GetBytes();
} else {
dbus::MessageReader reader(response);
// Return the response to rtanalytics.
const uint8_t* bytes = nullptr;
size_t size;
reader.PopArrayOfBytes(&bytes, &size);
serialized_response.assign(bytes, bytes + size);
}
rtanalytics_->RespondToFalconPtzTransition(configuration_name_,
serialized_response);
}
} // namespace mri