Warning: This is a development version. The latest stable version is Version 4.0.1.
In this example shows how to use the API of rely
to implement encoding
and decoding.
The example will walk through the basic functionality of class encoder and class decoder, and discuss how to use them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | #include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <regex>
#include <vector>
// Step 1 include the relevant header files.
// Encoder and decoders
#include <rely/decoder.hpp>
#include <rely/encoder.hpp>
// A real application would be encode and decode payloads from some
// source such as a video feed or network card.
// In this example such a source will be simulated by producing some random
// data.
int main()
{
// The size of packets produced by the encoder and consumed by the decoder.
// Make sure this is set so that packets generated by rely is less than the
// network's MTU.
std::size_t packet_bytes = 1400U;
// The timeout specifies how long the packets are kept.
// A higher value will result in a higher added latency but a greater level
// of protection. So it should be maximimed for the given use case.
int64_t timeout = 120U;
// The maximum number of packets that will be held by the decoder. This
// effectively limits the maximum memory consumption of the algorithm. In
// practice it makes sense to maximize this parameter as much as possible.
// If packets are removed due to a full stream rather than a timeout,
// decodable data be dropped.
std::size_t max_stream_size = 125U;
// Create the encoder and decoder
rely::encoder encoder;
rely::decoder decoder;
encoder.configure(packet_bytes, timeout, max_stream_size);
decoder.configure(packet_bytes, timeout, max_stream_size);
// To control the amount of repair produced two values are specified:
// 1. The number of source symbols between each repair phase.
uint64_t repair_interval = 9;
// 2. The number of packets with repair symbols in each repair phase.
uint64_t repair_target = 1;
// To get a good decoding performance the rate needs to be higher than the
// packet loss probability.
// In this case our rate is:
// repair_target / (repair_target + repair_interval) = 1/(1 + 10) ~ 0.09%
encoder.set_repair(repair_interval, repair_target);
// Maximum number of interations
const uint32_t max_iterations = 100000U;
uint32_t iterations = 0;
// Counter for keeping track of the number of lost packets
uint32_t lost = 0;
// In this example, time is simply simulated.
int64_t time = 0;
std::cout << "Running ..." << std::endl;
while (iterations < max_iterations)
{
// Increment the time.
time += 1;
// Create a payload, the size is between 50 and 1350 bytes.
std::vector<uint8_t> payload(50 + (rand() % 1300));
// Fill the payload with random data
std::generate(payload.begin(), payload.end(), rand);
// Insert the data into the encoder.
encoder.consume(payload.data(), payload.size(), time);
// Handle the the output from the encoder
while (encoder.can_produce())
{
// Simulate 5% packet loss
if (rand() % 100 < 5)
{
// Advance the encoder
encoder.produce_next();
lost += 1;
continue;
}
// Feed the produced packets from the encoder to the decoder.
decoder.consume(encoder.produce_data(), encoder.produce_bytes(),
time);
// Advance the encoder
encoder.produce_next();
}
// Handle the the output from the decoder
while (decoder.can_produce())
{
// Read the decoded payload
// In a real world scenario the payload would be forwarded to
// the appropriate application.
assert(decoder.produce_data() != nullptr);
assert(decoder.produce_bytes() != 0U);
// Advance the decoder to see if more decoded payloads are
// available.
decoder.produce_next();
}
++iterations;
}
uint64_t packets_produced =
encoder.counter_value(rely::counter::packets_produced);
uint64_t payloads_consumed =
encoder.counter_value(rely::counter::payloads_consumed);
uint64_t payloads_produced =
decoder.counter_value(rely::counter::payloads_produced);
std::cout << "\nStatistics\n";
std::cout << "Packets produced = " << packets_produced << "\n";
std::cout << "Lost packets = " << lost << "\n";
std::cout << "Packet loss rate = "
<< (lost / double(packets_produced)) * 100 << "%\n";
std::cout << "Repair rate = " << encoder.repair_rate() * 100 << "%\n";
std::cout << "Payloads consumed = " << payloads_consumed << "\n";
std::cout << "Payloads produced = " << payloads_produced << "\n";
std::cout << "Payload loss rate = "
<< (1.0 - payloads_produced / double(payloads_consumed)) * 100
<< "%\n";
std::cout << "Encoder performance counters:\n"
<< encoder.counters_to_json() << "\n";
std::cout << "Decoder performance counters:\n"
<< decoder.counters_to_json() << "\n";
return 0;
}
|