added walkietalkie example

This commit is contained in:
lasan
2024-10-26 12:53:37 +05:30
parent 2bbe5fac10
commit 120fc134d3
8 changed files with 594 additions and 13014 deletions
+13 -6
View File
@@ -2,17 +2,17 @@ bazel_dep(name = "platforms", version = "0.0.8")
bazel_dep(name = "rules_cc", version = "0.0.9")
bazel_dep(name = "rules_rust", version = "0.42.1")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "com_google_absl")
bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf")
bazel_dep(name = "googletest", version = "1.14.0", repo_name = "com_google_googletest")
bazel_dep(name = "boringssl", version = "0.0.0-20240126-22d349c")
git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "beto-core",
remote = "https://beto-core.googlesource.com/beto-core",
commit = "415bd032561d078720642d52e28fd3bc9d5155d4",
remote = "https://beto-core.googlesource.com/beto-core",
)
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
@@ -21,6 +21,7 @@ rust.toolchain(
versions = ["1.77.1"],
)
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
crate = use_extension(
@@ -46,7 +47,6 @@ http_archive(
http_archive(
name = "aappleby_smhasher",
strip_prefix = "smhasher-master",
build_file_content = """
package(default_visibility = ["//visibility:public"])
cc_library(
@@ -56,12 +56,12 @@ cc_library(
copts = ["-Wno-implicit-fallthrough"],
licenses = ["unencumbered"], # MurmurHash is explicity public-domain
)""",
strip_prefix = "smhasher-master",
urls = ["https://github.com/aappleby/smhasher/archive/master.zip"],
)
http_archive(
name = "nlohmann_json",
strip_prefix = "json-3.10.5",
build_file_content = """
cc_library(
name = "json",
@@ -72,6 +72,7 @@ cc_library(
visibility = ["//visibility:public"],
alwayslink = True,
)""",
strip_prefix = "json-3.10.5",
urls = [
"https://github.com/nlohmann/json/archive/refs/tags/v3.10.5.tar.gz",
],
@@ -85,8 +86,8 @@ cc_library(
# https://github.com/google-research/nisaba
http_archive(
name = "com_google_nisaba",
url = "https://github.com/google-research/nisaba/archive/refs/heads/main.zip",
strip_prefix = "nisaba-main",
url = "https://github.com/google-research/nisaba/archive/refs/heads/main.zip",
)
# -------------------------------------------------------------------------
@@ -94,6 +95,12 @@ http_archive(
# https://github.com/inazarenko/protobuf-matchers
http_archive(
name = "com_github_protobuf_matchers",
urls = ["https://github.com/inazarenko/protobuf-matchers/archive/refs/heads/master.zip"],
strip_prefix = "protobuf-matchers-master",
urls = ["https://github.com/inazarenko/protobuf-matchers/archive/refs/heads/master.zip"],
)
http_archive(
name = "bazel_pkg_config",
strip_prefix = "bazel_pkg_config-master",
urls = ["https://github.com/cherrry/bazel_pkg_config/archive/master.zip"],
)
+97 -13008
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -1,3 +1,36 @@
load("@bazel_pkg_config//:pkg_config.bzl", "pkg_config")
# ================================================ #
# All dependencies have been moved to MODULE.Bazel #
# ================================================ #
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
pkg_config(
name = "libsystemd",
pkg_name = "libsystemd",
)
pkg_config(
name = "libcurl",
pkg_name = "libcurl",
)
pkg_config(
name = "sdbus_cpp",
pkg_name = "sdbus-c++",
)
# gflags needed by glog
http_archive(
name = "com_github_gflags_gflags",
sha256 = "19713a36c9f32b33df59d1c79b4958434cb005b5b47dc5400a7a4b078111d9b5",
strip_prefix = "gflags-2.2.2",
url = "https://github.com/gflags/gflags/archive/v2.2.2.zip",
)
http_archive(
name = "com_google_glog",
sha256 = "f28359aeba12f30d73d9e4711ef356dc842886968112162bc73002645139c39c",
strip_prefix = "glog-0.4.0",
urls = ["https://github.com/google/glog/archive/v0.4.0.tar.gz"],
)
+30
View File
@@ -0,0 +1,30 @@
# Copyright 2023 Google LLC
#
# 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
#
# https://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.
licenses(["notice"])
cc_binary(
name = "walkietalkie",
srcs = ["main.cc",
"audio_player.h",
"walkietalkie.h",
"audio_player.cc",
],
linkopts = ["-lasound"],
deps = ["//connections:core",
"//connections:core_types",
"//internal/platform/implementation/linux:linux",
"@com_google_protobuf//:protobuf",
],
)
+105
View File
@@ -0,0 +1,105 @@
#include <array>
#include <cassert>
#include <iostream>
#include <mutex>
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>
#include "connections/payload.h"
#include "connections/walkietalkie/audio_player.h"
AudioPlayer::~AudioPlayer() {
stop();
snd_pcm_drain(pcm_);
snd_pcm_close(pcm_);
}
void AudioPlayer::start(
const std::shared_ptr<nearby::connections::Payload> &payload,
int64_t size) {
stop();
init_pcm();
stopped_ = false;
player_thread_ = std::make_unique<std::thread>([&, payload, size]() {
auto *stream = payload->AsStream();
assert(stream != nullptr);
auto result = stream->ReadExactly(size);
if (!result.ok()) {
LOG(INFO) << "error reading from payload, exception value: "
<< result.GetException().value;
return;
}
auto buffer = result.GetResult();
auto ret = snd_pcm_writei(pcm_, buffer.data(),
snd_pcm_bytes_to_frames(pcm_, buffer.size()));
if (ret == -EPIPE) {
if (ret = snd_pcm_prepare(pcm_); ret < 0) {
LOG(INFO) << "error preparing PCM device: " << snd_strerror(ret);
}
} else if (ret < 0) {
LOG(INFO) << "error writing to PCM device: " << snd_strerror(ret);
return;
}
});
}
void AudioPlayer::stop() {
stopped_ = true;
if (player_thread_ != nullptr && player_thread_->joinable())
player_thread_->join();
}
void AudioPlayer::init_pcm() {
if (pcm_ != nullptr) return;
auto ret = snd_pcm_open(&pcm_, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (ret < 0) {
LOG(ERROR) << "error opening PCM device: " << snd_strerror(ret);
return;
}
snd_pcm_hw_params_alloca(&pcm_params_);
snd_pcm_hw_params_any(pcm_, pcm_params_);
if (ret = snd_pcm_hw_params_set_access(pcm_, pcm_params_,
SND_PCM_ACCESS_RW_INTERLEAVED);
ret < 0) {
LOG(ERROR) << "error setting access type: " << snd_strerror(ret);
return;
}
if (ret = snd_pcm_hw_params_set_format(pcm_, pcm_params_,
SND_PCM_FORMAT_S16_LE);
ret < 0) {
LOG(ERROR) << "error setting PCM format: " << snd_strerror(ret);
return;
}
if (ret = snd_pcm_hw_params_set_channels(pcm_, pcm_params_, 1); ret < 0) {
LOG(ERROR) << "error setting number of channels: " << snd_strerror(ret);
return;
}
const std::array<unsigned int, 6> possible_sample_rates = {
8000, 11025, 16000, 22050, 44100, 48000};
for (auto sample_rate : possible_sample_rates) {
if (snd_pcm_hw_params_test_rate(pcm_, pcm_params_, sample_rate, 0) == 0) {
if (ret = snd_pcm_hw_params_set_rate_near(pcm_, pcm_params_, &sample_rate,
nullptr);
ret < 0) {
LOG(ERROR) << "error setting sample rate to " << sample_rate << ": "
<< snd_strerror(ret);
return;
}
break;
}
}
if (ret = snd_pcm_hw_params(pcm_, pcm_params_); ret < 0) {
LOG(ERROR) << "error setting hardware params: " << snd_strerror(ret);
return;
}
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef WALKIETALKIE_AUDIO_PLAYER_H_
#define WALKIETALKIE_AUDIO_PLAYER_H_
#include <atomic>
#include <mutex>
#include <thread>
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>
#include "connections/payload.h"
class AudioPlayer {
public:
AudioPlayer(const AudioPlayer &) = delete;
AudioPlayer(AudioPlayer &&) = delete;
AudioPlayer &operator=(const AudioPlayer &) = delete;
AudioPlayer &operator=(AudioPlayer &&) = delete;
AudioPlayer()
: stopped_(false),
player_thread_(nullptr),
pcm_(nullptr),
pcm_params_(nullptr) {}
~AudioPlayer();
void start(const std::shared_ptr<nearby::connections::Payload> &payload,
int64_t size);
void stop();
private:
void init_pcm();
std::atomic_bool stopped_;
std::unique_ptr<std::thread> player_thread_;
snd_pcm_t *pcm_;
snd_pcm_hw_params_t *pcm_params_;
};
#endif
+43
View File
@@ -0,0 +1,43 @@
#include <signal.h>
#include <cstdlib>
#include <ctime>
#include <mutex>
#include <shared_mutex>
#include <sstream>
#include <thread>
#include <unordered_map>
#include <vector>
#include <alsa/asoundlib.h>
#include "absl/synchronization/notification.h"
#include "absl/log/log.h"
#include "connections/implementation/service_controller_router.h"
#include "connections/walkietalkie/walkietalkie.h"
namespace {
static absl::Notification notification_;
void quit_handler(int sig, siginfo_t *siginfo, void *ignore) {
if (!notification_.HasBeenNotified()) notification_.Notify();
}
} // namespace
int main() {
std::srand(std::time(nullptr));
struct sigaction action {};
action.sa_sigaction = quit_handler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &action, nullptr);
sigaction(SIGTERM, &action, nullptr);
auto client = std::make_unique<WalkieTalkie>(
std::make_unique<nearby::connections::ServiceControllerRouter>());
client->start();
notification_.WaitForNotification();
std::cerr << "received signal, shutting down" << std::endl;
client.reset();
}
+232
View File
@@ -0,0 +1,232 @@
#ifndef WALKIETALKIE_CLIENT_H_
#define WALKIETALKIE_CLIENT_H_
#include <shared_mutex>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "connections/advertising_options.h"
#include "connections/connection_options.h"
#include "connections/core.h"
#include "connections/discovery_options.h"
#include "connections/implementation/analytics/throughput_recorder.h"
#include "connections/implementation/service_controller_router.h"
#include "connections/listeners.h"
#include "connections/params.h"
#include "connections/payload.h"
#include "connections/status.h"
#include "connections/strategy.h"
#include "connections/walkietalkie/audio_player.h"
#include "internal/interop/device_provider.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/payload_id.h"
class WalkieTalkie {
const constexpr static char* walkietalkie_service_id =
"com.google.location.nearby.apps.walkietalkie.manual.SERVICE_ID";
public:
WalkieTalkie(const WalkieTalkie&) = delete;
WalkieTalkie(WalkieTalkie&&) = delete;
WalkieTalkie& operator=(const WalkieTalkie&) = delete;
WalkieTalkie& operator=(WalkieTalkie&&) = delete;
explicit WalkieTalkie(
std::unique_ptr<nearby::connections::ServiceControllerRouter> router)
: router_(std::move(router)),
core_(std::make_unique<nearby::connections::Core>(router_.get())),
player_(nullptr),
pending_payloads_({}) {}
~WalkieTalkie() {
std::unique_lock lock(player_mutex_);
if (player_ != nullptr) player_->stop();
}
static std::string endpoint_name() {
std::stringstream name;
for (auto i = 0; i < 5; i++) {
name << (std::rand() % 10);
}
return name.str();
}
void start() {
nearby::connections::AdvertisingOptions advertising;
advertising.strategy = nearby::connections::Strategy::kP2pStar;
advertising.device_info = "walkietalkie";
advertising.allowed.SetAll(false);
advertising.allowed.wifi_direct = false;
advertising.allowed.wifi_hotspot = false;
advertising.allowed.wifi_lan = true ;
advertising.allowed.ble = false;
advertising.allowed.bluetooth = true;
advertising.low_power = false;
advertising.auto_upgrade_bandwidth = false;
auto local_endpoint_info = nearby::ByteArray(endpoint_name());
core_->StartAdvertising(
walkietalkie_service_id, advertising.CompatibleOptions(),
createConnReqInfo(local_endpoint_info),
[](nearby::connections::Status status) {
LOG(INFO) << "Advertising status: " << status.ToString();
});
nearby::connections::DiscoveryOptions discovery;
discovery.strategy = advertising.strategy;
discovery.allowed = advertising.allowed;
discovery.low_power = false;
nearby::connections::DiscoveryListener discovery_listener;
discovery_listener.endpoint_found_cb =
[this, local_endpoint_info](const std::string& remote_endpoint_id,
const nearby::ByteArray& endpoint_info,
const std::string& service_id) {
LOG(INFO) << "Found " << service_id << " with endpoint id "
<< remote_endpoint_id;
nearby::connections::ConnectionOptions conn_options;
conn_options.allowed.SetAll(false);
conn_options.allowed.bluetooth = true;
conn_options.allowed.wifi_hotspot = false;
conn_options.allowed.wifi_lan = true;
conn_options.auto_upgrade_bandwidth = false;
conn_options.connection_info.local_endpoint_info =
local_endpoint_info;
if (service_id == walkietalkie_service_id) {
LOG(INFO) << "Requesting a connection to " << service_id
<< " on remote endpoint id " << remote_endpoint_id;
core_->RequestConnection(
remote_endpoint_id, createConnReqInfo(endpoint_info),
conn_options, [](nearby::connections::Status status) {
LOG(INFO)
<< "Request connection status: " << status.ToString();
});
}
};
core_->StartDiscovery(
walkietalkie_service_id, discovery.CompatibleOptions(),
std::move(discovery_listener), [](nearby::connections::Status status) {
LOG(INFO) << "Discovery status: " << status.ToString();
});
}
nearby::connections::ConnectionRequestInfo createConnReqInfo(
const nearby::ByteArray& endpoint_info) {
nearby::connections::ConnectionRequestInfo con_req_info;
con_req_info.endpoint_info = endpoint_info;
con_req_info.listener.initiated_cb =
[&](const std::string& id,
const nearby::connections::ConnectionResponseInfo& info) {
connection_initiated(id, info);
};
con_req_info.listener.accepted_cb = [&](const std::string& id) {
connection_accepted(id);
};
con_req_info.listener.disconnected_cb = [&](const std::string& id) {
disconnected(id);
};
return con_req_info;
}
void add_pending_remote_endpoint(const std::string& endpoint_id,
nearby::ByteArray endpoint_info) {
std::unique_lock<std::shared_mutex> lock(pending_conns_mutex_);
pending_conns_.insert({endpoint_id, std::move(endpoint_info)});
}
void remove_pending_remote_endpoint(const std::string& endpoint_id) {
std::unique_lock lock(pending_conns_mutex_);
pending_conns_.erase(endpoint_id);
}
void connection_initiated(
const std::string& endpoint_id,
const nearby::connections::ConnectionResponseInfo& info) {
auto i = info;
LOG(INFO) << "connected initiated, endpoint id: '" << endpoint_id
<< "', remote endpoint id: '"
<< info.remote_endpoint_info.string_data() << "'";
add_pending_remote_endpoint(endpoint_id, info.remote_endpoint_info);
struct nearby::connections::PayloadListener listener;
listener.payload_cb = [&](absl::string_view endpoint_id,
nearby::connections::Payload payload) {
if (payload.GetType() == nearby::analytics::PayloadType::kStream) {
auto id = payload.GetId();
auto shared =
std::make_shared<nearby::connections::Payload>(std::move(payload));
{
std::unique_lock lock(pending_payloads_mutex_);
pending_payloads_.emplace(std::pair{std::string(endpoint_id), id},
shared);
}
}
};
listener.payload_progress_cb =
[&](absl::string_view endpoint_id,
const nearby::connections::PayloadProgressInfo& info) {
auto key = std::pair{std::string(endpoint_id), info.payload_id};
switch (info.status) {
case nearby::connections::PayloadProgressInfo::Status::kSuccess: {
std::shared_ptr<nearby::connections::Payload> payload = nullptr;
{
std::unique_lock payloads_lock(pending_payloads_mutex_);
payload = pending_payloads_[key];
pending_payloads_.erase(key);
}
std::unique_lock player_lock(player_mutex_);
if (player_ == nullptr) player_ = std::make_unique<AudioPlayer>();
player_->start(payload, info.bytes_transferred);
} break;
case nearby::connections::PayloadProgressInfo::Status::kCanceled:
case nearby::connections::PayloadProgressInfo::Status::kFailure: {
std::unique_lock lock(pending_payloads_mutex_);
pending_payloads_.erase(key);
}
}
};
core_->AcceptConnection(endpoint_id, std::move(listener),
[&](nearby::connections::Status status) {
LOG(INFO) << "AcceptConnection status: "
<< status.ToString();
});
}
void connection_accepted(const std::string& endpoint_id) {
LOG(INFO) << "connection accepted, remote endpoint id: '" << endpoint_id
<< "'";
remove_pending_remote_endpoint(endpoint_id);
core_->StopAdvertising([](nearby::connections::Status){});
// core_->StopDiscovery([](nearby::connections::Status) {});
}
void disconnected(const std::string& endpoint_id) {
LOG(INFO) << "disconnected, remote endpoint id: " << endpoint_id;
remove_pending_remote_endpoint(endpoint_id);
}
// Discovery callbacks
private:
std::unique_ptr<nearby::connections::ServiceControllerRouter> router_;
std::unique_ptr<nearby::connections::Core> core_;
std::mutex player_mutex_;
std::unique_ptr<AudioPlayer> player_;
std::shared_mutex pending_conns_mutex_;
std::unordered_map<std::string, nearby::ByteArray> pending_conns_;
std::mutex pending_payloads_mutex_;
std::map<std::pair<std::string, nearby::PayloadId>,
std::shared_ptr<nearby::connections::Payload>>
pending_payloads_;
};
#endif