You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
661 lines
21 KiB
661 lines
21 KiB
// Copyright 2018 The Chromium 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 <getopt.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "absl/strings/string_view.h"
|
|
#include "osp/msgs/osp_messages.h"
|
|
#include "osp/public/mdns_service_listener_factory.h"
|
|
#include "osp/public/mdns_service_publisher_factory.h"
|
|
#include "osp/public/message_demuxer.h"
|
|
#include "osp/public/network_service_manager.h"
|
|
#include "osp/public/presentation/presentation_controller.h"
|
|
#include "osp/public/presentation/presentation_receiver.h"
|
|
#include "osp/public/protocol_connection_client.h"
|
|
#include "osp/public/protocol_connection_client_factory.h"
|
|
#include "osp/public/protocol_connection_server.h"
|
|
#include "osp/public/protocol_connection_server_factory.h"
|
|
#include "osp/public/service_listener.h"
|
|
#include "osp/public/service_publisher.h"
|
|
#include "platform/api/network_interface.h"
|
|
#include "platform/api/time.h"
|
|
#include "platform/impl/logging.h"
|
|
#include "platform/impl/platform_client_posix.h"
|
|
#include "platform/impl/task_runner.h"
|
|
#include "platform/impl/text_trace_logging_platform.h"
|
|
#include "platform/impl/udp_socket_reader_posix.h"
|
|
#include "third_party/tinycbor/src/src/cbor.h"
|
|
#include "util/trace_logging.h"
|
|
|
|
namespace {
|
|
|
|
const char* kReceiverLogFilename = "_recv_fifo";
|
|
const char* kControllerLogFilename = "_cntl_fifo";
|
|
|
|
bool g_done = false;
|
|
bool g_dump_services = false;
|
|
|
|
void sigusr1_dump_services(int) {
|
|
g_dump_services = true;
|
|
}
|
|
|
|
void sigint_stop(int) {
|
|
OSP_LOG_INFO << "caught SIGINT, exiting...";
|
|
g_done = true;
|
|
}
|
|
|
|
void SignalThings() {
|
|
struct sigaction usr1_sa;
|
|
struct sigaction int_sa;
|
|
struct sigaction unused;
|
|
|
|
usr1_sa.sa_handler = &sigusr1_dump_services;
|
|
sigemptyset(&usr1_sa.sa_mask);
|
|
usr1_sa.sa_flags = 0;
|
|
|
|
int_sa.sa_handler = &sigint_stop;
|
|
sigemptyset(&int_sa.sa_mask);
|
|
int_sa.sa_flags = 0;
|
|
|
|
sigaction(SIGUSR1, &usr1_sa, &unused);
|
|
sigaction(SIGINT, &int_sa, &unused);
|
|
|
|
OSP_LOG_INFO << "signal handlers setup" << std::endl << "pid: " << getpid();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace openscreen {
|
|
namespace osp {
|
|
|
|
class DemoListenerObserver final : public ServiceListener::Observer {
|
|
public:
|
|
~DemoListenerObserver() override = default;
|
|
void OnStarted() override { OSP_LOG_INFO << "listener started!"; }
|
|
void OnStopped() override { OSP_LOG_INFO << "listener stopped!"; }
|
|
void OnSuspended() override { OSP_LOG_INFO << "listener suspended!"; }
|
|
void OnSearching() override { OSP_LOG_INFO << "listener searching!"; }
|
|
|
|
void OnReceiverAdded(const ServiceInfo& info) override {
|
|
OSP_LOG_INFO << "found! " << info.friendly_name;
|
|
}
|
|
void OnReceiverChanged(const ServiceInfo& info) override {
|
|
OSP_LOG_INFO << "changed! " << info.friendly_name;
|
|
}
|
|
void OnReceiverRemoved(const ServiceInfo& info) override {
|
|
OSP_LOG_INFO << "removed! " << info.friendly_name;
|
|
}
|
|
void OnAllReceiversRemoved() override { OSP_LOG_INFO << "all removed!"; }
|
|
void OnError(ServiceListenerError) override {}
|
|
void OnMetrics(ServiceListener::Metrics) override {}
|
|
};
|
|
|
|
std::string SanitizeServiceId(absl::string_view service_id) {
|
|
std::string safe_service_id(service_id);
|
|
for (auto& c : safe_service_id) {
|
|
if (c < ' ' || c > '~') {
|
|
c = '.';
|
|
}
|
|
}
|
|
return safe_service_id;
|
|
}
|
|
|
|
class DemoReceiverObserver final : public ReceiverObserver {
|
|
public:
|
|
~DemoReceiverObserver() override = default;
|
|
|
|
void OnRequestFailed(const std::string& presentation_url,
|
|
const std::string& service_id) override {
|
|
std::string safe_service_id = SanitizeServiceId(service_id);
|
|
OSP_LOG_WARN << "request failed: (" << presentation_url << ", "
|
|
<< safe_service_id << ")";
|
|
}
|
|
void OnReceiverAvailable(const std::string& presentation_url,
|
|
const std::string& service_id) override {
|
|
std::string safe_service_id = SanitizeServiceId(service_id);
|
|
safe_service_ids_.emplace(safe_service_id, service_id);
|
|
OSP_LOG_INFO << "available! " << safe_service_id;
|
|
}
|
|
void OnReceiverUnavailable(const std::string& presentation_url,
|
|
const std::string& service_id) override {
|
|
std::string safe_service_id = SanitizeServiceId(service_id);
|
|
safe_service_ids_.erase(safe_service_id);
|
|
OSP_LOG_INFO << "unavailable! " << safe_service_id;
|
|
}
|
|
|
|
const std::string& GetServiceId(const std::string& safe_service_id) {
|
|
OSP_DCHECK(safe_service_ids_.find(safe_service_id) !=
|
|
safe_service_ids_.end())
|
|
<< safe_service_id << " not found in map";
|
|
return safe_service_ids_[safe_service_id];
|
|
}
|
|
|
|
private:
|
|
std::map<std::string, std::string> safe_service_ids_;
|
|
};
|
|
|
|
class DemoPublisherObserver final : public ServicePublisher::Observer {
|
|
public:
|
|
~DemoPublisherObserver() override = default;
|
|
|
|
void OnStarted() override { OSP_LOG_INFO << "publisher started!"; }
|
|
void OnStopped() override { OSP_LOG_INFO << "publisher stopped!"; }
|
|
void OnSuspended() override { OSP_LOG_INFO << "publisher suspended!"; }
|
|
|
|
void OnError(ServicePublisherError) override {}
|
|
void OnMetrics(ServicePublisher::Metrics) override {}
|
|
};
|
|
|
|
class DemoConnectionClientObserver final
|
|
: public ProtocolConnectionServiceObserver {
|
|
public:
|
|
~DemoConnectionClientObserver() override = default;
|
|
void OnRunning() override {}
|
|
void OnStopped() override {}
|
|
|
|
void OnMetrics(const NetworkMetrics& metrics) override {}
|
|
void OnError(const Error& error) override {}
|
|
};
|
|
|
|
class DemoConnectionServerObserver final
|
|
: public ProtocolConnectionServer::Observer {
|
|
public:
|
|
class ConnectionObserver final : public ProtocolConnection::Observer {
|
|
public:
|
|
explicit ConnectionObserver(DemoConnectionServerObserver* parent)
|
|
: parent_(parent) {}
|
|
~ConnectionObserver() override = default;
|
|
|
|
void OnConnectionClosed(const ProtocolConnection& connection) override {
|
|
auto& connections = parent_->connections_;
|
|
connections.erase(
|
|
std::remove_if(
|
|
connections.begin(), connections.end(),
|
|
[this](const std::pair<std::unique_ptr<ConnectionObserver>,
|
|
std::unique_ptr<ProtocolConnection>>& p) {
|
|
return p.first.get() == this;
|
|
}),
|
|
connections.end());
|
|
}
|
|
|
|
private:
|
|
DemoConnectionServerObserver* const parent_;
|
|
};
|
|
|
|
~DemoConnectionServerObserver() override = default;
|
|
|
|
void OnRunning() override {}
|
|
void OnStopped() override {}
|
|
void OnSuspended() override {}
|
|
|
|
void OnMetrics(const NetworkMetrics& metrics) override {}
|
|
void OnError(const Error& error) override {}
|
|
|
|
void OnIncomingConnection(
|
|
std::unique_ptr<ProtocolConnection> connection) override {
|
|
auto observer = std::make_unique<ConnectionObserver>(this);
|
|
connection->SetObserver(observer.get());
|
|
connections_.emplace_back(std::move(observer), std::move(connection));
|
|
connections_.back().second->CloseWriteEnd();
|
|
}
|
|
|
|
private:
|
|
std::vector<std::pair<std::unique_ptr<ConnectionObserver>,
|
|
std::unique_ptr<ProtocolConnection>>>
|
|
connections_;
|
|
};
|
|
|
|
class DemoRequestDelegate final : public RequestDelegate {
|
|
public:
|
|
DemoRequestDelegate() = default;
|
|
~DemoRequestDelegate() override = default;
|
|
|
|
void OnConnection(std::unique_ptr<Connection> connection) override {
|
|
OSP_LOG_INFO << "request successful";
|
|
this->connection = std::move(connection);
|
|
}
|
|
|
|
void OnError(const Error& error) override {
|
|
OSP_LOG_INFO << "on request error";
|
|
}
|
|
|
|
std::unique_ptr<Connection> connection;
|
|
};
|
|
|
|
class DemoConnectionDelegate final : public Connection::Delegate {
|
|
public:
|
|
DemoConnectionDelegate() = default;
|
|
~DemoConnectionDelegate() override = default;
|
|
|
|
void OnConnected() override {
|
|
OSP_LOG_INFO << "presentation connection connected";
|
|
}
|
|
void OnClosedByRemote() override {
|
|
OSP_LOG_INFO << "presentation connection closed by remote";
|
|
}
|
|
void OnDiscarded() override {}
|
|
void OnError(const absl::string_view message) override {}
|
|
void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
|
|
|
|
void OnStringMessage(absl::string_view message) override {
|
|
OSP_LOG_INFO << "got message: " << message;
|
|
}
|
|
void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
|
|
};
|
|
|
|
class DemoReceiverConnectionDelegate final : public Connection::Delegate {
|
|
public:
|
|
DemoReceiverConnectionDelegate() = default;
|
|
~DemoReceiverConnectionDelegate() override = default;
|
|
|
|
void OnConnected() override {
|
|
OSP_LOG_INFO << "presentation connection connected";
|
|
}
|
|
void OnClosedByRemote() override {
|
|
OSP_LOG_INFO << "presentation connection closed by remote";
|
|
}
|
|
void OnDiscarded() override {}
|
|
void OnError(const absl::string_view message) override {}
|
|
void OnTerminated() override { OSP_LOG_INFO << "presentation terminated"; }
|
|
|
|
void OnStringMessage(const absl::string_view message) override {
|
|
OSP_LOG_INFO << "got message: " << message;
|
|
connection->SendString("--echo-- " + std::string(message));
|
|
}
|
|
void OnBinaryMessage(const std::vector<uint8_t>& data) override {}
|
|
|
|
Connection* connection;
|
|
};
|
|
|
|
class DemoReceiverDelegate final : public ReceiverDelegate {
|
|
public:
|
|
~DemoReceiverDelegate() override = default;
|
|
|
|
std::vector<msgs::UrlAvailability> OnUrlAvailabilityRequest(
|
|
uint64_t client_id,
|
|
uint64_t request_duration,
|
|
std::vector<std::string> urls) override {
|
|
std::vector<msgs::UrlAvailability> result;
|
|
result.reserve(urls.size());
|
|
for (const auto& url : urls) {
|
|
OSP_LOG_INFO << "got availability request for: " << url;
|
|
result.push_back(msgs::UrlAvailability::kAvailable);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool StartPresentation(
|
|
const Connection::PresentationInfo& info,
|
|
uint64_t source_id,
|
|
const std::vector<msgs::HttpHeader>& http_headers) override {
|
|
presentation_id = info.id;
|
|
connection = std::make_unique<Connection>(info, &cd, Receiver::Get());
|
|
cd.connection = connection.get();
|
|
Receiver::Get()->OnPresentationStarted(info.id, connection.get(),
|
|
ResponseResult::kSuccess);
|
|
return true;
|
|
}
|
|
|
|
bool ConnectToPresentation(uint64_t request_id,
|
|
const std::string& id,
|
|
uint64_t source_id) override {
|
|
connection = std::make_unique<Connection>(
|
|
Connection::PresentationInfo{id, connection->presentation_info().url},
|
|
&cd, Receiver::Get());
|
|
cd.connection = connection.get();
|
|
Receiver::Get()->OnConnectionCreated(request_id, connection.get(),
|
|
ResponseResult::kSuccess);
|
|
return true;
|
|
}
|
|
|
|
void TerminatePresentation(const std::string& id,
|
|
TerminationReason reason) override {
|
|
Receiver::Get()->OnPresentationTerminated(id, reason);
|
|
}
|
|
|
|
std::string presentation_id;
|
|
std::unique_ptr<Connection> connection;
|
|
DemoReceiverConnectionDelegate cd;
|
|
};
|
|
|
|
struct CommandLineSplit {
|
|
std::string command;
|
|
std::string argument_tail;
|
|
};
|
|
|
|
CommandLineSplit SeparateCommandFromArguments(const std::string& line) {
|
|
size_t split_index = line.find_first_of(' ');
|
|
// NOTE: |split_index| can be std::string::npos because not all commands
|
|
// accept arguments.
|
|
std::string command = line.substr(0, split_index);
|
|
std::string argument_tail =
|
|
split_index < line.size() ? line.substr(split_index + 1) : std::string();
|
|
return {std::move(command), std::move(argument_tail)};
|
|
}
|
|
|
|
struct CommandWaitResult {
|
|
bool done;
|
|
CommandLineSplit command_line;
|
|
};
|
|
|
|
CommandWaitResult WaitForCommand(pollfd* pollfd) {
|
|
while (poll(pollfd, 1, 10) >= 0) {
|
|
if (g_done) {
|
|
return {true};
|
|
}
|
|
|
|
if (pollfd->revents == 0) {
|
|
continue;
|
|
} else if (pollfd->revents & (POLLERR | POLLHUP)) {
|
|
return {true};
|
|
}
|
|
|
|
std::string line;
|
|
if (!std::getline(std::cin, line)) {
|
|
return {true};
|
|
}
|
|
|
|
CommandWaitResult result;
|
|
result.done = false;
|
|
result.command_line = SeparateCommandFromArguments(line);
|
|
return result;
|
|
}
|
|
return {true};
|
|
}
|
|
|
|
void RunControllerPollLoop(Controller* controller) {
|
|
DemoReceiverObserver receiver_observer;
|
|
DemoRequestDelegate request_delegate;
|
|
DemoConnectionDelegate connection_delegate;
|
|
Controller::ReceiverWatch watch;
|
|
Controller::ConnectRequest connect_request;
|
|
|
|
pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
|
|
while (true) {
|
|
OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
|
|
|
|
CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
|
|
if (command_result.done) {
|
|
break;
|
|
}
|
|
|
|
if (command_result.command_line.command == "avail") {
|
|
watch = controller->RegisterReceiverWatch(
|
|
{std::string(command_result.command_line.argument_tail)},
|
|
&receiver_observer);
|
|
} else if (command_result.command_line.command == "start") {
|
|
const absl::string_view& argument_tail =
|
|
command_result.command_line.argument_tail;
|
|
size_t next_split = argument_tail.find_first_of(' ');
|
|
const std::string& service_id = receiver_observer.GetServiceId(
|
|
std::string(argument_tail.substr(next_split + 1)));
|
|
const std::string url =
|
|
static_cast<std::string>(argument_tail.substr(0, next_split));
|
|
connect_request = controller->StartPresentation(
|
|
url, service_id, &request_delegate, &connection_delegate);
|
|
} else if (command_result.command_line.command == "msg") {
|
|
request_delegate.connection->SendString(
|
|
command_result.command_line.argument_tail);
|
|
} else if (command_result.command_line.command == "close") {
|
|
request_delegate.connection->Close(Connection::CloseReason::kClosed);
|
|
} else if (command_result.command_line.command == "reconnect") {
|
|
connect_request = controller->ReconnectConnection(
|
|
std::move(request_delegate.connection), &request_delegate);
|
|
} else if (command_result.command_line.command == "term") {
|
|
request_delegate.connection->Terminate(
|
|
TerminationReason::kControllerTerminateCalled);
|
|
}
|
|
}
|
|
|
|
watch = Controller::ReceiverWatch();
|
|
}
|
|
|
|
void ListenerDemo() {
|
|
SignalThings();
|
|
|
|
DemoListenerObserver listener_observer;
|
|
MdnsServiceListenerConfig listener_config;
|
|
auto mdns_listener = MdnsServiceListenerFactory::Create(
|
|
listener_config, &listener_observer,
|
|
PlatformClientPosix::GetInstance()->GetTaskRunner());
|
|
|
|
MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
|
|
DemoConnectionClientObserver client_observer;
|
|
auto connection_client = ProtocolConnectionClientFactory::Create(
|
|
&demuxer, &client_observer,
|
|
PlatformClientPosix::GetInstance()->GetTaskRunner());
|
|
|
|
auto* network_service = NetworkServiceManager::Create(
|
|
std::move(mdns_listener), nullptr, std::move(connection_client), nullptr);
|
|
auto controller = std::make_unique<Controller>(Clock::now);
|
|
|
|
network_service->GetMdnsServiceListener()->Start();
|
|
network_service->GetProtocolConnectionClient()->Start();
|
|
|
|
RunControllerPollLoop(controller.get());
|
|
|
|
network_service->GetMdnsServiceListener()->Stop();
|
|
network_service->GetProtocolConnectionClient()->Stop();
|
|
|
|
controller.reset();
|
|
|
|
NetworkServiceManager::Dispose();
|
|
}
|
|
|
|
void HandleReceiverCommand(absl::string_view command,
|
|
absl::string_view argument_tail,
|
|
DemoReceiverDelegate& delegate,
|
|
NetworkServiceManager* manager) {
|
|
if (command == "avail") {
|
|
ServicePublisher* publisher = manager->GetMdnsServicePublisher();
|
|
|
|
if (publisher->state() == ServicePublisher::State::kSuspended) {
|
|
publisher->Resume();
|
|
} else {
|
|
publisher->Suspend();
|
|
}
|
|
} else if (command == "close") {
|
|
delegate.connection->Close(Connection::CloseReason::kClosed);
|
|
} else if (command == "msg") {
|
|
delegate.connection->SendString(argument_tail);
|
|
} else if (command == "term") {
|
|
Receiver::Get()->OnPresentationTerminated(
|
|
delegate.presentation_id, TerminationReason::kReceiverUserTerminated);
|
|
} else {
|
|
OSP_LOG_FATAL << "Received unknown receiver command: " << command;
|
|
}
|
|
}
|
|
|
|
void RunReceiverPollLoop(pollfd& file_descriptor,
|
|
NetworkServiceManager* manager,
|
|
DemoReceiverDelegate& delegate) {
|
|
pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
|
|
while (true) {
|
|
OSP_CHECK_EQ(write(STDOUT_FILENO, "$ ", 2), 2);
|
|
|
|
CommandWaitResult command_result = WaitForCommand(&stdin_pollfd);
|
|
if (command_result.done) {
|
|
break;
|
|
}
|
|
|
|
HandleReceiverCommand(command_result.command_line.command,
|
|
command_result.command_line.argument_tail, delegate,
|
|
manager);
|
|
}
|
|
}
|
|
|
|
void CleanupPublisherDemo(NetworkServiceManager* manager) {
|
|
Receiver::Get()->SetReceiverDelegate(nullptr);
|
|
Receiver::Get()->Deinit();
|
|
manager->GetMdnsServicePublisher()->Stop();
|
|
manager->GetProtocolConnectionServer()->Stop();
|
|
|
|
NetworkServiceManager::Dispose();
|
|
}
|
|
|
|
void PublisherDemo(absl::string_view friendly_name) {
|
|
SignalThings();
|
|
|
|
constexpr uint16_t server_port = 6667;
|
|
|
|
DemoPublisherObserver publisher_observer;
|
|
// TODO(btolsch): aggregate initialization probably better?
|
|
ServicePublisher::Config publisher_config;
|
|
publisher_config.friendly_name = std::string(friendly_name);
|
|
publisher_config.hostname = "turtle-deadbeef";
|
|
publisher_config.service_instance_name = "deadbeef";
|
|
publisher_config.connection_server_port = server_port;
|
|
|
|
auto mdns_publisher = MdnsServicePublisherFactory::Create(
|
|
publisher_config, &publisher_observer,
|
|
PlatformClientPosix::GetInstance()->GetTaskRunner());
|
|
|
|
ServerConfig server_config;
|
|
for (const InterfaceInfo& interface : GetNetworkInterfaces()) {
|
|
OSP_VLOG << "Found interface: " << interface;
|
|
if (!interface.addresses.empty()) {
|
|
server_config.connection_endpoints.push_back(
|
|
IPEndpoint{interface.addresses[0].address, server_port});
|
|
}
|
|
}
|
|
OSP_LOG_IF(WARN, server_config.connection_endpoints.empty())
|
|
<< "No network interfaces had usable addresses for mDNS publishing.";
|
|
|
|
MessageDemuxer demuxer(Clock::now, MessageDemuxer::kDefaultBufferLimit);
|
|
DemoConnectionServerObserver server_observer;
|
|
auto connection_server = ProtocolConnectionServerFactory::Create(
|
|
server_config, &demuxer, &server_observer,
|
|
PlatformClientPosix::GetInstance()->GetTaskRunner());
|
|
|
|
auto* network_service =
|
|
NetworkServiceManager::Create(nullptr, std::move(mdns_publisher), nullptr,
|
|
std::move(connection_server));
|
|
|
|
DemoReceiverDelegate receiver_delegate;
|
|
Receiver::Get()->Init();
|
|
Receiver::Get()->SetReceiverDelegate(&receiver_delegate);
|
|
network_service->GetMdnsServicePublisher()->Start();
|
|
network_service->GetProtocolConnectionServer()->Start();
|
|
|
|
pollfd stdin_pollfd{STDIN_FILENO, POLLIN};
|
|
|
|
RunReceiverPollLoop(stdin_pollfd, network_service, receiver_delegate);
|
|
|
|
receiver_delegate.connection.reset();
|
|
CleanupPublisherDemo(network_service);
|
|
}
|
|
|
|
} // namespace osp
|
|
} // namespace openscreen
|
|
|
|
struct InputArgs {
|
|
absl::string_view friendly_server_name;
|
|
bool is_verbose;
|
|
bool is_help;
|
|
bool tracing_enabled;
|
|
};
|
|
|
|
void LogUsage(const char* argv0) {
|
|
std::cerr << R"(
|
|
usage: )" << argv0
|
|
<< R"( <options> <friendly_name>
|
|
|
|
friendly_name
|
|
Server name, runs the publisher demo. Omission runs the listener demo.
|
|
|
|
-t, --tracing: Enable performance trace logging.
|
|
|
|
-v, --verbose: Enable verbose logging.
|
|
|
|
-h, --help: Show this help message.
|
|
)";
|
|
}
|
|
|
|
InputArgs GetInputArgs(int argc, char** argv) {
|
|
// A note about modifying command line arguments: consider uniformity
|
|
// between all Open Screen executables. If it is a platform feature
|
|
// being exposed, consider if it applies to the standalone receiver,
|
|
// standalone sender, osp demo, and test_main argument options.
|
|
const struct option kArgumentOptions[] = {
|
|
{"tracing", no_argument, nullptr, 't'},
|
|
{"verbose", no_argument, nullptr, 'v'},
|
|
{"help", no_argument, nullptr, 'h'},
|
|
{nullptr, 0, nullptr, 0}};
|
|
|
|
InputArgs args = {};
|
|
int ch = -1;
|
|
while ((ch = getopt_long(argc, argv, "tvh", kArgumentOptions, nullptr)) !=
|
|
-1) {
|
|
switch (ch) {
|
|
case 't':
|
|
args.tracing_enabled = true;
|
|
break;
|
|
|
|
case 'v':
|
|
args.is_verbose = true;
|
|
break;
|
|
|
|
case 'h':
|
|
args.is_help = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
args.friendly_server_name = argv[optind];
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
using openscreen::Clock;
|
|
using openscreen::LogLevel;
|
|
using openscreen::PlatformClientPosix;
|
|
|
|
InputArgs args = GetInputArgs(argc, argv);
|
|
if (args.is_help) {
|
|
LogUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
std::unique_ptr<openscreen::TextTraceLoggingPlatform> trace_logging_platform;
|
|
if (args.tracing_enabled) {
|
|
trace_logging_platform =
|
|
std::make_unique<openscreen::TextTraceLoggingPlatform>();
|
|
}
|
|
|
|
const LogLevel level = args.is_verbose ? LogLevel::kVerbose : LogLevel::kInfo;
|
|
openscreen::SetLogLevel(level);
|
|
|
|
const bool is_receiver_demo = !args.friendly_server_name.empty();
|
|
const char* log_filename =
|
|
is_receiver_demo ? kReceiverLogFilename : kControllerLogFilename;
|
|
// TODO(jophba): Mac on Mojave hangs on this command forever.
|
|
openscreen::SetLogFifoOrDie(log_filename);
|
|
|
|
PlatformClientPosix::Create(std::chrono::milliseconds(50));
|
|
|
|
if (is_receiver_demo) {
|
|
OSP_LOG_INFO << "Running publisher demo...";
|
|
openscreen::osp::PublisherDemo(args.friendly_server_name);
|
|
} else {
|
|
OSP_LOG_INFO << "Running listener demo...";
|
|
openscreen::osp::ListenerDemo();
|
|
}
|
|
|
|
PlatformClientPosix::ShutDown();
|
|
|
|
return 0;
|
|
}
|