diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/PadInterface.cpp | 138 | ||||
-rw-r--r-- | src/PadInterface.h | 44 | ||||
-rw-r--r-- | src/odr-sourcecompanion.cpp | 58 |
4 files changed, 202 insertions, 39 deletions
diff --git a/Makefile.am b/Makefile.am index 6f925f6..1309ae0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,7 @@ odr_sourcecompanion_SOURCES = src/odr-sourcecompanion.cpp \ src/StatsPublish.h src/StatsPublish.cpp \ src/encryption.h src/encryption.c \ src/utils.h src/utils.c \ + src/PadInterface.h src/PadInterface.cpp \ lib/fec/char.h \ lib/fec/decode_rs_char.c \ lib/fec/decode_rs.h \ diff --git a/src/PadInterface.cpp b/src/PadInterface.cpp new file mode 100644 index 0000000..7e8a6ee --- /dev/null +++ b/src/PadInterface.cpp @@ -0,0 +1,138 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2020 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 "PadInterface.h" +#include <stdexcept> +#include <sstream> +#include <cstring> +#include <cerrno> +#include <cassert> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#define MESSAGE_REQUEST 1 +#define MESSAGE_PAD_DATA 2 + +using namespace std; + +void PadInterface::open(const std::string &pad_ident) +{ + m_pad_ident = pad_ident; + + m_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (m_sock == -1) { + throw runtime_error("PAD 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/%s.audioenc", m_pad_ident.c_str()); + if (unlink(claddr.sun_path) == -1 and errno != ENOENT) { + fprintf(stderr, "Unlinking of socket %s failed: %s\n", claddr.sun_path, strerror(errno)); + } + + int ret = bind(m_sock, (const struct sockaddr *) &claddr, sizeof(struct sockaddr_un)); + if (ret == -1) { + throw runtime_error("PAD socket bind failed " + string(strerror(errno))); + } +} + +vector<uint8_t> PadInterface::request(uint8_t padlen) +{ + if (m_pad_ident.empty()) { + throw logic_error("Uninitialised PadInterface::request() called"); + } + + // Sending requests allows the PadEnc to know both the padlen, but also + // will allow proper timing. + + uint8_t packet[2]; + + packet[0] = MESSAGE_REQUEST; // Message type, to allow future expansion + packet[1] = padlen; + + 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/%s.padenc", m_pad_ident.c_str()); + + ssize_t ret = sendto(m_sock, packet, sizeof(packet), 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_padenc_reachable) { + fprintf(stderr, "ODR-PadEnc at %s not reachable\n", claddr.sun_path); + m_padenc_reachable = false; + } + } + else { + fprintf(stderr, "PAD request send failed: %s\n", strerror(errno)); + } + } + else if (ret != sizeof(packet)) { + fprintf(stderr, "PAD request incorrect length sent: %zu bytes of %zu transmitted\n", + ret, sizeof(packet)); + } + else if (not m_padenc_reachable) { + fprintf(stderr, "ODR-PadEnc is now reachable at %s\n", claddr.sun_path); + m_padenc_reachable = true; + } + + vector<uint8_t> buffer(2048); + + while (true) { + ret = recvfrom(m_sock, buffer.data(), buffer.size(), 0, nullptr, nullptr); + + if (ret == -1) { + // This suppresses the -Wlogical-op warning +#if EAGAIN == EWOULDBLOCK + if (errno != EAGAIN) +#else + if (not (errno == EAGAIN or errno == EWOULDBLOCK)) +#endif + { + throw runtime_error(string("Can't receive data: ") + strerror(errno)); + } + + return {}; + } + else if (ret > 0) { + buffer.resize(ret); + + // We could check where the data comes from, but since we're using UNIX sockets + // the source is anyway local to the machine. + + if (buffer[0] == MESSAGE_PAD_DATA) { + vector<uint8_t> pad_data(buffer.size() - 1); + copy(buffer.begin() + 1, buffer.end(), pad_data.begin()); + return pad_data; + } + else { + continue; + } + } + } +} diff --git a/src/PadInterface.h b/src/PadInterface.h new file mode 100644 index 0000000..9787d06 --- /dev/null +++ b/src/PadInterface.h @@ -0,0 +1,44 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2020 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 <vector> +#include <cstdint> +#include <cstddef> +#include <cstdio> + +/*! \file PadInterface.h + * + * Handles communication with ODR-PadEnc using a socket + */ + +class PadInterface { + public: + /*! Create a new PAD data interface that binds to /tmp/pad_ident.audioenc and + * communicates with ODR-PadEnc at /tmp/pad_ident.padenc + */ + void open(const std::string &pad_ident); + + std::vector<uint8_t> request(uint8_t padlen); + + private: + std::string m_pad_ident; + int m_sock = -1; + bool m_padenc_reachable = true; +}; diff --git a/src/odr-sourcecompanion.cpp b/src/odr-sourcecompanion.cpp index 04561bf..7e57698 100644 --- a/src/odr-sourcecompanion.cpp +++ b/src/odr-sourcecompanion.cpp @@ -30,6 +30,7 @@ #include "Outputs.h" #include "AACDecoder.h" #include "StatsPublish.h" +#include "PadInterface.h" #include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -73,7 +74,7 @@ void usage(const char* name) { " For the AVT input:\n" " * The audio mode and bitrate will be sent to the encoder if option --control-uri\n" " and DAB+ specific options are set (-b -c -r --aaclc --sbr --ps)\n" - " * PAD Data can be send to the encoder with the options --pad-port --pad --pad-fifo\n" + " * PAD Data can be send to the encoder with the options --pad-port --pad --pad-socket\n" " -I, --input-uri=URI Input URI. (Supported: 'udp://...')\n" " --control-uri=URI Output control URI (Supported: 'udp://...')\n" " --timeout=ms Maximum frame waiting time, in milliseconds (def=2000)\n" @@ -97,14 +98,11 @@ void usage(const char* name) { " add a delay (in milliseconds) to the timestamps carried in EDI\n" " -k, --secret-key=FILE Enable ZMQ encryption with the given secret key.\n" " -p, --pad=BYTES Set PAD size in bytes.\n" - " -P, --pad-fifo=FILENAME Set PAD data input fifo name" - " (default: /tmp/pad.fifo).\n" + " -P, --pad-socket=IDENTIFIER Use the given identifier to communicate with ODR-PadEnc.\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" ); } @@ -140,8 +138,8 @@ int main(int argc, char *argv[]) unique_ptr<StatsPublisher> stats_publisher; /* For MOT Slideshow and DLS insertion */ - const char* pad_fifo = "/tmp/pad.fifo"; - int pad_fd = -1; + string pad_ident = ""; + PadInterface pad_intf; int padlen = 0; /* Whether to show the 'sox'-like measurement */ @@ -160,7 +158,7 @@ int main(int argc, char *argv[]) {"timestamp-delay", required_argument, 0, 'T'}, {"output", required_argument, 0, 'o'}, {"pad", required_argument, 0, 'p'}, - {"pad-fifo", required_argument, 0, 'P'}, + {"pad-socket", required_argument, 0, 'P'}, {"rate", required_argument, 0, 'r'}, {"stats", required_argument, 0, 'S'}, {"secret-key", required_argument, 0, 'k'}, @@ -261,7 +259,7 @@ int main(int argc, char *argv[]) padlen = stoi(optarg); break; case 'P': - pad_fifo = optarg; + pad_ident = optarg; break; case 'r': sample_rate = stoi(optarg); @@ -295,7 +293,7 @@ int main(int argc, char *argv[]) } } - if (padlen < 0) { + if (padlen < 0 or padlen > 255) { fprintf(stderr, "Invalid PAD length specified\n"); return 1; } @@ -361,24 +359,9 @@ int main(int argc, char *argv[]) edi_output.set_odr_version_tag(ss.str()); } - if (padlen != 0) { - int flags; - if (mkfifo(pad_fifo, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) != 0) { - if (errno != EEXIST) { - fprintf(stderr, "Can't create pad file: %d!\n", errno); - return 1; - } - } - pad_fd = open(pad_fifo, O_RDONLY | O_NONBLOCK); - if (pad_fd == -1) { - fprintf(stderr, "Can't open pad file!\n"); - return 1; - } - flags = fcntl(pad_fd, F_GETFL, 0); - if (fcntl(pad_fd, F_SETFL, flags | O_NONBLOCK)) { - fprintf(stderr, "Can't set non-blocking mode in pad file!\n"); - return 1; - } + if (padlen != 0 and not pad_ident.empty()) { + pad_intf.open(pad_ident); + fprintf(stderr, "PAD socket opened\n"); } AVTInput avtinput(avt_input_uri, avt_output_uri, avt_pad_port, avt_jitterBufferSize); @@ -425,8 +408,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "Warning: (outbuf_size mod 5) = %d\n", outbuf_size % 5); } - unsigned char pad_buf[padlen + 1]; - fprintf(stderr, "Starting encoding\n"); int retval = 0; @@ -450,21 +431,20 @@ int main(int argc, char *argv[]) while (!timedout and numOutBytes == 0) { // Fill the PAD Frame queue because multiple PAD frame requests // can come for each DAB+ Frames (up to 6), - if (padlen != 0 and pad_fd != -1) { + if (padlen != 0) { bool no_data = false; while (!no_data and !avtinput.padQueueFull()) { - const ssize_t pad_ret = read(pad_fd, pad_buf, padlen + 1); + vector<uint8_t> pad_data = pad_intf.request(padlen); - if ((pad_ret < 0 and errno == EAGAIN) or pad_ret == 0) { - no_data = true; + if (pad_data.empty()) { + /* no PAD available */ } - else if (pad_ret == padlen + 1) { - const int calculated_padlen = pad_buf[padlen]; - avtinput.pushPADFrame(pad_buf + (padlen - calculated_padlen), calculated_padlen); + else if (pad_data.size() == (size_t)padlen + 1) { + const size_t calculated_padlen = pad_data[padlen]; + avtinput.pushPADFrame(pad_data.data() + (padlen - calculated_padlen), calculated_padlen); } else { - // Some other error occurred during read. - fprintf(stderr, "Unable to read from PAD!\n"); + fprintf(stderr, "Incorrect PAD length received: %zu expected %d\n", pad_data.size(), padlen + 1); break; } } |