diff options
| -rw-r--r-- | doc/example.mux | 14 | ||||
| -rw-r--r-- | src/ConfigParser.cpp | 39 | ||||
| -rw-r--r-- | src/MuxElements.cpp | 12 | ||||
| -rw-r--r-- | src/MuxElements.h | 31 | ||||
| -rw-r--r-- | src/fig/FIG2.cpp | 115 | ||||
| -rw-r--r-- | src/fig/FIG2.h | 8 | 
6 files changed, 155 insertions, 64 deletions
| diff --git a/doc/example.mux b/doc/example.mux index caa4d2d..76d6828 100644 --- a/doc/example.mux +++ b/doc/example.mux @@ -124,7 +124,19 @@ ensemble {      ; The FIG2 label can be up to 16 characters long, and is in UTF-8.      ;fig2_label "ÓpêñÐigıtålRadiō" -    ; There is no FIG2 short label for the moment. +    ; FIG2 labels can either be sent with a character field (old spec) +    ; or with a text control (new draftETSI TS 103 176 v2.2.1). +    ; If unspecified, defaults to setting the text control with the values +    ; shown in the example below. +    ; +    ;fig2_label_character_flag "0xFF00" +    ; +    ;fig2_label_text_control { +    ; bidi false +    ; base_direction "LTR" +    ; contextual false +    ; combining false +    ;}  }  ; Definition of DAB services diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 62d81c7..fb49efc 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -81,13 +81,42 @@ static uint16_t get_announcement_flag_from_ptree(ptree& pt)      return flags;  } +static void parse_fig2_label(ptree& pt, DabLabel& label) { +    label.setFIG2Label(pt.get<string>("fig2_label", "")); + +    uint16_t character_field = hexparse(pt.get("fig2_label_character_flag", "0")); +    if (character_field) { +        label.setFIG2CharacterField(character_field); +    } + +    auto pt_tc = pt.get_child_optional("fig2_label_text_control"); +    if (pt_tc) { +        FIG2TextControl tc; +        tc.bidi_flag = pt_tc->get<bool>("bidi", tc.bidi_flag); +        auto base_direction = pt_tc->get<string>("bidi", "LTR"); + +        if (not(base_direction == "LTR" or base_direction == "RTL")) { +            tc.base_direction_is_rtl = (base_direction == "RTL"); +        } +        else { +            etiLog.level(error) << "Invalid value " << base_direction << +                " for fig2 text control base direction"; +            throw runtime_error("Invalid FIG2 text control definition"); +        } + +        tc.contextual_flag = pt_tc->get<bool>("contextual", tc.contextual_flag); +        tc.combining_flag = pt_tc->get<bool>("combining", tc.combining_flag); + +        label.setFIG2TextControl(tc); +    } +} +  // Parse the linkage section  static void parse_linkage(ptree& pt,          std::shared_ptr<dabEnsemble> ensemble)  {      auto pt_linking = pt.get_child_optional("linking"); -    if (pt_linking) -    { +    if (pt_linking) {          for (const auto& it : *pt_linking) {              const string setuid = it.first;              const ptree pt_set = it.second; @@ -447,7 +476,7 @@ static void parse_general(ptree& pt,              abort();      } -    ensemble->label.setFIG2Label(pt_ensemble.get<string>("fig2_label", "")); +    parse_fig2_label(pt_ensemble, ensemble->label);      try {          ptree pt_announcements = pt_ensemble.get_child("announcements"); @@ -586,7 +615,7 @@ void parse_ptree(                  abort();          } -        service->label.setFIG2Label(pt_service.get<string>("fig2_label", "")); +        parse_fig2_label(pt_service, service->label);          service->id = new_service_id;          service->ecc = hexparse(pt_service.get("ecc", "0")); @@ -748,7 +777,7 @@ void parse_ptree(                  abort();          } -        component->label.setFIG2Label(pt_comp.get<string>("fig2_label", "")); +        parse_fig2_label(pt_comp, component->label);          if (component->SCIdS == 0 and not component->label.long_label().empty()) {              etiLog.level(warn) << "Primary component " << component->uid << diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp index b7536b6..8c14d9a 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -347,6 +347,18 @@ int DabLabel::setFIG2Label(const std::string& label)      return 0;  } +void DabLabel::setFIG2CharacterField(uint16_t character_field) +{ +    m_fig2_use_text_control = false; +    m_fig2_character_field = character_field; +} + +void DabLabel::setFIG2TextControl(FIG2TextControl tc) +{ +    m_fig2_use_text_control = true; +    m_fig2_text_control = tc; +} +  void DabLabel::writeLabel(uint8_t* buf) const  {      memset(buf, ' ', DABLABEL_LENGTH); diff --git a/src/MuxElements.h b/src/MuxElements.h index 2e35e6a..9107a41 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -143,6 +143,13 @@ struct dabOutput {  #define DABLABEL_LENGTH 16 +struct FIG2TextControl { +    bool bidi_flag = false; +    bool base_direction_is_rtl = false; +    bool contextual_flag = false; +    bool combining_flag = false; +}; +  class DabLabel  {      public: @@ -172,20 +179,34 @@ class DabLabel           */          int setFIG2Label(const std::string& label); +        /* FIG2 can either be sent with a character field (old spec) +         * or with a text control (draftETSI TS 103 176 v2.2.1). +         * +         * Setting one clears the other, and selects the value of the +         * Rfu bit in the FIG 2 Data Field +         */ +        void setFIG2CharacterField(uint16_t character_field); +        void setFIG2TextControl(FIG2TextControl tc); + +        // For FIG 1 +          /* Write the label to the 16-byte buffer given in buf           * In the DAB standard, the label is 16 bytes long, and is           * padded using spaces.           */          void writeLabel(uint8_t* buf) const; -        // For FIG 1 -        bool has_fig1_label() const { return not m_fig1_label.empty(); }; +        bool has_fig1_label() const { return not m_fig1_label.empty(); }          uint16_t flag() const { return m_fig1_flag; }          const std::string long_label() const;          const std::string short_label() const;          // For FIG 2 -        bool has_fig2_label() const { return not m_fig2_label.empty(); }; +        bool has_fig2_label() const { return not m_fig2_label.empty(); } +        bool fig2_uses_text_control() const { return m_fig2_use_text_control; } +        FIG2TextControl fig2_text_control() const { return m_fig2_text_control; } +        uint16_t fig2_character_field() const { return m_fig2_character_field; } +          const std::string fig2_label() const;          /* FIG 2 labels are either in UCS-2 or in UTF-8. Because there are upcoming @@ -208,6 +229,10 @@ class DabLabel          /* FIG2 label, stored in UTF-8. TODO: support UCS-2 */          std::string m_fig2_label; +        bool m_fig2_use_text_control = true; // Default to the new variant +        uint16_t m_fig2_character_field = 0xFF00; +        FIG2TextControl m_fig2_text_control; +          /* Checks and calculates the flag. slabel must be EBU Latin Charset */          int setFIG1ShortLabel(const std::string& slabel);  }; diff --git a/src/fig/FIG2.cpp b/src/fig/FIG2.cpp index b88814c..7aecbe8 100644 --- a/src/fig/FIG2.cpp +++ b/src/fig/FIG2.cpp @@ -34,6 +34,44 @@ namespace FIC {  using namespace std; +static void write_fig2_segment_field( +        uint8_t *buf, ssize_t& remaining, FIG2_Segments& segments, const DabLabel& label) +{ +    if (segments.current_segment_index() == 0) { +        // EN 300 401 5.2.2.3.3 "The second segment shall be carried in a following +        // FIG type 2 data field ...", i.e. do not insert the header anymore. +        if (label.fig2_uses_text_control()) { +            auto ext = (FIG2_Extended_Label_WithTextControl*)buf; +            auto tc = label.fig2_text_control(); +            ext->TextControl = +                (tc.bidi_flag ? 0x8 : 0) | +                (tc.base_direction_is_rtl ? 0x4 : 0) | +                (tc.contextual_flag ? 0x2 : 0) | +                (tc.combining_flag ? 0x1 : 0); +            ext->SegmentCount = segments.segment_count(); +            ext->EncodingFlag = 0; // UTF-8 + +            buf += sizeof(FIG2_Extended_Label_WithTextControl); +            remaining -= sizeof(FIG2_Extended_Label_WithTextControl); +        } +        else { +            auto ext = (FIG2_Extended_Label_WithCharacterFlag*)buf; +            ext->Rfa = 0; +            ext->SegmentCount = segments.segment_count(); +            ext->EncodingFlag = 0; // UTF-8 +            ext->CharacterFlag = htons(label.fig2_character_field()); + +            buf += sizeof(FIG2_Extended_Label_WithCharacterFlag); +            remaining -= sizeof(FIG2_Extended_Label_WithCharacterFlag); +        } +    } + +    const auto character_field = segments.advance_segment(); +    copy(character_field.begin(), character_field.end(), buf); +    buf += character_field.size(); +    remaining -= character_field.size(); +} +  void FIG2_Segments::clear()  {      segments.clear(); @@ -132,8 +170,12 @@ FillStatus FIG2_0::fill(uint8_t *buf, size_t max_size)          }      } +    const size_t segment_header_length = ensemble->label.fig2_uses_text_control() ? +        sizeof(FIG2_Extended_Label_WithTextControl) : +        sizeof(FIG2_Extended_Label_WithCharacterFlag); +      const ssize_t required_bytes = (m_segments.current_segment_index() == 0) ? -        sizeof(FIGtype2) + 2 + sizeof(FIG2_Extended_Label) + m_segments.current_segment_length() : +        sizeof(FIGtype2) + 2 + segment_header_length + m_segments.current_segment_length() :          sizeof(FIGtype2) + 2 + m_segments.current_segment_length();      if (remaining < required_bytes) { @@ -147,7 +189,7 @@ FillStatus FIG2_0::fill(uint8_t *buf, size_t max_size)      fig2->FIGtypeNumber = 2;      fig2->Extension = 0; -    fig2->Rfu = 0; +    fig2->Rfu = ensemble->label.fig2_uses_text_control() ? 1 : 0;      fig2->SegmentIndex = m_segments.current_segment_index();      fig2->ToggleFlag = m_segments.toggle_flag(); @@ -160,23 +202,7 @@ FillStatus FIG2_0::fill(uint8_t *buf, size_t max_size)      buf += 2;      remaining -= 2; -    if (m_segments.current_segment_index() == 0) { -        // EN 300 401 5.2.2.3.3 "The second segment shall be carried in a following -        // FIG type 2 data field ...", i.e. do not insert the header anymore. -        auto ext = (FIG2_Extended_Label*)buf; -        ext->Rfa = 0; -        ext->SegmentCount = m_segments.segment_count(); -        ext->EncodingFlag = 0; // UTF-8 -        ext->CharacterFlag = htons(0xFF00); // Short label always truncation - -        buf += sizeof(FIG2_Extended_Label); -        remaining -= sizeof(FIG2_Extended_Label); -    } - -    const auto character_field = m_segments.advance_segment(); -    copy(character_field.begin(), character_field.end(), buf); -    buf += character_field.size(); -    remaining -= character_field.size(); +    write_fig2_segment_field(buf, remaining, m_segments, ensemble->label);      if (m_segments.complete()) {          fs.complete_fig_transmitted = true; @@ -218,8 +244,13 @@ FillStatus FIG2_1_and_5::fill(uint8_t *buf, size_t max_size)              const size_t id_length = (is_programme ? 2 : 4); -            const ssize_t required_bytes = sizeof(FIGtype2) + id_length + segments.current_segment_length() + -                ((segments.current_segment_index() == 0) ? sizeof(FIG2_Extended_Label) : 0); +            const size_t segment_header_length = ensemble->label.fig2_uses_text_control() ? +                sizeof(FIG2_Extended_Label_WithTextControl) : +                sizeof(FIG2_Extended_Label_WithCharacterFlag); + +            const ssize_t required_bytes = sizeof(FIGtype2) + id_length + +                segments.current_segment_length() + +                ((segments.current_segment_index() == 0) ? segment_header_length : 0);              if (remaining < required_bytes) {                  break; @@ -231,7 +262,7 @@ FillStatus FIG2_1_and_5::fill(uint8_t *buf, size_t max_size)              fig2->FIGtypeNumber = 2;              fig2->Extension = figextension(); -            fig2->Rfu = 0; +            fig2->Rfu = (*service)->label.fig2_uses_text_control() ? 1 : 0;              fig2->SegmentIndex = segments.current_segment_index();              fig2->ToggleFlag = segments.toggle_flag(); @@ -252,21 +283,7 @@ FillStatus FIG2_1_and_5::fill(uint8_t *buf, size_t max_size)              buf += id_length;              remaining -= id_length; -            if (segments.current_segment_index() == 0) { -                auto ext = (FIG2_Extended_Label*)buf; -                ext->Rfa = 0; -                ext->SegmentCount = segments.segment_count(); -                ext->EncodingFlag = 0; // UTF-8 -                ext->CharacterFlag = htons(0xFF00); // Short label always truncation - -                buf += sizeof(FIG2_Extended_Label); -                remaining -= sizeof(FIG2_Extended_Label); -            } - -            const auto character_field = segments.advance_segment(); -            copy(character_field.begin(), character_field.end(), buf); -            buf += character_field.size(); -            remaining -= character_field.size(); +            write_fig2_segment_field(buf, remaining, segments, (*service)->label);              if (segments.complete()) {                  segments.clear(); @@ -321,8 +338,12 @@ FillStatus FIG2_4::fill(uint8_t *buf, size_t max_size)                  sizeof(FIGtype2_4_Programme_Identifier) :                  sizeof(FIGtype2_4_Data_Identifier); +            const size_t segment_header_length = (*component)->label.fig2_uses_text_control() ? +                    sizeof(FIG2_Extended_Label_WithTextControl) : +                    sizeof(FIG2_Extended_Label_WithCharacterFlag); +              const ssize_t required_bytes = sizeof(FIGtype2) + id_length + segments.current_segment_length() + -                ((segments.current_segment_index() == 0) ? sizeof(FIG2_Extended_Label) : 0); +                ((segments.current_segment_index() == 0) ? segment_header_length : 0);              if (remaining < required_bytes) {                  break; @@ -334,7 +355,7 @@ FillStatus FIG2_4::fill(uint8_t *buf, size_t max_size)              fig2->FIGtypeNumber = 2;              fig2->Extension = 4; -            fig2->Rfu = 0; +            fig2->Rfu = (*component)->label.fig2_uses_text_control() ? 1 : 0;              fig2->SegmentIndex = segments.current_segment_index();              fig2->ToggleFlag = segments.toggle_flag(); @@ -365,21 +386,7 @@ FillStatus FIG2_4::fill(uint8_t *buf, size_t max_size)                  remaining -= sizeof(FIGtype2_4_Data_Identifier);              } -            if (segments.current_segment_index() == 0) { -                auto ext = (FIG2_Extended_Label*)buf; -                ext->Rfa = 0; -                ext->SegmentCount = segments.segment_count(); -                ext->EncodingFlag = 0; // UTF-8 -                ext->CharacterFlag = htons(0xFF00); // Short label always truncation - -                buf += sizeof(FIG2_Extended_Label); -                remaining -= sizeof(FIG2_Extended_Label); -            } - -            const auto character_field = segments.advance_segment(); -            copy(character_field.begin(), character_field.end(), buf); -            buf += character_field.size(); -            remaining -= character_field.size(); +            write_fig2_segment_field(buf, remaining, segments, (*component)->label);              if (segments.complete()) {                  segments.clear(); diff --git a/src/fig/FIG2.h b/src/fig/FIG2.h index 6fad658..ee3fed9 100644 --- a/src/fig/FIG2.h +++ b/src/fig/FIG2.h @@ -145,7 +145,7 @@ struct FIGtype2_4_Data_Identifier {      uint32_t SId;  } PACKED; -struct FIG2_Extended_Label { +struct FIG2_Extended_Label_WithCharacterFlag {      uint8_t Rfa:4;      uint8_t SegmentCount:3;      uint8_t EncodingFlag:1; @@ -153,6 +153,12 @@ struct FIG2_Extended_Label {      uint16_t CharacterFlag;  } PACKED; +struct FIG2_Extended_Label_WithTextControl { +    uint8_t TextControl:4; +    uint8_t SegmentCount:3; +    uint8_t EncodingFlag:1; +} PACKED; +  #ifdef _WIN32  #   pragma pack(pop)  #endif | 
