mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 12:15:34 +08:00
Add library OpenFEC v1.4.2
This commit is contained in:
471
tests/fec/simple_client.cpp
Executable file
471
tests/fec/simple_client.cpp
Executable file
@@ -0,0 +1,471 @@
|
||||
/* $Id: simple_client.c 216 2014-12-13 13:21:07Z roca $ */
|
||||
/*
|
||||
* OpenFEC.org AL-FEC Library.
|
||||
* (c) Copyright 2009-2014 INRIA - All rights reserved
|
||||
* Contact: vincent.roca@inria.fr
|
||||
*
|
||||
* This software is governed by the CeCILL-C license under French law and
|
||||
* abiding by the rules of distribution of free software. You can use,
|
||||
* modify and/ or redistribute the software under the terms of the CeCILL-C
|
||||
* license as circulated by CEA, CNRS and INRIA at the following URL
|
||||
* "http://www.cecill.info".
|
||||
*
|
||||
* As a counterpart to the access to the source code and rights to copy,
|
||||
* modify and redistribute granted by the license, users are provided only
|
||||
* with a limited warranty and the software's author, the holder of the
|
||||
* economic rights, and the successive licensors have only limited
|
||||
* liability.
|
||||
*
|
||||
* In this respect, the user's attention is drawn to the risks associated
|
||||
* with loading, using, modifying and/or developing or reproducing the
|
||||
* software by the user in light of its specific status of free software,
|
||||
* that may mean that it is complicated to manipulate, and that also
|
||||
* therefore means that it is reserved for developers and experienced
|
||||
* professionals having in-depth computer knowledge. Users are therefore
|
||||
* encouraged to load and test the software's suitability as regards their
|
||||
* requirements in conditions enabling the security of their systems and/or
|
||||
* data to be ensured and, more generally, to use and operate it in the
|
||||
* same conditions as regards security.
|
||||
*
|
||||
* The fact that you are presently reading this means that you have had
|
||||
* knowledge of the CeCILL-C license and that you accept its terms.
|
||||
*/
|
||||
|
||||
/* this is the decoder */
|
||||
#define OF_USE_DECODER
|
||||
|
||||
#include "simple_client_server.h"
|
||||
|
||||
/*
|
||||
* Chose which decoding method to use... Both should be equivalent.
|
||||
*/
|
||||
#define USE_DECODE_WITH_NEW_SYMBOL
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
/**
|
||||
* Opens and initializes a UDP socket, ready for receptions.
|
||||
*/
|
||||
static SOCKET init_socket(void);
|
||||
|
||||
/**
|
||||
* This function receives packets on the incoming UDP socket.
|
||||
* It allocates a buffer of size *len and updates the pkt/len arguments with
|
||||
* what has been actually received. It works in blocking mode the first time
|
||||
* it's called (as the client can be launched a few seconds before the server),
|
||||
* and after that in non blocking (i.e. polling) mode. If no packet is received
|
||||
* even after having waited a certain time (0.2s), it return OF_STATUS_FAILURE
|
||||
* to indicate that the sender probably stopped all transmissions.
|
||||
*/
|
||||
static of_status_t get_next_pkt(SOCKET so, void **pkt, INT32 *len);
|
||||
|
||||
/**
|
||||
* Dumps len32 32-bit words of a buffer (typically a symbol).
|
||||
*/
|
||||
static void dump_buffer_32(void *buf, UINT32 len32);
|
||||
|
||||
/*************************************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
of_codec_id_t codec_id; /* identifier of the codec to use */
|
||||
of_session_t *ses = NULL; /* openfec codec instance identifier */
|
||||
of_parameters_t *params =
|
||||
NULL; /* structure used to initialize the openfec session */
|
||||
void **recvd_symbols_tab =
|
||||
NULL; /* table containing pointers to received symbols (no FPI here).
|
||||
* The allocated buffer start 4 bytes (i.e., sizeof(FPI)) before...
|
||||
*/
|
||||
void **src_symbols_tab = NULL; /* table containing pointers to the source
|
||||
symbol buffers (no FPI here) */
|
||||
UINT32 symb_sz_32 =
|
||||
SYMBOL_SIZE / 4; /* symbol size in units of 32 bit words */
|
||||
UINT32 k; /* number of source symbols in the block */
|
||||
UINT32 n; /* number of encoding symbols (i.e. source + repair) in the block */
|
||||
UINT32 esi; /* Encoding Symbol ID, used to identify each encoding symbol */
|
||||
SOCKET so =
|
||||
INVALID_SOCKET; /* UDP socket for server => client communications */
|
||||
void *pkt_with_fpi = NULL; /* pointer to a buffer containing the FPI followed
|
||||
by the fixed size packet */
|
||||
fec_oti_t *fec_oti = NULL; /* FEC Object Transmission Information as received
|
||||
from the server */
|
||||
INT32 len; /* len of the received packet */
|
||||
SOCKADDR_IN dst_host;
|
||||
UINT32 n_received =
|
||||
0; /* number of symbols (source or repair) received so far */
|
||||
bool done = false; /* true as soon as all source symbols have been received or
|
||||
recovered */
|
||||
UINT32 ret;
|
||||
|
||||
/* First of all, initialize the UDP socket and wait for the FEC OTI to be
|
||||
* received. This is absolutely required to synchronize encoder and decoder.
|
||||
* We assume this first packet is NEVER lost otherwise decoding is not
|
||||
* possible. In practice the sender can transmit it periodically, or it is
|
||||
* sent through a separate reliable channel. */
|
||||
if ((so = init_socket()) == INVALID_SOCKET) {
|
||||
OF_PRINT_ERROR(("Error initializing socket!\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
len = sizeof(fec_oti_t); /* size of the expected packet */
|
||||
if ((ret = get_next_pkt(so, (void **)&fec_oti, &len)) != OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(("get_next_pkt failed (FEC OTI reception)\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if (len != sizeof(fec_oti_t)) {
|
||||
OF_PRINT_ERROR(
|
||||
("FEC OTI reception failed: bad size, expected %lu but received %d "
|
||||
"instead\n",
|
||||
sizeof(fec_oti_t), ret))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* convert back to host endianess */
|
||||
codec_id = (of_codec_id_t)ntohl(fec_oti->codec_id);
|
||||
fec_oti->codec_id = ntohl(fec_oti->codec_id);
|
||||
k = fec_oti->k = ntohl(fec_oti->k);
|
||||
n = fec_oti->n = ntohl(fec_oti->n);
|
||||
|
||||
printf("\nReceiving packets from %s/%d\n", DEST_IP, DEST_PORT);
|
||||
|
||||
/* and check the correctness of data received */
|
||||
if (k > n || k > 40000 || n > 40000) {
|
||||
OF_PRINT_ERROR(
|
||||
("Invalid FEC OTI received: k=%u or n=%u received are probably out of "
|
||||
"range\n",
|
||||
k, n))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* now we know which codec the sender has used along with the codec
|
||||
* parameters, we can prepar the params structure accordingly */
|
||||
switch (codec_id) {
|
||||
case OF_CODEC_REED_SOLOMON_GF_2_M_STABLE: {
|
||||
/* fill in the code specific part of the of_..._parameters_t structure */
|
||||
of_rs_2_m_parameters_t *my_params;
|
||||
|
||||
printf(
|
||||
"\nInitialize a Reed-Solomon over GF(2^m) codec instance, (n, "
|
||||
"k)=(%u, %u)...\n",
|
||||
n, k);
|
||||
if ((my_params = (of_rs_2_m_parameters_t *)calloc(
|
||||
1, sizeof(*my_params))) == NULL) {
|
||||
OF_PRINT_ERROR(("no memory for codec %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
my_params->m = 8;
|
||||
params = (of_parameters_t *)my_params;
|
||||
break;
|
||||
}
|
||||
|
||||
case OF_CODEC_LDPC_STAIRCASE_STABLE: {
|
||||
/* fill in the code specific part of the of_..._parameters_t structure */
|
||||
of_ldpc_parameters_t *my_params;
|
||||
|
||||
printf(
|
||||
"\nInitialize an LDPC-Staircase codec instance, (n, k)=(%u, %u)...\n",
|
||||
n, k);
|
||||
if ((my_params = (of_ldpc_parameters_t *)calloc(1, sizeof(*my_params))) ==
|
||||
NULL) {
|
||||
OF_PRINT_ERROR(("no memory for codec %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
my_params->prng_seed = rand();
|
||||
my_params->N1 = 7;
|
||||
params = (of_parameters_t *)my_params;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
OF_PRINT_ERROR(
|
||||
("Invalid FEC OTI received: codec_id=%u received is not valid\n",
|
||||
codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
params->nb_source_symbols =
|
||||
k; /* fill in the generic part of the of_parameters_t structure */
|
||||
params->nb_repair_symbols = n - k;
|
||||
params->encoding_symbol_length = SYMBOL_SIZE;
|
||||
|
||||
/* Open and initialize the openfec decoding session now that we know the
|
||||
* various parameters used by the sender/encoder... */
|
||||
if ((ret = of_create_codec_instance(&ses, codec_id, OF_DECODER, VERBOSITY)) !=
|
||||
OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(("of_create_codec_instance() failed\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if (of_set_fec_parameters(ses, params) != OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(
|
||||
("of_set_fec_parameters() failed for codec_id %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
printf("\nDecoding in progress. Waiting for new packets...\n");
|
||||
|
||||
/* allocate a table for the received encoding symbol buffers. We'll update it
|
||||
* progressively */
|
||||
if (((recvd_symbols_tab = (void **)calloc(n, sizeof(void *))) == NULL) ||
|
||||
((src_symbols_tab = (void **)calloc(n, sizeof(void *))) == NULL)) {
|
||||
OF_PRINT_ERROR(("no memory (calloc failed for enc_symbols_tab, n=%u)\n", n))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
len = SYMBOL_SIZE + 4; /* size of the expected packet */
|
||||
#ifdef USE_DECODE_WITH_NEW_SYMBOL
|
||||
/*
|
||||
* this is the standard method: submit each fresh symbol to the library ASAP,
|
||||
* upon reception (or later, but using the standard
|
||||
* of_decode_with_new_symbol() function).
|
||||
*/
|
||||
while ((ret = get_next_pkt(so, &pkt_with_fpi, &len)) == OF_STATUS_OK) {
|
||||
/* OK, new packet received... */
|
||||
n_received++;
|
||||
esi = ntohl(*(UINT32 *)pkt_with_fpi);
|
||||
if (esi > n) /* a sanity check, in case... */
|
||||
{
|
||||
OF_PRINT_ERROR(("invalid esi=%u received in a packet's FPI\n", esi))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
recvd_symbols_tab[esi] = (char *)pkt_with_fpi + 4; /* remember */
|
||||
printf("%05d => receiving symbol esi=%u (%s)\n", n_received, esi,
|
||||
(esi < k) ? "src" : "repair");
|
||||
if (of_decode_with_new_symbol(ses, (char *)pkt_with_fpi + 4, esi) ==
|
||||
OF_STATUS_ERROR) {
|
||||
OF_PRINT_ERROR(("of_decode_with_new_symbol() failed\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* check if completed in case we received k packets or more */
|
||||
if ((n_received >= k) && (of_is_decoding_complete(ses) == true)) {
|
||||
/* done, we recovered everything, no need to continue reception */
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
len = SYMBOL_SIZE +
|
||||
4; /* make sure len contains the size of the expected packet */
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* this is the alternative method: wait to receive all the symbols, then
|
||||
* submit them all to the library using the of_set_available_symbols()
|
||||
* function. In that case decoding will occur during the of_finish_decoding()
|
||||
* call.
|
||||
*/
|
||||
while ((ret = get_next_pkt(so, &pkt_with_fpi, &len)) == OF_STATUS_OK) {
|
||||
/* OK, new packet received... */
|
||||
n_received++;
|
||||
esi = ntohl(*(UINT32 *)pkt_with_fpi);
|
||||
if (esi > n) /* a sanity check, in case... */
|
||||
{
|
||||
OF_PRINT_ERROR(("invalid esi=%u received in a packet's FPI\n", esi))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
recvd_symbols_tab[esi] = (char *)pkt_with_fpi + 4; /* remember */
|
||||
printf("%05d => receiving symbol esi=%u (%s)\n", n_received, esi,
|
||||
(esi < k) ? "src" : "repair");
|
||||
len = SYMBOL_SIZE +
|
||||
4; /* make sure len contains the size of the expected packet */
|
||||
}
|
||||
/* now we received everything, submit them all to the codec if we received a
|
||||
* sufficiently high number of symbols (i.e. >= k) */
|
||||
if (n_received >= k &&
|
||||
(of_set_available_symbols(ses, recvd_symbols_tab) != OF_STATUS_OK)) {
|
||||
OF_PRINT_ERROR(("of_set_available_symbols() failed with error (%d)\n", ret))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
if (!done && (ret == OF_STATUS_FAILURE) && (n_received >= k)) {
|
||||
/* there's no packet any more but we received at least k, and the use of
|
||||
* of_decode_with_new_symbol() didn't succedd to decode, so try with
|
||||
* of_finish_decoding. NB: this is useless with MDS codes (e.g.
|
||||
* Reed-Solomon), but it is essential with LDPC-Staircase as
|
||||
* of_decode_with_new_symbol performs ITerative decoding, whereas
|
||||
* of_finish_decoding performs ML decoding */
|
||||
ret = of_finish_decoding(ses);
|
||||
if (ret == OF_STATUS_ERROR || ret == OF_STATUS_FATAL_ERROR) {
|
||||
OF_PRINT_ERROR(("of_finish_decoding() failed with error (%d)\n", ret))
|
||||
ret = -1;
|
||||
goto end;
|
||||
} else if (ret == OF_STATUS_OK) {
|
||||
done = true;
|
||||
}
|
||||
/* else ret == OF_STATUS_FAILURE, meaning of_finish_decoding didn't manage
|
||||
* to recover all source symbols */
|
||||
}
|
||||
if (done) {
|
||||
/* finally, get a copy of the pointers to all the source symbols, those
|
||||
* received (that we already know) and those decoded. In case of received
|
||||
* symbols, the library does not change the pointers (same value). */
|
||||
if (of_get_source_symbols_tab(ses, src_symbols_tab) != OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(("of_get_source_symbols_tab() failed\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
printf("\nDone! All source symbols rebuilt after receiving %u packets\n",
|
||||
n_received);
|
||||
if (VERBOSITY > 1) {
|
||||
for (esi = 0; esi < k; esi++) {
|
||||
printf("src[%u]= ", esi);
|
||||
dump_buffer_32(src_symbols_tab[esi], 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
"\nFailed to recover all erased source symbols even after receiving %u "
|
||||
"packets\n",
|
||||
n_received);
|
||||
}
|
||||
|
||||
end:
|
||||
/* Cleanup everything... */
|
||||
if (so != INVALID_SOCKET) {
|
||||
close(so);
|
||||
}
|
||||
if (ses) {
|
||||
of_release_codec_instance(ses);
|
||||
}
|
||||
if (params) {
|
||||
free(params);
|
||||
}
|
||||
if (fec_oti) {
|
||||
free(fec_oti);
|
||||
}
|
||||
if (recvd_symbols_tab && src_symbols_tab) {
|
||||
for (esi = 0; esi < n; esi++) {
|
||||
if (recvd_symbols_tab[esi]) {
|
||||
/* this is a symbol received from the network, without its FPI that
|
||||
* starts 4 bytes before */
|
||||
free((char *)recvd_symbols_tab[esi] - 4);
|
||||
} else if (esi < k && src_symbols_tab[esi]) {
|
||||
/* this is a source symbol decoded by the openfec codec, so free it */
|
||||
ASSERT(recvd_symbols_tab[esi] == NULL);
|
||||
free(src_symbols_tab[esi]);
|
||||
}
|
||||
}
|
||||
free(recvd_symbols_tab);
|
||||
free(src_symbols_tab);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and initializes a UDP socket, ready for receptions.
|
||||
*/
|
||||
static SOCKET init_socket() {
|
||||
SOCKET s;
|
||||
SOCKADDR_IN bindAddr;
|
||||
UINT32 sz = 1024 * 1024;
|
||||
|
||||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
|
||||
printf("Error: call to socket() failed\n");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
bindAddr.sin_family = AF_INET;
|
||||
bindAddr.sin_port = htons((short)DEST_PORT);
|
||||
bindAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
if (bind(s, (SOCKADDR *)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
||||
printf("bind() failed. Port %d may be already in use\n", DEST_PORT);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
/* increase the reception socket size as the default value may lead to a high
|
||||
* datagram loss rate */
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) == -1) {
|
||||
printf("setsockopt() failed to set new UDP socket size to %u\n", sz);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives packets on the incoming UDP socket.
|
||||
*/
|
||||
static of_status_t get_next_pkt(SOCKET so, void **pkt, INT32 *len) {
|
||||
static bool first_call = true;
|
||||
INT32 saved_len =
|
||||
*len; /* save it, in case we need to do several calls to recvfrom */
|
||||
|
||||
if ((*pkt = malloc(saved_len)) == NULL) {
|
||||
OF_PRINT_ERROR(("no memory (malloc failed for p)\n"))
|
||||
return OF_STATUS_ERROR;
|
||||
}
|
||||
if (first_call) {
|
||||
/* the first time we must be in blocking mode since the flow may be launched
|
||||
* after a few seconds... */
|
||||
first_call = false;
|
||||
*len = recvfrom(so, *pkt, saved_len, 0, NULL, NULL);
|
||||
if (*len < 0) {
|
||||
/* this is an anormal error, exit */
|
||||
perror("recvfrom");
|
||||
OF_PRINT_ERROR(("recvfrom failed\n"))
|
||||
free(*pkt); /* don't forget to free it, otherwise it will leak */
|
||||
return OF_STATUS_ERROR;
|
||||
}
|
||||
/* set the non blocking mode for this socket now that the flow has been
|
||||
* launched */
|
||||
if (fcntl(so, F_SETFL, O_NONBLOCK) < 0) {
|
||||
OF_PRINT_ERROR(("ERROR, fcntl failed to set non blocking mode\n"))
|
||||
exit(-1);
|
||||
}
|
||||
if (VERBOSITY > 1)
|
||||
printf("%s: pkt received 0, len=%u\n", __FUNCTION__, *len);
|
||||
return OF_STATUS_OK;
|
||||
}
|
||||
/* otherwise we are in non-blocking mode... */
|
||||
*len = recvfrom(so, *pkt, saved_len, 0, NULL, NULL);
|
||||
if (*len > 0) {
|
||||
if (VERBOSITY > 1)
|
||||
printf("%s: pkt received 1, len=%u\n", __FUNCTION__, *len);
|
||||
return OF_STATUS_OK;
|
||||
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
/* no packet available, sleep a little bit and retry */
|
||||
SLEEP(200); /* (in milliseconds) */
|
||||
*len = recvfrom(so, *pkt, saved_len, 0, NULL, NULL);
|
||||
if (*len > 0) {
|
||||
if (VERBOSITY > 1)
|
||||
printf("%s: pkt received 2, len=%u\n", __FUNCTION__, *len);
|
||||
return OF_STATUS_OK;
|
||||
} else {
|
||||
/* that's the end of the test, no packet available any more, we're sure of
|
||||
* that now... */
|
||||
if (VERBOSITY > 1)
|
||||
printf("%s: end of test, no packet after the sleep\n", __FUNCTION__);
|
||||
free(*pkt); /* don't forget to free it, otherwise it will leak */
|
||||
return OF_STATUS_FAILURE;
|
||||
}
|
||||
} else {
|
||||
/* this is an anormal error, exit */
|
||||
perror("recvfrom");
|
||||
OF_PRINT_ERROR(("ERROR, recvfrom failed\n"))
|
||||
free(*pkt); /* don't forget to free it, otherwise it will leak */
|
||||
return OF_STATUS_ERROR;
|
||||
}
|
||||
return OF_STATUS_ERROR; /* never called */
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps len32 32-bit words of a buffer (typically a symbol).
|
||||
*/
|
||||
static void dump_buffer_32(void *buf, UINT32 len32) {
|
||||
UINT32 *ptr;
|
||||
UINT32 j = 0;
|
||||
|
||||
printf("0x");
|
||||
for (ptr = (UINT32 *)buf; len32 > 0; len32--, ptr++) {
|
||||
/* convert to big endian format to be sure of byte order */
|
||||
printf("%08X", htonl(*ptr));
|
||||
if (++j == 10) {
|
||||
j = 0;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
103
tests/fec/simple_client_server.h
Executable file
103
tests/fec/simple_client_server.h
Executable file
@@ -0,0 +1,103 @@
|
||||
/* $Id: simple_client_server.h 207 2014-12-10 19:47:50Z roca $ */
|
||||
/*
|
||||
* OpenFEC.org AL-FEC Library.
|
||||
* (c) Copyright 2009-2014 INRIA - All rights reserved
|
||||
* Contact: vincent.roca@inria.fr
|
||||
*
|
||||
* This software is governed by the CeCILL-C license under French law and
|
||||
* abiding by the rules of distribution of free software. You can use,
|
||||
* modify and/ or redistribute the software under the terms of the CeCILL-C
|
||||
* license as circulated by CEA, CNRS and INRIA at the following URL
|
||||
* "http://www.cecill.info".
|
||||
*
|
||||
* As a counterpart to the access to the source code and rights to copy,
|
||||
* modify and redistribute granted by the license, users are provided only
|
||||
* with a limited warranty and the software's author, the holder of the
|
||||
* economic rights, and the successive licensors have only limited
|
||||
* liability.
|
||||
*
|
||||
* In this respect, the user's attention is drawn to the risks associated
|
||||
* with loading, using, modifying and/or developing or reproducing the
|
||||
* software by the user in light of its specific status of free software,
|
||||
* that may mean that it is complicated to manipulate, and that also
|
||||
* therefore means that it is reserved for developers and experienced
|
||||
* professionals having in-depth computer knowledge. Users are therefore
|
||||
* encouraged to load and test the software's suitability as regards their
|
||||
* requirements in conditions enabling the security of their systems and/or
|
||||
* data to be ensured and, more generally, to use and operate it in the
|
||||
* same conditions as regards security.
|
||||
*
|
||||
* The fact that you are presently reading this means that you have had
|
||||
* knowledge of the CeCILL-C license and that you accept its terms.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h> /* for gettimeofday */
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "lib_common/of_openfec_api.h"
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OS dependant definitions
|
||||
*/
|
||||
#define SOCKET int
|
||||
#define SOCKADDR struct sockaddr
|
||||
#define SOCKADDR_IN struct sockaddr_in
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define closesocket close
|
||||
#define SLEEP(t) usleep(t * 1000)
|
||||
|
||||
/*
|
||||
* Simulation parameters...
|
||||
* Change as required
|
||||
*/
|
||||
#define SYMBOL_SIZE \
|
||||
1024 /* symbol size, in bytes (must be multiple of 4 in this simple example) \
|
||||
*/
|
||||
#define DEFAULT_K 100 /* default k value */
|
||||
#define CODE_RATE 0.667 /* k/n = 2/3 means we add 50% of repair symbols */
|
||||
#define LOSS_RATE \
|
||||
0.30 /* we consider 30% of packet losses... It assumes there's no additional \
|
||||
loss during UDP transmissions */
|
||||
|
||||
#define VERBOSITY \
|
||||
2 /* Define the verbosity level: \
|
||||
* 0 : no trace \
|
||||
* 1 : main traces \
|
||||
* 2 : full traces with packet dumps */
|
||||
|
||||
#define DEST_IP "127.0.0.1" /* Destination IPv4 address */
|
||||
#define DEST_PORT 10978 /* Destination port (UDP) */
|
||||
|
||||
/*
|
||||
* Simplified FEC Object Transmission Information structure, used to synchronize
|
||||
* sender and receiver.
|
||||
*
|
||||
* NB: all the fields MUST be in Network Endian while sent over the network, so
|
||||
* use htonl (resp. ntohl) at the sender (resp. receiver).
|
||||
*/
|
||||
typedef struct {
|
||||
UINT32 codec_id; /* identifies the code/codec being used. In practice, the
|
||||
* "FEC encoding ID" that identifies the FEC Scheme should be
|
||||
* used instead (see [RFC5052]). In our example, we are not
|
||||
* compliant with the RFCs anyway, so keep it simple. */
|
||||
UINT32 k;
|
||||
UINT32 n;
|
||||
} fec_oti_t;
|
||||
362
tests/fec/simple_server.cpp
Executable file
362
tests/fec/simple_server.cpp
Executable file
@@ -0,0 +1,362 @@
|
||||
/* $Id: simple_server.c 216 2014-12-13 13:21:07Z roca $ */
|
||||
/*
|
||||
* OpenFEC.org AL-FEC Library.
|
||||
* (c) Copyright 2009-2014 INRIA - All rights reserved
|
||||
* Contact: vincent.roca@inria.fr
|
||||
*
|
||||
* This software is governed by the CeCILL-C license under French law and
|
||||
* abiding by the rules of distribution of free software. You can use,
|
||||
* modify and/ or redistribute the software under the terms of the CeCILL-C
|
||||
* license as circulated by CEA, CNRS and INRIA at the following URL
|
||||
* "http://www.cecill.info".
|
||||
*
|
||||
* As a counterpart to the access to the source code and rights to copy,
|
||||
* modify and redistribute granted by the license, users are provided only
|
||||
* with a limited warranty and the software's author, the holder of the
|
||||
* economic rights, and the successive licensors have only limited
|
||||
* liability.
|
||||
*
|
||||
* In this respect, the user's attention is drawn to the risks associated
|
||||
* with loading, using, modifying and/or developing or reproducing the
|
||||
* software by the user in light of its specific status of free software,
|
||||
* that may mean that it is complicated to manipulate, and that also
|
||||
* therefore means that it is reserved for developers and experienced
|
||||
* professionals having in-depth computer knowledge. Users are therefore
|
||||
* encouraged to load and test the software's suitability as regards their
|
||||
* requirements in conditions enabling the security of their systems and/or
|
||||
* data to be ensured and, more generally, to use and operate it in the
|
||||
* same conditions as regards security.
|
||||
*
|
||||
* The fact that you are presently reading this means that you have had
|
||||
* knowledge of the CeCILL-C license and that you accept its terms.
|
||||
*/
|
||||
|
||||
/* this is the encoder */
|
||||
#define OF_USE_ENCODER
|
||||
|
||||
#include "simple_client_server.h"
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
/**
|
||||
* Opens and initializes a UDP socket, ready for receptions.
|
||||
*/
|
||||
static SOCKET init_socket(SOCKADDR_IN *dst_host);
|
||||
|
||||
/**
|
||||
* Shuffles the array randomly.
|
||||
*/
|
||||
static void randomize_array(UINT32 **array, UINT32 arrayLen);
|
||||
|
||||
/**
|
||||
* Dumps len32 32-bit words of a buffer (typically a symbol).
|
||||
*/
|
||||
static void dump_buffer_32(void *buf, UINT32 len32);
|
||||
|
||||
/*************************************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
of_codec_id_t codec_id; /* identifier of the codec to use */
|
||||
of_session_t *ses = NULL; /* openfec codec instance identifier */
|
||||
of_parameters_t *params =
|
||||
NULL; /* structure used to initialize the openfec session */
|
||||
void **enc_symbols_tab = NULL; /* table containing pointers to the encoding
|
||||
(i.e. source + repair) symbols buffers */
|
||||
UINT32 symb_sz_32 =
|
||||
SYMBOL_SIZE / 4; /* symbol size in units of 32 bit words */
|
||||
UINT32 k; /* number of source symbols in the block */
|
||||
UINT32 n; /* number of encoding symbols (i.e. source + repair) in the block */
|
||||
UINT32 esi; /* Encoding Symbol ID, used to identify each encoding symbol */
|
||||
UINT32 i;
|
||||
UINT32 *rand_order = NULL; /* table used to determine a random transmission
|
||||
* order. This randomization process is essential
|
||||
* for LDPC-Staircase optimal performance */
|
||||
SOCKET so =
|
||||
INVALID_SOCKET; /* UDP socket for server => client communications */
|
||||
char *pkt_with_fpi = NULL; /* buffer containing a fixed size packet plus a
|
||||
header consisting only of the FPI */
|
||||
fec_oti_t
|
||||
fec_oti; /* FEC Object Transmission Information as sent to the client */
|
||||
INT32 lost_after_index = -1; /* all the packets to send after this index are
|
||||
considered as lost during transmission */
|
||||
SOCKADDR_IN dst_host;
|
||||
UINT32 ret = -1;
|
||||
|
||||
if (argc == 1) {
|
||||
/* k value is ommited, so use default */
|
||||
k = DEFAULT_K;
|
||||
} else {
|
||||
k = atoi(argv[1]);
|
||||
}
|
||||
n = (UINT32)floor((double)k / (double)CODE_RATE);
|
||||
/* Choose which codec is the most appropriate. If small enough, choose
|
||||
* Reed-Solomon (with m=8), otherwise LDPC-Staircase. Then finish the openfec
|
||||
* session initialization accordingly */
|
||||
if (n <= 255) {
|
||||
/* fill in the code specific part of the of_..._parameters_t structure */
|
||||
of_rs_2_m_parameters_t *my_params;
|
||||
|
||||
printf(
|
||||
"\nInitialize a Reed-Solomon over GF(2^m) codec instance, (n, k)=(%u, "
|
||||
"%u)...\n",
|
||||
n, k);
|
||||
codec_id = OF_CODEC_REED_SOLOMON_GF_2_M_STABLE;
|
||||
if ((my_params = (of_rs_2_m_parameters_t *)calloc(1, sizeof(*my_params))) ==
|
||||
NULL) {
|
||||
OF_PRINT_ERROR(("no memory for codec %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
my_params->m = 8;
|
||||
params = (of_parameters_t *)my_params;
|
||||
} else {
|
||||
/* fill in the code specific part of the of_..._parameters_t structure */
|
||||
of_ldpc_parameters_t *my_params;
|
||||
|
||||
printf(
|
||||
"\nInitialize an LDPC-Staircase codec instance, (n, k)=(%u, %u)...\n",
|
||||
n, k);
|
||||
codec_id = OF_CODEC_LDPC_STAIRCASE_STABLE;
|
||||
if ((my_params = (of_ldpc_parameters_t *)calloc(1, sizeof(*my_params))) ==
|
||||
NULL) {
|
||||
OF_PRINT_ERROR(("no memory for codec %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
my_params->prng_seed = rand();
|
||||
my_params->N1 = 7;
|
||||
params = (of_parameters_t *)my_params;
|
||||
}
|
||||
params->nb_source_symbols =
|
||||
k; /* fill in the generic part of the of_parameters_t structure */
|
||||
params->nb_repair_symbols = n - k;
|
||||
params->encoding_symbol_length = SYMBOL_SIZE;
|
||||
|
||||
/* Open and initialize the openfec session now... */
|
||||
if ((ret = of_create_codec_instance(&ses, codec_id, OF_ENCODER, VERBOSITY)) !=
|
||||
OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(("of_create_codec_instance() failed\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if (of_set_fec_parameters(ses, params) != OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(
|
||||
("of_set_fec_parameters() failed for codec_id %d\n", codec_id))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Allocate and initialize our source symbols...
|
||||
* In case of a file transmission, the opposite takes place: the file is read
|
||||
* and partitionned into a set of k source symbols. At the end, it's just
|
||||
* equivalent since there is a set of k source symbols that need to be sent
|
||||
* reliably thanks to an FEC encoding. */
|
||||
printf("\nFilling source symbols...\n");
|
||||
if ((enc_symbols_tab = (void **)calloc(n, sizeof(void *))) == NULL) {
|
||||
OF_PRINT_ERROR(("no memory (calloc failed for enc_symbols_tab, n=%u)\n", n))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* In order to detect corruption, the first symbol is filled with 0x1111...,
|
||||
* the second with 0x2222..., etc. NB: the 0x0 value is avoided since it is a
|
||||
* neutral element in the target finite fields, i.e. it prevents the detection
|
||||
* of symbol corruption */
|
||||
for (esi = 0; esi < k; esi++) {
|
||||
if ((enc_symbols_tab[esi] = calloc(symb_sz_32, sizeof(UINT32))) == NULL) {
|
||||
OF_PRINT_ERROR(
|
||||
("no memory (calloc failed for enc_symbols_tab[%d])\n", esi))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
memset(enc_symbols_tab[esi], (char)(esi + 1), SYMBOL_SIZE);
|
||||
if (VERBOSITY > 1) {
|
||||
printf("src[%03d]= ", esi);
|
||||
dump_buffer_32(enc_symbols_tab[esi], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now build the n-k repair symbols... */
|
||||
printf("\nBuilding repair symbols...\n");
|
||||
for (esi = k; esi < n; esi++) {
|
||||
if ((enc_symbols_tab[esi] = (char *)calloc(symb_sz_32, sizeof(UINT32))) ==
|
||||
NULL) {
|
||||
OF_PRINT_ERROR(
|
||||
("no memory (calloc failed for enc_symbols_tab[%d])\n", esi))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if (of_build_repair_symbol(ses, enc_symbols_tab, esi) != OF_STATUS_OK) {
|
||||
OF_PRINT_ERROR(
|
||||
("ERROR: of_build_repair_symbol() failed for esi=%u\n", esi))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if (VERBOSITY > 1) {
|
||||
printf("repair[%03d]= ", esi);
|
||||
dump_buffer_32(enc_symbols_tab[esi], 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Randomize the packet order, it's important for LDPC-Staircase codes for
|
||||
* instance... */
|
||||
printf("\nRandomizing transmit order...\n");
|
||||
if ((rand_order = (UINT32 *)calloc(n, sizeof(UINT32))) == NULL) {
|
||||
OF_PRINT_ERROR(("no memory (calloc failed for rand_order)\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
randomize_array(&rand_order, n);
|
||||
|
||||
/* Finally initialize the UDP socket and throw our packets... */
|
||||
if ((so = init_socket(&dst_host)) == INVALID_SOCKET) {
|
||||
OF_PRINT_ERROR(("Error initializing socket!\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
printf("First of all, send the FEC OTI for this object to %s/%d\n", DEST_IP,
|
||||
DEST_PORT);
|
||||
/* Initialize and send the FEC OTI to the client */
|
||||
/* convert back to host endianess */
|
||||
fec_oti.codec_id = htonl(codec_id);
|
||||
fec_oti.k = htonl(k);
|
||||
fec_oti.n = htonl(n);
|
||||
if ((ret = sendto(so, (void *)&fec_oti, sizeof(fec_oti), 0,
|
||||
(SOCKADDR *)&dst_host, sizeof(dst_host))) !=
|
||||
sizeof(fec_oti)) {
|
||||
OF_PRINT_ERROR(("Error while sending the FEC OTI\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
lost_after_index = n * (1 - LOSS_RATE);
|
||||
if (lost_after_index < k) {
|
||||
OF_PRINT_ERROR(
|
||||
("The loss rate %f is to high: only %u packets will be sent, whereas "
|
||||
"k=%u\n",
|
||||
LOSS_RATE, lost_after_index, k))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
printf(
|
||||
"Sending %u source and repair packets to %s/%d. All packets sent at "
|
||||
"index %u and higher are considered as lost\n",
|
||||
n, DEST_IP, DEST_PORT, lost_after_index);
|
||||
/* Allocate a buffer where we'll copy each symbol plus its simplistif FPI (in
|
||||
* this example consisting only of the ESI). This needs to be fixed in real
|
||||
* applications, with the actual FPI required for this code. Also doing a
|
||||
* memcpy is rather suboptimal in terms of performance! */
|
||||
if ((pkt_with_fpi = (char *)malloc(4 + SYMBOL_SIZE)) == NULL) {
|
||||
OF_PRINT_ERROR(("no memory (malloc failed for pkt_with_fpi)\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
if (i == lost_after_index) {
|
||||
/* the remaining packets are considered as lost, exit loop */
|
||||
break;
|
||||
}
|
||||
/* Add a pkt header wich only countains the ESI, i.e. a 32bits sequence
|
||||
* number, in network byte order in order to be portable regardless of the
|
||||
* local and remote byte endian representation (the receiver will do the
|
||||
* opposite with ntohl()...) */
|
||||
*(UINT32 *)pkt_with_fpi = htonl(rand_order[i]);
|
||||
memcpy(4 + pkt_with_fpi, enc_symbols_tab[rand_order[i]], SYMBOL_SIZE);
|
||||
printf("%05d => sending symbol %u (%s)\n", i + 1, rand_order[i],
|
||||
(rand_order[i] < k) ? "src" : "repair");
|
||||
if ((ret = sendto(so, pkt_with_fpi, SYMBOL_SIZE + 4, 0,
|
||||
(SOCKADDR *)&dst_host, sizeof(dst_host))) ==
|
||||
SOCKET_ERROR) {
|
||||
OF_PRINT_ERROR(("sendto() failed!\n"))
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
/* Perform a short usleep() to slow down transmissions and avoid UDP socket
|
||||
* saturation at the receiver. Note that the true solution consists in
|
||||
* adding some rate control mechanism here, like a leaky or token bucket. */
|
||||
usleep(500);
|
||||
}
|
||||
printf("\nCompleted! %d packets sent successfully.\n", i);
|
||||
ret = 1;
|
||||
|
||||
end:
|
||||
/* Cleanup everything... */
|
||||
if (so != INVALID_SOCKET) {
|
||||
close(so);
|
||||
}
|
||||
if (ses) {
|
||||
of_release_codec_instance(ses);
|
||||
}
|
||||
if (params) {
|
||||
free(params);
|
||||
}
|
||||
if (rand_order) {
|
||||
free(rand_order);
|
||||
}
|
||||
if (enc_symbols_tab) {
|
||||
for (esi = 0; esi < n; esi++) {
|
||||
if (enc_symbols_tab[esi]) {
|
||||
free(enc_symbols_tab[esi]);
|
||||
}
|
||||
}
|
||||
free(enc_symbols_tab);
|
||||
}
|
||||
if (pkt_with_fpi) {
|
||||
free(pkt_with_fpi);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Randomize an array of integers */
|
||||
void randomize_array(UINT32 **array, UINT32 arrayLen) {
|
||||
UINT32 backup = 0;
|
||||
UINT32 randInd = 0;
|
||||
UINT32 seed; /* random seed for the srand() function */
|
||||
UINT32 i;
|
||||
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL) < 0) {
|
||||
OF_PRINT_ERROR(("gettimeofday() failed"))
|
||||
exit(-1);
|
||||
}
|
||||
seed = (int)tv.tv_usec;
|
||||
srand(seed);
|
||||
for (i = 0; i < arrayLen; i++) {
|
||||
(*array)[i] = i;
|
||||
}
|
||||
for (i = 0; i < arrayLen; i++) {
|
||||
backup = (*array)[i];
|
||||
randInd = rand() % arrayLen;
|
||||
(*array)[i] = (*array)[randInd];
|
||||
(*array)[randInd] = backup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize our UDP socket */
|
||||
static SOCKET init_socket(SOCKADDR_IN *dst_host) {
|
||||
SOCKET s;
|
||||
|
||||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
|
||||
printf("Error: call to socket() failed\n");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
dst_host->sin_family = AF_INET;
|
||||
dst_host->sin_port = htons((short)DEST_PORT);
|
||||
dst_host->sin_addr.s_addr = inet_addr(DEST_IP);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void dump_buffer_32(void *buf, UINT32 len32) {
|
||||
UINT32 *ptr;
|
||||
UINT32 j = 0;
|
||||
|
||||
printf("0x");
|
||||
for (ptr = (UINT32 *)buf; len32 > 0; len32--, ptr++) {
|
||||
/* convert to big endian format to be sure of byte order */
|
||||
printf("%08X", htonl(*ptr));
|
||||
if (++j == 10) {
|
||||
j = 0;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
Reference in New Issue
Block a user