diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dls.cpp | 350 | ||||
-rw-r--r-- | src/dls.h | 155 | ||||
-rw-r--r-- | src/odr-padenc.cpp | 430 |
3 files changed, 512 insertions, 423 deletions
diff --git a/src/dls.cpp b/src/dls.cpp new file mode 100644 index 0000000..f41c9ff --- /dev/null +++ b/src/dls.cpp @@ -0,0 +1,350 @@ +/* + 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) 2015, 2016, 2017 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/>. +*/ +/*! + \file dls.cpp + \brief Dynamic Label (DL) related code + + \author Sergio Sagliocco <sergio.sagliocco@csp.it> + \author Matthias P. Braendli <matthias@mpb.li> + \author Stefan Pöschel <odr@basicmaster.de> +*/ + +#include "dls.h" + + +// --- DLSManager ----------------------------------------------------------------- +const size_t DLSManager::MAXDLS = 128; // chars +const size_t DLSManager::DLS_SEG_LEN_PREFIX = 2; +const size_t DLSManager::DLS_SEG_LEN_CHAR_MAX = 16; +const int DLSManager::DLS_REPETITION_WHILE_SLS = 50; +const std::string DLSManager::DL_PARAMS_OPEN = "##### parameters { #####"; +const std::string DLSManager::DL_PARAMS_CLOSE = "##### parameters } #####"; + + +DATA_GROUP* DLSManager::createDynamicLabelCommand(uint8_t command) { + DATA_GROUP* dg = new DATA_GROUP(2, 2, 3); + uint8_vector_t &seg_data = dg->data; + + // prefix: toggle? + first seg + last seg + command flag + command + seg_data[0] = + (dls_toggle ? (1 << 7) : 0) + + (1 << 6) + + (1 << 5) + + (1 << 4) + + command; + + // prefix: charset (though irrelevant here) + seg_data[1] = CHARSET_COMPLETE_EBU_LATIN; + + // CRC + dg->AppendCRC(); + + return dg; +} + +DATA_GROUP* DLSManager::createDynamicLabelPlus(const DL_STATE& dl_state) { + size_t tags_size = dl_state.dl_plus_tags.size(); + size_t len_dl_plus_cmd_field = 1 + 3 * tags_size; + DATA_GROUP* dg = new DATA_GROUP(2 + len_dl_plus_cmd_field, 2, 3); + uint8_vector_t &seg_data = dg->data; + + // prefix: toggle? + first seg + last seg + command flag + command + seg_data[0] = + (dls_toggle ? (1 << 7) : 0) + + (1 << 6) + + (1 << 5) + + (1 << 4) + + DLS_CMD_DL_PLUS; + + // prefix: link bit + length + seg_data[1] = + (dls_toggle ? (1 << 7) : 0) + + (len_dl_plus_cmd_field - 1); // -1 ! + + // DL Plus tags command: CId + IT + IR + NT + seg_data[2] = + (DL_PLUS_CMD_TAGS << 4) + + (dl_state.dl_plus_item_toggle ? (1 << 3) : 0) + + (dl_state.dl_plus_item_running ? (1 << 2) : 0) + + (tags_size - 1); // -1 ! + + for (size_t i = 0; i < tags_size; i++) { + // DL Plus tags command: Content Type + Start Marker + Length Marker + seg_data[3 + 3 * i] = dl_state.dl_plus_tags[i].content_type & 0x7F; + seg_data[4 + 3 * i] = dl_state.dl_plus_tags[i].start_marker & 0x7F; + seg_data[5 + 3 * i] = dl_state.dl_plus_tags[i].length_marker & 0x7F; + } + + // CRC + dg->AppendCRC(); + + return dg; +} + + +bool DLSManager::parse_dl_param_bool(const std::string &key, const std::string &value, bool &target) { + if (value == "0") { + target = 0; + return true; + } + if (value == "1") { + target = 1; + return true; + } + fprintf(stderr, "ODR-PadEnc Warning: DL parameter '%s' has unsupported value '%s' - ignored\n", key.c_str(), value.c_str()); + return false; +} + +bool DLSManager::parse_dl_param_int_dl_plus_tag(const std::string &key, const std::string &value, int &target) { + int value_int = atoi(value.c_str()); + if (value_int >= 0x00 && value_int <= 0x7F) { + target = value_int; + return true; + } + fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag parameter '%s' %d out of range - ignored\n", key.c_str(), value_int); + return false; +} + +void DLSManager::parse_dl_params(std::ifstream &dls_fstream, DL_STATE &dl_state) { + std::string line; + while (std::getline(dls_fstream, line)) { + // return on params close + if (line == DL_PARAMS_CLOSE) + return; + + // ignore empty lines and comments + if (line.empty() || line[0] == '#') + continue; + + // parse key/value pair + size_t separator_pos = line.find('='); + if (separator_pos == std::string::npos) { + fprintf(stderr, "ODR-PadEnc Warning: DL parameter line '%s' without separator - ignored\n", line.c_str()); + continue; + } + std::string key = line.substr(0, separator_pos); + std::string value = line.substr(separator_pos + 1); +#ifdef DEBUG + fprintf(stderr, "parse_dl_params: key: '%s', value: '%s'\n", key.c_str(), value.c_str()); +#endif + + if (key == "DL_PLUS") { + parse_dl_param_bool(key, value, dl_state.dl_plus_enabled); + continue; + } + if (key == "DL_PLUS_ITEM_TOGGLE") { + parse_dl_param_bool(key, value, dl_state.dl_plus_item_toggle); + continue; + } + if (key == "DL_PLUS_ITEM_RUNNING") { + parse_dl_param_bool(key, value, dl_state.dl_plus_item_running); + continue; + } + if (key == "DL_PLUS_TAG") { + if (dl_state.dl_plus_tags.size() == 4) { + fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag ignored, as already four tags present\n"); + continue; + } + + // split value + std::vector<std::string> params = split_string(value, ' '); + if (params.size() != 3) { + fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag value '%s' does not have three parts - ignored\n", value.c_str()); + continue; + } + + int content_type, start_marker, length_marker; + if (parse_dl_param_int_dl_plus_tag("content_type", params[0], content_type) & + parse_dl_param_int_dl_plus_tag("start_marker", params[1], start_marker) & + parse_dl_param_int_dl_plus_tag("length_marker", params[2], length_marker)) + dl_state.dl_plus_tags.push_back(DL_PLUS_TAG(content_type, start_marker, length_marker)); + continue; + } + + fprintf(stderr, "ODR-PadEnc Warning: DL parameter '%s' unknown - ignored\n", key.c_str()); + } + + fprintf(stderr, "ODR-PadEnc Warning: no param closing tag, so the DLS text will be empty\n"); +} + + +void DLSManager::writeDLS(PADPacketizer& pad_packetizer, const std::string& dls_file, uint8_t charset, bool raw_dls, bool remove_dls) { + std::ifstream dls_fstream(dls_file); + if (!dls_fstream.is_open()) { + std::cerr << "Could not open " << dls_file << std::endl; + return; + } + + DL_STATE dl_state; + + std::vector<std::string> dls_lines; + + std::string line; + // Read and convert lines one by one because the converter doesn't understand + // line endings + while (std::getline(dls_fstream, line)) { + if (line.empty()) + continue; + if (line == DL_PARAMS_OPEN) { + parse_dl_params(dls_fstream, dl_state); + } else { + if (not raw_dls && charset == CHARSET_UTF8) { + dls_lines.push_back(charset_converter.convert(line)); + } + else { + dls_lines.push_back(line); + } + // TODO handle the other charsets accordingly + } + } + + std::stringstream ss; + for (size_t i = 0; i < dls_lines.size(); i++) { + if (i != 0) { + if (charset == CHARSET_UCS2_BE) + ss << '\0' << '\n'; + else + ss << '\n'; + } + + // UCS-2 BE: if from file the first byte of \0\n remains, remove it + if (charset == CHARSET_UCS2_BE && dls_lines[i].size() % 2) { + dls_lines[i].resize(dls_lines[i].size() - 1); + } + + ss << dls_lines[i]; + } + + dl_state.dl_text = ss.str(); + if (dl_state.dl_text.size() > MAXDLS) + dl_state.dl_text.resize(MAXDLS); + + + // if DL Plus enabled, but no DL Plus tags were added, add the required DUMMY tag + if (dl_state.dl_plus_enabled && dl_state.dl_plus_tags.empty()) + dl_state.dl_plus_tags.push_back(DL_PLUS_TAG()); + + if (not raw_dls) + charset = CHARSET_COMPLETE_EBU_LATIN; + + + // toggle the toggle bit only on new DL state + bool dl_state_is_new = dl_state != dl_state_prev; + if (verbose) { + fprintf(stderr, "ODR-PadEnc writing %s DLS text \"%s\"\n", dl_state_is_new ? "new" : "old", dl_state.dl_text.c_str()); + if (dl_state.dl_plus_enabled) { + fprintf( + stderr, "ODR-PadEnc writing %s DL Plus tags (IT/IR: %d/%d): ", + dl_state_is_new ? "new" : "old", + dl_state.dl_plus_item_toggle ? 1 : 0, + dl_state.dl_plus_item_running ? 1 : 0); + for (dl_plus_tags_t::const_iterator it = dl_state.dl_plus_tags.begin(); it != dl_state.dl_plus_tags.end(); it++) { + if (it != dl_state.dl_plus_tags.begin()) + fprintf(stderr, ", "); + fprintf(stderr, "%d (S/L: %d/%d)", it->content_type, it->start_marker, it->length_marker); + } + fprintf(stderr, "\n"); + } + } + + DATA_GROUP *remove_label_dg = NULL; + if (dl_state_is_new) { + if (remove_dls) + remove_label_dg = createDynamicLabelCommand(DLS_CMD_REMOVE_LABEL); + + dls_toggle = !dls_toggle; // indicate changed text + + dl_state_prev = dl_state; + } + + prepend_dl_dgs(pad_packetizer, dl_state, charset); + if (remove_label_dg) + pad_packetizer.AddDG(remove_label_dg, true); +} + + +int DLSManager::dls_count(const std::string& text) { + size_t text_len = text.size(); + return text_len / DLS_SEG_LEN_CHAR_MAX + (text_len % DLS_SEG_LEN_CHAR_MAX ? 1 : 0); +} + + +DATA_GROUP* DLSManager::dls_get(const std::string& text, uint8_t charset, int seg_index) { + bool first_seg = seg_index == 0; + bool last_seg = seg_index == dls_count(text) - 1; + + int seg_text_offset = seg_index * DLS_SEG_LEN_CHAR_MAX; + const char *seg_text_start = text.c_str() + seg_text_offset; + size_t seg_text_len = std::min(text.size() - seg_text_offset, DLS_SEG_LEN_CHAR_MAX); + + DATA_GROUP* dg = new DATA_GROUP(DLS_SEG_LEN_PREFIX + seg_text_len, 2, 3); + uint8_vector_t &seg_data = dg->data; + + // prefix: toggle? + first seg? + last seg? + (seg len - 1) + seg_data[0] = + (dls_toggle ? (1 << 7) : 0) + + (first_seg ? (1 << 6) : 0) + + (last_seg ? (1 << 5) : 0) + + (seg_text_len - 1); + + // prefix: charset / seg index + seg_data[1] = (first_seg ? charset : seg_index) << 4; + + // character field + memcpy(&seg_data[DLS_SEG_LEN_PREFIX], seg_text_start, seg_text_len); + + // CRC + dg->AppendCRC(); + +#ifdef DEBUG + fprintf(stderr, "DL segment:"); + for (int i = 0; i < seg_data.size(); i++) + fprintf(stderr, " %02x", seg_data[i]); + fprintf(stderr, "\n"); +#endif + return dg; +} + + +void DLSManager::prepend_dl_dgs(PADPacketizer& pad_packetizer, const DL_STATE& dl_state, uint8_t charset) { + // process all DL segments + int seg_count = dls_count(dl_state.dl_text); + std::vector<DATA_GROUP*> segs; + for (int seg_index = 0; seg_index < seg_count; seg_index++) { +#ifdef DEBUG + fprintf(stderr, "Segment number %d\n", seg_index + 1); +#endif + segs.push_back(dls_get(dl_state.dl_text, charset, seg_index)); + } + + // if enabled, add DL Plus data group + if (dl_state.dl_plus_enabled) + segs.push_back(createDynamicLabelPlus(dl_state)); + + // prepend to packetizer + pad_packetizer.AddDGs(segs, true); + +#ifdef DEBUG + fprintf(stderr, "PAD length: %d\n", padlen); + fprintf(stderr, "DLS text: %s\n", text.c_str()); + fprintf(stderr, "Number of DL segments: %d\n", seg_count); +#endif +} diff --git a/src/dls.h b/src/dls.h new file mode 100644 index 0000000..f9e5c57 --- /dev/null +++ b/src/dls.h @@ -0,0 +1,155 @@ +/* + 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) 2015, 2016, 2017 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/>. +*/ +/*! + \file dls.h + \brief Dynamic Label (DL) related code + + \author Sergio Sagliocco <sergio.sagliocco@csp.it> + \author Matthias P. Braendli <matthias@mpb.li> + \author Stefan Pöschel <odr@basicmaster.de> +*/ + +#ifndef DLS_H_ +#define DLS_H_ + +#include <fstream> +#include <iostream> + +#include "pad_common.h" +#include "charset.h" + + +// Charsets from TS 101 756 +enum { + CHARSET_COMPLETE_EBU_LATIN = 0, //!< Complete EBU Latin based repertoire + CHARSET_EBU_LATIN_CY_GR = 1, //!< EBU Latin based common core, Cyrillic, Greek + CHARSET_EBU_LATIN_AR_HE_CY_GR = 2, //!< EBU Latin based core, Arabic, Hebrew, Cyrillic and Greek + CHARSET_ISO_LATIN_ALPHABET_2 = 3, //!< ISO Latin Alphabet No 2 + CHARSET_UCS2_BE = 6, //!< ISO/IEC 10646 using UCS-2 transformation format, big endian byte order + CHARSET_UTF8 = 15 //!< ISO Latin Alphabet No 2 +}; + +// DL/DL+ commands +enum { + DLS_CMD_REMOVE_LABEL = 0b0001, + DLS_CMD_DL_PLUS = 0b0010 +}; +enum { + DL_PLUS_CMD_TAGS = 0b0000 +}; + + +// --- DL_PLUS_TAG ----------------------------------------------------------------- +struct DL_PLUS_TAG { + int content_type; + int start_marker; + int length_marker; + + DL_PLUS_TAG() : + content_type(0), // = DUMMY + start_marker(0), + length_marker(0) + {} + + DL_PLUS_TAG(int content_type, int start_marker, int length_marker) : + content_type(content_type), + start_marker(start_marker), + length_marker(length_marker) + {} + + bool operator==(const DL_PLUS_TAG& other) const { + return + content_type == other.content_type && + start_marker == other.start_marker && + length_marker == other.length_marker; + } + bool operator!=(const DL_PLUS_TAG& other) const { + return !(*this == other); + } +}; + +typedef std::vector<DL_PLUS_TAG> dl_plus_tags_t; + + +// --- DL_STATE ----------------------------------------------------------------- +struct DL_STATE { + std::string dl_text; + + bool dl_plus_enabled; + bool dl_plus_item_toggle; + bool dl_plus_item_running; + dl_plus_tags_t dl_plus_tags; + + DL_STATE() : + dl_plus_enabled(false), + dl_plus_item_toggle(false), + dl_plus_item_running(false) + {} + + bool operator==(const DL_STATE& other) const { + if (dl_text != other.dl_text) + return false; + if (dl_plus_enabled != other.dl_plus_enabled) + return false; + if (dl_plus_enabled) { + if (dl_plus_item_toggle != other.dl_plus_item_toggle) + return false; + if (dl_plus_item_running != other.dl_plus_item_running) + return false; + if (dl_plus_tags != other.dl_plus_tags) + return false; + } + return true; + } + bool operator!=(const DL_STATE& other) const { + return !(*this == other); + } +}; + + +// --- DLSManager ----------------------------------------------------------------- +class DLSManager { +private: + DATA_GROUP* createDynamicLabelCommand(uint8_t command); + DATA_GROUP* createDynamicLabelPlus(const DL_STATE& dl_state); + bool parse_dl_param_bool(const std::string &key, const std::string &value, bool &target); + bool parse_dl_param_int_dl_plus_tag(const std::string &key, const std::string &value, int &target); + void parse_dl_params(std::ifstream &dls_fstream, DL_STATE &dl_state); + int dls_count(const std::string& text); + DATA_GROUP* dls_get(const std::string& text, uint8_t charset, int seg_index); + void prepend_dl_dgs(PADPacketizer& pad_packetizer, const DL_STATE& dl_state, uint8_t charset); + + CharsetConverter charset_converter; + bool dls_toggle; + DL_STATE dl_state_prev; +public: + static const size_t MAXDLS; + static const size_t DLS_SEG_LEN_PREFIX; + static const size_t DLS_SEG_LEN_CHAR_MAX; + static const int DLS_REPETITION_WHILE_SLS; + static const std::string DL_PARAMS_OPEN; + static const std::string DL_PARAMS_CLOSE; + + DLSManager() : dls_toggle(false) {} + void writeDLS(PADPacketizer& pad_packetizer, const std::string& dls_file, uint8_t charset, bool raw_dls, bool remove_dls); +}; + +#endif /* DLS_H_ */ diff --git a/src/odr-padenc.cpp b/src/odr-padenc.cpp index ccc18f1..c2a7c33 100644 --- a/src/odr-padenc.cpp +++ b/src/odr-padenc.cpp @@ -42,8 +42,8 @@ #include <dirent.h> #include <getopt.h> -#include "charset.h" #include "pad_common.h" +#include "dls.h" #if HAVE_MAGICKWAND # include <wand/magick_wand.h> @@ -56,7 +56,6 @@ #define XSTR(x) #x #define STR(x) XSTR(x) -static const size_t MAXDLS = 128; // chars static const size_t MAXSEGLEN = 8189; // Bytes (EN 301 234 v2.1.1, ch. 5.1.1) static const size_t MAXSLIDESIZE = 51200; // Bytes (TS 101 499 v3.1.1, ch. 9.1.2) @@ -64,15 +63,6 @@ static const int MAXSLIDEID = 9999; // Roll-over value for fidx static const size_t MAXHISTORYLEN = 50; // How many slides to keep in history static const int MINQUALITY = 40; // Do not allow the image compressor to go below JPEG quality 40 -// Charsets from TS 101 756 -enum { - CHARSET_COMPLETE_EBU_LATIN = 0, //!< Complete EBU Latin based repertoire - CHARSET_EBU_LATIN_CY_GR = 1, //!< EBU Latin based common core, Cyrillic, Greek - CHARSET_EBU_LATIN_AR_HE_CY_GR = 2, //!< EBU Latin based core, Arabic, Hebrew, Cyrillic and Greek - CHARSET_ISO_LATIN_ALPHABET_2 = 3, //!< ISO Latin Alphabet No 2 - CHARSET_UCS2_BE = 6, //!< ISO/IEC 10646 using UCS-2 transformation format, big endian byte order - CHARSET_UTF8 = 15 //!< ISO Latin Alphabet No 2 -}; struct MSCDG { // MSC Data Group Header (extension field not supported) @@ -212,10 +202,6 @@ void createMscDG(MSCDG* msc, unsigned short int dgtype, int *cindex, unsigned sh DATA_GROUP* packMscDG(MSCDG* msc); -struct DL_STATE; -void prepend_dl_dgs(PADPacketizer& pad_packetizer, const DL_STATE& dl_state, uint8_t charset); -void writeDLS(PADPacketizer& pad_packetizer, const std::string& dls_file, uint8_t charset, bool raw_dls, bool remove_dls); - // MOT Slideshow related static int cindex_header = 0; @@ -327,98 +313,6 @@ void MOTHeader::AddExtension(int param_id, const uint8_t* data_field, size_t dat -// DLS related -static const size_t DLS_SEG_LEN_PREFIX = 2; -static const size_t DLS_SEG_LEN_CHAR_MAX = 16; -static const int DLS_REPETITION_WHILE_SLS = 50; -enum { - DLS_CMD_REMOVE_LABEL = 0b0001, - DLS_CMD_DL_PLUS = 0b0010 -}; -static const int DL_PLUS_CMD_TAGS = 0b0000; - -#define DL_PARAMS_OPEN "##### parameters { #####" -#define DL_PARAMS_CLOSE "##### parameters } #####" - -static CharsetConverter charset_converter; - -struct DL_PLUS_TAG { - int content_type; - int start_marker; - int length_marker; - - DL_PLUS_TAG() : - content_type(0), // = DUMMY - start_marker(0), - length_marker(0) - {} - - DL_PLUS_TAG(int content_type, int start_marker, int length_marker) : - content_type(content_type), - start_marker(start_marker), - length_marker(length_marker) - {} - - bool operator==(const DL_PLUS_TAG& other) const { - return - content_type == other.content_type && - start_marker == other.start_marker && - length_marker == other.length_marker; - } - bool operator!=(const DL_PLUS_TAG& other) const { - return !(*this == other); - } -}; - -typedef std::vector<DL_PLUS_TAG> dl_plus_tags_t; - -struct DL_STATE { - std::string dl_text; - - bool dl_plus_enabled; - bool dl_plus_item_toggle; - bool dl_plus_item_running; - dl_plus_tags_t dl_plus_tags; - - DL_STATE() : - dl_plus_enabled(false), - dl_plus_item_toggle(false), - dl_plus_item_running(false) - {} - - bool operator==(const DL_STATE& other) const { - if (dl_text != other.dl_text) - return false; - if (dl_plus_enabled != other.dl_plus_enabled) - return false; - if (dl_plus_enabled) { - if (dl_plus_item_toggle != other.dl_plus_item_toggle) - return false; - if (dl_plus_item_running != other.dl_plus_item_running) - return false; - if (dl_plus_tags != other.dl_plus_tags) - return false; - } - return true; - } - bool operator!=(const DL_STATE& other) const { - return !(*this == other); - } -}; - - -static bool dls_toggle = false; -static DL_STATE dl_state_prev; - - - - - - - - - - void usage(char* name) { fprintf(stderr, "DAB PAD encoder %s for MOT Slideshow and DLS\n\n" @@ -626,6 +520,7 @@ int main(int argc, char *argv[]) #endif PADPacketizer pad_packetizer(padlen); + DLSManager dls_manager; std::list<slide_metadata_t> slides_to_transmit; History slides_history(MAXHISTORYLEN); @@ -674,7 +569,7 @@ int main(int argc, char *argv[]) // if ATM no slides, transmit at least DLS if (slides_to_transmit.empty()) { if (not dls_file.empty()) { - writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); + dls_manager.writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); } @@ -702,14 +597,14 @@ int main(int argc, char *argv[]) // while flushing, insert DLS after a certain PAD amout while (pad_packetizer.QueueFilled()) { if (not dls_file.empty()) - writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); + dls_manager.writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); - pad_packetizer.WriteAllPADs(output_fd, DLS_REPETITION_WHILE_SLS); + pad_packetizer.WriteAllPADs(output_fd, DLSManager::DLS_REPETITION_WHILE_SLS); } // after the slide, output a last DLS if (not dls_file.empty()) - writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); + dls_manager.writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); sleep(sleepdelay); @@ -719,7 +614,7 @@ int main(int argc, char *argv[]) } } else { // only DLS // Always retransmit DLS, we want it to be updated frequently - writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); + dls_manager.writeDLS(pad_packetizer, dls_file, charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); sleep(sleepdelay); @@ -1247,317 +1142,6 @@ DATA_GROUP* packMscDG(MSCDG* msc) } -DATA_GROUP* createDynamicLabelCommand(uint8_t command) { - DATA_GROUP* dg = new DATA_GROUP(2, 2, 3); - uint8_vector_t &seg_data = dg->data; - - // prefix: toggle? + first seg + last seg + command flag + command - seg_data[0] = - (dls_toggle ? (1 << 7) : 0) + - (1 << 6) + - (1 << 5) + - (1 << 4) + - command; - - // prefix: charset (though irrelevant here) - seg_data[1] = CHARSET_COMPLETE_EBU_LATIN; - - // CRC - dg->AppendCRC(); - - return dg; -} - -DATA_GROUP* createDynamicLabelPlus(const DL_STATE& dl_state) { - size_t tags_size = dl_state.dl_plus_tags.size(); - size_t len_dl_plus_cmd_field = 1 + 3 * tags_size; - DATA_GROUP* dg = new DATA_GROUP(2 + len_dl_plus_cmd_field, 2, 3); - uint8_vector_t &seg_data = dg->data; - - // prefix: toggle? + first seg + last seg + command flag + command - seg_data[0] = - (dls_toggle ? (1 << 7) : 0) + - (1 << 6) + - (1 << 5) + - (1 << 4) + - DLS_CMD_DL_PLUS; - - // prefix: link bit + length - seg_data[1] = - (dls_toggle ? (1 << 7) : 0) + - (len_dl_plus_cmd_field - 1); // -1 ! - - // DL Plus tags command: CId + IT + IR + NT - seg_data[2] = - (DL_PLUS_CMD_TAGS << 4) + - (dl_state.dl_plus_item_toggle ? (1 << 3) : 0) + - (dl_state.dl_plus_item_running ? (1 << 2) : 0) + - (tags_size - 1); // -1 ! - - for (size_t i = 0; i < tags_size; i++) { - // DL Plus tags command: Content Type + Start Marker + Length Marker - seg_data[3 + 3 * i] = dl_state.dl_plus_tags[i].content_type & 0x7F; - seg_data[4 + 3 * i] = dl_state.dl_plus_tags[i].start_marker & 0x7F; - seg_data[5 + 3 * i] = dl_state.dl_plus_tags[i].length_marker & 0x7F; - } - - // CRC - dg->AppendCRC(); - - return dg; -} - - -bool parse_dl_param_bool(const std::string &key, const std::string &value, bool &target) { - if (value == "0") { - target = 0; - return true; - } - if (value == "1") { - target = 1; - return true; - } - fprintf(stderr, "ODR-PadEnc Warning: DL parameter '%s' has unsupported value '%s' - ignored\n", key.c_str(), value.c_str()); - return false; -} - -bool parse_dl_param_int_dl_plus_tag(const std::string &key, const std::string &value, int &target) { - int value_int = atoi(value.c_str()); - if (value_int >= 0x00 && value_int <= 0x7F) { - target = value_int; - return true; - } - fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag parameter '%s' %d out of range - ignored\n", key.c_str(), value_int); - return false; -} - -void parse_dl_params(std::ifstream &dls_fstream, DL_STATE &dl_state) { - std::string line; - while (std::getline(dls_fstream, line)) { - // return on params close - if (line == DL_PARAMS_CLOSE) - return; - - // ignore empty lines and comments - if (line.empty() || line[0] == '#') - continue; - - // parse key/value pair - size_t separator_pos = line.find('='); - if (separator_pos == std::string::npos) { - fprintf(stderr, "ODR-PadEnc Warning: DL parameter line '%s' without separator - ignored\n", line.c_str()); - continue; - } - std::string key = line.substr(0, separator_pos); - std::string value = line.substr(separator_pos + 1); -#ifdef DEBUG - fprintf(stderr, "parse_dl_params: key: '%s', value: '%s'\n", key.c_str(), value.c_str()); -#endif - - if (key == "DL_PLUS") { - parse_dl_param_bool(key, value, dl_state.dl_plus_enabled); - continue; - } - if (key == "DL_PLUS_ITEM_TOGGLE") { - parse_dl_param_bool(key, value, dl_state.dl_plus_item_toggle); - continue; - } - if (key == "DL_PLUS_ITEM_RUNNING") { - parse_dl_param_bool(key, value, dl_state.dl_plus_item_running); - continue; - } - if (key == "DL_PLUS_TAG") { - if (dl_state.dl_plus_tags.size() == 4) { - fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag ignored, as already four tags present\n"); - continue; - } - - // split value - std::vector<std::string> params = split_string(value, ' '); - if (params.size() != 3) { - fprintf(stderr, "ODR-PadEnc Warning: DL Plus tag value '%s' does not have three parts - ignored\n", value.c_str()); - continue; - } - - int content_type, start_marker, length_marker; - if (parse_dl_param_int_dl_plus_tag("content_type", params[0], content_type) & - parse_dl_param_int_dl_plus_tag("start_marker", params[1], start_marker) & - parse_dl_param_int_dl_plus_tag("length_marker", params[2], length_marker)) - dl_state.dl_plus_tags.push_back(DL_PLUS_TAG(content_type, start_marker, length_marker)); - continue; - } - - fprintf(stderr, "ODR-PadEnc Warning: DL parameter '%s' unknown - ignored\n", key.c_str()); - } - - fprintf(stderr, "ODR-PadEnc Warning: no param closing tag, so the DLS text will be empty\n"); -} - - -void writeDLS(PADPacketizer& pad_packetizer, const std::string& dls_file, uint8_t charset, bool raw_dls, bool remove_dls) -{ - std::ifstream dls_fstream(dls_file); - if (!dls_fstream.is_open()) { - std::cerr << "Could not open " << dls_file << std::endl; - return; - } - - DL_STATE dl_state; - - std::vector<std::string> dls_lines; - - std::string line; - // Read and convert lines one by one because the converter doesn't understand - // line endings - while (std::getline(dls_fstream, line)) { - if (line.empty()) - continue; - if (line == DL_PARAMS_OPEN) { - parse_dl_params(dls_fstream, dl_state); - } else { - if (not raw_dls && charset == CHARSET_UTF8) { - dls_lines.push_back(charset_converter.convert(line)); - } - else { - dls_lines.push_back(line); - } - // TODO handle the other charsets accordingly - } - } - - std::stringstream ss; - for (size_t i = 0; i < dls_lines.size(); i++) { - if (i != 0) { - if (charset == CHARSET_UCS2_BE) - ss << '\0' << '\n'; - else - ss << '\n'; - } - - // UCS-2 BE: if from file the first byte of \0\n remains, remove it - if (charset == CHARSET_UCS2_BE && dls_lines[i].size() % 2) { - dls_lines[i].resize(dls_lines[i].size() - 1); - } - - ss << dls_lines[i]; - } - - dl_state.dl_text = ss.str(); - if (dl_state.dl_text.size() > MAXDLS) - dl_state.dl_text.resize(MAXDLS); - - - // if DL Plus enabled, but no DL Plus tags were added, add the required DUMMY tag - if (dl_state.dl_plus_enabled && dl_state.dl_plus_tags.empty()) - dl_state.dl_plus_tags.push_back(DL_PLUS_TAG()); - - if (not raw_dls) - charset = CHARSET_COMPLETE_EBU_LATIN; - - - // toggle the toggle bit only on new DL state - bool dl_state_is_new = dl_state != dl_state_prev; - if (verbose) { - fprintf(stderr, "ODR-PadEnc writing %s DLS text \"%s\"\n", dl_state_is_new ? "new" : "old", dl_state.dl_text.c_str()); - if (dl_state.dl_plus_enabled) { - fprintf( - stderr, "ODR-PadEnc writing %s DL Plus tags (IT/IR: %d/%d): ", - dl_state_is_new ? "new" : "old", - dl_state.dl_plus_item_toggle ? 1 : 0, - dl_state.dl_plus_item_running ? 1 : 0); - for (dl_plus_tags_t::const_iterator it = dl_state.dl_plus_tags.begin(); it != dl_state.dl_plus_tags.end(); it++) { - if (it != dl_state.dl_plus_tags.begin()) - fprintf(stderr, ", "); - fprintf(stderr, "%d (S/L: %d/%d)", it->content_type, it->start_marker, it->length_marker); - } - fprintf(stderr, "\n"); - } - } - - DATA_GROUP *remove_label_dg = NULL; - if (dl_state_is_new) { - if (remove_dls) - remove_label_dg = createDynamicLabelCommand(DLS_CMD_REMOVE_LABEL); - - dls_toggle = !dls_toggle; // indicate changed text - - dl_state_prev = dl_state; - } - - prepend_dl_dgs(pad_packetizer, dl_state, charset); - if (remove_label_dg) - pad_packetizer.AddDG(remove_label_dg, true); -} - - -int dls_count(const std::string& text) { - size_t text_len = text.size(); - return text_len / DLS_SEG_LEN_CHAR_MAX + (text_len % DLS_SEG_LEN_CHAR_MAX ? 1 : 0); -} - - -DATA_GROUP* dls_get(const std::string& text, uint8_t charset, int seg_index) { - bool first_seg = seg_index == 0; - bool last_seg = seg_index == dls_count(text) - 1; - - int seg_text_offset = seg_index * DLS_SEG_LEN_CHAR_MAX; - const char *seg_text_start = text.c_str() + seg_text_offset; - size_t seg_text_len = std::min(text.size() - seg_text_offset, DLS_SEG_LEN_CHAR_MAX); - - DATA_GROUP* dg = new DATA_GROUP(DLS_SEG_LEN_PREFIX + seg_text_len, 2, 3); - uint8_vector_t &seg_data = dg->data; - - // prefix: toggle? + first seg? + last seg? + (seg len - 1) - seg_data[0] = - (dls_toggle ? (1 << 7) : 0) + - (first_seg ? (1 << 6) : 0) + - (last_seg ? (1 << 5) : 0) + - (seg_text_len - 1); - - // prefix: charset / seg index - seg_data[1] = (first_seg ? charset : seg_index) << 4; - - // character field - memcpy(&seg_data[DLS_SEG_LEN_PREFIX], seg_text_start, seg_text_len); - - // CRC - dg->AppendCRC(); - -#ifdef DEBUG - fprintf(stderr, "DL segment:"); - for (int i = 0; i < seg_data.size(); i++) - fprintf(stderr, " %02x", seg_data[i]); - fprintf(stderr, "\n"); -#endif - return dg; -} - - -void prepend_dl_dgs(PADPacketizer& pad_packetizer, const DL_STATE& dl_state, uint8_t charset) { - // process all DL segments - int seg_count = dls_count(dl_state.dl_text); - std::vector<DATA_GROUP*> segs; - for (int seg_index = 0; seg_index < seg_count; seg_index++) { -#ifdef DEBUG - fprintf(stderr, "Segment number %d\n", seg_index + 1); -#endif - segs.push_back(dls_get(dl_state.dl_text, charset, seg_index)); - } - - // if enabled, add DL Plus data group - if (dl_state.dl_plus_enabled) - segs.push_back(createDynamicLabelPlus(dl_state)); - - // prepend to packetizer - pad_packetizer.AddDGs(segs, true); - -#ifdef DEBUG - fprintf(stderr, "PAD length: %d\n", padlen); - fprintf(stderr, "DLS text: %s\n", text.c_str()); - fprintf(stderr, "Number of DL segments: %d\n", seg_count); -#endif -} - int History::find(const fingerprint_t& fp) const { size_t i; |