diff options
| -rw-r--r-- | Makefile.am | 2 | ||||
| -rw-r--r-- | src/dls.cpp | 350 | ||||
| -rw-r--r-- | src/dls.h | 155 | ||||
| -rw-r--r-- | src/odr-padenc.cpp | 430 | 
4 files changed, 514 insertions, 423 deletions
| diff --git a/Makefile.am b/Makefile.am index 6e2a78c..031a485 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,8 @@ odr_padenc_SOURCES  = \  					  src/common.h \  					  src/pad_common.cpp \  					  src/pad_common.h \ +					  src/dls.cpp \ +					  src/dls.h \  					  src/charset.cpp \  					  src/charset.h \  					  src/crc.cpp \ 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; | 
