Warning: This is a development version. The latest stable version is Version 7.0.0.

Code Example: Encode and Decode

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
#include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <regex>
#include <vector>

// Include the relevant header files.
#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.
    std::size_t repair_interval = 9;

    // 2. The number of packets with repair symbols in each repair phase.
    std::size_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_trigger(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 trigger rate = " << encoder.repair_trigger_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;
}