diff --git a/src/modules/rtp_rtcp/source/fec_private_tables_bursty.h b/src/modules/rtp_rtcp/source/fec_private_tables_bursty.h new file mode 100644 index 0000000000..1de9325e8a --- /dev/null +++ b/src/modules/rtp_rtcp/source/fec_private_tables_bursty.h @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_ + +// This file contains a set of packets masks for the FEC code. The masks in +// this table are specifically designed to favor recovery of bursty/consecutive +// loss network conditions. The tradeoff is worse recovery for random losses. +// These packet masks are currently defined to protect up to 12 media packets. +// They have the following property: for any packet mask defined by the +// parameters (k,m), where k = number of media packets, m = number of FEC +// packets, all "consecutive" losses of size <= m are completely recoverable. +// By consecutive losses we mean consecutive with respect to the sequence +// number ordering of the list (media and FEC) of packets. The difference +// between these masks (|kFecMaskBursty|) and |kFecMaskRandom| type, defined +// in fec_private_tables.h, is more significant for longer codes +// (i.e., more packets/symbols in the code, so larger (k,m), i.e., k > 4, +// m > 3). + +#include "typedefs.h" + +namespace { + +const uint8_t kMaskBursty1_1[2] = { + 0x80, 0x00 +}; + +const uint8_t kMaskBursty2_1[2] = { + 0xc0, 0x00 +}; + +const uint8_t kMaskBursty2_2[4] = { + 0x80, 0x00, + 0xc0, 0x00 +}; + +const uint8_t kMaskBursty3_1[2] = { + 0xe0, 0x00 +}; + +const uint8_t kMaskBursty3_2[4] = { + 0xc0, 0x00, + 0xa0, 0x00 +}; + +const uint8_t kMaskBursty3_3[6] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00 +}; + +const uint8_t kMaskBursty4_1[2] = { + 0xf0, 0x00 +}; + +const uint8_t kMaskBursty4_2[4] = { + 0xa0, 0x00, + 0xd0, 0x00 +}; + +const uint8_t kMaskBursty4_3[6] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x90, 0x00 +}; + +const uint8_t kMaskBursty4_4[8] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00 +}; + +const uint8_t kMaskBursty5_1[2] = { + 0xf8, 0x00 +}; + +const uint8_t kMaskBursty5_2[4] = { + 0xd0, 0x00, + 0xa8, 0x00 +}; + +const uint8_t kMaskBursty5_3[6] = { + 0x70, 0x00, + 0x90, 0x00, + 0xc8, 0x00 +}; + +const uint8_t kMaskBursty5_4[8] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x88, 0x00 +}; + +const uint8_t kMaskBursty5_5[10] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00 +}; + +const uint8_t kMaskBursty6_1[2] = { + 0xfc, 0x00 +}; + +const uint8_t kMaskBursty6_2[4] = { + 0xa8, 0x00, + 0xd4, 0x00 +}; + +const uint8_t kMaskBursty6_3[6] = { + 0x94, 0x00, + 0xc8, 0x00, + 0x64, 0x00 +}; + +const uint8_t kMaskBursty6_4[8] = { + 0x60, 0x00, + 0x38, 0x00, + 0x88, 0x00, + 0xc4, 0x00 +}; + +const uint8_t kMaskBursty6_5[10] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x84, 0x00 +}; + +const uint8_t kMaskBursty6_6[12] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00 +}; + +const uint8_t kMaskBursty7_1[2] = { + 0xfe, 0x00 +}; + +const uint8_t kMaskBursty7_2[4] = { + 0xd4, 0x00, + 0xaa, 0x00 +}; + +const uint8_t kMaskBursty7_3[6] = { + 0xc8, 0x00, + 0x74, 0x00, + 0x92, 0x00 +}; + +const uint8_t kMaskBursty7_4[8] = { + 0x38, 0x00, + 0x8a, 0x00, + 0xc4, 0x00, + 0x62, 0x00 +}; + +const uint8_t kMaskBursty7_5[10] = { + 0x60, 0x00, + 0x30, 0x00, + 0x1c, 0x00, + 0x84, 0x00, + 0xc2, 0x00 +}; + +const uint8_t kMaskBursty7_6[12] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x82, 0x00 +}; + +const uint8_t kMaskBursty7_7[14] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00 +}; + +const uint8_t kMaskBursty8_1[2] = { + 0xff, 0x00 +}; + +const uint8_t kMaskBursty8_2[4] = { + 0xaa, 0x00, + 0xd5, 0x00 +}; + +const uint8_t kMaskBursty8_3[6] = { + 0x74, 0x00, + 0x92, 0x00, + 0xc9, 0x00 +}; + +const uint8_t kMaskBursty8_4[8] = { + 0x8a, 0x00, + 0xc5, 0x00, + 0x62, 0x00, + 0x31, 0x00 +}; + +const uint8_t kMaskBursty8_5[10] = { + 0x30, 0x00, + 0x1c, 0x00, + 0x85, 0x00, + 0xc2, 0x00, + 0x61, 0x00 +}; + +const uint8_t kMaskBursty8_6[12] = { + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0e, 0x00, + 0x82, 0x00, + 0xc1, 0x00 +}; + +const uint8_t kMaskBursty8_7[14] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x81, 0x00 +}; + +const uint8_t kMaskBursty8_8[16] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00 +}; + +const uint8_t kMaskBursty9_1[2] = { + 0xff, 0x80 +}; + +const uint8_t kMaskBursty9_2[4] = { + 0xd5, 0x00, + 0xaa, 0x80 +}; + +const uint8_t kMaskBursty9_3[6] = { + 0x92, 0x00, + 0xc9, 0x00, + 0x74, 0x80 +}; + +const uint8_t kMaskBursty9_4[8] = { + 0xc5, 0x00, + 0x62, 0x00, + 0x39, 0x00, + 0x8a, 0x80 +}; + +const uint8_t kMaskBursty9_5[10] = { + 0x1c, 0x00, + 0x85, 0x00, + 0xc2, 0x80, + 0x61, 0x00, + 0x30, 0x80 +}; + +const uint8_t kMaskBursty9_6[12] = { + 0x30, 0x00, + 0x18, 0x00, + 0x0e, 0x00, + 0x82, 0x80, + 0xc1, 0x00, + 0x60, 0x80 +}; + +const uint8_t kMaskBursty9_7[14] = { + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x07, 0x00, + 0x81, 0x00, + 0xc0, 0x80 +}; + +const uint8_t kMaskBursty9_8[16] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x80, 0x80 +}; + +const uint8_t kMaskBursty9_9[18] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80 +}; + +const uint8_t kMaskBursty10_1[2] = { + 0xff, 0xc0 +}; + +const uint8_t kMaskBursty10_2[4] = { + 0xaa, 0x80, + 0xd5, 0x40 +}; + +const uint8_t kMaskBursty10_3[6] = { + 0xc9, 0x00, + 0x74, 0x80, + 0x92, 0x40 +}; + +const uint8_t kMaskBursty10_4[8] = { + 0x62, 0x00, + 0x39, 0x00, + 0x8a, 0x80, + 0xc5, 0x40 +}; + +const uint8_t kMaskBursty10_5[10] = { + 0x85, 0x00, + 0xc2, 0x80, + 0x61, 0x40, + 0x30, 0x80, + 0x18, 0x40 +}; + +const uint8_t kMaskBursty10_6[12] = { + 0x18, 0x00, + 0x0e, 0x00, + 0x82, 0x80, + 0xc1, 0x40, + 0x60, 0x80, + 0x30, 0x40 +}; + +const uint8_t kMaskBursty10_7[14] = { + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x07, 0x00, + 0x81, 0x40, + 0xc0, 0x80, + 0x60, 0x40 +}; + +const uint8_t kMaskBursty10_8[16] = { + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x80, 0x80, + 0xc0, 0x40 +}; + +const uint8_t kMaskBursty10_9[18] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x80, 0x40 +}; + +const uint8_t kMaskBursty10_10[20] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0 +}; + +const uint8_t kMaskBursty11_1[2] = { + 0xff, 0xe0 +}; + +const uint8_t kMaskBursty11_2[4] = { + 0xd5, 0x40, + 0xaa, 0xa0 +}; + +const uint8_t kMaskBursty11_3[6] = { + 0x74, 0x80, + 0x92, 0x40, + 0xc9, 0x20 +}; + +const uint8_t kMaskBursty11_4[8] = { + 0x39, 0x00, + 0x8a, 0x80, + 0xc5, 0x40, + 0x62, 0x20 +}; + +const uint8_t kMaskBursty11_5[10] = { + 0xc2, 0xc0, + 0x61, 0x00, + 0x30, 0xa0, + 0x1c, 0x40, + 0x85, 0x20 +}; + +const uint8_t kMaskBursty11_6[12] = { + 0x0e, 0x00, + 0x82, 0x80, + 0xc1, 0x40, + 0x60, 0xa0, + 0x30, 0x40, + 0x18, 0x20 +}; + +const uint8_t kMaskBursty11_7[14] = { + 0x18, 0x00, + 0x0c, 0x00, + 0x07, 0x00, + 0x81, 0x40, + 0xc0, 0xa0, + 0x60, 0x40, + 0x30, 0x20 +}; + +const uint8_t kMaskBursty11_8[16] = { + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x40, + 0x80, 0xa0, + 0xc0, 0x40, + 0x60, 0x20 +}; + +const uint8_t kMaskBursty11_9[18] = { + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x80, 0x40, + 0xc0, 0x20 +}; + +const uint8_t kMaskBursty11_10[20] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0, + 0x80, 0x20 +}; + +const uint8_t kMaskBursty11_11[22] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0, + 0x00, 0x60 +}; + +const uint8_t kMaskBursty12_1[2] = { + 0xff, 0xf0 +}; + +const uint8_t kMaskBursty12_2[4] = { + 0xaa, 0xa0, + 0xd5, 0x50 +}; + +const uint8_t kMaskBursty12_3[6] = { + 0x92, 0x40, + 0xc9, 0x20, + 0x74, 0x90 +}; + +const uint8_t kMaskBursty12_4[8] = { + 0x8a, 0x80, + 0xc5, 0x40, + 0x62, 0x20, + 0x39, 0x10 +}; + +const uint8_t kMaskBursty12_5[10] = { + 0x61, 0x00, + 0x30, 0xa0, + 0x1c, 0x50, + 0x85, 0x20, + 0xc2, 0x90 +}; + +const uint8_t kMaskBursty12_6[12] = { + 0x82, 0x90, + 0xc1, 0x40, + 0x60, 0xa0, + 0x30, 0x50, + 0x18, 0x20, + 0x0c, 0x10 +}; + +const uint8_t kMaskBursty12_7[14] = { + 0x0c, 0x00, + 0x07, 0x00, + 0x81, 0x40, + 0xc0, 0xa0, + 0x60, 0x50, + 0x30, 0x20, + 0x18, 0x10 +}; + +const uint8_t kMaskBursty12_8[16] = { + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x80, 0xa0, + 0xc0, 0x50, + 0x60, 0x20, + 0x30, 0x10 +}; + +const uint8_t kMaskBursty12_9[18] = { + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x80, 0x50, + 0xc0, 0x20, + 0x60, 0x10 +}; + +const uint8_t kMaskBursty12_10[20] = { + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0, + 0x80, 0x20, + 0xc0, 0x10 +}; + +const uint8_t kMaskBursty12_11[22] = { + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0, + 0x00, 0x60, + 0x80, 0x10 +}; + +const uint8_t kMaskBursty12_12[24] = { + 0x80, 0x00, + 0xc0, 0x00, + 0x60, 0x00, + 0x30, 0x00, + 0x18, 0x00, + 0x0c, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x01, 0x80, + 0x00, 0xc0, + 0x00, 0x60, + 0x00, 0x30 +}; + +const uint8_t* kPacketMaskBursty1[1] = { + kMaskBursty1_1 +}; + +const uint8_t* kPacketMaskBursty2[2] = { + kMaskBursty2_1, + kMaskBursty2_2 +}; + +const uint8_t* kPacketMaskBursty3[3] = { + kMaskBursty3_1, + kMaskBursty3_2, + kMaskBursty3_3 +}; + +const uint8_t* kPacketMaskBursty4[4] = { + kMaskBursty4_1, + kMaskBursty4_2, + kMaskBursty4_3, + kMaskBursty4_4 +}; + +const uint8_t* kPacketMaskBursty5[5] = { + kMaskBursty5_1, + kMaskBursty5_2, + kMaskBursty5_3, + kMaskBursty5_4, + kMaskBursty5_5 +}; + +const uint8_t* kPacketMaskBursty6[6] = { + kMaskBursty6_1, + kMaskBursty6_2, + kMaskBursty6_3, + kMaskBursty6_4, + kMaskBursty6_5, + kMaskBursty6_6 +}; + +const uint8_t* kPacketMaskBursty7[7] = { + kMaskBursty7_1, + kMaskBursty7_2, + kMaskBursty7_3, + kMaskBursty7_4, + kMaskBursty7_5, + kMaskBursty7_6, + kMaskBursty7_7 +}; + +const uint8_t* kPacketMaskBursty8[8] = { + kMaskBursty8_1, + kMaskBursty8_2, + kMaskBursty8_3, + kMaskBursty8_4, + kMaskBursty8_5, + kMaskBursty8_6, + kMaskBursty8_7, + kMaskBursty8_8 +}; + +const uint8_t* kPacketMaskBursty9[9] = { + kMaskBursty9_1, + kMaskBursty9_2, + kMaskBursty9_3, + kMaskBursty9_4, + kMaskBursty9_5, + kMaskBursty9_6, + kMaskBursty9_7, + kMaskBursty9_8, + kMaskBursty9_9 +}; + +const uint8_t* kPacketMaskBursty10[10] = { + kMaskBursty10_1, + kMaskBursty10_2, + kMaskBursty10_3, + kMaskBursty10_4, + kMaskBursty10_5, + kMaskBursty10_6, + kMaskBursty10_7, + kMaskBursty10_8, + kMaskBursty10_9, + kMaskBursty10_10 +}; + +const uint8_t* kPacketMaskBursty11[11] = { + kMaskBursty11_1, + kMaskBursty11_2, + kMaskBursty11_3, + kMaskBursty11_4, + kMaskBursty11_5, + kMaskBursty11_6, + kMaskBursty11_7, + kMaskBursty11_8, + kMaskBursty11_9, + kMaskBursty11_10, + kMaskBursty11_11 +}; + +const uint8_t* kPacketMaskBursty12[12] = { + kMaskBursty12_1, + kMaskBursty12_2, + kMaskBursty12_3, + kMaskBursty12_4, + kMaskBursty12_5, + kMaskBursty12_6, + kMaskBursty12_7, + kMaskBursty12_8, + kMaskBursty12_9, + kMaskBursty12_10, + kMaskBursty12_11, + kMaskBursty12_12 +}; + +const uint8_t** kPacketMaskBurstyTbl[12] = { + kPacketMaskBursty1, + kPacketMaskBursty2, + kPacketMaskBursty3, + kPacketMaskBursty4, + kPacketMaskBursty5, + kPacketMaskBursty6, + kPacketMaskBursty7, + kPacketMaskBursty8, + kPacketMaskBursty9, + kPacketMaskBursty10, + kPacketMaskBursty11, + kPacketMaskBursty12 +}; + +} // namespace +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_ + diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.cc b/src/modules/rtp_rtcp/source/forward_error_correction.cc index c252c01911..bb73ba3236 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/src/modules/rtp_rtcp/source/forward_error_correction.cc @@ -97,6 +97,7 @@ int32_t ForwardErrorCorrection::GenerateFEC( uint8_t protectionFactor, int numImportantPackets, bool useUnequalProtection, + FecMaskType fec_mask_type, PacketList* fecPacketList) { if (mediaPacketList.empty()) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, @@ -171,13 +172,15 @@ int32_t ForwardErrorCorrection::GenerateFEC( fecPacketList->push_back(&_generatedFecPackets[i]); } + const internal::PacketMaskTable mask_table(fec_mask_type, numMediaPackets); + // -- Generate packet masks -- // Always allocate space for a large mask. uint8_t* packetMask = new uint8_t[numFecPackets * kMaskSizeLBitSet]; memset(packetMask, 0, numFecPackets * numMaskBytes); internal::GeneratePacketMasks(numMediaPackets, numFecPackets, numImportantPackets, useUnequalProtection, - packetMask); + mask_table, packetMask); int numMaskBits = InsertZerosInBitMasks(mediaPacketList, packetMask, numMaskBytes, numFecPackets); @@ -199,8 +202,8 @@ int32_t ForwardErrorCorrection::GenerateFEC( return 0; } -int ForwardErrorCorrection::GetNumberOfFecPackets(uint16_t numMediaPackets, - uint8_t protectionFactor) { +int ForwardErrorCorrection::GetNumberOfFecPackets(int numMediaPackets, + int protectionFactor) { // Result in Q0 with an unsigned round. int numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8; // Generate at least one FEC packet if we need protection. diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.h b/src/modules/rtp_rtcp/source/forward_error_correction.h index 2fb59a63bc..3587a1fb67 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.h +++ b/src/modules/rtp_rtcp/source/forward_error_correction.h @@ -21,6 +21,16 @@ namespace webrtc { +// Types for the FEC packet masks. +// The type |kFecMaskRandom| selects the mask defined in fec_private_tables.h, +// and is based on a random loss model. The type |kFecMaskBursty| selects the +// mask defined in fec_private_tables_bursty.h, and is based on a bursty loss +// model. Please refer to those files for a more detailed description. +enum FecMaskType { + kFecMaskRandom, + kFecMaskBursty, +}; + // Forward declaration. class FecPacket; @@ -155,6 +165,12 @@ class ForwardErrorCorrection { * UEP will allocate more protection to the * numImportantPackets from the start of the * mediaPacketList. + * \param[in] fec_mask_type The type of packet mask used in the FEC. + * Random or bursty type may be selected. The + * bursty type is only defined up to 12 media + * packets. If the number of media packets is + * above 12, the packets masks from the + * random table will be selected. * \param[out] fecPacketList List of FEC packets, of type #Packet. Must * be empty on entry. The memory available * through the list will be valid until the @@ -166,6 +182,7 @@ class ForwardErrorCorrection { uint8_t protectionFactor, int numImportantPackets, bool useUnequalProtection, + FecMaskType fec_mask_type, PacketList* fecPacketList); /** @@ -201,8 +218,8 @@ class ForwardErrorCorrection { // Get the number of FEC packets, given the number of media packets and the // protection factor. - int GetNumberOfFecPackets(uint16_t numMediaPackets, - uint8_t protectionFactor); + int GetNumberOfFecPackets(int numMediaPackets, + int protectionFactor); /** * Gets the size in bytes of the FEC/ULP headers, which must be accounted for diff --git a/src/modules/rtp_rtcp/source/forward_error_correction_internal.cc b/src/modules/rtp_rtcp/source/forward_error_correction_internal.cc index 5338174b43..01b0c94384 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction_internal.cc +++ b/src/modules/rtp_rtcp/source/forward_error_correction_internal.cc @@ -8,12 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "forward_error_correction_internal.h" -#include "fec_private_tables.h" +#include "modules/rtp_rtcp/source/forward_error_correction_internal.h" #include #include +#include "modules/rtp_rtcp/source/fec_private_tables.h" +#include "modules/rtp_rtcp/source/fec_private_tables_bursty.h" + namespace { // Allow for different modes of protection for packets in UEP case. @@ -149,19 +151,69 @@ void ShiftFitSubMask(int numMaskBytes, } } - -} //namespace +} // namespace namespace webrtc { namespace internal { +PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type, + int num_media_packets) + : fec_mask_type_(InitMaskType(fec_mask_type, num_media_packets)), + fec_packet_mask_table_(InitMaskTable(fec_mask_type_)) { +} + +// Sets |fec_mask_type_| to the type of packet mask selected. The type of +// packet mask selected is based on |fec_mask_type| and |numMediaPackets|. +// If |numMediaPackets| is larger than the maximum allowed by |fec_mask_type| +// for the bursty type, then the random type is selected. +FecMaskType PacketMaskTable::InitMaskType(FecMaskType fec_mask_type, + int num_media_packets) { + // The mask should not be bigger than |packetMaskTbl|. + assert(num_media_packets <= static_cast(sizeof(packetMaskTbl) / + sizeof(*packetMaskTbl))); + switch (fec_mask_type) { + case kFecMaskRandom: { + // TODO(marpan): in separate CL rename the fec_private_tables.h file and + // its packet masks to indicate "random" loss case. + return kFecMaskRandom; + } + case kFecMaskBursty: { + int max_media_packets = static_cast(sizeof(kPacketMaskBurstyTbl) / + sizeof(*kPacketMaskBurstyTbl)); + if (num_media_packets > max_media_packets) { + return kFecMaskRandom; + } else { + return kFecMaskBursty; + } + } + } + assert(false); + return kFecMaskRandom; +} + +// Returns the pointer to the packet mask tables corresponding to type +// |fec_mask_type|. +const uint8_t*** PacketMaskTable::InitMaskTable(FecMaskType fec_mask_type) { + switch (fec_mask_type) { + case kFecMaskRandom: { + return packetMaskTbl; + } + case kFecMaskBursty: { + return kPacketMaskBurstyTbl; + } + } + assert(false); + return packetMaskTbl; +} + // Remaining protection after important (first partition) packet protection void RemainingPacketProtection(int numMediaPackets, int numFecRemaining, int numFecForImpPackets, int numMaskBytes, ProtectionMode mode, - uint8_t* packetMask) + uint8_t* packetMask, + const PacketMaskTable& mask_table) { if (mode == kModeNoOverlap) { @@ -174,8 +226,9 @@ void RemainingPacketProtection(int numMediaPackets, (lBit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; const uint8_t* packetMaskSub21 = - packetMaskTbl[numMediaPackets - numFecForImpPackets - 1] - [numFecRemaining - 1]; + mask_table.fec_packet_mask_table() + [numMediaPackets - numFecForImpPackets - 1] + [numFecRemaining - 1]; ShiftFitSubMask(numMaskBytes, resMaskBytes, numFecForImpPackets, (numFecForImpPackets + numFecRemaining), @@ -187,7 +240,8 @@ void RemainingPacketProtection(int numMediaPackets, // subMask22 const uint8_t* packetMaskSub22 = - packetMaskTbl[numMediaPackets - 1][numFecRemaining - 1]; + mask_table.fec_packet_mask_table() + [numMediaPackets - 1][numFecRemaining - 1]; FitSubMask(numMaskBytes, numMaskBytes, numFecRemaining, packetMaskSub22, &packetMask[numFecForImpPackets * numMaskBytes]); @@ -212,7 +266,8 @@ void RemainingPacketProtection(int numMediaPackets, void ImportantPacketProtection(int numFecForImpPackets, int numImpPackets, int numMaskBytes, - uint8_t* packetMask) + uint8_t* packetMask, + const PacketMaskTable& mask_table) { const int lBit = numImpPackets > 16 ? 1 : 0; const int numImpMaskBytes = @@ -220,7 +275,8 @@ void ImportantPacketProtection(int numFecForImpPackets, // Get subMask1 from table const uint8_t* packetMaskSub1 = - packetMaskTbl[numImpPackets - 1][numFecForImpPackets - 1]; + mask_table.fec_packet_mask_table() + [numImpPackets - 1][numFecForImpPackets - 1]; FitSubMask(numMaskBytes, numImpMaskBytes, numFecForImpPackets, packetMaskSub1, packetMask); @@ -302,7 +358,8 @@ void UnequalProtectionMask(int numMediaPackets, int numFecPackets, int numImpPackets, int numMaskBytes, - uint8_t* packetMask) + uint8_t* packetMask, + const PacketMaskTable& mask_table) { // Set Protection type and allocation @@ -327,7 +384,8 @@ void UnequalProtectionMask(int numMediaPackets, if (numFecForImpPackets > 0) { ImportantPacketProtection(numFecForImpPackets, numImpPackets, - numMaskBytes, packetMask); + numMaskBytes, packetMask, + mask_table); } // @@ -337,7 +395,7 @@ void UnequalProtectionMask(int numMediaPackets, { RemainingPacketProtection(numMediaPackets, numFecRemaining, numFecForImpPackets, numMaskBytes, - mode, packetMask); + mode, packetMask, mask_table); } } @@ -346,10 +404,9 @@ void GeneratePacketMasks(int numMediaPackets, int numFecPackets, int numImpPackets, bool useUnequalProtection, + const PacketMaskTable& mask_table, uint8_t* packetMask) { - assert(numMediaPackets <= static_cast(sizeof(packetMaskTbl) / - sizeof(*packetMaskTbl))); assert(numMediaPackets > 0); assert(numFecPackets <= numMediaPackets && numFecPackets > 0); assert(numImpPackets <= numMediaPackets && numImpPackets >= 0); @@ -365,16 +422,16 @@ void GeneratePacketMasks(int numMediaPackets, // Mask = (k,n-k), with protection factor = (n-k)/k, // where k = numMediaPackets, n=total#packets, (n-k)=numFecPackets. memcpy(packetMask, - packetMaskTbl[numMediaPackets - 1][numFecPackets - 1], + mask_table.fec_packet_mask_table()[numMediaPackets - 1] + [numFecPackets - 1], numFecPackets * numMaskBytes); } else //UEP case { UnequalProtectionMask(numMediaPackets, numFecPackets, numImpPackets, - numMaskBytes, packetMask); + numMaskBytes, packetMask, mask_table); } // End of UEP modification - } //End of GetPacketMasks } // namespace internal diff --git a/src/modules/rtp_rtcp/source/forward_error_correction_internal.h b/src/modules/rtp_rtcp/source/forward_error_correction_internal.h index e137ef228b..799ce04cf2 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction_internal.h +++ b/src/modules/rtp_rtcp/source/forward_error_correction_internal.h @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "modules/rtp_rtcp/source/forward_error_correction.h" #include "typedefs.h" namespace webrtc { @@ -19,6 +20,23 @@ static const int kMaskSizeLBitClear = 2; namespace internal { +class PacketMaskTable { + public: + PacketMaskTable(FecMaskType fec_mask_type, int num_media_packets); + ~PacketMaskTable() { + } + FecMaskType fec_mask_type() const { return fec_mask_type_; } + const uint8_t*** fec_packet_mask_table() const { + return fec_packet_mask_table_; + } + private: + FecMaskType InitMaskType(FecMaskType fec_mask_type, + int num_media_packets); + const uint8_t*** InitMaskTable(FecMaskType fec_mask_type_); + const FecMaskType fec_mask_type_; + const uint8_t*** fec_packet_mask_table_; +}; + /** * Returns an array of packet masks. The mask of a single FEC packet * corresponds to a number of mask bytes. The mask indicates which @@ -34,6 +52,10 @@ namespace internal { * protection scenario. * \param[in] useUnequalProtection Enables unequal protection: allocates * more protection to the numImpPackets. + * \param[in] mask_table An instance of the |PacketMaskTable| + * class, which contains the type of FEC + * packet mask used, and a pointer to the + * corresponding packet masks. * \param[out] packetMask A pointer to hold the packet mask array, * of size: * numFecPackets * "number of mask bytes". @@ -42,6 +64,7 @@ void GeneratePacketMasks(int numMediaPackets, int numFecPackets, int numImpPackets, bool useUnequalProtection, + const PacketMaskTable& mask_table, uint8_t* packetMask); } // namespace internal diff --git a/src/modules/rtp_rtcp/source/producer_fec.cc b/src/modules/rtp_rtcp/source/producer_fec.cc index a2ec0e3ff9..5b7427b1ae 100644 --- a/src/modules/rtp_rtcp/source/producer_fec.cc +++ b/src/modules/rtp_rtcp/source/producer_fec.cc @@ -164,10 +164,14 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer, (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) { assert(num_first_partition_ <= static_cast(ForwardErrorCorrection::kMaxMediaPackets)); + // TODO(marpan): The setting of the mask type (|kFecMaskRandom| or + // |kFecMaskBursty|) should be part of FecProtectionParams and passed + // in from the VCM. int ret = fec_->GenerateFEC(media_packets_fec_, params_.fec_rate, num_first_partition_, params_.use_uep_protection, + kFecMaskBursty, &fec_packets_); if (fec_packets_.empty()) { num_frames_ = 0; diff --git a/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc b/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc index 8438ca480d..93b6e7a228 100644 --- a/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc +++ b/src/modules/rtp_rtcp/source/receiver_fec_unittest.cc @@ -47,6 +47,7 @@ class ReceiverFecTest : public ::testing::Test { num_fec_packets * 255 / media_packets->size(), 0, false, + kFecMaskBursty, fec_packets)); ASSERT_EQ(num_fec_packets, fec_packets->size()); } @@ -246,6 +247,7 @@ TEST_F(ReceiverFecTest, TooManyFrames) { kNumFecPackets * 255 / kNumMediaPackets, 0, false, + kFecMaskBursty, &fec_packets)); DeletePackets(&media_packets); diff --git a/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc b/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc index ec1497b375..fef60a0090 100644 --- a/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc +++ b/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc @@ -93,61 +93,68 @@ class RtpFecTest : public ::testing::Test { void TearDown(); }; +// TODO(marpan): Consider adding table for input/output to simplify tests. + TEST_F(RtpFecTest, HandleIncorrectInputs) { - int num_important_packets = 0; - bool use_unequal_protection = false; - uint8_t protection_factor = 60; + int kNumImportantPackets = 0; + bool kUseUnequalProtection = false; + uint8_t kProtectionFactor = 60; // Media packet list is empty. EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); int num_media_packets = 10; ConstructMediaPackets(num_media_packets); - num_important_packets = -1; + kNumImportantPackets = -1; // Number of important packets below 0. EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); - num_important_packets = 12; + kNumImportantPackets = 12; // Number of important packets greater than number of media packets. EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); num_media_packets = kMaxNumberMediaPackets + 1; ConstructMediaPackets(num_media_packets); - num_important_packets = 0; + kNumImportantPackets = 0; // Number of media packet is above maximum allowed (kMaxNumberMediaPackets). EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); } TEST_F(RtpFecTest, FecRecoveryNoLoss) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 4; - uint8_t protection_factor = 60; + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 4; + uint8_t kProtectionFactor = 60; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 1 FEC packet. @@ -166,17 +173,18 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) { } TEST_F(RtpFecTest, FecRecoveryWithLoss) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 4; - uint8_t protection_factor = 60; + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 4; + uint8_t kProtectionFactor = 60; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 1 FEC packet. @@ -209,59 +217,141 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) { EXPECT_FALSE(IsRecoveryComplete()); } -TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 4; - const uint8_t protection_factor = 255; +// Test 50% protection with random mask type: Two cases are considered: +// a 50% non-consecutive loss which can be fully recovered, and a 50% +// consecutive loss which cannot be fully recovered. +TEST_F(RtpFecTest, FecRecoveryWithLoss50percRandomMask) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 4; + const uint8_t kProtectionFactor = 255; - // Packet Mask for (4,4,0) code: - // (num_media_packets = 4; num_fec_packets = 4, num_important_packets = 0) + // Packet Mask for (4,4,0) code, from random mask table. + // (kNumMediaPackets = 4; num_fec_packets = 4, kNumImportantPackets = 0) // media#0 media#1 media#2 media#3 // fec#0: 1 1 0 0 // fec#1: 1 0 1 0 - // fec#2: 0 1 0 1 - // fec#3: 0 0 1 1 + // fec#2: 0 0 1 1 + // fec#3: 0 1 0 1 // - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskRandom, &fec_packet_list_)); // Expect 4 FEC packets. EXPECT_EQ(4, static_cast(fec_packet_list_.size())); - // 4 packets lost: 3 media packets and one FEC packet#2 lost. + // 4 packets lost: 3 media packets (0, 2, 3), and one FEC packet (0) lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - fec_loss_mask_[2] = 1; + fec_loss_mask_[0] = 1; media_loss_mask_[0] = 1; media_loss_mask_[2] = 1; media_loss_mask_[3] = 1; - NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_)); - // With media packet#1 and FEC packets #0, #1, #3, expect complete recovery. + // With media packet#1 and FEC packets #1, #2, #3, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); FreeRecoveredPacketList(); - // 4 packets lost: all media packets + // 4 consecutive packets lost: media packets 0, 1, 2, 3. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); - memset(fec_loss_mask_, 1, sizeof(fec_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); media_loss_mask_[0] = 1; media_loss_mask_[1] = 1; media_loss_mask_[2] = 1; media_loss_mask_[3] = 1; NetworkReceivedPackets(); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); + + // Cannot get complete recovery for this loss configuration with random mask. + EXPECT_FALSE(IsRecoveryComplete()); +} + +// Test 50% protection with bursty type: Three cases are considered: +// two 50% consecutive losses which can be fully recovered, and one +// non-consecutive which cannot be fully recovered. +TEST_F(RtpFecTest, FecRecoveryWithLoss50percBurstyMask) { + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 4; + const uint8_t kProtectionFactor = 255; + + // Packet Mask for (4,4,0) code, from bursty mask table. + // (kNumMediaPackets = 4; num_fec_packets = 4, kNumImportantPackets = 0) + + // media#0 media#1 media#2 media#3 + // fec#0: 1 0 0 0 + // fec#1: 1 1 0 0 + // fec#2: 0 1 1 0 + // fec#3: 0 0 1 1 + // + + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); + + EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, + &fec_packet_list_)); + + // Expect 4 FEC packets. + EXPECT_EQ(4, static_cast(fec_packet_list_.size())); + + // 4 consecutive packets lost: media packets 0,1,2,3. + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + media_loss_mask_[0] = 1; + media_loss_mask_[1] = 1; + media_loss_mask_[2] = 1; + media_loss_mask_[3] = 1; + NetworkReceivedPackets(); + + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_, + &recovered_packet_list_)); + + // Expect complete recovery for consecutive packet loss <= 50%. + EXPECT_TRUE(IsRecoveryComplete()); + FreeRecoveredPacketList(); + + // 4 consecutive packets lost: media packets 1,2, 3, and FEC packet 0. + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + fec_loss_mask_[0] = 1; + media_loss_mask_[1] = 1; + media_loss_mask_[2] = 1; + media_loss_mask_[3] = 1; + NetworkReceivedPackets(); + + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , + &recovered_packet_list_)); + + // Expect complete recovery for consecutive packet loss <= 50%. + EXPECT_TRUE(IsRecoveryComplete()); + FreeRecoveredPacketList(); + + // 4 packets lost (non-consecutive loss): media packets 0, 3, and FEC# 0, 3. + memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); + memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); + fec_loss_mask_[0] = 1; + fec_loss_mask_[3] = 1; + media_loss_mask_[0] = 1; + media_loss_mask_[3] = 1; + NetworkReceivedPackets(); + EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_)); @@ -270,17 +360,18 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) { } TEST_F(RtpFecTest, FecRecoveryNoLossUep) { - const int num_important_packets = 2; - const bool use_unequal_protection = true; - const int num_media_packets = 4; - const uint8_t protection_factor = 60; + const int kNumImportantPackets = 2; + const bool kUseUnequalProtection = true; + const int kNumMediaPackets = 4; + const uint8_t kProtectionFactor = 60; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 1 FEC packet. @@ -299,17 +390,18 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) { } TEST_F(RtpFecTest, FecRecoveryWithLossUep) { - const int num_important_packets = 2; - const bool use_unequal_protection = true; - const int num_media_packets = 4; - const uint8_t protection_factor = 60; + const int kNumImportantPackets = 2; + const bool kUseUnequalProtection = true; + const int kNumMediaPackets = 4; + const uint8_t kProtectionFactor = 60; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 1 FEC packet. @@ -342,14 +434,15 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) { EXPECT_FALSE(IsRecoveryComplete()); } -TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { - const int num_important_packets = 1; - const bool use_unequal_protection = true; - const int num_media_packets = 4; - const uint8_t protection_factor = 255; +// Test 50% protection with random mask type for UEP on. +TEST_F(RtpFecTest, FecRecoveryWithLoss50percUepRandomMask) { + const int kNumImportantPackets = 1; + const bool kUseUnequalProtection = true; + const int kNumMediaPackets = 4; + const uint8_t kProtectionFactor = 255; - // Packet Mask for (4,4,1) code: - // (num_media_packets = 4; num_fec_packets = 4, num_important_packets = 2) + // Packet Mask for (4,4,1) code, from random mask table. + // (kNumMediaPackets = 4; num_fec_packets = 4, kNumImportantPackets = 1) // media#0 media#1 media#2 media#3 // fec#0: 1 0 0 0 @@ -358,12 +451,13 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { // fec#3: 0 1 1 0 // - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskRandom, &fec_packet_list_)); // Expect 4 FEC packets. @@ -381,15 +475,16 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_)); - // With media packet#1 and FEC packets #0, #2, #3, expect complete recovery. + // With media packet#3 and FEC packets #0, #1, #3, expect complete recovery. EXPECT_TRUE(IsRecoveryComplete()); FreeRecoveredPacketList(); - // 4 packets lost: 3 media packets and one FEC packet#2 lost. + // 5 packets lost: 4 media packets and one FEC packet#2 lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); fec_loss_mask_[2] = 1; media_loss_mask_[0] = 1; + media_loss_mask_[1] = 1; media_loss_mask_[2] = 1; media_loss_mask_[3] = 1; NetworkReceivedPackets(); @@ -402,12 +497,12 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) { } TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 5; - uint8_t protection_factor = 60; + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 5; + uint8_t kProtectionFactor = 60; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); // Create a new temporary packet list for generating FEC packets. // This list should have every other packet removed. @@ -420,9 +515,10 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) { } EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 1 FEC packet. @@ -469,12 +565,12 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) { } TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 21; - uint8_t protection_factor = 127; + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 21; + uint8_t kProtectionFactor = 127; - fec_seq_num_ = ConstructMediaPackets(num_media_packets); + fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets); // Create a new temporary packet list for generating FEC packets. // This list should have every other packet removed. @@ -490,9 +586,10 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { // mask since the number of actual packets are 21, while the number // of protected packets are 11. EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 5 FEC packet. @@ -501,7 +598,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { // Last protected media packet lost memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 1] = 1; + media_loss_mask_[kNumMediaPackets - 1] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , @@ -514,7 +611,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { // Last unprotected packet lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 2] = 1; + media_loss_mask_[kNumMediaPackets - 2] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , @@ -527,12 +624,12 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { // 6 media packets lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 11] = 1; - media_loss_mask_[num_media_packets - 9] = 1; - media_loss_mask_[num_media_packets - 7] = 1; - media_loss_mask_[num_media_packets - 5] = 1; - media_loss_mask_[num_media_packets - 3] = 1; - media_loss_mask_[num_media_packets - 1] = 1; + media_loss_mask_[kNumMediaPackets - 11] = 1; + media_loss_mask_[kNumMediaPackets - 9] = 1; + media_loss_mask_[kNumMediaPackets - 7] = 1; + media_loss_mask_[kNumMediaPackets - 5] = 1; + media_loss_mask_[kNumMediaPackets - 3] = 1; + media_loss_mask_[kNumMediaPackets - 1] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , @@ -543,12 +640,12 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) { } TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) { - const int num_important_packets = 0; - const bool use_unequal_protection = false; - const int num_media_packets = 21; - uint8_t protection_factor = 127; + const int kNumImportantPackets = 0; + const bool kUseUnequalProtection = false; + const int kNumMediaPackets = 21; + uint8_t kProtectionFactor = 127; - fec_seq_num_ = ConstructMediaPacketsSeqNum(num_media_packets, 0xFFFF - 5); + fec_seq_num_ = ConstructMediaPacketsSeqNum(kNumMediaPackets, 0xFFFF - 5); // Create a new temporary packet list for generating FEC packets. // This list should have every other packet removed. @@ -564,9 +661,10 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) { // mask since the number of actual packets are 21, while the number // of protected packets are 11. EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets, - protection_factor, - num_important_packets, - use_unequal_protection, + kProtectionFactor, + kNumImportantPackets, + kUseUnequalProtection, + webrtc::kFecMaskBursty, &fec_packet_list_)); // Expect 5 FEC packet. @@ -575,7 +673,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) { // Last protected media packet lost memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 1] = 1; + media_loss_mask_[kNumMediaPackets - 1] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , @@ -588,7 +686,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) { // Last unprotected packet lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 2] = 1; + media_loss_mask_[kNumMediaPackets - 2] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , @@ -601,12 +699,12 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) { // 6 media packets lost. memset(media_loss_mask_, 0, sizeof(media_loss_mask_)); memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_)); - media_loss_mask_[num_media_packets - 11] = 1; - media_loss_mask_[num_media_packets - 9] = 1; - media_loss_mask_[num_media_packets - 7] = 1; - media_loss_mask_[num_media_packets - 5] = 1; - media_loss_mask_[num_media_packets - 3] = 1; - media_loss_mask_[num_media_packets - 1] = 1; + media_loss_mask_[kNumMediaPackets - 11] = 1; + media_loss_mask_[kNumMediaPackets - 9] = 1; + media_loss_mask_[kNumMediaPackets - 7] = 1; + media_loss_mask_[kNumMediaPackets - 5] = 1; + media_loss_mask_[kNumMediaPackets - 3] = 1; + media_loss_mask_[kNumMediaPackets - 1] = 1; NetworkReceivedPackets(); EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp.gypi b/src/modules/rtp_rtcp/source/rtp_rtcp.gypi index 2017dff9c8..5d6adadf47 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp.gypi +++ b/src/modules/rtp_rtcp/source/rtp_rtcp.gypi @@ -63,6 +63,7 @@ # Video Files 'bwe_defines.h', 'fec_private_tables.h', + 'fec_private_tables_bursty.h', 'forward_error_correction.cc', 'forward_error_correction.h', 'forward_error_correction_internal.cc', diff --git a/src/modules/rtp_rtcp/test/testFec/test_fec.cc b/src/modules/rtp_rtcp/test/testFec/test_fec.cc index 867c5021d3..d6cd8beaa9 100644 --- a/src/modules/rtp_rtcp/test/testFec/test_fec.cc +++ b/src/modules/rtp_rtcp/test/testFec/test_fec.cc @@ -20,8 +20,9 @@ #include #include -#include "forward_error_correction.h" -#include "forward_error_correction_internal.h" +#include "modules/rtp_rtcp/source/fec_private_tables_bursty.h" +#include "modules/rtp_rtcp/source/forward_error_correction.h" +#include "modules/rtp_rtcp/source/forward_error_correction_internal.h" #include "rtp_utility.h" #include "testsupport/fileutils.h" @@ -36,6 +37,7 @@ void ReceivePackets( WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate); int main() { + // TODO(marpan): Split this function into subroutines/helper functions. enum { kMaxNumberMediaPackets = 48 }; enum { kMaxNumberFecPackets = 48 }; @@ -45,6 +47,17 @@ int main() { // FOR UEP const bool kUseUnequalProtection = true; + // FEC mask types. + const FecMaskType kMaskTypes[] = {kFecMaskRandom, kFecMaskBursty}; + const int kNumFecMaskTypes = sizeof(kMaskTypes) / sizeof(*kMaskTypes); + // Maximum number of media packets allowed for the mask type. + const uint16_t kMaxMediaPackets[] = {kMaxNumberMediaPackets, + sizeof(kPacketMaskBurstyTbl) / sizeof(*kPacketMaskBurstyTbl)}; + if (kMaxMediaPackets[1] != 12) { + printf("ERROR: max media packets for bursty mode not equal to 12 \n"); + return -1; + } + WebRtc_UWord32 id = 0; ForwardErrorCorrection fec(id); @@ -80,365 +93,381 @@ int main() { WebRtc_UWord32 timeStamp = static_cast(rand()); const WebRtc_UWord32 ssrc = static_cast(rand()); - for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; - lossRateIdx++) { - WebRtc_UWord8* packetMask = - new WebRtc_UWord8[kMaxNumberMediaPackets * kNumMaskBytesL1]; + // Loop over the mask types: random and bursty. + for (int mask_type_idx = 0; mask_type_idx < kNumFecMaskTypes; + ++mask_type_idx) { - printf("Loss rate: %.2f\n", lossRate[lossRateIdx]); - for (WebRtc_UWord32 numMediaPackets = 1; - numMediaPackets <= kMaxNumberMediaPackets; - numMediaPackets++) { + for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; + ++lossRateIdx) { - for (WebRtc_UWord32 numFecPackets = 1; - numFecPackets <= numMediaPackets && - numFecPackets <= kMaxNumberFecPackets; - numFecPackets++) { + printf("Loss rate: %.2f, Mask type %d \n", lossRate[lossRateIdx], + mask_type_idx); - // Loop over numImpPackets: these are usually <= (0.3*numMediaPackets). - // For this test we check up to ~ (0.5*numMediaPackets). - WebRtc_UWord32 maxNumImpPackets = numMediaPackets / 2 + 1; - for (WebRtc_UWord32 numImpPackets = 0; - numImpPackets <= maxNumImpPackets && - numImpPackets <= kMaxNumberMediaPackets; - numImpPackets++) { + const WebRtc_UWord32 packetMaskMax = kMaxMediaPackets[mask_type_idx]; + WebRtc_UWord8* packetMask = + new WebRtc_UWord8[packetMaskMax * kNumMaskBytesL1]; - WebRtc_UWord8 protectionFactor = static_cast - (numFecPackets * 255 / numMediaPackets); + FecMaskType fec_mask_type = kMaskTypes[mask_type_idx]; - const WebRtc_UWord32 maskBytesPerFecPacket = - (numMediaPackets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0; + for (WebRtc_UWord32 numMediaPackets = 1; + numMediaPackets <= packetMaskMax; + numMediaPackets++) { + internal::PacketMaskTable mask_table(fec_mask_type, numMediaPackets); - memset(packetMask, 0, numMediaPackets * maskBytesPerFecPacket); + for (WebRtc_UWord32 numFecPackets = 1; + numFecPackets <= numMediaPackets && + numFecPackets <= packetMaskMax; + numFecPackets++) { - // Transfer packet masks from bit-mask to byte-mask. - internal::GeneratePacketMasks(numMediaPackets, - numFecPackets, - numImpPackets, - kUseUnequalProtection, - packetMask); + // Loop over numImpPackets: usually <= (0.3*numMediaPackets). + // For this test we check up to ~ (0.5*numMediaPackets). + WebRtc_UWord32 maxNumImpPackets = numMediaPackets / 2 + 1; + for (WebRtc_UWord32 numImpPackets = 0; + numImpPackets <= maxNumImpPackets && + numImpPackets <= packetMaskMax; + numImpPackets++) { + + WebRtc_UWord8 protectionFactor = static_cast + (numFecPackets * 255 / numMediaPackets); + + const WebRtc_UWord32 maskBytesPerFecPacket = + (numMediaPackets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0; + + memset(packetMask, 0, numMediaPackets * maskBytesPerFecPacket); + + // Transfer packet masks from bit-mask to byte-mask. + internal::GeneratePacketMasks(numMediaPackets, + numFecPackets, + numImpPackets, + kUseUnequalProtection, + mask_table, + packetMask); #ifdef VERBOSE_OUTPUT - printf("%u media packets, %u FEC packets, %u numImpPackets, " - "loss rate = %.2f \n", - numMediaPackets, numFecPackets, numImpPackets, lossRate[lossRateIdx]); - printf("Packet mask matrix \n"); + printf("%u media packets, %u FEC packets, %u numImpPackets, " + "loss rate = %.2f \n", + numMediaPackets, numFecPackets, numImpPackets, + lossRate[lossRateIdx]); + printf("Packet mask matrix \n"); #endif - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { - for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++) { - const WebRtc_UWord8 byteMask = - packetMask[i * maskBytesPerFecPacket + j / 8]; - const WebRtc_UWord32 bitPosition = (7 - j % 8); - fecPacketMasks[i][j] = - (byteMask & (1 << bitPosition)) >> bitPosition; + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++) { + const WebRtc_UWord8 byteMask = + packetMask[i * maskBytesPerFecPacket + j / 8]; + const WebRtc_UWord32 bitPosition = (7 - j % 8); + fecPacketMasks[i][j] = + (byteMask & (1 << bitPosition)) >> bitPosition; #ifdef VERBOSE_OUTPUT - printf("%u ", fecPacketMasks[i][j]); + printf("%u ", fecPacketMasks[i][j]); +#endif + } +#ifdef VERBOSE_OUTPUT + printf("\n"); #endif } #ifdef VERBOSE_OUTPUT printf("\n"); #endif - } -#ifdef VERBOSE_OUTPUT - printf("\n"); -#endif - // Check for all zero rows or columns: indicates incorrect mask - WebRtc_UWord32 rowLimit = numMediaPackets; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { - WebRtc_UWord32 rowSum = 0; - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) { - rowSum += fecPacketMasks[i][j]; - } - if (rowSum == 0) { - printf("ERROR: row is all zero %d \n",i); - return -1; - } - } - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) { - WebRtc_UWord32 columnSum = 0; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { - columnSum += fecPacketMasks[i][j]; - } - if (columnSum == 0) { - printf("ERROR: column is all zero %d \n",j); - return -1; - } - } - // Construct media packets. - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { - mediaPacket = new ForwardErrorCorrection::Packet; - mediaPacketList.push_back(mediaPacket); - mediaPacket->length = - static_cast((static_cast(rand()) / - RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 - - ForwardErrorCorrection::PacketOverhead())); - if (mediaPacket->length < 12) { - mediaPacket->length = 12; - } - // Generate random values for the first 2 bytes - mediaPacket->data[0] = static_cast(rand() % 256); - mediaPacket->data[1] = static_cast(rand() % 256); - - // The first two bits are assumed to be 10 by the - // FEC encoder. In fact the FEC decoder will set the - // two first bits to 10 regardless of what they - // actually were. Set the first two bits to 10 - // so that a memcmp can be performed for the - // whole restored packet. - mediaPacket->data[0] |= 0x80; - mediaPacket->data[0] &= 0xbf; - - // FEC is applied to a whole frame. - // A frame is signaled by multiple packets without - // the marker bit set followed by the last packet of - // the frame for which the marker bit is set. - // Only push one (fake) frame to the FEC. - mediaPacket->data[1] &= 0x7f; - - ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], - seqNum); - ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], - timeStamp); - ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], - ssrc); - // Generate random values for payload - for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++) { - mediaPacket->data[j] = - static_cast (rand() % 256); - } - seqNum++; - } - mediaPacket->data[1] |= 0x80; - - if (fec.GenerateFEC(mediaPacketList, protectionFactor, - numImpPackets, kUseUnequalProtection, - &fecPacketList) != 0) { - printf("Error: GenerateFEC() failed\n"); - return -1; - } - - if (fecPacketList.size() != numFecPackets) { - printf("Error: we requested %u FEC packets, " - "but GenerateFEC() produced %u\n", - numFecPackets, - static_cast(fecPacketList.size())); - return -1; - } - memset(mediaLossMask, 0, sizeof(mediaLossMask)); - ForwardErrorCorrection::PacketList::iterator - mediaPacketListItem = mediaPacketList.begin(); - ForwardErrorCorrection::ReceivedPacket* receivedPacket; - WebRtc_UWord32 mediaPacketIdx = 0; - - while (mediaPacketListItem != mediaPacketList.end()) { - mediaPacket = *mediaPacketListItem; - const float lossRandomVariable = (static_cast(rand()) / - (RAND_MAX)); - - if (lossRandomVariable >= lossRate[lossRateIdx]) - { - mediaLossMask[mediaPacketIdx] = 1; - receivedPacket = - new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = new ForwardErrorCorrection::Packet; - receivedPacketList.push_back(receivedPacket); - - receivedPacket->pkt->length = mediaPacket->length; - memcpy(receivedPacket->pkt->data, mediaPacket->data, - mediaPacket->length); - receivedPacket->seqNum = - ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); - receivedPacket->isFec = false; - } - mediaPacketIdx++; - ++mediaPacketListItem; - } - memset(fecLossMask, 0, sizeof(fecLossMask)); - ForwardErrorCorrection::PacketList::iterator - fecPacketListItem = fecPacketList.begin(); - ForwardErrorCorrection::Packet* fecPacket; - WebRtc_UWord32 fecPacketIdx = 0; - while (fecPacketListItem != fecPacketList.end()) { - fecPacket = *fecPacketListItem; - const float lossRandomVariable = - (static_cast(rand()) / (RAND_MAX)); - if (lossRandomVariable >= lossRate[lossRateIdx]) { - fecLossMask[fecPacketIdx] = 1; - receivedPacket = - new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = new ForwardErrorCorrection::Packet; - - receivedPacketList.push_back(receivedPacket); - - receivedPacket->pkt->length = fecPacket->length; - memcpy(receivedPacket->pkt->data, fecPacket->data, - fecPacket->length); - - receivedPacket->seqNum = seqNum; - receivedPacket->isFec = true; - receivedPacket->ssrc = ssrc; - - fecMaskList.push_back(fecPacketMasks[fecPacketIdx]); - } - fecPacketIdx++; - seqNum++; - ++fecPacketListItem; - } - -#ifdef VERBOSE_OUTPUT - printf("Media loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { - printf("%u ", mediaLossMask[i]); - } - printf("\n\n"); - - printf("FEC loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { - printf("%u ", fecLossMask[i]); - } - printf("\n\n"); -#endif - - std::list::iterator fecMaskIt = fecMaskList.begin(); - WebRtc_UWord8* fecMask; - while (fecMaskIt != fecMaskList.end()) { - fecMask = *fecMaskIt; - WebRtc_UWord32 hammingDist = 0; - WebRtc_UWord32 recoveryPosition = 0; - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { - if (mediaLossMask[i] == 0 && fecMask[i] == 1) { - recoveryPosition = i; - hammingDist++; + // Check for all zero rows or columns: indicates incorrect mask. + WebRtc_UWord32 rowLimit = numMediaPackets; + for (WebRtc_UWord32 i = 0; i < numFecPackets; ++i) { + WebRtc_UWord32 rowSum = 0; + for (WebRtc_UWord32 j = 0; j < rowLimit; ++j) { + rowSum += fecPacketMasks[i][j]; } - } - std::list::iterator itemToDelete = fecMaskIt; - ++fecMaskIt; - - if (hammingDist == 1) { - // Recovery possible. Restart search. - mediaLossMask[recoveryPosition] = 1; - fecMaskIt = fecMaskList.begin(); - } else if (hammingDist == 0) { - // FEC packet cannot provide further recovery. - fecMaskList.erase(itemToDelete); - } - } -#ifdef VERBOSE_OUTPUT - printf("Recovery mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { - printf("%u ", mediaLossMask[i]); - } - printf("\n\n"); -#endif - bool fecPacketReceived = false; // For error-checking frame completion. - while (!receivedPacketList.empty()) { - WebRtc_UWord32 numPacketsToDecode = static_cast - ((static_cast(rand()) / RAND_MAX) * - receivedPacketList.size() + 0.5); - if (numPacketsToDecode < 1) { - numPacketsToDecode = 1; - } - ReceivePackets(&toDecodeList, &receivedPacketList, - numPacketsToDecode, reorderRate, duplicateRate); - - if (fecPacketReceived == false) { - ForwardErrorCorrection::ReceivedPacketList::iterator - toDecodeIt = toDecodeList.begin(); - while (toDecodeIt != toDecodeList.end()) { - receivedPacket = *toDecodeIt; - if (receivedPacket->isFec) { - fecPacketReceived = true; - } - ++toDecodeIt; - } - } - if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList) - != 0) { - printf("Error: DecodeFEC() failed\n"); - return -1; - } - if (!toDecodeList.empty()) { - printf("Error: received packet list is not empty\n"); - return -1; - } - } - mediaPacketListItem = mediaPacketList.begin(); - mediaPacketIdx = 0; - while (mediaPacketListItem != mediaPacketList.end()) { - if (mediaLossMask[mediaPacketIdx] == 1) { - // Should have recovered this packet. - ForwardErrorCorrection::RecoveredPacketList::iterator - recoveredPacketListItem = recoveredPacketList.begin(); - - if (recoveredPacketListItem == recoveredPacketList.end()) { - printf("Error: insufficient number of recovered packets.\n"); + if (rowSum == 0) { + printf("ERROR: row is all zero %d \n", i); return -1; } + } + for (WebRtc_UWord32 j = 0; j < rowLimit; ++j) { + WebRtc_UWord32 columnSum = 0; + for (WebRtc_UWord32 i = 0; i < numFecPackets; ++i) { + columnSum += fecPacketMasks[i][j]; + } + if (columnSum == 0) { + printf("ERROR: column is all zero %d \n", j); + return -1; + } + } + + // Construct media packets. + for (WebRtc_UWord32 i = 0; i < numMediaPackets; ++i) { + mediaPacket = new ForwardErrorCorrection::Packet; + mediaPacketList.push_back(mediaPacket); + mediaPacket->length = + static_cast((static_cast(rand()) / + RAND_MAX) * (IP_PACKET_SIZE - 12 - + 28 - ForwardErrorCorrection::PacketOverhead())); + if (mediaPacket->length < 12) { + mediaPacket->length = 12; + } + // Generate random values for the first 2 bytes. + mediaPacket->data[0] = static_cast(rand() % 256); + mediaPacket->data[1] = static_cast(rand() % 256); + + // The first two bits are assumed to be 10 by the + // FEC encoder. In fact the FEC decoder will set the + // two first bits to 10 regardless of what they + // actually were. Set the first two bits to 10 + // so that a memcmp can be performed for the + // whole restored packet. + mediaPacket->data[0] |= 0x80; + mediaPacket->data[0] &= 0xbf; + + // FEC is applied to a whole frame. + // A frame is signaled by multiple packets without + // the marker bit set followed by the last packet of + // the frame for which the marker bit is set. + // Only push one (fake) frame to the FEC. + mediaPacket->data[1] &= 0x7f; + + ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], + seqNum); + ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], + timeStamp); + ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], + ssrc); + // Generate random values for payload + for (WebRtc_Word32 j = 12; j < mediaPacket->length; ++j) { + mediaPacket->data[j] = + static_cast (rand() % 256); + } + seqNum++; + } + mediaPacket->data[1] |= 0x80; + + if (fec.GenerateFEC(mediaPacketList, protectionFactor, + numImpPackets, kUseUnequalProtection, + fec_mask_type, &fecPacketList) != 0) { + printf("Error: GenerateFEC() failed\n"); + return -1; + } + + if (fecPacketList.size() != numFecPackets) { + printf("Error: we requested %u FEC packets, " + "but GenerateFEC() produced %u\n", + numFecPackets, + static_cast(fecPacketList.size())); + return -1; + } + memset(mediaLossMask, 0, sizeof(mediaLossMask)); + ForwardErrorCorrection::PacketList::iterator + mediaPacketListItem = mediaPacketList.begin(); + ForwardErrorCorrection::ReceivedPacket* receivedPacket; + WebRtc_UWord32 mediaPacketIdx = 0; + + while (mediaPacketListItem != mediaPacketList.end()) { mediaPacket = *mediaPacketListItem; - ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - *recoveredPacketListItem; + // We want a value between 0 and 1. + const float lossRandomVariable = (static_cast(rand()) / + (RAND_MAX)); - if (recoveredPacket->pkt->length != mediaPacket->length) { - printf("Error: recovered packet length not identical to " - "original media packet\n"); - return -1; + if (lossRandomVariable >= lossRate[lossRateIdx]) { + mediaLossMask[mediaPacketIdx] = 1; + receivedPacket = + new ForwardErrorCorrection::ReceivedPacket; + receivedPacket->pkt = new ForwardErrorCorrection::Packet; + receivedPacketList.push_back(receivedPacket); + + receivedPacket->pkt->length = mediaPacket->length; + memcpy(receivedPacket->pkt->data, mediaPacket->data, + mediaPacket->length); + receivedPacket->seqNum = + ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); + receivedPacket->isFec = false; } - if (memcmp(recoveredPacket->pkt->data, mediaPacket->data, - mediaPacket->length) != 0) { - printf("Error: recovered packet payload not identical to " - "original media packet\n"); - return -1; - } - delete recoveredPacket; - recoveredPacketList.pop_front(); + mediaPacketIdx++; + ++mediaPacketListItem; } - mediaPacketIdx++; - ++mediaPacketListItem; - } - fec.ResetState(&recoveredPacketList); - if (!recoveredPacketList.empty()) { - printf("Error: excessive number of recovered packets.\n"); - printf("\t size is:%u\n", - static_cast(recoveredPacketList.size())); - return -1; - } - // -- Teardown -- - mediaPacketListItem = mediaPacketList.begin(); - while (mediaPacketListItem != mediaPacketList.end()) { - delete *mediaPacketListItem; - ++mediaPacketListItem; - mediaPacketList.pop_front(); - } - assert(mediaPacketList.empty()); + memset(fecLossMask, 0, sizeof(fecLossMask)); + ForwardErrorCorrection::PacketList::iterator + fecPacketListItem = fecPacketList.begin(); + ForwardErrorCorrection::Packet* fecPacket; + WebRtc_UWord32 fecPacketIdx = 0; + while (fecPacketListItem != fecPacketList.end()) { + fecPacket = *fecPacketListItem; + const float lossRandomVariable = + (static_cast(rand()) / (RAND_MAX)); + if (lossRandomVariable >= lossRate[lossRateIdx]) { + fecLossMask[fecPacketIdx] = 1; + receivedPacket = + new ForwardErrorCorrection::ReceivedPacket; + receivedPacket->pkt = new ForwardErrorCorrection::Packet; - fecPacketListItem = fecPacketList.begin(); - while (fecPacketListItem != fecPacketList.end()) { - ++fecPacketListItem; - fecPacketList.pop_front(); - } + receivedPacketList.push_back(receivedPacket); - // Delete received packets we didn't pass to DecodeFEC(), due to early - // frame completion. - ForwardErrorCorrection::ReceivedPacketList::iterator - receivedPacketIt = receivedPacketList.begin(); - while (receivedPacketIt != receivedPacketList.end()) { - receivedPacket = *receivedPacketIt; - delete receivedPacket; - ++receivedPacketIt; - receivedPacketList.pop_front(); - } - assert(receivedPacketList.empty()); + receivedPacket->pkt->length = fecPacket->length; + memcpy(receivedPacket->pkt->data, fecPacket->data, + fecPacket->length); - while (!fecMaskList.empty()) { - fecMaskList.pop_front(); - } - timeStamp += 90000 / 30; - } //loop over numImpPackets - } //loop over FecPackets - } //loop over numMediaPackets - delete [] packetMask; - } // loop over loss rates + receivedPacket->seqNum = seqNum; + receivedPacket->isFec = true; + receivedPacket->ssrc = ssrc; + + fecMaskList.push_back(fecPacketMasks[fecPacketIdx]); + } + ++fecPacketIdx; + ++seqNum; + ++fecPacketListItem; + } + +#ifdef VERBOSE_OUTPUT + printf("Media loss mask:\n"); + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + printf("%u ", mediaLossMask[i]); + } + printf("\n\n"); + + printf("FEC loss mask:\n"); + for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) { + printf("%u ", fecLossMask[i]); + } + printf("\n\n"); +#endif + + std::list::iterator fecMaskIt = fecMaskList.begin(); + WebRtc_UWord8* fecMask; + while (fecMaskIt != fecMaskList.end()) { + fecMask = *fecMaskIt; + WebRtc_UWord32 hammingDist = 0; + WebRtc_UWord32 recoveryPosition = 0; + for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) { + if (mediaLossMask[i] == 0 && fecMask[i] == 1) { + recoveryPosition = i; + ++hammingDist; + } + } + std::list::iterator itemToDelete = fecMaskIt; + ++fecMaskIt; + + if (hammingDist == 1) { + // Recovery possible. Restart search. + mediaLossMask[recoveryPosition] = 1; + fecMaskIt = fecMaskList.begin(); + } else if (hammingDist == 0) { + // FEC packet cannot provide further recovery. + fecMaskList.erase(itemToDelete); + } + } +#ifdef VERBOSE_OUTPUT + printf("Recovery mask:\n"); + for (WebRtc_UWord32 i = 0; i < numMediaPackets; ++i) { + printf("%u ", mediaLossMask[i]); + } + printf("\n\n"); +#endif + // For error-checking frame completion. + bool fecPacketReceived = false; + while (!receivedPacketList.empty()) { + WebRtc_UWord32 numPacketsToDecode = static_cast + ((static_cast(rand()) / RAND_MAX) * + receivedPacketList.size() + 0.5); + if (numPacketsToDecode < 1) { + numPacketsToDecode = 1; + } + ReceivePackets(&toDecodeList, &receivedPacketList, + numPacketsToDecode, reorderRate, duplicateRate); + + if (fecPacketReceived == false) { + ForwardErrorCorrection::ReceivedPacketList::iterator + toDecodeIt = toDecodeList.begin(); + while (toDecodeIt != toDecodeList.end()) { + receivedPacket = *toDecodeIt; + if (receivedPacket->isFec) { + fecPacketReceived = true; + } + ++toDecodeIt; + } + } + if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList) + != 0) { + printf("Error: DecodeFEC() failed\n"); + return -1; + } + if (!toDecodeList.empty()) { + printf("Error: received packet list is not empty\n"); + return -1; + } + } + mediaPacketListItem = mediaPacketList.begin(); + mediaPacketIdx = 0; + while (mediaPacketListItem != mediaPacketList.end()) { + if (mediaLossMask[mediaPacketIdx] == 1) { + // Should have recovered this packet. + ForwardErrorCorrection::RecoveredPacketList::iterator + recoveredPacketListItem = recoveredPacketList.begin(); + + if (recoveredPacketListItem == recoveredPacketList.end()) { + printf("Error: insufficient number of recovered packets.\n"); + return -1; + } + mediaPacket = *mediaPacketListItem; + ForwardErrorCorrection::RecoveredPacket* recoveredPacket = + *recoveredPacketListItem; + + if (recoveredPacket->pkt->length != mediaPacket->length) { + printf("Error: recovered packet length not identical to " + "original media packet\n"); + return -1; + } + if (memcmp(recoveredPacket->pkt->data, mediaPacket->data, + mediaPacket->length) != 0) { + printf("Error: recovered packet payload not identical to " + "original media packet\n"); + return -1; + } + delete recoveredPacket; + recoveredPacketList.pop_front(); + } + ++mediaPacketIdx; + ++mediaPacketListItem; + } + fec.ResetState(&recoveredPacketList); + if (!recoveredPacketList.empty()) { + printf("Error: excessive number of recovered packets.\n"); + printf("\t size is:%u\n", + static_cast(recoveredPacketList.size())); + return -1; + } + // -- Teardown -- + mediaPacketListItem = mediaPacketList.begin(); + while (mediaPacketListItem != mediaPacketList.end()) { + delete *mediaPacketListItem; + ++mediaPacketListItem; + mediaPacketList.pop_front(); + } + assert(mediaPacketList.empty()); + + fecPacketListItem = fecPacketList.begin(); + while (fecPacketListItem != fecPacketList.end()) { + ++fecPacketListItem; + fecPacketList.pop_front(); + } + + // Delete received packets we didn't pass to DecodeFEC(), due to + // early frame completion. + ForwardErrorCorrection::ReceivedPacketList::iterator + receivedPacketIt = receivedPacketList.begin(); + while (receivedPacketIt != receivedPacketList.end()) { + receivedPacket = *receivedPacketIt; + delete receivedPacket; + ++receivedPacketIt; + receivedPacketList.pop_front(); + } + assert(receivedPacketList.empty()); + + while (!fecMaskList.empty()) { + fecMaskList.pop_front(); + } + timeStamp += 90000 / 30; + } // loop over numImpPackets + } // loop over FecPackets + } // loop over numMediaPackets + delete [] packetMask; + } // loop over loss rates + } // loop over mask types // Have DecodeFEC free allocated memory. fec.ResetState(&recoveredPacketList);