libflute
Receiver.cpp
Go to the documentation of this file.
1 // libflute - FLUTE/ALC library
2 //
3 // Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG)
4 //
5 // Licensed under the License terms and conditions for use, reproduction, and
6 // distribution of 5G-MAG software (the “License”). You may not use this file
7 // except in compliance with the License. You may obtain a copy of the License at
8 // https://www.5g-mag.com/reference-tools. Unless required by applicable law or
9 // agreed to in writing, software distributed under the License is distributed on
10 // an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 // or implied.
12 //
13 // See the License for the specific language governing permissions and limitations
14 // under the License.
15 //
16 #include "Receiver.h"
17 #include "AlcPacket.h"
18 #include <iostream>
19 #include <string>
20 #include "spdlog/spdlog.h"
21 #include "IpSec.h"
22 
23 
24 LibFlute::Receiver::Receiver ( const std::string& iface, const std::string& address,
25  short port, uint64_t tsi,
26  boost::asio::io_context& io_context)
27  : _socket(io_context)
28  , _tsi(tsi)
29  , _mcast_address(address)
30 {
31  boost::asio::ip::udp::endpoint listen_endpoint(
32  boost::asio::ip::make_address(iface), port);
33  _socket.open(listen_endpoint.protocol());
34  _socket.set_option(boost::asio::ip::multicast::enable_loopback(true));
35  _socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
36  _socket.set_option(boost::asio::socket_base::receive_buffer_size(16*1024*1024));
37  _socket.bind(listen_endpoint);
38 
39  // Join the multicast group.
40  _socket.set_option(
41  boost::asio::ip::multicast::join_group(
42  boost::asio::ip::make_address(address)));
43 
44  _socket.async_receive_from(
45  boost::asio::buffer(_data, max_length), _sender_endpoint,
46  boost::bind(&LibFlute::Receiver::handle_receive_from, this,
47  boost::asio::placeholders::error,
48  boost::asio::placeholders::bytes_transferred));
49 }
50 
51 auto LibFlute::Receiver::enable_ipsec(uint32_t spi, const std::string& key) -> void
52 {
54 }
55 
56 auto LibFlute::Receiver::handle_receive_from(const boost::system::error_code& error,
57  size_t bytes_recvd) -> void
58 {
59  if (!_running) return;
60 
61  if (!error)
62  {
63  spdlog::trace("Received {} bytes", bytes_recvd);
64  try {
65  auto alc = LibFlute::AlcPacket(_data, bytes_recvd);
66 
67  if (alc.tsi() == _tsi) {
68 
69  const std::lock_guard<std::mutex> lock(_files_mutex);
70 
71  if (alc.toi() == 0 && (!_fdt || _fdt->instance_id() != alc.fdt_instance_id())) {
72  if (_files.find(alc.toi()) == _files.end()) {
73  FileDeliveryTable::FileEntry fe{0, "", static_cast<uint32_t>(alc.fec_oti().transfer_length), "", "", 0, alc.fec_oti()};
74  _files.emplace(alc.toi(), std::make_shared<LibFlute::File>(fe));
75  }
76  }
77 
78  if (_files.find(alc.toi()) != _files.end() && !_files[alc.toi()]->complete()) {
79  auto encoding_symbols = LibFlute::EncodingSymbol::from_payload(
80  _data + alc.header_length(),
81  bytes_recvd - alc.header_length(),
82  _files[alc.toi()]->fec_oti(),
83  alc.content_encoding());
84 
85  for (const auto& symbol : encoding_symbols) {
86  spdlog::debug("received TOI {} SBN {} ID {}", alc.toi(), symbol.source_block_number(), symbol.id() );
87  _files[alc.toi()]->put_symbol(symbol);
88  }
89 
90  auto file = _files[alc.toi()].get();
91  if (_files[alc.toi()]->complete()) {
92  for (auto it = _files.cbegin(); it != _files.cend();)
93  {
94  if (it->second.get() != file && it->second->meta().content_location == file->meta().content_location)
95  {
96  spdlog::debug("Replacing file with TOI {}", it->first);
97  it = _files.erase(it);
98  }
99  else
100  {
101  ++it;
102  }
103  }
104 
105  file->decode();
106 
107  spdlog::debug("File with TOI {} completed", alc.toi());
108  if (alc.toi() != 0 && _completion_cb) {
109  _completion_cb(_files[alc.toi()]);
110  _files.erase(alc.toi());
111  }
112 
113  if (alc.toi() == 0) { // parse complete FDT
114  _fdt = std::make_unique<LibFlute::FileDeliveryTable>(
115  alc.fdt_instance_id(), _files[alc.toi()]->buffer(), _files[alc.toi()]->length());
116 
117  _files.erase(alc.toi());
118  for (const auto& file_entry : _fdt->file_entries()) {
119  // automatically receive all files in the FDT
120  if (_files.find(file_entry.toi) == _files.end()) {
121  spdlog::debug("Starting reception for file with TOI {}: {} ({})", file_entry.toi,
122  file_entry.content_location, file_entry.content_type);
123  _files.emplace(file_entry.toi, std::make_shared<LibFlute::File>(file_entry));
124  }
125  }
126  }
127  }
128  } else {
129  spdlog::trace("Discarding packet for unknown or already completed file with TOI {}", alc.toi());
130  }
131  } else {
132  spdlog::warn("Discarding packet for unknown TSI {}", alc.tsi());
133  }
134  } catch (const std::exception &ex) {
135  spdlog::warn("Failed to decode ALC/FLUTE packet: {}", ex.what());
136  }
137 
138  _socket.async_receive_from(
139  boost::asio::buffer(_data, max_length), _sender_endpoint,
140  boost::bind(&LibFlute::Receiver::handle_receive_from, this,
141  boost::asio::placeholders::error,
142  boost::asio::placeholders::bytes_transferred));
143  }
144  else
145  {
146  spdlog::error("receive_from error: {}", error.message());
147  }
148 }
149 
150 auto LibFlute::Receiver::file_list() -> std::vector<std::shared_ptr<LibFlute::File>>
151 {
152  std::vector<std::shared_ptr<LibFlute::File>> files;
153  for (auto& f : _files) {
154  files.push_back(f.second);
155  }
156  return files;
157 }
158 
159 auto LibFlute::Receiver::remove_expired_files(unsigned max_age) -> void
160 {
161  const std::lock_guard<std::mutex> lock(_files_mutex);
162  for (auto it = _files.cbegin(); it != _files.cend();)
163  {
164  auto age = time(nullptr) - it->second->received_at();
165  if ( it->second->meta().content_location != "bootstrap.multipart" && age > max_age) {
166  it = _files.erase(it);
167  } else {
168  ++it;
169  }
170  }
171 }
172 
173 auto LibFlute::Receiver::remove_file_with_content_location(const std::string& cl) -> void
174 {
175  const std::lock_guard<std::mutex> lock(_files_mutex);
176  for (auto it = _files.cbegin(); it != _files.cend();)
177  {
178  if ( it->second->meta().content_location == cl) {
179  it = _files.erase(it);
180  } else {
181  ++it;
182  }
183  }
184 }
A class for parsing and creating ALC packets.
Definition: AlcPacket.h:27
static std::vector< EncodingSymbol > from_payload(char *encoded_data, size_t data_len, const FecOti &fec_oti, ContentEncoding encoding)
Parse and construct all encoding symbols from a payload data buffer.
void remove_file_with_content_location(const std::string &cl)
Remove a file from the list that matches the passed content location.
Definition: Receiver.cpp:173
std::vector< std::shared_ptr< LibFlute::File > > file_list()
List all current files.
Definition: Receiver.cpp:150
void enable_ipsec(uint32_t spi, const std::string &aes_key)
Enable IPSEC ESP decryption of FLUTE payloads.
Definition: Receiver.cpp:51
Receiver(const std::string &iface, const std::string &address, short port, uint64_t tsi, boost::asio::io_context &io_context)
Default constructor.
Definition: Receiver.cpp:24
void remove_expired_files(unsigned max_age)
Remove files from the list that are older than max_age seconds.
Definition: Receiver.cpp:159
void enable_esp(uint32_t spi, const std::string &dest_address, Direction direction, const std::string &key)
Definition: IpSec.cpp:124
An entry for a file in the FDT.