OpenFec 基本使用

广昊昊
2023-12-01

    OpenFec(官网:http://www.openfec.org/)是一个项目,主要处理正向纠错码,在通信中加上一些冗余数据,在网络质量差、丢包的时候,可以根据这些冗余数据恢复出原来的数据。目前OpenFec项目支持以下编码:

 (1) Reed-Solomon over GF(28)

 (2) Reed-Solomon stable codec over GF(2m)

 (3) 1D-2D parity check matrix stable codec

 (4) LDPC-staircase stable codec

 (5) LDPC from file advanced codec

  下面介绍几个比较重要的结构体:

   of_codec_id_t

   描述编码类型的枚举变量

typedef enum
{
	OF_CODEC_NIL				= 0,
	OF_CODEC_REED_SOLOMON_GF_2_8_STABLE	= 1,
	OF_CODEC_REED_SOLOMON_GF_2_M_STABLE	= 2,
	OF_CODEC_LDPC_STAIRCASE_STABLE		= 3,
//	OF_CODEC_LDPC_TRIANGLE_STABLE		= 4,
	OF_CODEC_2D_PARITY_MATRIX_STABLE	= 5,
	OF_CODEC_LDPC_FROM_FILE_ADVANCED	= 6
} of_codec_id_t;

 of_session_t

 指向于当前编解码实例的一个结构体,起标识作用

typedef struct of_session
{
	of_codec_id_t	codec_id;
	of_codec_type_t	codec_type;
} of_session_t;

  of_parameters

   配置FEC 变量相关的结构体,在of_set_fec_parameters中被调用

typedef struct of_parameters
{
	UINT32		nb_source_symbols;
	UINT32		nb_repair_symbols;
	UINT32		encoding_symbol_length;
} of_parameters_t;

 主要调用流程:

  1. 使用解码器创建解码实例(of_create_codec_instance),配置解码参数(of_set_fec_parameters)。
  2. 接收数据包,调用of_decode_with_new_symbol 进行数据填充
  3. 统计接收到的数据包数量n_received,当接收的数据包 >= 源符号包数量时 ,调用of_is_decoding_complete 判断解码是否已完成
  4. 调用of_get_source_symbols_tab 获取恢复后的数据,释放相关资源。

 

 在FEC 中,一个包必须是4的倍数,一个包由src符号 + repair符号组成, k代表src符号,n代表完整的一个包,n-k代表repair符号。

 以下代码是官方例子simple_client.c修改而来

 

void decode()
{
	int ret;
	byte **data;
	UINT32 esi;
	UINT32 k;//源符号数量
	UINT32 n;//源符号+ 修复符号 数量

	bool		done = false;
	UINT32		n_received = 0;
	of_codec_id_t codec_id;
	of_session_t	*ses = NULL;
	of_parameters_t	*params = NULL;
	
	void**		recvd_symbols_tab = NULL;//存放指针
	void**		src_symbols_tab = NULL;//用于存放还原后的数据

	int len = sizeof(fec_oti_t);
	byte *head = (byte*)malloc(sizeof(byte) * len);
	ret = ReceiveUdpData(head, len);//接收指定长度的udp数据包

	if (ret != len)
	{
		printf("接收失败\n");
		return;
	}
	else
	{
		printf("接收成功\n");
	}

	fec_oti_t *fec_oti = (fec_oti_t*)head;
	codec_id  = (of_codec_id_t)ntohl(fec_oti->codec_id);
	k = fec_oti->k = ntohl(fec_oti->k);
	n = fec_oti->n = ntohl(fec_oti->n);

	switch (codec_id)
	{	
		case OF_CODEC_REED_SOLOMON_GF_2_M_STABLE:
		{
			{
				of_rs_2_m_parameters_t	*my_params;
				my_params = (of_rs_2_m_parameters_t	*)calloc(1, sizeof(*my_params) );
				my_params->m = 8;
				params = (of_parameters_t *)my_params;
			}
			break;
		}

		case OF_CODEC_LDPC_STAIRCASE_STABLE:
		{
			{
				of_ldpc_parameters_t	*my_params;
				my_params = (of_ldpc_parameters_t	*)calloc(1, sizeof(*my_params));
				my_params->prng_seed = rand();
				my_params->N1 = 7;
				params = (of_parameters_t *)my_params;
			}
			break;
		}
	}

	params->nb_source_symbols = k;
	params->nb_repair_symbols = n - k;
	params->encoding_symbol_length = 1024;

	if ((ret = of_create_codec_instance(&ses, codec_id, OF_DECODER, 2)) != 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;
	}

	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 = 1024 + 4;//前4个字节是fec相关的信息,不参与解码
	data = (byte**)malloc(sizeof(byte*) * len * n);

	for (size_t i = 0; i < n; i++)
	{
		data[i] = (byte*)malloc(sizeof(byte) * len);
	}

	while (ReceiveUdpData(data[n_received], len) > 0)
	{	
		esi = ntohl(*(UINT32*)data[n_received]);

		if (esi > n)
		{
			goto end;
		}

		recvd_symbols_tab[esi] = (char*)data[n_received] + 4;//保存指针
		if (of_decode_with_new_symbol(ses, (char*)data[n_received] + 4, esi) == OF_STATUS_ERROR)
		{
			OF_PRINT_ERROR(("of_decode_with_new_symbol() failed\n"))
				ret = -1;
			goto end;
		}

		if ((n_received >= k) && (of_is_decoding_complete(ses) == true))
		{
			done = true;
			break;
		}

		n_received++;
	}

	if (!done && (ret == OF_STATUS_FAILURE) && (n_received >= k))
	{
		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;
		}
		
	}

	if (done)
	{
		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 (2 > 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:
	closesocket(localSocket);
	
	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])
			{
				free((char*)recvd_symbols_tab[esi] - 4);
			}
			else if (esi < k && src_symbols_tab[esi])
			{
				ASSERT(recvd_symbols_tab[esi] == NULL);
				free(src_symbols_tab[esi]);
			}
		}
		free(recvd_symbols_tab);
		free(src_symbols_tab);
	}

	printf("完成\n");
}

 

 类似资料: