diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-07-03 15:49:17 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-07-03 15:49:17 +0200 |
commit | 1aec1e2bf1021dc11f1a9f56b4a0048d533d9271 (patch) | |
tree | ae0d6aa76251c002a3856714e4712d060bcee78b | |
parent | a39150e63ad80016ea533337c9d6cf4d7516245d (diff) | |
download | ODR-SourceCompanion-1aec1e2bf1021dc11f1a9f56b4a0048d533d9271.tar.gz ODR-SourceCompanion-1aec1e2bf1021dc11f1a9f56b4a0048d533d9271.tar.bz2 ODR-SourceCompanion-1aec1e2bf1021dc11f1a9f56b4a0048d533d9271.zip |
Add Stats output through UNIX DGRAM socket
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/StatsPublish.cpp | 123 | ||||
-rw-r--r-- | src/StatsPublish.h | 65 | ||||
-rw-r--r-- | src/odr-sourcecompanion.cpp | 34 |
4 files changed, 223 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index aff4694..1bac77d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,7 @@ odr_sourcecompanion_SOURCES = src/odr-sourcecompanion.cpp \ src/AACDecoder.h src/AACDecoder.cpp \ src/AVTInput.h src/AVTInput.cpp \ src/OrderedQueue.h src/OrderedQueue.cpp \ + src/StatsPublish.h src/StatsPublish.cpp \ src/crc.h src/crc.c \ src/encryption.h src/encryption.c \ src/utils.h src/utils.c \ diff --git a/src/StatsPublish.cpp b/src/StatsPublish.cpp new file mode 100644 index 0000000..0bad833 --- /dev/null +++ b/src/StatsPublish.cpp @@ -0,0 +1,123 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2019 Matthias P. Braendli + * + * 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 "config.h" +#include "StatsPublish.h" +#include <stdexcept> +#include <sstream> +#include <cstring> +#include <cerrno> +#include <cassert> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +using namespace std; + +StatsPublisher::StatsPublisher(const string& socket_path) : + m_socket_path(socket_path) +{ + // The client socket binds to a socket whose name depends on PID, and connects to + // `socket_path` + + m_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (m_sock == -1) { + throw runtime_error("Stats socket creation failed: " + string(strerror(errno))); + } + + struct sockaddr_un claddr; + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/odr-audioenc.%ld", (long) getpid()); + + int ret = bind(m_sock, (const struct sockaddr *) &claddr, sizeof(struct sockaddr_un)); + if (ret == -1) { + throw runtime_error("Stats socket bind failed " + string(strerror(errno))); + } +} + +void StatsPublisher::update_audio_levels(int16_t audiolevel_left, int16_t audiolevel_right) +{ + m_audio_left = audiolevel_left; + m_audio_right = audiolevel_right; +} + +void StatsPublisher::notify_underrun() +{ + m_num_underruns++; +} + +void StatsPublisher::notify_overrun() +{ + m_num_overruns++; +} + +void StatsPublisher::send_stats() +{ + // Manually build YAML, as it's quite easy. + stringstream yaml; + yaml << "---\n"; + yaml << "program: " << PACKAGE_NAME << "\n"; + yaml << "version: " << +#if defined(GITVERSION) + GITVERSION +#else + PACKAGE_VERSION +#endif + << "\n"; + yaml << "audiolevels: { left: " << m_audio_left << ", right: " << m_audio_right << "}\n"; + yaml << "driftcompensation: { underruns: " << m_num_underruns << ", overruns: " << m_num_overruns << "}\n"; + + const auto yamlstr = yaml.str(); + + struct sockaddr_un claddr; + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + snprintf(claddr.sun_path, sizeof(claddr.sun_path), "%s", m_socket_path.c_str()); + + int ret = sendto(m_sock, yamlstr.data(), yamlstr.size(), 0, + (struct sockaddr *) &claddr, sizeof(struct sockaddr_un)); + if (ret == -1) { + // This suppresses the -Wlogical-op warning + if (errno == EAGAIN +#if EAGAIN != EWOULDBLOCK + or errno == EWOULDBLOCK +#endif + or errno == ECONNREFUSED + or errno == ENOENT) { + if (m_destination_available) { + fprintf(stderr, "Stats destination not available at %s\n", m_socket_path.c_str()); + m_destination_available = false; + } + } + else { + fprintf(stderr, "Statistics send failed: %s\n", strerror(errno)); + } + } + else if (ret != (ssize_t)yamlstr.size()) { + fprintf(stderr, "Statistics send incorrect length: %d bytes of %zu transmitted\n", + ret, yamlstr.size()); + } + else if (not m_destination_available) { + fprintf(stderr, "Stats destination is now available at %s\n", m_socket_path.c_str()); + m_destination_available = true; + } + + m_audio_left = 0; + m_audio_right = 0; +} diff --git a/src/StatsPublish.h b/src/StatsPublish.h new file mode 100644 index 0000000..f593c7c --- /dev/null +++ b/src/StatsPublish.h @@ -0,0 +1,65 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2019 Matthias P. Braendli + * + * 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. + * ------------------------------------------------------------------- + */ + +#pragma once +#include <string> +#include <cstdint> +#include <cstddef> +#include <cstdio> + +/*! \file StatsPublish.h + * + * Collects and sends some stats to a UNIX DGRAM socket so that an external tool + * like ODR-EncoderManager can display it. + * + * Currently, only audio levels are collected. + * + * Output is formatted in YAML + */ +class StatsPublisher { + public: + StatsPublisher(const std::string& socket_path); + + /*! Update peak audio level information */ + void update_audio_levels(int16_t audiolevel_left, int16_t audiolevel_right); + + /*! Increments the underrun counter */ + void notify_underrun(); + + /*! Increments the overrun counter */ + void notify_overrun(); + + /*! Send the collected stats to the socket, doesn't block. If the socket is + * not connected, the data is lost. + * + * Clears the collected data. */ + void send_stats(); + + private: + std::string m_socket_path; + int m_sock = -1; + + int16_t m_audio_left = 0; + int16_t m_audio_right = 0; + + size_t m_num_underruns = 0; + size_t m_num_overruns = 0; + + bool m_destination_available = true; +}; + diff --git a/src/odr-sourcecompanion.cpp b/src/odr-sourcecompanion.cpp index df8feac..a69f705 100644 --- a/src/odr-sourcecompanion.cpp +++ b/src/odr-sourcecompanion.cpp @@ -28,6 +28,7 @@ #include "AVTInput.h" #include "AACDecoder.h" +#include "StatsPublish.h" #include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -97,6 +98,8 @@ void usage(const char* name) { " -P, --pad-fifo=FILENAME Set PAD data input fifo name" " (default: /tmp/pad.fifo).\n" " -l, --level Show peak audio level indication.\n" + " -S, --stats=SOCKET_NAME Connect to the specified UNIX Datagram socket and send statistics.\n" + " This allows external tools to collect audio and drift compensation stats.\n" "\n" "Only the tcp:// zeromq transport has been tested until now,\n" " but epgm:// and pgm:// are also accepted\n" @@ -120,6 +123,7 @@ int main(int argc, char *argv[]) std::vector<std::string> output_uris; AACDecoder decoder; + unique_ptr<StatsPublisher> stats_publisher; /* For MOT Slideshow and DLS insertion */ const char* pad_fifo = "/tmp/pad.fifo"; @@ -129,6 +133,9 @@ int main(int argc, char *argv[]) /* Whether to show the 'sox'-like measurement */ bool show_level = false; + /* If not empty, send stats over UNIX DGRAM socket */ + string send_stats_to = ""; + /* Data for ZMQ CURVE authentication */ char* keyfile = nullptr; char secretkey[CURVE_KEYLEN+1]; @@ -140,6 +147,7 @@ int main(int argc, char *argv[]) {"pad", required_argument, 0, 'p'}, {"pad-fifo", required_argument, 0, 'P'}, {"rate", required_argument, 0, 'r'}, + {"stats", required_argument, 0, 'S'}, {"secret-key", required_argument, 0, 'k'}, {"input-uri", required_argument, 0, 'I'}, {"control-uri", required_argument, 0, 6 }, @@ -219,6 +227,9 @@ int main(int argc, char *argv[]) case 'r': sample_rate = atoi(optarg); break; + case 'S': + send_stats_to = optarg; + break; case 'I': avt_input_uri = optarg; fprintf(stderr, "AVT Encoder Mode\n"); @@ -323,6 +334,21 @@ int main(int argc, char *argv[]) return 1; } + if (not send_stats_to.empty()) { + StatsPublisher *s = nullptr; + try { + s = new StatsPublisher(send_stats_to); + stats_publisher.reset(s); + } + catch (const runtime_error& e) { + fprintf(stderr, "Failed to initialise Stats Publisher: %s", e.what()); + if (s != nullptr) { + delete s; + } + return 1; + } + } + int outbuf_size; std::vector<uint8_t> zmqframebuf; std::vector<uint8_t> outbuf; @@ -414,6 +440,10 @@ int main(int argc, char *argv[]) peak_left = 0; peak_right = 0; } + + if (stats_publisher) { + stats_publisher->update_audio_levels(peak_left, peak_right); + } } read_bytes = numOutBytes; @@ -461,6 +491,10 @@ int main(int argc, char *argv[]) peak_right = 0; peak_left = 0; + + if (stats_publisher) { + stats_publisher->send_stats(); + } } } while (read_bytes > 0); |