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 |