aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2020-07-08 09:17:09 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2020-07-08 09:17:09 +0200
commite36865985abf9c4f31d34a9542c790f5baf8fe60 (patch)
tree8f35e10213ac643c30bda20275b5417dc1fedacd
parent11767aaaf3b60791c7c29c6cfc55d676fd4cdf28 (diff)
downloadODR-PadEnc-e36865985abf9c4f31d34a9542c790f5baf8fe60.tar.gz
ODR-PadEnc-e36865985abf9c4f31d34a9542c790f5baf8fe60.tar.bz2
ODR-PadEnc-e36865985abf9c4f31d34a9542c790f5baf8fe60.zip
Replace PAD fifo by UNIX socket
-rw-r--r--Makefile.am2
-rw-r--r--src/odr-padenc.cpp242
-rw-r--r--src/odr-padenc.h102
-rw-r--r--src/pad_common.cpp43
-rw-r--r--src/pad_common.h2
-rw-r--r--src/pad_interface.cpp144
-rw-r--r--src/pad_interface.h53
7 files changed, 319 insertions, 269 deletions
diff --git a/Makefile.am b/Makefile.am
index e61b436..8b31205 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,8 @@ odr_padenc_LDFLAGS = -pie -z now
odr_padenc_SOURCES = \
src/odr-padenc.cpp \
src/odr-padenc.h \
+ src/pad_interface.cpp \
+ src/pad_interface.h \
src/common.cpp \
src/common.h \
src/pad_common.cpp \
diff --git a/src/odr-padenc.cpp b/src/odr-padenc.cpp
index 0a7629a..41a1ea1 100644
--- a/src/odr-padenc.cpp
+++ b/src/odr-padenc.cpp
@@ -1,7 +1,7 @@
/*
Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)
- Copyright (C) 2014, 2015 Matthias P. Braendli (http://opendigitalradio.org)
+ Copyright (C) 2014-2020 Matthias P. Braendli (http://opendigitalradio.org)
Copyright (C) 2015-2019 Stefan Pöschel (http://opendigitalradio.org)
@@ -28,14 +28,13 @@
*/
#include "odr-padenc.h"
+#include <memory>
-
-static PadEncoder *pad_encoder = NULL;
+std::atomic<bool> do_exit;
static void break_handler(int) {
fprintf(stderr, "...ODR-PadEnc exits...\n");
- if(pad_encoder)
- pad_encoder->DoExit();
+ do_exit.store(true);
}
static void header() {
@@ -61,13 +60,9 @@ static void usage(const char* name) {
" been encoded.\n"
" -s, --sleep=DUR Wait DUR seconds between each slide\n"
" Default: %d\n"
- " -o, --output=FILENAME FIFO to write PAD data into.\n"
- " Default: %s\n"
+ " -o, --output=IDENTIFIER Socket to communicate with audio encoder\n"
" -t, --dls=FILENAME FIFO or file to read DLS text from.\n"
- " If specified more than once, use next file after slide switch (for uniform PAD encoder, -l is used instead).\n"
- " -p, --pad=LENGTH Set the PAD length in bytes.\n"
- " Possible values: %s\n"
- " Default: %zu\n"
+ " If specified more than once, use next file after -l delay.\n"
" -c, --charset=ID ID of the character set encoding used for DLS text input.\n"
" ID = 0: Complete EBU Latin based repertoire\n"
" ID = 6: ISO/IEC 10646 using UCS-2 BE\n"
@@ -85,24 +80,21 @@ static void usage(const char* name) {
" -v, --verbose Print more information to the console (may be used more than once)\n"
"\n"
"Parameters for uniform PAD encoder only:\n"
- " -f, --frame-dur=DUR Enable the uniform PAD encoder and set the duration of one frame/AU in milliseconds.\n"
" -l, --label=DUR Wait DUR seconds between each label (if more than one file used)\n"
" Default: %d\n"
" -L, --label-ins=DUR Insert label every DUR milliseconds\n"
" Default: %d\n"
- " -i, --init-burst=COUNT Sets a PAD burst amount to initially fill the output FIFO\n"
- " Default: %d\n"
" -X, --xpad-interval=COUNT Output X-PAD every COUNT frames/AUs (otherwise: only F-PAD)\n"
- " Default: %d\n",
+ " Default: %d\n"
+ "\n"
+ "The PAD length is configured on the audio encoder and communicated over the socket to ODR-PadEnc\n"
+ "Allowed PAD lengths are: %s\n",
options_default.slide_interval,
- options_default.output,
- PADPacketizer::ALLOWED_PADLEN.c_str(),
- options_default.padlen,
options_default.max_slide_size,
options_default.label_interval,
options_default.label_insertion,
- options_default.init_burst,
- options_default.xpad_interval
+ options_default.xpad_interval,
+ PADPacketizer::ALLOWED_PADLEN.c_str()
);
}
@@ -133,22 +125,19 @@ int main(int argc, char *argv[]) {
{"output", required_argument, 0, 'o'},
{"dls", required_argument, 0, 't'},
{"item-state", required_argument, 0, 'I'},
- {"pad", required_argument, 0, 'p'},
{"sleep", required_argument, 0, 's'},
{"max-slide-size", required_argument, 0, 'm'},
{"raw-slides", no_argument, 0, 'R'},
{"help", no_argument, 0, 'h'},
- {"frame-dur", required_argument, 0, 'f'},
{"label", required_argument, 0, 'l'},
{"label-ins", required_argument, 0, 'L'},
- {"init-burst", required_argument, 0, 'i'},
{"xpad-interval", required_argument, 0, 'X'},
{"verbose", no_argument, 0, 'v'},
{0,0,0,0},
};
int ch;
- while((ch = getopt_long(argc, argv, "eChRrc:d:o:p:s:t:I:f:l:L:i:X:vm:", longopts, NULL)) != -1) {
+ while((ch = getopt_long(argc, argv, "eChRrc:d:o:s:t:I:l:L:X:vm:", longopts, NULL)) != -1) {
switch (ch) {
case 'c':
options.dl_params.charset = (DABCharset) atoi(optarg);
@@ -166,7 +155,7 @@ int main(int argc, char *argv[]) {
options.erase_after_tx = true;
break;
case 'o':
- options.output = optarg;
+ options.socket_ident = optarg;
break;
case 's':
options.slide_interval = atoi(optarg);
@@ -177,27 +166,18 @@ int main(int argc, char *argv[]) {
case 'I':
options.item_state_file = optarg;
break;
- case 'p':
- options.padlen = atoi(optarg);
- break;
case 'm':
options.max_slide_size = atoi(optarg);
break;
case 'R':
options.raw_slides = true;
break;
- case 'f':
- options.frame_dur = atoi(optarg);
- break;
case 'l':
options.label_interval = atoi(optarg);
break;
case 'L':
options.label_insertion = atoi(optarg);
break;
- case 'i':
- options.init_burst = atoi(optarg);
- break;
case 'X':
options.xpad_interval = atoi(optarg);
break;
@@ -211,11 +191,6 @@ int main(int argc, char *argv[]) {
}
}
- if (!PADPacketizer::CheckPADLen(options.padlen)) {
- fprintf(stderr, "ODR-PadEnc Error: PAD length %zu invalid: Possible values: %s\n",
- options.padlen, PADPacketizer::ALLOWED_PADLEN.c_str());
- return 2;
- }
if (options.max_slide_size > SLSEncoder::MAXSLIDESIZE_SIMPLE) {
fprintf(stderr, "ODR-PadEnc Error: max slide size %zu exceeds Simple Profile limit %zu\n",
options.max_slide_size, SLSEncoder::MAXSLIDESIZE_SIMPLE);
@@ -223,16 +198,16 @@ int main(int argc, char *argv[]) {
}
if (options.sls_dir && not options.dls_files.empty()) {
- fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' and DLS from %s to '%s' (PAD length: %zu)\n",
- options.sls_dir, list_dls_files(options.dls_files).c_str(), options.output, options.padlen);
+ fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' and DLS from %s to '%s'\n",
+ options.sls_dir, list_dls_files(options.dls_files).c_str(), options.socket_ident.c_str());
}
else if (options.sls_dir) {
- fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' to '%s' (PAD length: %zu). No DLS.\n",
- options.sls_dir, options.output, options.padlen);
+ fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' to '%s'. No DLS.\n",
+ options.sls_dir, options.socket_ident.c_str());
}
else if (not options.dls_files.empty()) {
- fprintf(stderr, "ODR-PadEnc encoding DLS from %s to '%s' (PAD length: %zu). No Slideshow.\n",
- list_dls_files(options.dls_files).c_str(), options.output, options.padlen);
+ fprintf(stderr, "ODR-PadEnc encoding DLS from %s to '%s'. No Slideshow.\n",
+ list_dls_files(options.dls_files).c_str(), options.socket_ident.c_str());
}
else {
fprintf(stderr, "ODR-PadEnc Error: Neither DLS nor Slideshow to encode !\n");
@@ -294,41 +269,6 @@ int main(int argc, char *argv[]) {
return 1;
}
-
- // invoke selected encoder
- if (options.frame_dur) {
- fprintf(stderr, "ODR-PadEnc using uniform PAD encoder\n");
- pad_encoder = new UniformPadEncoder(options);
- } else {
- fprintf(stderr, "ODR-PadEnc using burst PAD encoder\n");
- pad_encoder = new BurstPadEncoder(options);
- }
- int result = pad_encoder->Main();
- delete pad_encoder;
-
- return result;
-}
-
-
-// --- PadEncoder -----------------------------------------------------------------
-int PadEncoder::Main() {
- output_fd = open(options.output, O_WRONLY);
- if (output_fd == -1) {
- perror("ODR-PadEnc Error: failed to open output");
- return 3;
- }
-
- // check for FIFO
- struct stat fifo_stat;
- if (fstat(output_fd, &fifo_stat)) {
- perror("ODR-PadEnc Error: could not retrieve output file stat");
- return 1;
- }
- if ((fifo_stat.st_mode & S_IFMT) != S_IFIFO) {
- fprintf(stderr, "ODR-PadEnc Error: the output file must be a FIFO!\n");
- return 3;
- }
-
#if HAVE_MAGICKWAND
MagickWandGenesis();
if (verbose)
@@ -349,23 +289,47 @@ int PadEncoder::Main() {
return 1;
}
- // invoke actual encoder
int result = 0;
- while (!do_exit) {
- result = Encode();
- // abort on error
- if (result)
- break;
+ PadInterface intf;
+ try {
+ intf.open(options.socket_ident);
- // sleep until next run
- std::this_thread::sleep_until(run_timeline);
- }
+ uint8_t previous_padlen = 0;
- // cleanup
- if (close(output_fd)) {
- perror("ODR-PadEnc Error: failed to close output");
- return 1;
+ std::shared_ptr<PadEncoder> pad_encoder;
+
+ while (!do_exit) {
+ options.padlen = intf.receive_request();
+
+ if (previous_padlen != options.padlen) {
+ previous_padlen = options.padlen;
+
+ if (options.padlen == 0) {
+ /* ignore */
+ }
+ else if (!PADPacketizer::CheckPADLen(options.padlen)) {
+ fprintf(stderr, "ODR-PadEnc Error: PAD length %d invalid: Possible values: %s\n",
+ options.padlen, PADPacketizer::ALLOWED_PADLEN.c_str());
+ result = 2;
+ break;
+ }
+ else {
+ fprintf(stderr, "ODR-PadEnc Reinitialise PAD length to %d\n", options.padlen);
+ pad_encoder = std::make_shared<PadEncoder>(options);
+ }
+ }
+
+ if (options.padlen > 0) {
+ result = pad_encoder->Encode(intf);
+ if (result > 0) {
+ break;
+ }
+ }
+ }
+ }
+ catch (const std::runtime_error& e) {
+ fprintf(stderr, "ODR-PadEnc failure: %s\n", e.what());
}
#if HAVE_MAGICKWAND
@@ -375,6 +339,28 @@ int PadEncoder::Main() {
return result;
}
+
+// --- PadEncoder -----------------------------------------------------------------
+PadEncoder::PadEncoder(PadEncoderOptions options) :
+ options(options),
+ pad_packetizer(PADPacketizer(options.padlen)),
+ dls_encoder(DLSEncoder(&pad_packetizer)),
+ sls_encoder(SLSEncoder(&pad_packetizer)),
+ slides_success(false),
+ curr_dls_file(0)
+{
+ // PAD related timelines
+ next_slide = next_label = next_label_insertion = steady_clock::now();
+
+ // if multiple DLS files, ensure that initial increment leads to first one
+ if (options.dls_files.size() > 1) {
+ curr_dls_file = -1;
+ }
+
+ xpad_interval_counter = 0;
+}
+
+
int PadEncoder::CheckRereadFile(const std::string& type, const std::string& path) {
struct stat path_stat;
if (stat(path.c_str(), &path_stat)) {
@@ -460,69 +446,9 @@ int PadEncoder::EncodeLabel(bool skip_if_already_queued) {
}
-// --- BurstPadEncoder -----------------------------------------------------------------
-const int BurstPadEncoder::DLS_REPETITION_WHILE_SLS = 50; // PADs
-
-int BurstPadEncoder::Encode() {
- int result = 0;
-
- // encode SLS
- if (options.SLSEnabled()) {
- result = EncodeSlide(false);
- if (result)
- return result;
-
- // while flushing, insert DLS (if present) after a certain PAD amout
- while (pad_packetizer.QueueFilled()) {
- if (options.DLSEnabled()) {
- result = EncodeLabel(false);
- if (result)
- return result;
- }
-
- pad_packetizer.WriteAllPADs(output_fd, DLS_REPETITION_WHILE_SLS);
- }
- }
-
- // encode (a last) DLS (if present)
- if (options.DLSEnabled()) {
- result = EncodeLabel(false);
- if (result)
- return result;
-
- // switch to next DLS file
- curr_dls_file = (curr_dls_file + 1) % options.dls_files.size();
- }
-
- // flush all remaining PADs
- pad_packetizer.WriteAllPADs(output_fd);
-
- // schedule next run at next slide interval
- run_timeline += std::chrono::seconds(options.slide_interval);
-
- return 0;
-}
-
-
-// --- UniformPadEncoder -----------------------------------------------------------------
-UniformPadEncoder::UniformPadEncoder(PadEncoderOptions options) : PadEncoder(options) {
- // PAD related timelines
- pad_timeline = steady_clock::now();
- next_slide = pad_timeline;
- next_label = pad_timeline;
- next_label_insertion = pad_timeline;
-
- // consider initial burst
- run_timeline -= std::chrono::milliseconds(options.init_burst * options.frame_dur);
-
- // if multiple DLS files, ensure that initial increment leads to first one
- if (options.dls_files.size() > 1)
- curr_dls_file = -1;
-
- xpad_interval_counter = 0;
-}
+int PadEncoder::Encode(PadInterface& intf) {
+ steady_clock::time_point pad_timeline = std::chrono::steady_clock::now();
-int UniformPadEncoder::Encode() {
int result = 0;
// handle SLS
@@ -580,11 +506,9 @@ int UniformPadEncoder::Encode() {
return result;
// flush one PAD (considering X-PAD output interval)
- pad_packetizer.WriteAllPADs(output_fd, 1, true, xpad_interval_counter == 0);
- pad_timeline += std::chrono::milliseconds(options.frame_dur);
+ auto pad = pad_packetizer.GetNextPAD(xpad_interval_counter == 0);
- // schedule next run at next frame/AU
- run_timeline += std::chrono::milliseconds(options.frame_dur);
+ intf.send_pad_data(pad.data(), pad.size());
// update X-PAD output interval counter
xpad_interval_counter = (xpad_interval_counter + 1) % options.xpad_interval;
diff --git a/src/odr-padenc.h b/src/odr-padenc.h
index 9ecf4a4..85dd11b 100644
--- a/src/odr-padenc.h
+++ b/src/odr-padenc.h
@@ -1,7 +1,7 @@
/*
Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)
- Copyright (C) 2014, 2015 Matthias P. Braendli (http://opendigitalradio.org)
+ Copyright (C) 2014-2020 Matthias P. Braendli (http://opendigitalradio.org)
Copyright (C) 2015-2019 Stefan Pöschel (http://opendigitalradio.org)
@@ -41,6 +41,7 @@
#include <getopt.h>
#include <unistd.h>
+#include "pad_interface.h"
#include "pad_common.h"
#include "dls.h"
#include "sls.h"
@@ -50,41 +51,23 @@ using std::chrono::steady_clock;
// --- PadEncoderOptions -----------------------------------------------------------------
struct PadEncoderOptions {
- size_t padlen;
- bool erase_after_tx;
- int slide_interval;
- int frame_dur; // uniform PAD encoder only
- int label_interval; // uniform PAD encoder only
- int label_insertion; // uniform PAD encoder only
- int init_burst; // uniform PAD encoder only
- int xpad_interval; // uniform PAD encoder only
- size_t max_slide_size;
- bool raw_slides;
+ uint8_t padlen = 0;
+ bool erase_after_tx = false;
+ int slide_interval = 10;
+ int label_interval = 12; // uniform PAD encoder only
+ int label_insertion = 1200; // uniform PAD encoder only
+ int xpad_interval = 1; // uniform PAD encoder only
+ size_t max_slide_size = SLSEncoder::MAXSLIDESIZE_SIMPLE;
+ bool raw_slides = false;
DL_PARAMS dl_params;
- const char* sls_dir;
- const char* output;
+ const char *sls_dir = nullptr;
+ std::string socket_ident;
std::vector<std::string> dls_files;
- const char* item_state_file;
-
- PadEncoderOptions() :
- padlen(58),
- erase_after_tx(false),
- slide_interval(10),
- frame_dur(0),
- label_interval(12),
- label_insertion(1200),
- init_burst(12),
- xpad_interval(1),
- max_slide_size(SLSEncoder::MAXSLIDESIZE_SIMPLE),
- raw_slides(false),
- sls_dir(NULL),
- output("/tmp/pad.fifo"),
- item_state_file(NULL)
- {}
-
- bool DLSEnabled() {return !dls_files.empty();}
- bool SLSEnabled() {return sls_dir;}
+ const char *item_state_file = nullptr;
+
+ bool DLSEnabled() const { return !dls_files.empty(); }
+ bool SLSEnabled() const { return sls_dir; }
};
@@ -98,56 +81,19 @@ protected:
SlideStore slides;
bool slides_success;
int curr_dls_file;
- int output_fd;
- steady_clock::time_point run_timeline;
-
- std::atomic<bool> do_exit;
-
- PadEncoder(PadEncoderOptions options) :
- options(options),
- pad_packetizer(PADPacketizer(options.padlen)),
- dls_encoder(DLSEncoder(&pad_packetizer)),
- sls_encoder(SLSEncoder(&pad_packetizer)),
- slides_success(false),
- curr_dls_file(0),
- output_fd(-1),
- run_timeline(steady_clock::now()),
- do_exit(false)
- {}
-
- virtual int Encode() = 0;
+ steady_clock::time_point next_slide;
+ steady_clock::time_point next_label;
+ steady_clock::time_point next_label_insertion;
+ size_t xpad_interval_counter;
+
int EncodeSlide(bool skip_if_already_queued);
int EncodeLabel(bool skip_if_already_queued);
static int CheckRereadFile(const std::string& type, const std::string& path);
+
public:
+ PadEncoder(PadEncoderOptions options);
virtual ~PadEncoder() {}
- int Main();
- void DoExit() {do_exit = true;}
-};
-
-
-// --- BurstPadEncoder -----------------------------------------------------------------
-class BurstPadEncoder : public PadEncoder {
-private:
- static const int DLS_REPETITION_WHILE_SLS;
-
- int Encode();
-public:
- BurstPadEncoder(PadEncoderOptions options) : PadEncoder(options) {}
+ int Encode(PadInterface& intf);
};
-
-// --- UniformPadEncoder -----------------------------------------------------------------
-class UniformPadEncoder : public PadEncoder {
-private:
- steady_clock::time_point pad_timeline;
- steady_clock::time_point next_slide;
- steady_clock::time_point next_label;
- steady_clock::time_point next_label_insertion;
- size_t xpad_interval_counter;
-
- int Encode();
-public:
- UniformPadEncoder(PadEncoderOptions options);
-};
diff --git a/src/pad_common.cpp b/src/pad_common.cpp
index 0c3f0b3..8ee2cd8 100644
--- a/src/pad_common.cpp
+++ b/src/pad_common.cpp
@@ -139,40 +139,21 @@ pad_t* PADPacketizer::GetPAD() {
return FlushPAD();
}
-
-void PADPacketizer::WriteAllPADs(int output_fd, int limit, bool output_sole_fpad, bool output_xpad) {
- size_t error_count = 0;
- size_t error_bytes = 0;
-
- // output a limited amount of PADs (-1 = no limit)
- for (int i = 0; i != limit; i++) {
- pad_t* pad = output_xpad ? GetPAD() : FlushPAD();
-
- // if only F-PAD present, abort (if desired)
- if (pad->back() == FPAD_LEN && !output_sole_fpad) {
- delete pad;
- break;
- }
-
- if (verbose >= 2) {
- fprintf(stderr, "ODR-PadEnc writing PAD (%zu bytes):", pad->size());
- for (size_t j = 0; j < pad->size(); j++) {
- const char sep = (j == (pad->size() - 1) || j == (pad->size() - 1 - FPAD_LEN)) ? '|' : ' ';
- fprintf(stderr, "%c%02X", sep , (*pad)[j]);
- }
- fprintf(stderr, "\n");
- }
-
- if (write(output_fd, &(*pad)[0], pad->size()) != (signed) pad->size()) {
- error_count++;
- error_bytes += pad->size();
+std::vector<uint8_t> PADPacketizer::GetNextPAD(bool output_xpad) {
+ pad_t* pad = output_xpad ? GetPAD() : FlushPAD();
+
+ if (verbose >= 2) {
+ fprintf(stderr, "ODR-PadEnc writing PAD (%zu bytes):", pad->size());
+ for (size_t j = 0; j < pad->size(); j++) {
+ const char sep = (j == (pad->size() - 1) || j == (pad->size() - 1 - FPAD_LEN)) ? '|' : ' ';
+ fprintf(stderr, "%c%02X", sep , (*pad)[j]);
}
-
- delete pad;
+ fprintf(stderr, "\n");
}
- if (error_count)
- fprintf(stderr, "ODR-PadEnc Error: Could not write %zu PAD(s) with %zu Bytes\n", error_count, error_bytes);
+ std::vector<uint8_t> p = *pad;
+ delete pad;
+ return p;
}
diff --git a/src/pad_common.h b/src/pad_common.h
index 7568d8c..d0fec13 100644
--- a/src/pad_common.h
+++ b/src/pad_common.h
@@ -123,7 +123,7 @@ public:
bool QueueFilled();
bool QueueContainsDG(int apptype_start);
- void WriteAllPADs(int output_fd, int limit = -1, bool output_sole_fpad = false, bool output_xpad = true);
+ std::vector<uint8_t> GetNextPAD(bool output_xpad);
static DATA_GROUP* CreateDataGroupLengthIndicator(size_t len);
static bool CheckPADLen(size_t len);
diff --git a/src/pad_interface.cpp b/src/pad_interface.cpp
new file mode 100644
index 0000000..3db5308
--- /dev/null
+++ b/src/pad_interface.cpp
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)
+
+ Copyright (C) 2014-2020 Matthias P. Braendli (http://opendigitalradio.org)
+
+ Copyright (C) 2015-2019 Stefan Pöschel (http://opendigitalradio.org)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+#include "pad_interface.h"
+#include <stdexcept>
+#include <sstream>
+#include <cstring>
+#include <cerrno>
+#include <cassert>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <poll.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, 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.padenc", 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)));
+ }
+}
+
+uint8_t PadInterface::receive_request()
+{
+ if (m_pad_ident.empty()) {
+ throw logic_error("Uninitialised PadInterface::request() called");
+ }
+
+ vector<uint8_t> buffer(4);
+
+ while (true) {
+ struct pollfd fds[1];
+ fds[0].fd = m_sock;
+ fds[0].events = POLLIN;
+ int timeout_ms = 240;
+
+ int retval = poll(fds, 1, timeout_ms);
+
+ if (retval == -1) {
+ std::string errstr(strerror(errno));
+ throw std::runtime_error("PAD socket poll error: " + errstr);
+ }
+ else if (retval > 0) {
+ ssize_t ret = recvfrom(m_sock, buffer.data(), buffer.size(), 0, nullptr, nullptr);
+
+ if (ret == -1) {
+ throw runtime_error(string("Can't receive data: ") + strerror(errno));
+ }
+ else {
+ 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_REQUEST) {
+ uint8_t padlen = buffer[1];
+ return padlen;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+void PadInterface::send_pad_data(const uint8_t *data, size_t len)
+{
+ 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());
+
+ vector<uint8_t> message(len + 1);
+ message[0] = MESSAGE_PAD_DATA;
+ copy(data, data + len, message.begin() + 1);
+
+ ssize_t ret = sendto(m_sock, message.data(), message.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_audioenc_reachable) {
+ fprintf(stderr, "ODR-PadEnc at %s not reachable\n", claddr.sun_path);
+ m_audioenc_reachable = false;
+ }
+ }
+ else {
+ fprintf(stderr, "PAD send failed: %s\n", strerror(errno));
+ }
+ }
+ else if ((size_t)ret != message.size()) {
+ fprintf(stderr, "PAD incorrect length sent: %zu bytes of %zu transmitted\n", ret, len);
+ }
+ else if (not m_audioenc_reachable) {
+ fprintf(stderr, "Audio encoder is now reachable at %s\n", claddr.sun_path);
+ m_audioenc_reachable = true;
+ }
+}
diff --git a/src/pad_interface.h b/src/pad_interface.h
new file mode 100644
index 0000000..f0307bd
--- /dev/null
+++ b/src/pad_interface.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)
+
+ Copyright (C) 2014-2020 Matthias P. Braendli (http://opendigitalradio.org)
+
+ Copyright (C) 2015-2019 Stefan Pöschel (http://opendigitalradio.org)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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.padenc and
+ * communicates with ODR-AudioEnc at /tmp/pad_ident.audioenc
+ */
+ void open(const std::string &pad_ident);
+
+ /*! Receives a request from the audio encoder
+ *
+ * \return the desired padlen
+ */
+ uint8_t receive_request();
+
+ void send_pad_data(const uint8_t *data, size_t len);
+
+ private:
+ std::string m_pad_ident;
+ int m_sock = -1;
+ bool m_audioenc_reachable = true;
+};