diff --git a/src/fec/fec_decoder.cpp b/src/fec/fec_decoder.cpp new file mode 100644 index 0000000..b7a5d2f --- /dev/null +++ b/src/fec/fec_decoder.cpp @@ -0,0 +1,127 @@ +#include "fec_decoder.h" + +#include "log.h" + +FecDecoder::FecDecoder() {} + +FecDecoder::~FecDecoder() {} + +int FecDecoder::Init() { + fec_codec_id_ = OF_CODEC_REED_SOLOMON_GF_2_M_STABLE; + + fec_rs_params_ = (of_rs_2_m_parameters_t *)calloc(1, sizeof(*fec_params_)); + if (nullptr == fec_rs_params_) { + LOG_ERROR("Create FEC decoder params failed"); + return -1; + } + + fec_rs_params_->m = 8; + fec_params_ = (of_parameters_t *)fec_rs_params_; + + if (OF_STATUS_OK != + of_create_codec_instance(&fec_session_, fec_codec_id_, OF_DECODER, 2)) { + LOG_ERROR("Create FEC decoder instance failed"); + return -1; + } + + return 0; +} + +int FecDecoder::Release() { + if (!fec_session_) { + LOG_ERROR("Invalid FEC decoder instance"); + return -1; + } + + { + if (OF_STATUS_OK != of_release_codec_instance(fec_session_)) { + LOG_ERROR("Release FEC decoder instance failed"); + return -1; + } + } + + if (fec_rs_params_) { + free(fec_rs_params_); + } + + return 0; +} + +int FecDecoder::ResetParams(unsigned int source_symbol_num) { + if (!fec_session_) { + LOG_ERROR("Invalid FEC decoder instance"); + return -1; + } + + num_of_received_symbols_ = 0; + num_of_source_packets_ = source_symbol_num; + num_of_total_packets_ = + (unsigned int)floor((double)source_symbol_num / (double)code_rate_); + + LOG_ERROR("Set s[{}] r[{}]", num_of_source_packets_, + num_of_total_packets_ - source_symbol_num); + + fec_params_->nb_source_symbols = source_symbol_num; + fec_params_->nb_repair_symbols = num_of_total_packets_ - source_symbol_num; + fec_params_->encoding_symbol_length = max_size_of_packet_; + + if (OF_STATUS_OK != of_set_fec_parameters(fec_session_, fec_params_)) { + LOG_ERROR("Set FEC params failed for codec_id {}", fec_codec_id_); + return -1; + } + + return 0; +} + +uint8_t **FecDecoder::DecodeWithNewSymbol(const char *fec_symbol, + unsigned int fec_symbol_id) { + if (!fec_session_) { + LOG_ERROR("Invalid FEC decoder instance"); + return nullptr; + } + + num_of_received_symbols_++; + if (OF_STATUS_ERROR == of_decode_with_new_symbol( + fec_session_, (char *)fec_symbol, fec_symbol_id)) { + LOG_ERROR("Decode wit new symbol failed"); + return nullptr; + } + + if ((num_of_received_symbols_ >= fec_params_->nb_source_symbols) && + (true == of_is_decoding_complete(fec_session_))) { + uint8_t **source_packets = + (uint8_t **)calloc(num_of_total_packets_, sizeof(uint8_t *)); + if (!source_packets) { + LOG_ERROR("Calloc failed for source_packets with size [{}])", + num_of_total_packets_); + } + + if (OF_STATUS_OK != + of_get_source_symbols_tab(fec_session_, (void **)source_packets)) { + LOG_ERROR("Get source symbols failed"); + return nullptr; + } + + return source_packets; + } + + return nullptr; +} + +int FecDecoder::ReleaseSourcePackets(uint8_t **source_packets) { + if (nullptr == source_packets) { + LOG_ERROR( + "Release source packets failed, due to source_packets is nullptr"); + return -1; + } + + // for (unsigned int index = 0; index < num_of_source_packets_; index++) { + // if (source_packets[index]) { + // LOG_ERROR("Free [{}]", index); + // free(source_packets[index]); + // } + // } + free(source_packets); + + return 0; +} diff --git a/src/fec/fec_decoder.h b/src/fec/fec_decoder.h new file mode 100644 index 0000000..97e09f0 --- /dev/null +++ b/src/fec/fec_decoder.h @@ -0,0 +1,50 @@ +/* + * @Author: DI JUNKUN + * @Date: 2023-11-15 + * Copyright (c) 2023 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _FEC_DECODER_H_ +#define _FEC_DECODER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#include "lib_common/of_openfec_api.h" +#ifdef __cplusplus +}; +#endif + +class FecDecoder { + public: + FecDecoder(); + ~FecDecoder(); + + public: + int Init(); + int Release(); + int ResetParams(unsigned int source_symbol_num); + uint8_t **DecodeWithNewSymbol(const char *fec_symbol, + unsigned int fec_symbol_id); + int ReleaseSourcePackets(uint8_t **source_packets); + + private: + double code_rate_ = 0.667; + int max_size_of_packet_ = 1400; + + private: + of_codec_id_t fec_codec_id_ = OF_CODEC_REED_SOLOMON_GF_2_M_STABLE; + of_session_t *fec_session_ = nullptr; + of_parameters_t *fec_params_ = nullptr; + of_rs_2_m_parameters_t *fec_rs_params_ = nullptr; + of_ldpc_parameters_t *fec_ldpc_params_ = nullptr; + + unsigned int num_of_received_symbols_ = 0; + unsigned int num_of_source_packets_ = 0; + unsigned int num_of_total_packets_ = 0; +}; + +#endif \ No newline at end of file diff --git a/src/fec/fec_encoder.cpp b/src/fec/fec_encoder.cpp index 41e0f96..629ecc9 100644 --- a/src/fec/fec_encoder.cpp +++ b/src/fec/fec_encoder.cpp @@ -40,6 +40,10 @@ int FecEncoder::Release() { } } + if (fec_rs_params_) { + free(fec_rs_params_); + } + return 0; } diff --git a/src/media/video/encode/openh264/openh264_encoder.h b/src/media/video/encode/openh264/openh264_encoder.h index 778396b..bff16d1 100644 --- a/src/media/video/encode/openh264/openh264_encoder.h +++ b/src/media/video/encode/openh264/openh264_encoder.h @@ -38,7 +38,7 @@ class OpenH264Encoder : public VideoEncoder { private: int frame_width_ = 1280; int frame_height_ = 720; - int key_frame_interval_ = 3000; + int key_frame_interval_ = 1; int target_bitrate_ = 1000; int max_bitrate_ = 500000; int max_payload_size_ = 1400; diff --git a/src/pc/peer_connection.cpp b/src/pc/peer_connection.cpp index 9af1f31..13523ab 100644 --- a/src/pc/peer_connection.cpp +++ b/src/pc/peer_connection.cpp @@ -425,7 +425,7 @@ int PeerConnection::SendVideoData(const char *data, size_t size) { int ret = video_encoder_->Encode( (uint8_t *)data, size, [this](char *encoded_frame, size_t size) -> int { for (auto &ice_trans : ice_transmission_list_) { - // LOG_ERROR("H264 frame size: [{}]", size); + LOG_ERROR("H264 frame size: [{}]", size); ice_trans.second->SendData(IceTransmission::DATA_TYPE::VIDEO, encoded_frame, size); } diff --git a/src/rtp/rtp_codec.cpp b/src/rtp/rtp_codec.cpp index 3f81139..0e6ffe5 100644 --- a/src/rtp/rtp_codec.cpp +++ b/src/rtp/rtp_codec.cpp @@ -52,14 +52,14 @@ void RtpCodec::Encode(uint8_t* buffer, size_t size, timestamp_ = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - for (size_t index = 0; index < num_of_total_packets; index++) { + for (unsigned int index = 0; index < num_of_total_packets; index++) { RtpPacket rtp_packet; if (index < num_of_source_packets) { rtp_packet.SetVerion(version_); rtp_packet.SetHasPadding(has_padding_); rtp_packet.SetHasExtension(has_extension_); rtp_packet.SetMarker(index == num_of_source_packets - 1 ? 1 : 0); - rtp_packet.SetPayloadType(RtpPacket::PAYLOAD_TYPE::H264); + rtp_packet.SetPayloadType(RtpPacket::PAYLOAD_TYPE::H264_FEC_SOURCE); rtp_packet.SetSequenceNumber(sequence_number_++); rtp_packet.SetTimestamp(timestamp_); rtp_packet.SetSsrc(ssrc_); @@ -89,12 +89,16 @@ void RtpCodec::Encode(uint8_t* buffer, size_t size, if (index == num_of_source_packets - 1) { if (last_packet_size > 0) { - rtp_packet.EncodeH264Fua(fec_packets[index], last_packet_size); + rtp_packet.EncodeH264FecSource(fec_packets[index], + last_packet_size, index, + num_of_source_packets); } else { - rtp_packet.EncodeH264Fua(fec_packets[index], MAX_NALU_LEN); + rtp_packet.EncodeH264FecSource(fec_packets[index], MAX_NALU_LEN, + index, num_of_source_packets); } } else { - rtp_packet.EncodeH264Fua(fec_packets[index], MAX_NALU_LEN); + rtp_packet.EncodeH264FecSource(fec_packets[index], MAX_NALU_LEN, + index, num_of_source_packets); } } else if (index >= num_of_source_packets && @@ -103,7 +107,7 @@ void RtpCodec::Encode(uint8_t* buffer, size_t size, rtp_packet.SetHasPadding(has_padding_); rtp_packet.SetHasExtension(has_extension_); rtp_packet.SetMarker(index == num_of_total_packets - 1 ? 1 : 0); - rtp_packet.SetPayloadType(RtpPacket::PAYLOAD_TYPE::H264_FEC); + rtp_packet.SetPayloadType(RtpPacket::PAYLOAD_TYPE::H264_FEC_REPAIR); rtp_packet.SetSequenceNumber(sequence_number_++); rtp_packet.SetTimestamp(timestamp_); rtp_packet.SetSsrc(ssrc_); @@ -116,7 +120,8 @@ void RtpCodec::Encode(uint8_t* buffer, size_t size, rtp_packet.SetExtensionProfile(extension_profile_); rtp_packet.SetExtensionData(extension_data_, extension_len_); } - rtp_packet.EncodeH264Fec(fec_packets[index], MAX_NALU_LEN); + rtp_packet.EncodeH264FecRepair(fec_packets[index], MAX_NALU_LEN, + index, num_of_source_packets); } packets.emplace_back(rtp_packet); @@ -238,6 +243,7 @@ size_t RtpCodec::Decode(RtpPacket& packet, uint8_t* payload) { // if ((packet.Buffer()[13] >> 7) & 0x01) { // LOG_ERROR("Start bit!!!!!!!!!!!!!!!"); // } + auto nal_unit_type = packet.Buffer()[12] & 0x1F; if (NALU == nal_unit_type) { diff --git a/src/rtp/rtp_packet.cpp b/src/rtp/rtp_packet.cpp index 1b948b8..6d9d4f8 100644 --- a/src/rtp/rtp_packet.cpp +++ b/src/rtp/rtp_packet.cpp @@ -12,8 +12,11 @@ void RtpPacket::TryToDecodeRtpPacket() { } else if (NAL_UNIT_TYPE::FU_A == nal_unit_type_) { DecodeH264Fua(); } - } else if (PAYLOAD_TYPE::H264_FEC == PAYLOAD_TYPE(buffer_[1] & 0x7F)) { - DecodeH264Fec(); + } else if (PAYLOAD_TYPE::H264_FEC_SOURCE == PAYLOAD_TYPE(buffer_[1] & 0x7F)) { + nal_unit_type_ = NAL_UNIT_TYPE::FU_A; + DecodeH264FecSource(); + } else if (PAYLOAD_TYPE::H264_FEC_REPAIR == PAYLOAD_TYPE(buffer_[1] & 0x7F)) { + DecodeH264FecRepair(); } else if (PAYLOAD_TYPE::DATA == PAYLOAD_TYPE(buffer_[1] & 0x7F)) { DecodeData(); } else { @@ -245,7 +248,9 @@ const uint8_t *RtpPacket::EncodeH264Fua(uint8_t *payload, size_t payload_size) { return buffer_; } -const uint8_t *RtpPacket::EncodeH264Fec(uint8_t *payload, size_t payload_size) { +const uint8_t *RtpPacket::EncodeH264FecSource( + uint8_t *payload, size_t payload_size, unsigned int fec_symbol_id, + unsigned int fec_source_symbol_num) { buffer_[0] = (version_ << 6) | (has_padding_ << 5) | (has_extension_ << 4) | total_csrc_number_; buffer_[1] = (marker_ << 7) | payload_type_; @@ -279,10 +284,75 @@ const uint8_t *RtpPacket::EncodeH264Fec(uint8_t *payload, size_t payload_size) { } uint32_t fec_symbol_id_offset = - extension_offset + (has_extension_ && extension_data_ ? 4 : 0); - buffer_[12 + fec_symbol_id_offset] = fec_symbol_id_; + (has_extension_ && extension_data_ ? extension_len_ : 0) + + extension_offset; - uint32_t payload_offset = fec_symbol_id_offset + 1; + buffer_[12 + fec_symbol_id_offset] = fec_symbol_id; + + uint32_t fec_source_symbol_num_offset = fec_symbol_id_offset + 1; + buffer_[12 + fec_source_symbol_num_offset] = fec_source_symbol_num; + + uint32_t payload_offset = fec_source_symbol_num_offset + 1; + + buffer_[12 + payload_offset] = fu_indicator_.forbidden_bit << 7 | + fu_indicator_.nal_reference_idc << 6 | + fu_indicator_.nal_unit_type; + + buffer_[13 + payload_offset] = fu_header_.start << 7 | fu_header_.end << 6 | + fu_header_.remain_bit << 1 | + fu_header_.nal_unit_type; + + memcpy(buffer_ + 14 + payload_offset, payload, payload_size); + size_ = payload_size + (14 + payload_offset); + + return buffer_; +} + +const uint8_t *RtpPacket::EncodeH264FecRepair( + uint8_t *payload, size_t payload_size, unsigned int fec_symbol_id, + unsigned int fec_source_symbol_num) { + buffer_[0] = (version_ << 6) | (has_padding_ << 5) | (has_extension_ << 4) | + total_csrc_number_; + buffer_[1] = (marker_ << 7) | payload_type_; + buffer_[2] = (sequence_number_ >> 8) & 0xFF; + buffer_[3] = sequence_number_ & 0xFF; + buffer_[4] = (timestamp_ >> 24) & 0xFF; + buffer_[5] = (timestamp_ >> 16) & 0xFF; + buffer_[6] = (timestamp_ >> 8) & 0xFF; + buffer_[7] = timestamp_ & 0xFF; + buffer_[8] = (ssrc_ >> 24) & 0xFF; + buffer_[9] = (ssrc_ >> 16) & 0xFF; + buffer_[10] = (ssrc_ >> 8) & 0xFF; + buffer_[11] = ssrc_ & 0xFF; + + for (uint32_t index = 0; index < total_csrc_number_ && !csrcs_.empty(); + index++) { + buffer_[12 + index] = (csrcs_[index] >> 24) & 0xFF; + buffer_[13 + index] = (csrcs_[index] >> 16) & 0xFF; + buffer_[14 + index] = (csrcs_[index] >> 8) & 0xFF; + buffer_[15 + index] = csrcs_[index] & 0xFF; + } + + uint32_t extension_offset = + total_csrc_number_ && !csrcs_.empty() ? total_csrc_number_ * 4 : 0; + if (has_extension_ && extension_data_) { + buffer_[12 + extension_offset] = extension_profile_ >> 8; + buffer_[13 + extension_offset] = extension_profile_ & 0xff; + buffer_[14 + extension_offset] = (extension_len_ >> 8) & 0xFF; + buffer_[15 + extension_offset] = extension_len_ & 0xFF; + memcpy(buffer_ + 16 + extension_offset, extension_data_, extension_len_); + } + + uint32_t fec_symbol_id_offset = + (has_extension_ && extension_data_ ? extension_len_ : 0) + + extension_offset; + + buffer_[12 + fec_symbol_id_offset] = fec_symbol_id; + + uint32_t fec_source_symbol_num_offset = fec_symbol_id_offset + 1; + buffer_[12 + fec_source_symbol_num_offset] = fec_source_symbol_num; + + uint32_t payload_offset = fec_source_symbol_num_offset + 1; buffer_[12 + payload_offset] = fu_indicator_.forbidden_bit << 7 | fu_indicator_.nal_reference_idc << 6 | @@ -438,7 +508,7 @@ size_t RtpPacket::DecodeH264Fua(uint8_t *payload) { return payload_size_; } -size_t RtpPacket::DecodeH264Fec(uint8_t *payload) { +size_t RtpPacket::DecodeH264FecSource(uint8_t *payload) { version_ = (buffer_[0] >> 6) & 0x03; has_padding_ = (buffer_[0] >> 5) & 0x01; has_extension_ = (buffer_[0] >> 4) & 0x01; @@ -467,10 +537,69 @@ size_t RtpPacket::DecodeH264Fec(uint8_t *payload) { extension_data_ = buffer_ + 16 + extension_offset; } - uint32_t fec_symbol_id_offset = extension_offset + (has_extension_ ? 4 : 0); + uint32_t fec_symbol_id_offset = + extension_offset + (has_extension_ ? extension_len_ : 0); fec_symbol_id_ = buffer_[12 + fec_symbol_id_offset]; - uint32_t payload_offset = fec_symbol_id_offset + 1; + uint32_t fec_source_symbol_num_offset = fec_symbol_id_offset + 1; + fec_source_symbol_num_ = buffer_[12 + fec_source_symbol_num_offset]; + + uint32_t payload_offset = fec_source_symbol_num_offset + 1; + + fu_indicator_.forbidden_bit = (buffer_[12 + payload_offset] >> 7) & 0x01; + fu_indicator_.nal_reference_idc = (buffer_[12 + payload_offset] >> 5) & 0x03; + fu_indicator_.nal_unit_type = buffer_[12 + payload_offset] & 0x1F; + + fu_header_.start = (buffer_[13 + payload_offset] >> 7) & 0x01; + fu_header_.end = (buffer_[13 + payload_offset] >> 6) & 0x01; + fu_header_.remain_bit = (buffer_[13 + payload_offset] >> 5) & 0x01; + fu_header_.nal_unit_type = buffer_[13 + payload_offset] & 0x1F; + + payload_size_ = size_ - (14 + payload_offset); + payload_ = buffer_ + 14 + payload_offset; + if (payload) { + memcpy(payload, payload_, payload_size_); + } + return payload_size_; +} + +size_t RtpPacket::DecodeH264FecRepair(uint8_t *payload) { + version_ = (buffer_[0] >> 6) & 0x03; + has_padding_ = (buffer_[0] >> 5) & 0x01; + has_extension_ = (buffer_[0] >> 4) & 0x01; + total_csrc_number_ = buffer_[0] & 0x0f; + marker_ = (buffer_[1] >> 7) & 0x01; + payload_type_ = buffer_[1] & 0x7f; + sequence_number_ = (buffer_[2] << 8) | buffer_[3]; + timestamp_ = + (buffer_[4] << 24) | (buffer_[5] << 16) | (buffer_[6] << 8) | buffer_[7]; + ssrc_ = (buffer_[8] << 24) | (buffer_[9] << 16) | (buffer_[10] << 8) | + buffer_[11]; + + for (uint32_t index = 0; index < total_csrc_number_; index++) { + uint32_t csrc = (buffer_[12 + index] << 24) | (buffer_[13 + index] << 16) | + (buffer_[14 + index] << 8) | buffer_[15 + index]; + csrcs_.push_back(csrc); + } + + uint32_t extension_offset = total_csrc_number_ * 4; + if (has_extension_) { + extension_profile_ = + (buffer_[12 + extension_offset] << 8) | buffer_[13 + extension_offset]; + extension_len_ = + (buffer_[14 + extension_offset] << 8) | buffer_[15 + extension_offset]; + + extension_data_ = buffer_ + 16 + extension_offset; + } + + uint32_t fec_symbol_id_offset = + extension_offset + (has_extension_ ? extension_len_ : 0); + fec_symbol_id_ = buffer_[12 + fec_symbol_id_offset]; + + uint32_t fec_source_symbol_num_offset = fec_symbol_id_offset + 1; + fec_source_symbol_num_ = buffer_[12 + fec_source_symbol_num_offset]; + + uint32_t payload_offset = fec_source_symbol_num_offset + 1; payload_size_ = size_ - (14 + payload_offset); payload_ = buffer_ + 14 + payload_offset; diff --git a/src/rtp/rtp_packet.h b/src/rtp/rtp_packet.h index cd07452..cf85f1a 100644 --- a/src/rtp/rtp_packet.h +++ b/src/rtp/rtp_packet.h @@ -63,7 +63,7 @@ // |F|NRI| Type |S|E|R| Type | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// H264 FEC +// H264 FEC source symbol // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -81,7 +81,33 @@ // | Extensions |x // | .... |x // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// | FEC symbol id | | +// | FEC symbol id | src sym num | FU indicator | FU header | +// | | +// | FU Payload | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | padding | Padding size | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// H264 FEC repair symbol +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P|X| CC |M| PT | sequence number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | synchronization source (SSRC) identifier | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | Contributing source (CSRC) identifiers |x +// | .... |x +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | defined by profile | length |x +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Extensions |x +// | .... |x +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | FEC symbol id | src sym num | | // | | // | Fec Payload | // | | @@ -96,7 +122,8 @@ class RtpPacket { public: typedef enum { H264 = 96, - H264_FEC = 97, + H264_FEC_SOURCE = 97, + H264_FEC_REPAIR = 98, OPUS = 111, DATA = 127 } PAYLOAD_TYPE; @@ -171,11 +198,17 @@ class RtpPacket { const uint8_t *Encode(uint8_t *payload, size_t payload_size); const uint8_t *EncodeH264Nalu(uint8_t *payload, size_t payload_size); const uint8_t *EncodeH264Fua(uint8_t *payload, size_t payload_size); - const uint8_t *EncodeH264Fec(uint8_t *payload, size_t payload_size); + const uint8_t *EncodeH264FecSource(uint8_t *payload, size_t payload_size, + unsigned int fec_symbol_id, + unsigned int fec_source_symbol_num); + const uint8_t *EncodeH264FecRepair(uint8_t *payload, size_t payload_size, + unsigned int fec_symbol_id, + unsigned int fec_source_symbol_num); size_t DecodeData(uint8_t *payload = nullptr); size_t DecodeH264Nalu(uint8_t *payload = nullptr); size_t DecodeH264Fua(uint8_t *payload = nullptr); - size_t DecodeH264Fec(uint8_t *payload = nullptr); + size_t DecodeH264FecSource(uint8_t *payload = nullptr); + size_t DecodeH264FecRepair(uint8_t *payload = nullptr); public: // Get Header @@ -226,6 +259,8 @@ class RtpPacket { const uint8_t FecSymbolId() { return fec_symbol_id_; } + const uint8_t FecSourceSymbolNum() { return fec_source_symbol_num_; } + // Payload const uint8_t *Payload() { ParseRtpData(); @@ -277,6 +312,7 @@ class RtpPacket { FU_INDICATOR fu_indicator_; FU_HEADER fu_header_; uint8_t fec_symbol_id_ = 0; + uint8_t fec_source_symbol_num_ = 0; // Payload uint8_t *payload_ = nullptr; diff --git a/src/rtp/rtp_video_receiver.cpp b/src/rtp/rtp_video_receiver.cpp index bc7096c..f115804 100644 --- a/src/rtp/rtp_video_receiver.cpp +++ b/src/rtp/rtp_video_receiver.cpp @@ -48,15 +48,118 @@ void RtpVideoReceiver::InsertRtpPacket(RtpPacket& rtp_packet) { rtcp_rr.Encode(); - SendRtcpRR(rtcp_rr); + // SendRtcpRR(rtcp_rr); } - if (RtpPacket::NAL_UNIT_TYPE::NALU == rtp_packet.NalUnitType()) { - compelete_video_frame_queue_.push( - VideoFrame(rtp_packet.Payload(), rtp_packet.Size())); - } else if (RtpPacket::NAL_UNIT_TYPE::FU_A == rtp_packet.NalUnitType()) { - incomplete_frame_list_[rtp_packet.SequenceNumber()] = rtp_packet; - bool complete = CheckIsFrameCompleted(rtp_packet); + if (!fec_enable_) { + if (RtpPacket::PAYLOAD_TYPE::H264 == rtp_packet.PayloadType()) { + if (RtpPacket::NAL_UNIT_TYPE::NALU == rtp_packet.NalUnitType()) { + compelete_video_frame_queue_.push( + VideoFrame(rtp_packet.Payload(), rtp_packet.Size())); + } else if (RtpPacket::NAL_UNIT_TYPE::FU_A == rtp_packet.NalUnitType()) { + incomplete_frame_list_[rtp_packet.SequenceNumber()] = rtp_packet; + bool complete = CheckIsFrameCompleted(rtp_packet); + } + } + } else { + if (RtpPacket::PAYLOAD_TYPE::H264 == rtp_packet.PayloadType()) { + if (RtpPacket::NAL_UNIT_TYPE::NALU == rtp_packet.NalUnitType()) { + compelete_video_frame_queue_.push( + VideoFrame(rtp_packet.Payload(), rtp_packet.Size())); + } else if (RtpPacket::NAL_UNIT_TYPE::FU_A == rtp_packet.NalUnitType()) { + incomplete_frame_list_[rtp_packet.SequenceNumber()] = rtp_packet; + bool complete = CheckIsFrameCompleted(rtp_packet); + } + } else if (RtpPacket::PAYLOAD_TYPE::H264_FEC_SOURCE == + rtp_packet.PayloadType()) { + if (last_packet_ts_ != rtp_packet.Timestamp()) { + fec_decoder_.Init(); + fec_decoder_.ResetParams(rtp_packet.FecSourceSymbolNum()); + last_packet_ts_ = rtp_packet.Timestamp(); + } + + incomplete_fec_packet_list_[rtp_packet.Timestamp()] + [rtp_packet.SequenceNumber()] = rtp_packet; + + uint8_t** complete_frame = fec_decoder_.DecodeWithNewSymbol( + (const char*)incomplete_fec_packet_list_[rtp_packet.Timestamp()] + [rtp_packet.SequenceNumber()] + .Payload(), + rtp_packet.FecSymbolId()); + + if (nullptr != complete_frame) { + if (!nv12_data_) { + nv12_data_ = new uint8_t[NV12_BUFFER_SIZE]; + } + + size_t complete_frame_size = 0; + for (int index = 0; index < rtp_packet.FecSourceSymbolNum(); index++) { + if (nullptr == complete_frame[index]) { + LOG_ERROR("Invalid complete_frame[{}]", index); + } + memcpy(nv12_data_ + complete_frame_size, complete_frame[index], 1400); + complete_frame_size += 1400; + } + + fec_decoder_.ReleaseSourcePackets(complete_frame); + fec_decoder_.Release(); + LOG_ERROR("Release incomplete_fec_packet_list_"); + incomplete_fec_packet_list_.erase(rtp_packet.Timestamp()); + + if (incomplete_fec_frame_list_.end() != + incomplete_fec_frame_list_.find(rtp_packet.Timestamp())) { + incomplete_fec_frame_list_.erase(rtp_packet.Timestamp()); + } + + compelete_video_frame_queue_.push( + VideoFrame(nv12_data_, complete_frame_size)); + } else { + incomplete_fec_frame_list_.insert(rtp_packet.Timestamp()); + } + } else if (RtpPacket::PAYLOAD_TYPE::H264_FEC_REPAIR == + rtp_packet.PayloadType()) { + if (incomplete_fec_frame_list_.end() == + incomplete_fec_frame_list_.find(rtp_packet.Timestamp())) { + return; + } + + if (last_packet_ts_ != rtp_packet.Timestamp()) { + fec_decoder_.Init(); + fec_decoder_.ResetParams(rtp_packet.FecSourceSymbolNum()); + last_packet_ts_ = rtp_packet.Timestamp(); + } + + incomplete_fec_packet_list_[rtp_packet.Timestamp()] + [rtp_packet.SequenceNumber()] = rtp_packet; + + uint8_t** complete_frame = fec_decoder_.DecodeWithNewSymbol( + (const char*)incomplete_fec_packet_list_[rtp_packet.Timestamp()] + [rtp_packet.SequenceNumber()] + .Payload(), + rtp_packet.FecSymbolId()); + + if (nullptr != complete_frame) { + if (!nv12_data_) { + nv12_data_ = new uint8_t[NV12_BUFFER_SIZE]; + } + + size_t complete_frame_size = 0; + for (int index = 0; index < rtp_packet.FecSourceSymbolNum(); index++) { + if (nullptr == complete_frame[index]) { + LOG_ERROR("Invalid complete_frame[{}]", index); + } + memcpy(nv12_data_ + complete_frame_size, complete_frame[index], 1400); + complete_frame_size += 1400; + } + + fec_decoder_.ReleaseSourcePackets(complete_frame); + fec_decoder_.Release(); + incomplete_fec_packet_list_.erase(rtp_packet.Timestamp()); + + compelete_video_frame_queue_.push( + VideoFrame(nv12_data_, complete_frame_size)); + } + } } } diff --git a/src/rtp/rtp_video_receiver.h b/src/rtp/rtp_video_receiver.h index 0332dd8..02b8702 100644 --- a/src/rtp/rtp_video_receiver.h +++ b/src/rtp/rtp_video_receiver.h @@ -4,7 +4,9 @@ #include #include #include +#include +#include "fec_decoder.h" #include "frame.h" #include "ringbuffer.h" #include "rtcp_receiver_report.h" @@ -46,6 +48,16 @@ class RtpVideoReceiver : public ThreadBase { std::unique_ptr rtp_statistics_ = nullptr; uint32_t last_send_rtcp_rr_packet_ts_ = 0; std::function data_send_func_ = nullptr; + + private: + bool fec_enable_ = true; + FecDecoder fec_decoder_; + uint32_t last_packet_ts_ = 0; + // std::map incomplete_fec_frame_list_; + // std::map> fec_source_symbol_list_; + // std::map> fec_repair_symbol_list_; + std::set incomplete_fec_frame_list_; + std::map> incomplete_fec_packet_list_; }; #endif diff --git a/src/transmission/ice_transmission.cpp b/src/transmission/ice_transmission.cpp index 7a84998..29d28ba 100644 --- a/src/transmission/ice_transmission.cpp +++ b/src/transmission/ice_transmission.cpp @@ -313,7 +313,9 @@ uint8_t IceTransmission::CheckIsVideoPacket(const char *buffer, size_t size) { } uint8_t pt = buffer[1] & 0x7F; - if (RtpPacket::PAYLOAD_TYPE::H264 == pt) { + if (RtpPacket::PAYLOAD_TYPE::H264 == pt || + RtpPacket::PAYLOAD_TYPE::H264_FEC_SOURCE == pt || + RtpPacket::PAYLOAD_TYPE::H264_FEC_REPAIR == pt) { return pt; } else { return 0; diff --git a/thirdparty/openfec/src/lib_stable/reed-solomon_gf_2_m/of_reed-solomon_gf_2_m_api.c b/thirdparty/openfec/src/lib_stable/reed-solomon_gf_2_m/of_reed-solomon_gf_2_m_api.c index 533a194..33f6906 100644 --- a/thirdparty/openfec/src/lib_stable/reed-solomon_gf_2_m/of_reed-solomon_gf_2_m_api.c +++ b/thirdparty/openfec/src/lib_stable/reed-solomon_gf_2_m/of_reed-solomon_gf_2_m_api.c @@ -33,237 +33,226 @@ #include "of_reed-solomon_gf_2_m_includes.h" - #ifdef OF_USE_REED_SOLOMON_2_M_CODEC +of_status_t of_rs_2_m_create_codec_instance(of_rs_2_m_cb_t** of_cb) { + of_codec_type_t codec_type; /* temporary value */ + of_rs_2_m_cb_t* cb; -of_status_t of_rs_2_m_create_codec_instance (of_rs_2_m_cb_t** of_cb) -{ - of_codec_type_t codec_type; /* temporary value */ - of_rs_2_m_cb_t *cb; - - OF_ENTER_FUNCTION - cb = (of_rs_2_m_cb_t*) of_realloc (*of_cb, sizeof (of_rs_2_m_cb_t)); - *of_cb = cb; - /* realloc does not initialize the additional buffer space, so do that manually, - * then re-initialize a few parameters */ - codec_type = cb->codec_type; - memset(cb, 0, sizeof(*cb)); - cb->codec_type = codec_type; - cb->codec_id = OF_CODEC_REED_SOLOMON_GF_2_M_STABLE; - /** - * max nb source symbols and max nb encoding symbols are computed - * in the of_rs_2_m_set_fec_parameters function (m is needed). - */ - cb->max_m = OF_REED_SOLOMON_2_M_MAX_M; /* init it immediately... */ - OF_EXIT_FUNCTION - return OF_STATUS_OK; + OF_ENTER_FUNCTION + cb = (of_rs_2_m_cb_t*)of_realloc(*of_cb, sizeof(of_rs_2_m_cb_t)); + *of_cb = cb; + /* realloc does not initialize the additional buffer space, so do that + * manually, then re-initialize a few parameters */ + codec_type = cb->codec_type; + memset(cb, 0, sizeof(*cb)); + cb->codec_type = codec_type; + cb->codec_id = OF_CODEC_REED_SOLOMON_GF_2_M_STABLE; + /** + * max nb source symbols and max nb encoding symbols are computed + * in the of_rs_2_m_set_fec_parameters function (m is needed). + */ + cb->max_m = OF_REED_SOLOMON_2_M_MAX_M; /* init it immediately... */ + OF_EXIT_FUNCTION + return OF_STATUS_OK; } - -of_status_t of_rs_2_m_release_codec_instance (of_rs_2_m_cb_t* ofcb) -{ - OF_ENTER_FUNCTION - of_rs_2m_release((of_galois_field_code_cb_t*)ofcb); +of_status_t of_rs_2_m_release_codec_instance(of_rs_2_m_cb_t* ofcb) { + OF_ENTER_FUNCTION + of_rs_2m_release((of_galois_field_code_cb_t*)ofcb); #ifdef OF_USE_DECODER - if (ofcb->available_symbols_tab != NULL) - { - of_free(ofcb->available_symbols_tab); - ofcb->available_symbols_tab = NULL; - } -#endif /* OF_USE_DECODER */ + if (ofcb->available_symbols_tab != NULL) { + of_free(ofcb->available_symbols_tab); + ofcb->available_symbols_tab = NULL; + } +#endif /* OF_USE_DECODER */ #ifdef OF_DEBUG /* { */ - OF_TRACE_LVL(1, ("\tTo be added: Reed-Solomon GF(2^4): total of %ld bytes for precalculated tables (incl. %ld for the optimized mult table)\n", - sizeof(of_gf_2_4_log) + sizeof(of_gf_2_4_exp) + sizeof(of_gf_2_4_inv) + sizeof(of_gf_2_4_mul_table) + sizeof(of_gf_2_4_opt_mul_table), sizeof(of_gf_2_4_opt_mul_table))) - OF_TRACE_LVL(1, ("\tTo be added: Reed-Solomon GF(2^8): total of %ld bytes for precalculated tables (incl. %ld for mult table)\n", - sizeof(of_gf_2_8_log) + sizeof(of_gf_2_8_exp) + sizeof(of_gf_2_8_inv) + sizeof(of_gf_2_8_mul_table), sizeof(of_gf_2_8_mul_table))) -#endif /* } OF_DEBUG */ - OF_EXIT_FUNCTION - return OF_STATUS_OK; + OF_TRACE_LVL( + 1, + ("\tTo be added: Reed-Solomon GF(2^4): total of %ld bytes for " + "precalculated tables (incl. %ld for the optimized mult table)\n", + sizeof(of_gf_2_4_log) + sizeof(of_gf_2_4_exp) + sizeof(of_gf_2_4_inv) + + sizeof(of_gf_2_4_mul_table) + sizeof(of_gf_2_4_opt_mul_table), + sizeof(of_gf_2_4_opt_mul_table))) + OF_TRACE_LVL(1, ("\tTo be added: Reed-Solomon GF(2^8): total of %ld bytes " + "for precalculated tables (incl. %ld for mult table)\n", + sizeof(of_gf_2_8_log) + sizeof(of_gf_2_8_exp) + + sizeof(of_gf_2_8_inv) + sizeof(of_gf_2_8_mul_table), + sizeof(of_gf_2_8_mul_table))) +#endif /* } OF_DEBUG */ + OF_EXIT_FUNCTION + return OF_STATUS_OK; } - -of_status_t of_rs_2_m_set_fec_parameters (of_rs_2_m_cb_t* ofcb, - of_rs_2_m_parameters_t* params) -{ - OF_ENTER_FUNCTION - ofcb->m = params->m; - if ((ofcb->m != 4) && (ofcb->m != 8)) { - OF_PRINT_ERROR(("ERROR: invalid m parameter (must be 4 or 8)")); - goto error; - } - ofcb->field_size = (1 << ofcb->m) - 1; - ofcb->max_nb_encoding_symbols = ofcb->max_nb_source_symbols = ofcb->field_size; - if ((ofcb->nb_source_symbols = params->nb_source_symbols) > ofcb->max_nb_source_symbols) { - OF_PRINT_ERROR(("ERROR: invalid nb_source_symbols parameter (got %d, maximum is %d)", - ofcb->nb_source_symbols, ofcb->max_nb_source_symbols)) - goto error; - } - ofcb->nb_source_symbols = params->nb_source_symbols; - ofcb->nb_repair_symbols = params->nb_repair_symbols; - ofcb->encoding_symbol_length = params->encoding_symbol_length; - ofcb->nb_encoding_symbols = ofcb->nb_source_symbols + ofcb->nb_repair_symbols; +of_status_t of_rs_2_m_set_fec_parameters(of_rs_2_m_cb_t* ofcb, + of_rs_2_m_parameters_t* params) { + OF_ENTER_FUNCTION + ofcb->m = params->m; + if ((ofcb->m != 4) && (ofcb->m != 8)) { + OF_PRINT_ERROR(("ERROR: invalid m parameter (must be 4 or 8)")); + goto error; + } + ofcb->field_size = (1 << ofcb->m) - 1; + ofcb->max_nb_encoding_symbols = ofcb->max_nb_source_symbols = + ofcb->field_size; + if ((ofcb->nb_source_symbols = params->nb_source_symbols) > + ofcb->max_nb_source_symbols) { + OF_PRINT_ERROR( + ("ERROR: invalid nb_source_symbols parameter (got %d, maximum is %d)", + ofcb->nb_source_symbols, ofcb->max_nb_source_symbols)) + goto error; + } + ofcb->nb_source_symbols = params->nb_source_symbols; + ofcb->nb_repair_symbols = params->nb_repair_symbols; + ofcb->encoding_symbol_length = params->encoding_symbol_length; + ofcb->nb_encoding_symbols = ofcb->nb_source_symbols + ofcb->nb_repair_symbols; #ifdef OF_USE_DECODER - ofcb->available_symbols_tab = (void**) of_calloc (ofcb->nb_encoding_symbols, sizeof (void*)); - ofcb->nb_available_symbols = 0; - ofcb->nb_available_source_symbols = 0; -#endif /* OF_USE_DECODER */ - OF_EXIT_FUNCTION - return OF_STATUS_OK; + ofcb->available_symbols_tab = + (void**)of_calloc(ofcb->nb_encoding_symbols, sizeof(void*)); + ofcb->nb_available_symbols = 0; + ofcb->nb_available_source_symbols = 0; +#endif /* OF_USE_DECODER */ + OF_EXIT_FUNCTION + return OF_STATUS_OK; error: - OF_EXIT_FUNCTION - return OF_STATUS_FATAL_ERROR; + OF_EXIT_FUNCTION + return OF_STATUS_FATAL_ERROR; } - -of_status_t of_rs_2_m_set_callback_functions (of_rs_2_m_cb_t* ofcb, - void* (*decoded_source_symbol_callback) (void *context, - _UINT32 size, /* size of decoded source symbol */ - _UINT32 esi), /* encoding symbol ID in {0..k-1} */ - void* (*decoded_repair_symbol_callback) (void *context, - _UINT32 size, /* size of decoded repair symbol */ - _UINT32 esi), /* encoding symbol ID in {0..k-1} */ - void* context_4_callback) -{ - ofcb->decoded_source_symbol_callback = decoded_source_symbol_callback; - ofcb->decoded_repair_symbol_callback = decoded_repair_symbol_callback; - ofcb->context_4_callback = context_4_callback; - if (ofcb->decoded_repair_symbol_callback != NULL) - { - OF_PRINT_ERROR(("WARNING, the decoded repair symbol callback is never called with Reed-Solomon codes, since those repair symbols are never decoded\n")) - } - return OF_STATUS_OK; +of_status_t of_rs_2_m_set_callback_functions( + of_rs_2_m_cb_t* ofcb, + void* (*decoded_source_symbol_callback)( + void* context, _UINT32 size, /* size of decoded source symbol */ + _UINT32 esi), /* encoding symbol ID in {0..k-1} */ + void* (*decoded_repair_symbol_callback)( + void* context, _UINT32 size, /* size of decoded repair symbol */ + _UINT32 esi), /* encoding symbol ID in {0..k-1} */ + void* context_4_callback) { + ofcb->decoded_source_symbol_callback = decoded_source_symbol_callback; + ofcb->decoded_repair_symbol_callback = decoded_repair_symbol_callback; + ofcb->context_4_callback = context_4_callback; + if (ofcb->decoded_repair_symbol_callback != NULL) { + OF_PRINT_ERROR( + ("WARNING, the decoded repair symbol callback is never called with " + "Reed-Solomon codes, since those repair symbols are never decoded\n")) + } + return OF_STATUS_OK; } - #ifdef OF_USE_ENCODER -of_status_t of_rs_2_m_build_repair_symbol (of_rs_2_m_cb_t* ofcb, - void* encoding_symbols_tab[], - _UINT32 esi_of_symbol_to_build) -{ - OF_ENTER_FUNCTION - if (esi_of_symbol_to_build < ofcb->nb_source_symbols || esi_of_symbol_to_build >= ofcb->nb_encoding_symbols) - { - OF_PRINT_ERROR(("ERROR: bad esi of encoding symbol (%d)\n", esi_of_symbol_to_build)) - goto error; - } - if (encoding_symbols_tab[esi_of_symbol_to_build] == NULL) - { - if ((encoding_symbols_tab[esi_of_symbol_to_build] = of_calloc (1, ofcb->encoding_symbol_length)) == NULL) - { - OF_PRINT_ERROR(("ERROR: no memory\n")) - goto error; - } - } - if (ofcb->enc_matrix == NULL) - { - if (of_rs_2m_build_encoding_matrix((of_galois_field_code_cb_t*)ofcb) != OF_STATUS_OK) - { - OF_PRINT_ERROR(("ERROR: creating encoding matrix failed\n")) - goto error; - } - } - if (of_rs_2m_encode((of_galois_field_code_cb_t*) ofcb, (gf**)encoding_symbols_tab, - encoding_symbols_tab[esi_of_symbol_to_build], esi_of_symbol_to_build, - ofcb->encoding_symbol_length) != OF_STATUS_OK) - { - OF_PRINT_ERROR(("ERROR: of_rs_encode failed\n")) - goto error; - } - OF_EXIT_FUNCTION - return OF_STATUS_OK; +of_status_t of_rs_2_m_build_repair_symbol(of_rs_2_m_cb_t* ofcb, + void* encoding_symbols_tab[], + _UINT32 esi_of_symbol_to_build) { + OF_ENTER_FUNCTION + if (esi_of_symbol_to_build < ofcb->nb_source_symbols || + esi_of_symbol_to_build >= ofcb->nb_encoding_symbols) { + OF_PRINT_ERROR( + ("ERROR: bad esi of encoding symbol (%d)\n", esi_of_symbol_to_build)) + goto error; + } + if (encoding_symbols_tab[esi_of_symbol_to_build] == NULL) { + if ((encoding_symbols_tab[esi_of_symbol_to_build] = + of_calloc(1, ofcb->encoding_symbol_length)) == NULL) { + OF_PRINT_ERROR(("ERROR: no memory\n")) + goto error; + } + } + if (ofcb->enc_matrix == NULL) { + if (of_rs_2m_build_encoding_matrix((of_galois_field_code_cb_t*)ofcb) != + OF_STATUS_OK) { + OF_PRINT_ERROR(("ERROR: creating encoding matrix failed\n")) + goto error; + } + } + if (of_rs_2m_encode( + (of_galois_field_code_cb_t*)ofcb, (gf**)encoding_symbols_tab, + encoding_symbols_tab[esi_of_symbol_to_build], esi_of_symbol_to_build, + ofcb->encoding_symbol_length) != OF_STATUS_OK) { + OF_PRINT_ERROR(("ERROR: of_rs_encode failed\n")) + goto error; + } + OF_EXIT_FUNCTION + return OF_STATUS_OK; error: - OF_EXIT_FUNCTION - return OF_STATUS_ERROR; + OF_EXIT_FUNCTION + return OF_STATUS_ERROR; } -#endif //OF_USE_ENCODER - +#endif // OF_USE_ENCODER #ifdef OF_USE_DECODER -of_status_t of_rs_2_m_decode_with_new_symbol (of_rs_2_m_cb_t* ofcb, - void* new_symbol, - _UINT32 new_symbol_esi) -{ - OF_ENTER_FUNCTION - if (ofcb->decoding_finished) - { - OF_TRACE_LVL(2, ("%s: decoding already done\n", __FUNCTION__)); - return OF_STATUS_OK; - } - if (ofcb->available_symbols_tab[new_symbol_esi] != NULL) - { - /* duplicated symbol, ignore */ - OF_TRACE_LVL(2, ("%s: symbol (esi=%d) duplicated\n", __FUNCTION__, new_symbol_esi)); - goto end; - } - ofcb->available_symbols_tab[new_symbol_esi] = new_symbol; - ofcb->nb_available_symbols++; - if (new_symbol_esi < ofcb->nb_source_symbols) - { - /* remember a new source symbol is available */ - ofcb->nb_available_source_symbols++; - } +of_status_t of_rs_2_m_decode_with_new_symbol(of_rs_2_m_cb_t* ofcb, + void* new_symbol, + _UINT32 new_symbol_esi) { + OF_ENTER_FUNCTION + if (ofcb->decoding_finished) { + OF_TRACE_LVL(2, ("%s: decoding already done\n", __FUNCTION__)); + return OF_STATUS_OK; + } + if (ofcb->available_symbols_tab[new_symbol_esi] != NULL) { + /* duplicated symbol, ignore */ + OF_TRACE_LVL( + 2, ("%s: symbol (esi=%d) duplicated\n", __FUNCTION__, new_symbol_esi)); + goto end; + } + ofcb->available_symbols_tab[new_symbol_esi] = new_symbol; + ofcb->nb_available_symbols++; + if (new_symbol_esi < ofcb->nb_source_symbols) { + /* remember a new source symbol is available */ + ofcb->nb_available_source_symbols++; + } - if (ofcb->nb_available_source_symbols == ofcb->nb_source_symbols) - { - /* we received all the k source symbols, so it's finished */ - ofcb->decoding_finished = true; - OF_TRACE_LVL(2, ("%s: done, all source symbols have been received\n", __FUNCTION__)); - OF_EXIT_FUNCTION - return OF_STATUS_OK; - } - if (ofcb->nb_available_symbols >= ofcb->nb_source_symbols) - { - /* we received a sufficient number of symbols, so let's decode */ - if (of_rs_2_m_finish_decoding(ofcb) != OF_STATUS_OK) - { - OF_PRINT_ERROR(("ERROR: of_rs_decode failure\n")) - goto error; - } - ASSERT(ofcb->decoding_finished == true); - OF_TRACE_LVL(2, ("%s: done, decoding successful\n", __FUNCTION__)); - OF_EXIT_FUNCTION - return OF_STATUS_OK; - } + if (ofcb->nb_available_source_symbols == ofcb->nb_source_symbols) { + /* we received all the k source symbols, so it's finished */ + ofcb->decoding_finished = true; + OF_TRACE_LVL( + 2, ("%s: done, all source symbols have been received\n", __FUNCTION__)); + OF_EXIT_FUNCTION + return OF_STATUS_OK; + } + if (ofcb->nb_available_symbols >= ofcb->nb_source_symbols) { + /* we received a sufficient number of symbols, so let's decode */ + if (of_rs_2_m_finish_decoding(ofcb) != OF_STATUS_OK) { + OF_PRINT_ERROR(("ERROR: of_rs_decode failure\n")) + goto error; + } + ASSERT(ofcb->decoding_finished == true); + OF_TRACE_LVL(2, ("%s: done, decoding successful\n", __FUNCTION__)); + OF_EXIT_FUNCTION + return OF_STATUS_OK; + } end: - OF_TRACE_LVL(2, ("%s: okay, but not yet finished\n", __FUNCTION__)); - OF_EXIT_FUNCTION - return OF_STATUS_OK; + OF_TRACE_LVL(2, ("%s: okay, but not yet finished\n", __FUNCTION__)); + OF_EXIT_FUNCTION + return OF_STATUS_OK; error: - OF_EXIT_FUNCTION - return OF_STATUS_ERROR; + OF_EXIT_FUNCTION + return OF_STATUS_ERROR; } +of_status_t of_rs_2_m_set_available_symbols( + of_rs_2_m_cb_t* ofcb, void* const encoding_symbols_tab[]) { + _UINT32 i; -of_status_t of_rs_2_m_set_available_symbols (of_rs_2_m_cb_t* ofcb, - void* const encoding_symbols_tab[]) -{ - _UINT32 i; - - OF_ENTER_FUNCTION - ofcb->nb_available_symbols = 0; - ofcb->nb_available_source_symbols = 0; - for (i = 0; i < ofcb->nb_encoding_symbols; i++) - { - if ((ofcb->available_symbols_tab[i] = encoding_symbols_tab[i]) == NULL) - { - continue; - } - if (i < ofcb->nb_source_symbols) - { - ofcb->nb_available_source_symbols++; - } - ofcb->nb_available_symbols++; - } - OF_EXIT_FUNCTION - return OF_STATUS_OK; + OF_ENTER_FUNCTION + ofcb->nb_available_symbols = 0; + ofcb->nb_available_source_symbols = 0; + for (i = 0; i < ofcb->nb_encoding_symbols; i++) { + if ((ofcb->available_symbols_tab[i] = encoding_symbols_tab[i]) == NULL) { + continue; + } + if (i < ofcb->nb_source_symbols) { + ofcb->nb_available_source_symbols++; + } + ofcb->nb_available_symbols++; + } + OF_EXIT_FUNCTION + return OF_STATUS_OK; } - -#if 0 /* new */ +#if 0 /* new */ of_status_t of_rs_2_m_finish_decoding (of_rs_2_m_cb_t* ofcb) { _UINT32 k; @@ -370,205 +359,192 @@ error: #else /* old follows */ -of_status_t of_rs_2_m_finish_decoding (of_rs_2_m_cb_t* ofcb) -{ - _UINT32 k; - _UINT32 n; - // char *tmp_buf[ofcb->nb_source_symbols];/* keep available source/repair symbol buffers here... */ - // int tmp_esi[ofcb->nb_source_symbols]; /* ...and their esi here. In fact we only need k entries - // * in these tables, but in order to avoid using malloc (time - // * consumming), we use an automatic table of maximum size for - // * both tmp_buf[] and tmp_esi[]. */ - _INT32 tmp_idx; /* index in tmp_buf[] and tmp_esi[] tabs */ - char *large_buf = NULL; /* single large buffer where to copy all source/repair symbols */ - _UINT32 off; /* offset, in unit of characters, in large_buf */ - void **ass_buf; /* tmp pointer to the current available source symbol entry in available_symbols_tab[] */ - _UINT32 ass_esi; /* corresponding available source symbol ESI */ - void **ars_buf; /* tmp pointer to the current available repair symbol entry in available_symbols_tab[] */ - _UINT32 ars_esi; /* corresponding available repair symbol ESI */ +of_status_t of_rs_2_m_finish_decoding(of_rs_2_m_cb_t* ofcb) { + _UINT32 k; + _UINT32 n; + // char *tmp_buf[ofcb->nb_source_symbols];/* keep available + // source/repair symbol buffers here... */ int + // tmp_esi[ofcb->nb_source_symbols]; /* ...and their esi here. In fact we only + // need k entries + // * in these tables, but in order to avoid using malloc (time + // * consumming), we use an automatic table of maximum size for + // * both tmp_buf[] and tmp_esi[]. */ + _INT32 tmp_idx; /* index in tmp_buf[] and tmp_esi[] tabs */ + char* large_buf = + NULL; /* single large buffer where to copy all source/repair symbols */ + _UINT32 off; /* offset, in unit of characters, in large_buf */ + void** ass_buf; /* tmp pointer to the current available source symbol entry in + available_symbols_tab[] */ + _UINT32 ass_esi; /* corresponding available source symbol ESI */ + void** ars_buf; /* tmp pointer to the current available repair symbol entry in + available_symbols_tab[] */ + _UINT32 ars_esi; /* corresponding available repair symbol ESI */ - OF_ENTER_FUNCTION - if (ofcb->decoding_finished) - { - return OF_STATUS_OK; - } - k = ofcb->nb_source_symbols; - n = ofcb->nb_encoding_symbols; - int *tmp_esi = (int*)malloc(ofcb->field_size*sizeof(int)); - char ** tmp_buf = (char**)malloc(n*sizeof(char*)); // ???? WRT RS(255) - for(int i=0; inb_available_symbols < k) - { - OF_PRINT_ERROR(("ERROR: nb received symbols < nb source symbols\n")) - OF_EXIT_FUNCTION - return OF_STATUS_FAILURE; - } - if (ofcb->nb_available_source_symbols == k) - { - /* we received all the k source symbols, so it's finished */ - ofcb->decoding_finished = true; - OF_EXIT_FUNCTION - return OF_STATUS_OK; - } - /* - * Copy available source and repair symbols in a single large buffer first. - * NB: this is required by the current FEC codec which modifies - * the tmp_buf buffers!!! - */ - large_buf = (char *) of_malloc(k * ofcb->encoding_symbol_length); - if (large_buf == NULL) - { - goto no_mem; - } - /* Then remember the location of each symbol buffer */ - for (tmp_idx = 0, off = 0; tmp_idx < k; tmp_idx++, off += ofcb->encoding_symbol_length) { - tmp_buf[tmp_idx] = large_buf + off; - } - tmp_idx = 0; /* index in tmp_buf and tmp_esi tables */ - /* - * Copy all the available source symbols (it's essential for decoding speed purposes) and - * a sufficient number of repair symbols to the tmp_buf array. Copy data as well in the - * large_buf buffer. - * Because of of_rs_decode internal details, we put source symbols at their right location - * and fill in the gaps (i.e. erased source symbols) with repair symbols. - */ - ass_esi = 0; - ars_esi = k; - ass_buf = ofcb->available_symbols_tab; - ars_buf = ofcb->available_symbols_tab + k; - for (tmp_idx = 0; tmp_idx < k; tmp_idx++) - { - if (*ass_buf == NULL) - { - /* this source symbol is not available, replace it with a repair */ - while (*ars_buf == NULL) - { - ars_esi++; - ars_buf++; - } - OF_TRACE_LVL(2, ("of_rs_finish_decoding: copy repair with esi=%d in tmp_buf[%d]\n", - ars_esi, tmp_idx)) - memcpy(tmp_buf[tmp_idx], *ars_buf, ofcb->encoding_symbol_length); - tmp_esi[tmp_idx] = ars_esi; - ars_esi++; - ars_buf++; - } - else - { - OF_TRACE_LVL(2, ("of_rs_finish_decoding: copy source with esi=%d in tmp_buf[%d]\n", - ars_esi, tmp_idx)) + OF_ENTER_FUNCTION + if (ofcb->decoding_finished) { + return OF_STATUS_OK; + } + k = ofcb->nb_source_symbols; + n = ofcb->nb_encoding_symbols; + int* tmp_esi = (int*)malloc(ofcb->field_size * sizeof(int)); + char** tmp_buf = (char**)malloc(n * sizeof(char*)); // ???? WRT RS(255) + for (int i = 0; i < n; i++) { + tmp_buf[i] = NULL; + } + if (ofcb->nb_available_symbols < k) { + OF_PRINT_ERROR(("ERROR: nb received symbols < nb source symbols\n")) + OF_EXIT_FUNCTION + return OF_STATUS_FAILURE; + } + if (ofcb->nb_available_source_symbols == k) { + /* we received all the k source symbols, so it's finished */ + ofcb->decoding_finished = true; + OF_EXIT_FUNCTION + return OF_STATUS_OK; + } + /* + * Copy available source and repair symbols in a single large buffer first. + * NB: this is required by the current FEC codec which modifies + * the tmp_buf buffers!!! + */ + large_buf = (char*)of_malloc(k * ofcb->encoding_symbol_length); + if (large_buf == NULL) { + goto no_mem; + } + /* Then remember the location of each symbol buffer */ + for (tmp_idx = 0, off = 0; tmp_idx < k; + tmp_idx++, off += ofcb->encoding_symbol_length) { + tmp_buf[tmp_idx] = large_buf + off; + } + tmp_idx = 0; /* index in tmp_buf and tmp_esi tables */ + /* + * Copy all the available source symbols (it's essential for decoding speed + * purposes) and a sufficient number of repair symbols to the tmp_buf array. + * Copy data as well in the large_buf buffer. Because of of_rs_decode internal + * details, we put source symbols at their right location and fill in the gaps + * (i.e. erased source symbols) with repair symbols. + */ + ass_esi = 0; + ars_esi = k; + ass_buf = ofcb->available_symbols_tab; + ars_buf = ofcb->available_symbols_tab + k; + for (tmp_idx = 0; tmp_idx < k; tmp_idx++) { + if (*ass_buf == NULL) { + /* this source symbol is not available, replace it with a repair */ + while (*ars_buf == NULL) { + ars_esi++; + ars_buf++; + } + OF_TRACE_LVL( + 2, ("of_rs_finish_decoding: copy repair with esi=%d in tmp_buf[%d]\n", + ars_esi, tmp_idx)) + memcpy(tmp_buf[tmp_idx], *ars_buf, ofcb->encoding_symbol_length); + tmp_esi[tmp_idx] = ars_esi; + ars_esi++; + ars_buf++; + } else { + OF_TRACE_LVL( + 2, ("of_rs_finish_decoding: copy source with esi=%d in tmp_buf[%d]\n", + ars_esi, tmp_idx)) - int i; - for (i=0;iencoding_symbol_length;i++) { - } - memcpy(tmp_buf[tmp_idx], *ass_buf, ofcb->encoding_symbol_length); - tmp_esi[tmp_idx] = ass_esi; - } - ass_esi++; - ass_buf++; - } + int i; + for (i = 0; i < ofcb->encoding_symbol_length; i++) { + } + memcpy(tmp_buf[tmp_idx], *ass_buf, ofcb->encoding_symbol_length); + tmp_esi[tmp_idx] = ass_esi; + } + ass_esi++; + ass_buf++; + } #if 0 for (tmp_idx = 0; tmp_idx < k; tmp_idx++) { OF_TRACE_LVL(2, ("Before of_rs_decode: esi=%d, k=%d, tmp_idx=%d\n", tmp_esi[tmp_idx], k, tmp_idx)) } #endif - if (ofcb->enc_matrix == NULL) - { - if (of_rs_2m_build_encoding_matrix((of_galois_field_code_cb_t*)ofcb) != OF_STATUS_OK) - { - OF_PRINT_ERROR(("ERROR: creating encoding matrix failed\n")) - goto error; - } - } - if (of_rs_2m_decode ((of_galois_field_code_cb_t*)ofcb, (gf**)tmp_buf, (int*)tmp_esi, ofcb->encoding_symbol_length) != OF_STATUS_OK) - { - OF_PRINT_ERROR(("ERROR: of_rs_decode failure\n")) - goto error; - } - ofcb->decoding_finished = true; + if (ofcb->enc_matrix == NULL) { + if (of_rs_2m_build_encoding_matrix((of_galois_field_code_cb_t*)ofcb) != + OF_STATUS_OK) { + OF_PRINT_ERROR(("ERROR: creating encoding matrix failed\n")) + goto error; + } + } + if (of_rs_2m_decode((of_galois_field_code_cb_t*)ofcb, (gf**)tmp_buf, + (int*)tmp_esi, + ofcb->encoding_symbol_length) != OF_STATUS_OK) { + OF_PRINT_ERROR(("ERROR: of_rs_decode failure\n")) + goto error; + } + ofcb->decoding_finished = true; #if 0 for (tmp_idx = 0; tmp_idx < k; tmp_idx++) { OF_TRACE_LVL(2, ("After of_rs_decode: esi=%d, k=%d, tmp_idx=%d\n", tmp_esi[tmp_idx], k, tmp_idx)) } #endif - /* - * finally update the source_symbols_tab table with the decoded source symbols. - */ - for (tmp_idx = 0; tmp_idx < k; tmp_idx++) - { - ASSERT(tmp_idx < k); - ass_buf = &(ofcb->available_symbols_tab[tmp_idx]); - if (*ass_buf != NULL) - { - /* nothing to do, this source symbol has already been received. */ - continue; - } - /* - * This is a new, decoded source symbol. - * First copy it into a permanent buffer. - * Call either the associated callback or allocate memory, and then - * copy the symbol content in it. - */ - if (ofcb->decoded_source_symbol_callback != NULL) - { - *ass_buf = ofcb->decoded_source_symbol_callback (ofcb->context_4_callback, - ofcb->encoding_symbol_length, tmp_idx); - } - else - { - *ass_buf = (void *) of_malloc (ofcb->encoding_symbol_length); - } - if (*ass_buf == NULL) - { - goto no_mem; - } - memcpy(*ass_buf, tmp_buf[tmp_idx], ofcb->encoding_symbol_length); - OF_TRACE_LVL(2, ("of_rs_finish_decoding: decoded source symbol esi=%d from tmp_buf[%d]\n", - tmp_idx, tmp_idx)) - } - of_free(large_buf); - free(tmp_esi); - for(int i=0;iavailable_symbols_tab[tmp_idx]); + if (*ass_buf != NULL) { + /* nothing to do, this source symbol has already been received. */ + continue; + } + /* + * This is a new, decoded source symbol. + * First copy it into a permanent buffer. + * Call either the associated callback or allocate memory, and then + * copy the symbol content in it. + */ + if (ofcb->decoded_source_symbol_callback != NULL) { + *ass_buf = ofcb->decoded_source_symbol_callback( + ofcb->context_4_callback, ofcb->encoding_symbol_length, tmp_idx); + } else { + *ass_buf = (void*)of_malloc(ofcb->encoding_symbol_length); + } + if (*ass_buf == NULL) { + goto no_mem; + } + memcpy(*ass_buf, tmp_buf[tmp_idx], ofcb->encoding_symbol_length); + OF_TRACE_LVL(2, ("of_rs_finish_decoding: decoded source symbol esi=%d from " + "tmp_buf[%d]\n", + tmp_idx, tmp_idx)) + } + of_free(large_buf); + free(tmp_esi); + free(tmp_buf); + + OF_EXIT_FUNCTION + return OF_STATUS_OK; no_mem: - OF_PRINT_ERROR(("ERROR: out of memory.\n")) - free(tmp_esi); - for(int i=0;idecoding_finished; +bool of_rs_2_m_is_decoding_complete(of_rs_2_m_cb_t* ofcb) { + OF_ENTER_FUNCTION + OF_EXIT_FUNCTION + return ofcb->decoding_finished; } - -of_status_t of_rs_2_m_get_source_symbols_tab (of_rs_2_m_cb_t* ofcb, - void* source_symbols_tab[]) -{ - OF_ENTER_FUNCTION - if (of_rs_2_m_is_decoding_complete(ofcb) == false) - { - OF_PRINT_ERROR(("ERROR: decoding not complete.\n")) - OF_EXIT_FUNCTION - return OF_STATUS_ERROR; - } +of_status_t of_rs_2_m_get_source_symbols_tab(of_rs_2_m_cb_t* ofcb, + void* source_symbols_tab[]) { + OF_ENTER_FUNCTION + if (of_rs_2_m_is_decoding_complete(ofcb) == false) { + OF_PRINT_ERROR(("ERROR: decoding not complete.\n")) + OF_EXIT_FUNCTION + return OF_STATUS_ERROR; + } #if 0 _UINT32 i; for (i = 0; i < ofcb->nb_source_symbols; i++) @@ -579,95 +555,106 @@ of_status_t of_rs_2_m_get_source_symbols_tab (of_rs_2_m_cb_t* ofcb, } } #else - memcpy(source_symbols_tab, ofcb->available_symbols_tab, ofcb->nb_source_symbols * sizeof(void*)); + memcpy(source_symbols_tab, ofcb->available_symbols_tab, + ofcb->nb_source_symbols * sizeof(void*)); #endif - OF_EXIT_FUNCTION - return OF_STATUS_OK; + OF_EXIT_FUNCTION + return OF_STATUS_OK; } +#endif // OF_USE_DECODER -#endif //OF_USE_DECODER +of_status_t of_rs_2_m_set_control_parameter(of_rs_2_m_cb_t* ofcb, _UINT32 type, + void* value, _UINT32 length) { + _UINT16 m; -of_status_t of_rs_2_m_set_control_parameter (of_rs_2_m_cb_t* ofcb, - _UINT32 type, - void* value, - _UINT32 length) -{ - _UINT16 m; + OF_ENTER_FUNCTION + switch (type) { + case OF_RS_CTRL_SET_FIELD_SIZE: + if (value == NULL || length != sizeof(_UINT16)) { + OF_PRINT_ERROR( + ("OF_CTRL_SET_FIELD_SIZE ERROR: null value or bad length (got %d, " + "expected %zu)\n", + length, sizeof(_UINT16))) + goto error; + } + m = *(_UINT16*)value; + if (m != 4 && m != 8) { + OF_PRINT_ERROR(("ERROR: invalid m=%d parameter (must be 4 or 8)\n", m)); + goto error; + } + ofcb->m = m; + ofcb->field_size = (1 << ofcb->m) - 1; + ofcb->max_nb_encoding_symbols = ofcb->max_nb_source_symbols = + ofcb->field_size; + break; - OF_ENTER_FUNCTION - switch (type) { - case OF_RS_CTRL_SET_FIELD_SIZE: - if (value == NULL || length != sizeof(_UINT16)) { - OF_PRINT_ERROR(("OF_CTRL_SET_FIELD_SIZE ERROR: null value or bad length (got %d, expected %zu)\n", length, sizeof(_UINT16))) - goto error; - } - m = *(_UINT16*)value; - if (m != 4 && m != 8) { - OF_PRINT_ERROR(("ERROR: invalid m=%d parameter (must be 4 or 8)\n", m)); - goto error; - } - ofcb->m = m; - ofcb->field_size = (1 << ofcb->m) - 1; - ofcb->max_nb_encoding_symbols = ofcb->max_nb_source_symbols = ofcb->field_size; - break; + default: + OF_PRINT_ERROR(("ERROR: unknown type (%d)\n", type)) + break; + } + OF_EXIT_FUNCTION + return OF_STATUS_OK; - default: - OF_PRINT_ERROR(("ERROR: unknown type (%d)\n", type)) - break; - } - OF_EXIT_FUNCTION - return OF_STATUS_OK; - error: - OF_EXIT_FUNCTION - return OF_STATUS_ERROR; + OF_EXIT_FUNCTION + return OF_STATUS_ERROR; } +of_status_t of_rs_2_m_get_control_parameter(of_rs_2_m_cb_t* ofcb, _UINT32 type, + void* value, _UINT32 length) { + OF_ENTER_FUNCTION + switch (type) { + case OF_CTRL_GET_MAX_K: + if (value == NULL || length != sizeof(_UINT32)) { + OF_PRINT_ERROR( + ("OF_CTRL_GET_MAX_K ERROR: null value or bad length (got %d, " + "expected %zu)\n", + length, sizeof(_UINT32))) + goto error; + } + if (ofcb->max_nb_source_symbols == 0) { + OF_PRINT_ERROR( + ("OF_CTRL_GET_MAX_K ERROR: this parameter is not initialized. Use " + "the of_rs_2_m_set_fec_parameters function to initialize it or " + "of_rs_2_m_set_control_parameter.\n")) + goto error; + } + *(_UINT32*)value = ofcb->max_nb_source_symbols; + OF_TRACE_LVL( + 1, ("%s: OF_CTRL_GET_MAX_K (%d)\n", __FUNCTION__, *(_UINT32*)value)) + break; -of_status_t of_rs_2_m_get_control_parameter (of_rs_2_m_cb_t* ofcb, - _UINT32 type, - void* value, - _UINT32 length) -{ - OF_ENTER_FUNCTION - switch (type) { - case OF_CTRL_GET_MAX_K: - if (value == NULL || length != sizeof(_UINT32)) { - OF_PRINT_ERROR(("OF_CTRL_GET_MAX_K ERROR: null value or bad length (got %d, expected %zu)\n", length, sizeof(_UINT32))) - goto error; - } - if (ofcb->max_nb_source_symbols == 0) { - OF_PRINT_ERROR(("OF_CTRL_GET_MAX_K ERROR: this parameter is not initialized. Use the of_rs_2_m_set_fec_parameters function to initialize it or of_rs_2_m_set_control_parameter.\n")) - goto error; - } - *(_UINT32*)value = ofcb->max_nb_source_symbols; - OF_TRACE_LVL(1, ("%s: OF_CTRL_GET_MAX_K (%d)\n", __FUNCTION__, *(_UINT32*)value)) - break; + case OF_CTRL_GET_MAX_N: + if (value == NULL || length != sizeof(_UINT32)) { + OF_PRINT_ERROR( + ("OF_CTRL_GET_MAX_N ERROR: null value or bad length (got %d, " + "expected %zu)\n", + length, sizeof(_UINT32))) + goto error; + } + if (ofcb->max_nb_encoding_symbols == 0) { + OF_PRINT_ERROR( + ("OF_CTRL_GET_MAX_N ERROR: this parameter is not initialized. Use " + "the of_rs_2_m_set_fec_parameters function to initialize it or " + "of_rs_2_m_set_control_parameter.\n")) + goto error; + } + *(_UINT32*)value = ofcb->max_nb_encoding_symbols; + OF_TRACE_LVL( + 1, ("%s: OF_CTRL_GET_MAX_N (%d)\n", __FUNCTION__, *(_UINT32*)value)) + break; - case OF_CTRL_GET_MAX_N: - if (value == NULL || length != sizeof(_UINT32)) { - OF_PRINT_ERROR(("OF_CTRL_GET_MAX_N ERROR: null value or bad length (got %d, expected %zu)\n", length, sizeof(_UINT32))) - goto error; - } - if (ofcb->max_nb_encoding_symbols == 0) { - OF_PRINT_ERROR(("OF_CTRL_GET_MAX_N ERROR: this parameter is not initialized. Use the of_rs_2_m_set_fec_parameters function to initialize it or of_rs_2_m_set_control_parameter.\n")) - goto error; - } - *(_UINT32*)value = ofcb->max_nb_encoding_symbols; - OF_TRACE_LVL(1, ("%s: OF_CTRL_GET_MAX_N (%d)\n", __FUNCTION__, *(_UINT32*)value)) - break; - - default: - OF_PRINT_ERROR(("ERROR: unknown type (%d)\n", type)) - goto error; - } - OF_EXIT_FUNCTION - return OF_STATUS_OK; + default: + OF_PRINT_ERROR(("ERROR: unknown type (%d)\n", type)) + goto error; + } + OF_EXIT_FUNCTION + return OF_STATUS_OK; error: - OF_EXIT_FUNCTION - return OF_STATUS_ERROR; + OF_EXIT_FUNCTION + return OF_STATUS_ERROR; } #endif