libflute
File.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 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Affero General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Affero General Public License for more details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 #include "File.h"
19 #include <iostream>
20 #include <string>
21 #include <cstring>
22 #include <cmath>
23 #include <cassert>
24 #include <algorithm>
25 #include <sstream>
26 #include <iomanip>
27 #include <openssl/md5.h>
28 #include "base64.h"
29 #include "spdlog/spdlog.h"
30 
32  : _meta( std::move(entry) )
33  , _received_at( time(nullptr) )
34 {
35  // Allocate a data buffer
36  _buffer = (char*)malloc(_meta.fec_oti.transfer_length);
37  if (_buffer == nullptr)
38  {
39  throw "Failed to allocate file buffer";
40  }
41  _own_buffer = true;
42 
43  calculate_partitioning();
44  create_blocks();
45 }
46 
47 LibFlute::File::File(uint32_t toi,
48  FecOti fec_oti,
49  std::string content_location,
50  std::string content_type,
51  uint64_t expires,
52  char* data,
53  size_t length,
54  bool copy_data)
55 {
56  if (copy_data) {
57  _buffer = (char*)malloc(length);
58  if (_buffer == nullptr)
59  {
60  throw "Failed to allocate file buffer";
61  }
62  memcpy(_buffer, data, length);
63  _own_buffer = true;
64  } else {
65  _buffer = data;
66  }
67 
68  unsigned char md5[MD5_DIGEST_LENGTH];
69  MD5((const unsigned char*)data, length, md5);
70 
71  _meta.toi = toi;
72  _meta.content_location = std::move(content_location);
73  _meta.content_type = std::move(content_type);
74  _meta.content_length = length;
75  _meta.content_md5 = base64_encode(md5, MD5_DIGEST_LENGTH);
76  _meta.expires = expires;
77  _meta.fec_oti = fec_oti;
78 
79  // for no-code
80  if (_meta.fec_oti.encoding_id == FecScheme::CompactNoCode) {
81  _meta.fec_oti.transfer_length = length;
82  } else {
83  throw "Unsupported FEC scheme";
84  }
85 
86  calculate_partitioning();
87  create_blocks();
88 }
89 
91 {
92  if (_own_buffer && _buffer != nullptr)
93  {
94  free(_buffer);
95  }
96 }
97 
99 {
100  if (symbol.source_block_number() > _source_blocks.size()) {
101  throw "Source Block number too high";
102  }
103 
104  SourceBlock& source_block = _source_blocks[ symbol.source_block_number() ];
105 
106  if (symbol.id() > source_block.symbols.size()) {
107  throw "Encoding Symbol ID too high";
108  }
109 
110  SourceBlock::Symbol& target_symbol = source_block.symbols[symbol.id()];
111 
112  if (!target_symbol.complete) {
113  symbol.decode_to(target_symbol.data, target_symbol.length);
114  target_symbol.complete = true;
115 
116  check_source_block_completion(source_block);
117  check_file_completion();
118  }
119 
120 }
121 
122 auto LibFlute::File::check_source_block_completion( SourceBlock& block ) -> void
123 {
124  block.complete = std::all_of(block.symbols.begin(), block.symbols.end(), [](const auto& symbol){ return symbol.second.complete; });
125 }
126 
127 auto LibFlute::File::check_file_completion() -> void
128 {
129  _complete = std::all_of(_source_blocks.begin(), _source_blocks.end(), [](const auto& block){ return block.second.complete; });
130 
131  if (_complete && !_meta.content_md5.empty()) {
132  //check MD5 sum
133  unsigned char md5[MD5_DIGEST_LENGTH];
134  MD5((const unsigned char*)buffer(), length(), md5);
135 
136  auto content_md5 = base64_decode(_meta.content_md5);
137  if (memcmp(md5, content_md5.c_str(), MD5_DIGEST_LENGTH) != 0) {
138  spdlog::debug("MD5 mismatch for TOI {}, discarding", _meta.toi);
139 
140  // MD5 mismatch, try again
141  for (auto& block : _source_blocks) {
142  for (auto& symbol : block.second.symbols) {
143  symbol.second.complete = false;
144  }
145  block.second.complete = false;
146  }
147  _complete = false;
148  }
149  }
150 }
151 
152 auto LibFlute::File::calculate_partitioning() -> void
153 {
154  // Calculate source block partitioning (RFC5052 9.1)
155  _nof_source_symbols = ceil((double)_meta.fec_oti.transfer_length / (double)_meta.fec_oti.encoding_symbol_length);
156  _nof_source_blocks = ceil((double)_nof_source_symbols / (double)_meta.fec_oti.max_source_block_length);
157  _large_source_block_length = ceil((double)_nof_source_symbols / (double)_nof_source_blocks);
158  _small_source_block_length = floor((double)_nof_source_symbols / (double)_nof_source_blocks);
159  _nof_large_source_blocks = _nof_source_symbols - _small_source_block_length * _nof_source_blocks;
160 }
161 
162 auto LibFlute::File::create_blocks() -> void
163 {
164  // Create the required source blocks and encoding symbols
165  auto buffer_ptr = _buffer;
166  size_t remaining_size = _meta.fec_oti.transfer_length;
167  auto number = 0;
168  while (remaining_size > 0) {
169  SourceBlock block;
170  auto symbol_id = 0;
171  auto block_length = ( number < _nof_large_source_blocks ) ? _large_source_block_length : _small_source_block_length;
172 
173  for (int i = 0; i < block_length; i++) {
174  auto symbol_length = std::min(remaining_size, (size_t)_meta.fec_oti.encoding_symbol_length);
175  assert(buffer_ptr + symbol_length <= _buffer + _meta.fec_oti.transfer_length);
176 
177  SourceBlock::Symbol symbol{.data = buffer_ptr, .length = symbol_length, .complete = false};
178  block.symbols[ symbol_id++ ] = symbol;
179 
180  remaining_size -= symbol_length;
181  buffer_ptr += symbol_length;
182 
183  if (remaining_size <= 0) break;
184  }
185  _source_blocks[number++] = block;
186  }
187 }
188 
189 auto LibFlute::File::get_next_symbols(size_t max_size) -> std::vector<EncodingSymbol>
190 {
191  auto block = _source_blocks.begin();
192  int nof_symbols = std::ceil((float)(max_size - 4) / (float)_meta.fec_oti.encoding_symbol_length);
193  auto cnt = 0;
194  std::vector<EncodingSymbol> symbols;
195 
196  for (auto& block : _source_blocks) {
197  if (cnt >= nof_symbols) break;
198 
199  if (!block.second.complete) {
200  for (auto& symbol : block.second.symbols) {
201  if (cnt >= nof_symbols) break;
202 
203  if (!symbol.second.complete && !symbol.second.queued) {
204  symbols.emplace_back(symbol.first, block.first, symbol.second.data, symbol.second.length, _meta.fec_oti.encoding_id);
205  symbol.second.queued = true;
206  cnt++;
207  }
208  }
209  }
210  }
211  return symbols;
212 
213 }
214 
215 auto LibFlute::File::mark_completed(const std::vector<EncodingSymbol>& symbols, bool success) -> void
216 {
217  for (auto& symbol : symbols) {
218  auto block = _source_blocks.find(symbol.source_block_number());
219  if (block != _source_blocks.end()) {
220  auto sym = block->second.symbols.find(symbol.id());
221  if (sym != block->second.symbols.end()) {
222  sym->second.queued = false;
223  sym->second.complete = success;
224  }
225  check_source_block_completion(block->second);
226  check_file_completion();
227  }
228  }
229 }
A class for handling FEC encoding symbols.
void put_symbol(const EncodingSymbol &symbol)
Write the data from an encoding symbol into the appropriate place in the buffer.
Definition: File.cpp:98
virtual ~File()
Default destructor.
Definition: File.cpp:90
File(LibFlute::FileDeliveryTable::FileEntry entry)
Create a file from an FDT entry (used for reception)
Definition: File.cpp:31
void mark_completed(const std::vector< EncodingSymbol > &symbols, bool success)
Mark encoding symbols as completed.
Definition: File.cpp:215
std::vector< EncodingSymbol > get_next_symbols(size_t max_size)
Get the next encoding symbols that fit in max_size bytes.
Definition: File.cpp:189
OTI values struct.
Definition: flute_types.h:53
uint64_t transfer_length
Definition: flute_types.h:55
An entry for a file in the FDT.