summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-02-16 09:21:11 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-02-16 09:21:11 +0100
commiteb15f7fc5e461c71a8d397d8fb34c27976876946 (patch)
treeebb4769adee81ae9c587e8583e1c7866d93099d5
parent9d593f25423a547960d844a2c497de46213a020b (diff)
downloaddabmux-eb15f7fc5e461c71a8d397d8fb34c27976876946.tar.gz
dabmux-eb15f7fc5e461c71a8d397d8fb34c27976876946.tar.bz2
dabmux-eb15f7fc5e461c71a8d397d8fb34c27976876946.zip
Add FIG2 options for character field and text control
-rw-r--r--doc/example.mux14
-rw-r--r--src/ConfigParser.cpp39
-rw-r--r--src/MuxElements.cpp12
-rw-r--r--src/MuxElements.h31
-rw-r--r--src/fig/FIG2.cpp115
-rw-r--r--src/fig/FIG2.h8
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