22 #include "spdlog/spdlog.h"
27 XMLNamespaces() :_default_ns() ,_prefix_to_ns_map() {};
28 XMLNamespaces(
const XMLNamespaces &to_copy) :_default_ns(to_copy._default_ns) ,_prefix_to_ns_map(to_copy._prefix_to_ns_map) {};
29 XMLNamespaces(XMLNamespaces &&to_move) :_default_ns(std::move(to_move._default_ns)) ,_prefix_to_ns_map(std::move(to_move._prefix_to_ns_map)) {};
30 XMLNamespaces(
const tinyxml2::XMLElement *element,
const XMLNamespaces &parent_ns = XMLNamespaces());
32 const tinyxml2::XMLElement *findChildElement(
const tinyxml2::XMLElement *element,
const std::string &name,
const std::string &ns = std::string())
const;
33 const tinyxml2::XMLElement *findSiblingElement(
const tinyxml2::XMLElement *child_elem,
const std::string &name,
const std::string &ns = std::string())
const;
34 const tinyxml2::XMLAttribute *findAttribute(
const tinyxml2::XMLElement *element,
const std::string &name,
const std::string &ns = std::string())
const;
35 bool matches(
const std::string &prefixed_name,
const std::string &name,
const std::string &ns = std::string())
const;
36 const std::string &elementNamespace(
const tinyxml2::XMLElement *element)
const;
39 std::string _default_ns;
40 std::map<std::string, std::string> _prefix_to_ns_map;
43 XMLNamespaces::XMLNamespaces(
const tinyxml2::XMLElement *element,
const XMLNamespaces &parent_ns)
44 :_default_ns(parent_ns._default_ns)
45 ,_prefix_to_ns_map(parent_ns._prefix_to_ns_map)
48 for (
auto attr_ptr = element->FirstAttribute(); attr_ptr; attr_ptr = attr_ptr->Next()) {
49 std::string attr_name = attr_ptr->Name();
50 if (attr_name ==
"xmlns") {
51 _default_ns = std::string(attr_ptr->Value());
52 }
else if (attr_name.substr(0,6) ==
"xmlns:") {
53 _prefix_to_ns_map.insert(std::make_pair(attr_name.substr(6), std::string(attr_ptr->Value())));
58 const tinyxml2::XMLElement *XMLNamespaces::findChildElement(
const tinyxml2::XMLElement *element,
const std::string &name,
const std::string &ns)
const
60 if (!element)
return nullptr;
61 auto elem_ptr = element->FirstChildElement();
62 if (!elem_ptr)
return nullptr;
63 XMLNamespaces child_ns(elem_ptr, *
this);
64 if (child_ns.matches(elem_ptr->Name(), name, ns))
return elem_ptr;
65 return findSiblingElement(elem_ptr, name, ns);
68 const tinyxml2::XMLElement *XMLNamespaces::findSiblingElement(
const tinyxml2::XMLElement *child_elem,
const std::string &name,
const std::string &ns)
const
70 if (!child_elem)
return nullptr;
71 for (
auto elem_ptr = child_elem->NextSiblingElement(); elem_ptr; elem_ptr = elem_ptr->NextSiblingElement()) {
72 XMLNamespaces child_ns(elem_ptr, *
this);
73 if (child_ns.matches(elem_ptr->Name(), name, ns))
return elem_ptr;
78 const tinyxml2::XMLAttribute *XMLNamespaces::findAttribute(
const tinyxml2::XMLElement *element,
const std::string &name,
const std::string &ns)
const
80 const std::string &elem_ns = elementNamespace(element);
81 for (
auto attr_ptr = element->FirstAttribute(); attr_ptr; attr_ptr = attr_ptr->Next()) {
82 XMLNamespaces attr_ns(*
this);
83 attr_ns._default_ns = elem_ns;
84 if (attr_ns.matches(attr_ptr->Name(), name, ns))
return attr_ptr;
89 bool XMLNamespaces::matches(
const std::string &prefixed_name,
const std::string &name,
const std::string &ns)
const
91 std::string match_ns(_default_ns);
92 std::string match_name(prefixed_name);
93 auto pos = match_name.find_first_of(
':');
94 if (pos != std::string::npos) {
95 auto prefix = match_name.substr(0,pos);
96 match_name.erase(0,pos+1);
97 auto it = _prefix_to_ns_map.find(prefix);
98 if (it != _prefix_to_ns_map.end()) {
99 match_ns = _prefix_to_ns_map.at(prefix);
104 return name == match_name && ns == match_ns;
107 const std::string &XMLNamespaces::elementNamespace(
const tinyxml2::XMLElement *element)
const
109 std::string elem_name(element->Name());
110 auto pos = elem_name.find_first_of(
':');
111 if (pos != std::string::npos) {
112 auto elem_prefix = elem_name.substr(0,pos);
113 auto it = _prefix_to_ns_map.find(elem_prefix);
114 if (it != _prefix_to_ns_map.end()) {
115 return _prefix_to_ns_map.at(elem_prefix);
117 static const std::string unknown(
"<Unknown>");
137 , _global_fec_oti( fec_oti )
138 , _fdt_namespace( fdt_namespace )
143 : _instance_id( instance_id )
144 , _instance_id_sent( instance_id - 1 )
147 static const std::string mbms2007_ns(
"urn:3GPP:metadata:2007:MBMS:FLUTE:FDT");
148 static const std::string mbms2012_ns(
"urn:3GPP:metadata:2012:MBMS:FLUTE:FDT");
149 tinyxml2::XMLDocument
doc(
true, tinyxml2::COLLAPSE_WHITESPACE);
150 doc.Parse(buffer, len);
151 auto fdt_instance =
doc.RootElement();
152 XMLNamespaces root_ns(fdt_instance);
153 auto fdt_ns = root_ns.elementNamespace(fdt_instance);
154 if (!root_ns.matches(fdt_instance->Name(),
"FDT-Instance", fdt_ns)) {
155 throw "Root element is not FDT-Instance";
160 }
else if (fdt_ns ==
"http://www.example.com/flute") {
162 }
else if (fdt_ns ==
"urn:IETF:metadata:2005:FLUTE:FDT") {
166 }
else if (fdt_ns ==
"urn:3GPP:metadata:2022:FLUTE:FDT") {
169 throw "FDT namespace not recognised";
172 _expires = std::stoull(root_ns.findAttribute(fdt_instance,
"Expires", fdt_ns)->Value());
174 spdlog::debug(
"Received new FDT with instance ID {}: {}",
instance_id, buffer);
176 auto val = root_ns.findAttribute(fdt_instance,
"FEC-OTI-FEC-Encoding-ID", fdt_ns);
177 if (val !=
nullptr) {
181 val = root_ns.findAttribute(fdt_instance,
"FEC-OTI-FEC-Instance-ID", fdt_ns);
182 if (val !=
nullptr) {
183 _global_fec_oti.
instance_id = strtoul(val->Value(),
nullptr, 0);
186 val = root_ns.findAttribute(fdt_instance,
"FEC-OTI-Maximum-Source-Block-Length", fdt_ns);
187 if (val !=
nullptr) {
191 val = root_ns.findAttribute(fdt_instance,
"FEC-OTI-Encoding-Symbol-Length", fdt_ns);
192 if (val !=
nullptr) {
196 val = root_ns.findAttribute(fdt_instance,
"FEC-OTI-Max-Number-of-Encoding-Symbols", fdt_ns);
197 if (val !=
nullptr) {
201 for (
auto file = root_ns.findChildElement(fdt_instance,
"File", fdt_ns);
202 file !=
nullptr; file = root_ns.findSiblingElement(file,
"File", fdt_ns)) {
204 XMLNamespaces file_ns(file, root_ns);
207 auto toi_str = file_ns.findAttribute(file,
"TOI", fdt_ns);
208 if (toi_str ==
nullptr) {
209 throw "Missing TOI attribute on File element";
211 uint32_t toi = strtoull(toi_str->Value(),
nullptr, 0);
213 auto content_location = file_ns.findAttribute(file,
"Content-Location", fdt_ns);
214 if (content_location ==
nullptr) {
215 throw "Missing Content-Location attribute on File element";
219 uint32_t content_length = 0;
220 val = file_ns.findAttribute(file,
"Content-Length", fdt_ns);
221 if (val !=
nullptr) {
222 content_length = strtoull(val->Value(),
nullptr, 0);
225 uint32_t transfer_length = 0;
226 val = file_ns.findAttribute(file,
"Transfer-Length", fdt_ns);
227 if (val !=
nullptr) {
228 transfer_length = strtoull(val->Value(),
nullptr, 0);
230 transfer_length = content_length;
233 auto content_md5 = std::string();
234 val = file_ns.findAttribute(file,
"Content-MD5", fdt_ns);
235 if (val !=
nullptr) {
236 content_md5 = val->Value();
239 auto content_encoding = std::string();
240 val = file_ns.findAttribute(file,
"Content-Encoding", fdt_ns);
241 if (val !=
nullptr) {
242 content_encoding = val->Value();
245 auto content_type = std::string();
246 val = file_ns.findAttribute(file,
"Content-Type", fdt_ns);
247 if (val !=
nullptr) {
248 content_type = val->Value();
252 val = file_ns.findAttribute(file,
"FEC-OTI-FEC-Encoding-ID", fdt_ns);
253 if (val !=
nullptr) {
254 encoding_id =
static_cast<FecScheme>(strtoul(val->Value(),
nullptr, 0));
257 auto fec_instance_id = _global_fec_oti.
instance_id;
258 val = file_ns.findAttribute(file,
"FEC-OTI-FEC-Instance-ID", fdt_ns);
259 if (val !=
nullptr) {
260 fec_instance_id = strtoul(val->Value(),
nullptr, 0);
264 val = file_ns.findAttribute(file,
"FEC-OTI-Maximum-Source-Block-Length", fdt_ns);
265 if (val !=
nullptr) {
266 max_source_block_length = strtoul(val->Value(),
nullptr, 0);
270 val = file_ns.findAttribute(file,
"FEC-OTI-Encoding-Symbol-Length", fdt_ns);
271 if (val !=
nullptr) {
272 encoding_symbol_length = strtoul(val->Value(),
nullptr, 0);
276 val = file_ns.findAttribute(file,
"FEC-OTI-Max-Number-of-Encoding-Symbols", fdt_ns);
277 if (val !=
nullptr) {
278 max_number_of_encoding_symbols = strtoul(val->Value(),
nullptr, 0);
281 auto mbms2012_file_etag =
"";
282 val = file_ns.findAttribute(file,
"File-ETag", mbms2012_ns);
283 if (val !=
nullptr) {
284 mbms2012_file_etag = val->Value();
289 bool no_cache =
false;
291 std::optional<uint64_t> cache_expires = std::nullopt;
292 auto cc = file_ns.findChildElement(file,
"Cache-Control", mbms2007_ns);
294 XMLNamespaces cc_ns(cc, file_ns);
298 auto no_cache_elem = cc_ns.findChildElement(cc,
"no-cache", mbms2007_ns);
308 auto expires_elem = cc_ns.findChildElement(cc,
"Expires", mbms2007_ns);
310 cache_expires = strtoul(expires_elem->GetText(),
nullptr, 0);
317 .transfer_length = transfer_length,
318 .encoding_symbol_length = encoding_symbol_length,
319 .max_source_block_length = max_source_block_length,
320 .max_number_of_encoding_symbols = max_number_of_encoding_symbols
325 std::string(content_location->Value()),
329 (cache_expires)?(cache_expires.value()):0,
338 _file_entries.push_back(fe);
344 if (_instance_id == _instance_id_sent) _instance_id++;
345 _file_entries.push_back(fe);
350 for (
auto it = _file_entries.cbegin(); it != _file_entries.cend();) {
351 if (it->toi == toi) {
352 it = _file_entries.erase(it);
357 if (_instance_id == _instance_id_sent) _instance_id++;
361 tinyxml2::XMLDocument
doc;
362 doc.InsertFirstChild(
doc.NewDeclaration() );
363 auto root =
doc.NewElement(
"FDT-Instance");
364 switch (_fdt_namespace) {
367 root->SetAttribute(
"xmlns",
"http://www.example.com/flute");
369 case FDT_NS_DRAFT_2005:
371 root->SetAttribute(
"xmlns",
"urn:IETF:metadata:2005:FLUTE:FDT");
377 case FDT_NS_3GPP_CONSOLIDATED_V2:
379 root->SetAttribute(
"xmlns",
"urn:3GPP:metadata:2022:FLUTE:FDT");
384 root->SetAttribute(
"Expires", std::to_string(_expires).c_str());
385 root->SetAttribute(
"FEC-OTI-FEC-Encoding-ID", (
unsigned)_global_fec_oti.encoding_id);
386 if (_global_fec_oti.instance_id) root->SetAttribute(
"FEC-OTI-FEC-Instance-ID", (
unsigned)_global_fec_oti.instance_id);
387 root->SetAttribute(
"FEC-OTI-Maximum-Source-Block-Length", (
unsigned)_global_fec_oti.max_source_block_length);
388 root->SetAttribute(
"FEC-OTI-Encoding-Symbol-Length", (
unsigned)_global_fec_oti.encoding_symbol_length);
389 root->SetAttribute(
"xmlns:mbms2007",
"urn:3GPP:metadata:2007:MBMS:FLUTE:FDT");
390 root->SetAttribute(
"xmlns:mbms2012",
"urn:3GPP:metadata:2012:MBMS:FLUTE:FDT");
391 doc.InsertEndChild(root);
393 for (
const auto& file : _file_entries) {
394 auto f =
doc.NewElement(
"File");
395 f->SetAttribute(
"TOI", file.toi);
396 f->SetAttribute(
"Content-Location", file.content_location.c_str());
397 f->SetAttribute(
"Content-Length", file.content_length);
398 if (file.fec_oti.transfer_length) f->SetAttribute(
"Transfer-Length", file.fec_oti.transfer_length);
399 if (!file.content_md5.empty()) f->SetAttribute(
"Content-MD5", file.content_md5.c_str());
400 if (!file.content_encoding.empty()) f->SetAttribute(
"Content-Encoding", file.content_encoding.c_str());
401 if (!file.content_type.empty()) f->SetAttribute(
"Content-Type", file.content_type.c_str());
402 if (file.fec_oti.encoding_id != _global_fec_oti.encoding_id)
403 f->SetAttribute(
"FEC-OTI-FEC-Encoding-ID", (
unsigned)file.fec_oti.encoding_id);
404 if (file.fec_oti.instance_id != 0 && file.fec_oti.instance_id != _global_fec_oti.instance_id)
405 f->SetAttribute(
"FEC-OTI-FEC-Instance-ID", (
unsigned)file.fec_oti.instance_id);
406 if (file.fec_oti.max_source_block_length != 0 &&
407 file.fec_oti.max_source_block_length != _global_fec_oti.max_source_block_length)
408 f->SetAttribute(
"FEC-OTI-Maximum-Source-Block-Length", (
unsigned)file.fec_oti.max_source_block_length);
409 if (file.fec_oti.encoding_symbol_length != 0 &&
410 file.fec_oti.encoding_symbol_length != _global_fec_oti.encoding_symbol_length)
411 f->SetAttribute(
"FEC-OTI-Encoding-Symbol-Length", (
unsigned)file.fec_oti.encoding_symbol_length);
412 if (!file.etag.empty()) f->SetAttribute(
"mbms2012:File-ETag", file.etag.c_str());
413 if (file.cache_control.no_cache || file.cache_control.cache_expires) {
414 auto cc =
doc.NewElement(
"mbms2007:Cache-Control");
415 if (file.cache_control.no_cache) {
416 auto noc =
doc.NewElement(
"mbms2007:no-cache");
417 noc->SetText(
"true");
418 cc->InsertEndChild(noc);
420 auto exp =
doc.NewElement(
"mbms2007:Expires");
421 exp->SetText(std::to_string(file.expires).c_str());
422 cc->InsertEndChild(exp);
424 f->InsertEndChild(cc);
426 root->InsertEndChild(f);
430 tinyxml2::XMLPrinter printer;
432 return std::string(printer.CStr());
FdtNamespace
FDT namespace enumeration.
@ FDT_NS_3GPP_CONSOLIDATED_V2
uint32_t instance_id()
Get the FDT instance ID.
void remove(uint32_t toi)
Remove a file entry.
std::string to_string() const
Serialize the FDT to an XML string.
void add(const FileEntry &entry)
Add a file entry.
FileDeliveryTable(uint32_t instance_id, FecOti fec_oti, FdtNamespace fdt_namespace=FDT_NS_NONE)
Create an empty FDT.
FecScheme
Error correction schemes.
uint32_t max_source_block_length
uint32_t max_number_of_encoding_symbols
uint32_t encoding_symbol_length
An entry for a file in the FDT.
std::string content_location
std::optional< uint64_t > cache_expires
std::string content_encoding
struct LibFlute::FileDeliveryTable::FileEntry::@0 cache_control
bool operator==(const FileEntry &other) const