• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
12 
13 #include <string.h>
14 
15 #include <algorithm>
16 
17 #include "modules/rtp_rtcp/source/fec_private_tables_bursty.h"
18 #include "modules/rtp_rtcp/source/fec_private_tables_random.h"
19 #include "rtc_base/checks.h"
20 
21 namespace {
22 // Allow for different modes of protection for packets in UEP case.
23 enum ProtectionMode {
24   kModeNoOverlap,
25   kModeOverlap,
26   kModeBiasFirstPacket,
27 };
28 
29 // Fits an input mask (sub_mask) to an output mask.
30 // The mask is a matrix where the rows are the FEC packets,
31 // and the columns are the source packets the FEC is applied to.
32 // Each row of the mask is represented by a number of mask bytes.
33 //
34 // \param[in]  num_mask_bytes     The number of mask bytes of output mask.
35 // \param[in]  num_sub_mask_bytes The number of mask bytes of input mask.
36 // \param[in]  num_rows           The number of rows of the input mask.
37 // \param[in]  sub_mask           A pointer to hold the input mask, of size
38 //                                [0, num_rows * num_sub_mask_bytes]
39 // \param[out] packet_mask        A pointer to hold the output mask, of size
40 //                                [0, x * num_mask_bytes], where x >= num_rows.
FitSubMask(int num_mask_bytes,int num_sub_mask_bytes,int num_rows,const uint8_t * sub_mask,uint8_t * packet_mask)41 void FitSubMask(int num_mask_bytes,
42                 int num_sub_mask_bytes,
43                 int num_rows,
44                 const uint8_t* sub_mask,
45                 uint8_t* packet_mask) {
46   if (num_mask_bytes == num_sub_mask_bytes) {
47     memcpy(packet_mask, sub_mask, num_rows * num_sub_mask_bytes);
48   } else {
49     for (int i = 0; i < num_rows; ++i) {
50       int pkt_mask_idx = i * num_mask_bytes;
51       int pkt_mask_idx2 = i * num_sub_mask_bytes;
52       for (int j = 0; j < num_sub_mask_bytes; ++j) {
53         packet_mask[pkt_mask_idx] = sub_mask[pkt_mask_idx2];
54         pkt_mask_idx++;
55         pkt_mask_idx2++;
56       }
57     }
58   }
59 }
60 
61 // Shifts a mask by number of columns (bits), and fits it to an output mask.
62 // The mask is a matrix where the rows are the FEC packets,
63 // and the columns are the source packets the FEC is applied to.
64 // Each row of the mask is represented by a number of mask bytes.
65 //
66 // \param[in]  num_mask_bytes     The number of mask bytes of output mask.
67 // \param[in]  num_sub_mask_bytes The number of mask bytes of input mask.
68 // \param[in]  num_column_shift   The number columns to be shifted, and
69 //                                the starting row for the output mask.
70 // \param[in]  end_row            The ending row for the output mask.
71 // \param[in]  sub_mask           A pointer to hold the input mask, of size
72 //                                [0, (end_row_fec - start_row_fec) *
73 //                                    num_sub_mask_bytes]
74 // \param[out] packet_mask        A pointer to hold the output mask, of size
75 //                                [0, x * num_mask_bytes],
76 //                                where x >= end_row_fec.
77 // TODO(marpan): This function is doing three things at the same time:
78 // shift within a byte, byte shift and resizing.
79 // Split up into subroutines.
ShiftFitSubMask(int num_mask_bytes,int res_mask_bytes,int num_column_shift,int end_row,const uint8_t * sub_mask,uint8_t * packet_mask)80 void ShiftFitSubMask(int num_mask_bytes,
81                      int res_mask_bytes,
82                      int num_column_shift,
83                      int end_row,
84                      const uint8_t* sub_mask,
85                      uint8_t* packet_mask) {
86   // Number of bit shifts within a byte
87   const int num_bit_shifts = (num_column_shift % 8);
88   const int num_byte_shifts = num_column_shift >> 3;
89 
90   // Modify new mask with sub-mask21.
91 
92   // Loop over the remaining FEC packets.
93   for (int i = num_column_shift; i < end_row; ++i) {
94     // Byte index of new mask, for row i and column res_mask_bytes,
95     // offset by the number of bytes shifts
96     int pkt_mask_idx =
97         i * num_mask_bytes + res_mask_bytes - 1 + num_byte_shifts;
98     // Byte index of sub_mask, for row i and column res_mask_bytes
99     int pkt_mask_idx2 =
100         (i - num_column_shift) * res_mask_bytes + res_mask_bytes - 1;
101 
102     uint8_t shift_right_curr_byte = 0;
103     uint8_t shift_left_prev_byte = 0;
104     uint8_t comb_new_byte = 0;
105 
106     // Handle case of num_mask_bytes > res_mask_bytes:
107     // For a given row, copy the rightmost "numBitShifts" bits
108     // of the last byte of sub_mask into output mask.
109     if (num_mask_bytes > res_mask_bytes) {
110       shift_left_prev_byte = (sub_mask[pkt_mask_idx2] << (8 - num_bit_shifts));
111       packet_mask[pkt_mask_idx + 1] = shift_left_prev_byte;
112     }
113 
114     // For each row i (FEC packet), shift the bit-mask of the sub_mask.
115     // Each row of the mask contains "resMaskBytes" of bytes.
116     // We start from the last byte of the sub_mask and move to first one.
117     for (int j = res_mask_bytes - 1; j > 0; j--) {
118       // Shift current byte of sub21 to the right by "numBitShifts".
119       shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts;
120 
121       // Fill in shifted bits with bits from the previous (left) byte:
122       // First shift the previous byte to the left by "8-numBitShifts".
123       shift_left_prev_byte =
124           (sub_mask[pkt_mask_idx2 - 1] << (8 - num_bit_shifts));
125 
126       // Then combine both shifted bytes into new mask byte.
127       comb_new_byte = shift_right_curr_byte | shift_left_prev_byte;
128 
129       // Assign to new mask.
130       packet_mask[pkt_mask_idx] = comb_new_byte;
131       pkt_mask_idx--;
132       pkt_mask_idx2--;
133     }
134     // For the first byte in the row (j=0 case).
135     shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts;
136     packet_mask[pkt_mask_idx] = shift_right_curr_byte;
137   }
138 }
139 
140 }  // namespace
141 
142 namespace webrtc {
143 namespace internal {
144 
PacketMaskTable(FecMaskType fec_mask_type,int num_media_packets)145 PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type,
146                                  int num_media_packets)
147     : table_(PickTable(fec_mask_type, num_media_packets)) {}
148 
149 PacketMaskTable::~PacketMaskTable() = default;
150 
LookUp(int num_media_packets,int num_fec_packets)151 rtc::ArrayView<const uint8_t> PacketMaskTable::LookUp(int num_media_packets,
152                                                       int num_fec_packets) {
153   RTC_DCHECK_GT(num_media_packets, 0);
154   RTC_DCHECK_GT(num_fec_packets, 0);
155   RTC_DCHECK_LE(num_media_packets, kUlpfecMaxMediaPackets);
156   RTC_DCHECK_LE(num_fec_packets, num_media_packets);
157 
158   if (num_media_packets <= 12) {
159     return LookUpInFecTable(table_, num_media_packets - 1, num_fec_packets - 1);
160   }
161   int mask_length =
162       static_cast<int>(PacketMaskSize(static_cast<size_t>(num_media_packets)));
163 
164   // Generate FEC code mask for {num_media_packets(M), num_fec_packets(N)} (use
165   // N FEC packets to protect M media packets) In the mask, each FEC packet
166   // occupies one row, each bit / coloumn represent one media packet. E.g. Row
167   // A, Col/Bit B is set to 1, means FEC packet A will have protection for media
168   // packet B.
169 
170   // Loop through each fec packet.
171   for (int row = 0; row < num_fec_packets; row++) {
172     // Loop through each fec code in a row, one code has 8 bits.
173     // Bit X will be set to 1 if media packet X shall be protected by current
174     // FEC packet. In this implementation, the protection is interleaved, thus
175     // media packet X will be protected by FEC packet (X % N)
176     for (int col = 0; col < mask_length; col++) {
177       fec_packet_mask_[row * mask_length + col] =
178           ((col * 8) % num_fec_packets == row && (col * 8) < num_media_packets
179                ? 0x80
180                : 0x00) |
181           ((col * 8 + 1) % num_fec_packets == row &&
182                    (col * 8 + 1) < num_media_packets
183                ? 0x40
184                : 0x00) |
185           ((col * 8 + 2) % num_fec_packets == row &&
186                    (col * 8 + 2) < num_media_packets
187                ? 0x20
188                : 0x00) |
189           ((col * 8 + 3) % num_fec_packets == row &&
190                    (col * 8 + 3) < num_media_packets
191                ? 0x10
192                : 0x00) |
193           ((col * 8 + 4) % num_fec_packets == row &&
194                    (col * 8 + 4) < num_media_packets
195                ? 0x08
196                : 0x00) |
197           ((col * 8 + 5) % num_fec_packets == row &&
198                    (col * 8 + 5) < num_media_packets
199                ? 0x04
200                : 0x00) |
201           ((col * 8 + 6) % num_fec_packets == row &&
202                    (col * 8 + 6) < num_media_packets
203                ? 0x02
204                : 0x00) |
205           ((col * 8 + 7) % num_fec_packets == row &&
206                    (col * 8 + 7) < num_media_packets
207                ? 0x01
208                : 0x00);
209     }
210   }
211   return {&fec_packet_mask_[0],
212           static_cast<size_t>(num_fec_packets * mask_length)};
213 }
214 
215 // If |num_media_packets| is larger than the maximum allowed by |fec_mask_type|
216 // for the bursty type, or the random table is explicitly asked for, then the
217 // random type is selected. Otherwise the bursty table callback is returned.
PickTable(FecMaskType fec_mask_type,int num_media_packets)218 const uint8_t* PacketMaskTable::PickTable(FecMaskType fec_mask_type,
219                                           int num_media_packets) {
220   RTC_DCHECK_GE(num_media_packets, 0);
221   RTC_DCHECK_LE(static_cast<size_t>(num_media_packets), kUlpfecMaxMediaPackets);
222 
223   if (fec_mask_type != kFecMaskRandom &&
224       num_media_packets <=
225           static_cast<int>(fec_private_tables::kPacketMaskBurstyTbl[0])) {
226     return &fec_private_tables::kPacketMaskBurstyTbl[0];
227   }
228 
229   return &fec_private_tables::kPacketMaskRandomTbl[0];
230 }
231 
232 // Remaining protection after important (first partition) packet protection
RemainingPacketProtection(int num_media_packets,int num_fec_remaining,int num_fec_for_imp_packets,int num_mask_bytes,ProtectionMode mode,uint8_t * packet_mask,PacketMaskTable * mask_table)233 void RemainingPacketProtection(int num_media_packets,
234                                int num_fec_remaining,
235                                int num_fec_for_imp_packets,
236                                int num_mask_bytes,
237                                ProtectionMode mode,
238                                uint8_t* packet_mask,
239                                PacketMaskTable* mask_table) {
240   if (mode == kModeNoOverlap) {
241     // sub_mask21
242 
243     const int res_mask_bytes =
244         PacketMaskSize(num_media_packets - num_fec_for_imp_packets);
245 
246     auto end_row = (num_fec_for_imp_packets + num_fec_remaining);
247     rtc::ArrayView<const uint8_t> packet_mask_sub_21 = mask_table->LookUp(
248         num_media_packets - num_fec_for_imp_packets, num_fec_remaining);
249 
250     ShiftFitSubMask(num_mask_bytes, res_mask_bytes, num_fec_for_imp_packets,
251                     end_row, &packet_mask_sub_21[0], packet_mask);
252 
253   } else if (mode == kModeOverlap || mode == kModeBiasFirstPacket) {
254     // sub_mask22
255     rtc::ArrayView<const uint8_t> packet_mask_sub_22 =
256         mask_table->LookUp(num_media_packets, num_fec_remaining);
257 
258     FitSubMask(num_mask_bytes, num_mask_bytes, num_fec_remaining,
259                &packet_mask_sub_22[0],
260                &packet_mask[num_fec_for_imp_packets * num_mask_bytes]);
261 
262     if (mode == kModeBiasFirstPacket) {
263       for (int i = 0; i < num_fec_remaining; ++i) {
264         int pkt_mask_idx = i * num_mask_bytes;
265         packet_mask[pkt_mask_idx] = packet_mask[pkt_mask_idx] | (1 << 7);
266       }
267     }
268   } else {
269     RTC_NOTREACHED();
270   }
271 }
272 
273 // Protection for important (first partition) packets
ImportantPacketProtection(int num_fec_for_imp_packets,int num_imp_packets,int num_mask_bytes,uint8_t * packet_mask,PacketMaskTable * mask_table)274 void ImportantPacketProtection(int num_fec_for_imp_packets,
275                                int num_imp_packets,
276                                int num_mask_bytes,
277                                uint8_t* packet_mask,
278                                PacketMaskTable* mask_table) {
279   const int num_imp_mask_bytes = PacketMaskSize(num_imp_packets);
280 
281   // Get sub_mask1 from table
282   rtc::ArrayView<const uint8_t> packet_mask_sub_1 =
283       mask_table->LookUp(num_imp_packets, num_fec_for_imp_packets);
284 
285   FitSubMask(num_mask_bytes, num_imp_mask_bytes, num_fec_for_imp_packets,
286              &packet_mask_sub_1[0], packet_mask);
287 }
288 
289 // This function sets the protection allocation: i.e., how many FEC packets
290 // to use for num_imp (1st partition) packets, given the: number of media
291 // packets, number of FEC packets, and number of 1st partition packets.
SetProtectionAllocation(int num_media_packets,int num_fec_packets,int num_imp_packets)292 int SetProtectionAllocation(int num_media_packets,
293                             int num_fec_packets,
294                             int num_imp_packets) {
295   // TODO(marpan): test different cases for protection allocation:
296 
297   // Use at most (alloc_par * num_fec_packets) for important packets.
298   float alloc_par = 0.5;
299   int max_num_fec_for_imp = alloc_par * num_fec_packets;
300 
301   int num_fec_for_imp_packets = (num_imp_packets < max_num_fec_for_imp)
302                                     ? num_imp_packets
303                                     : max_num_fec_for_imp;
304 
305   // Fall back to equal protection in this case
306   if (num_fec_packets == 1 && (num_media_packets > 2 * num_imp_packets)) {
307     num_fec_for_imp_packets = 0;
308   }
309 
310   return num_fec_for_imp_packets;
311 }
312 
313 // Modification for UEP: reuse the off-line tables for the packet masks.
314 // Note: these masks were designed for equal packet protection case,
315 // assuming random packet loss.
316 
317 // Current version has 3 modes (options) to build UEP mask from existing ones.
318 // Various other combinations may be added in future versions.
319 // Longer-term, we may add another set of tables specifically for UEP cases.
320 // TODO(marpan): also consider modification of masks for bursty loss cases.
321 
322 // Mask is characterized as (#packets_to_protect, #fec_for_protection).
323 // Protection factor defined as: (#fec_for_protection / #packets_to_protect).
324 
325 // Let k=num_media_packets, n=total#packets, (n-k)=num_fec_packets,
326 // m=num_imp_packets.
327 
328 // For ProtectionMode 0 and 1:
329 // one mask (sub_mask1) is used for 1st partition packets,
330 // the other mask (sub_mask21/22, for 0/1) is for the remaining FEC packets.
331 
332 // In both mode 0 and 1, the packets of 1st partition (num_imp_packets) are
333 // treated equally important, and are afforded more protection than the
334 // residual partition packets.
335 
336 // For num_imp_packets:
337 // sub_mask1 = (m, t): protection = t/(m), where t=F(k,n-k,m).
338 // t=F(k,n-k,m) is the number of packets used to protect first partition in
339 // sub_mask1. This is determined from the function SetProtectionAllocation().
340 
341 // For the left-over protection:
342 // Mode 0: sub_mask21 = (k-m,n-k-t): protection = (n-k-t)/(k-m)
343 // mode 0 has no protection overlap between the two partitions.
344 // For mode 0, we would typically set t = min(m, n-k).
345 
346 // Mode 1: sub_mask22 = (k, n-k-t), with protection (n-k-t)/(k)
347 // mode 1 has protection overlap between the two partitions (preferred).
348 
349 // For ProtectionMode 2:
350 // This gives 1st packet of list (which is 1st packet of 1st partition) more
351 // protection. In mode 2, the equal protection mask (which is obtained from
352 // mode 1 for t=0) is modified (more "1s" added in 1st column of packet mask)
353 // to bias higher protection for the 1st source packet.
354 
355 // Protection Mode 2 may be extended for a sort of sliding protection
356 // (i.e., vary the number/density of "1s" across columns) across packets.
357 
UnequalProtectionMask(int num_media_packets,int num_fec_packets,int num_imp_packets,int num_mask_bytes,uint8_t * packet_mask,PacketMaskTable * mask_table)358 void UnequalProtectionMask(int num_media_packets,
359                            int num_fec_packets,
360                            int num_imp_packets,
361                            int num_mask_bytes,
362                            uint8_t* packet_mask,
363                            PacketMaskTable* mask_table) {
364   // Set Protection type and allocation
365   // TODO(marpan): test/update for best mode and some combinations thereof.
366 
367   ProtectionMode mode = kModeOverlap;
368   int num_fec_for_imp_packets = 0;
369 
370   if (mode != kModeBiasFirstPacket) {
371     num_fec_for_imp_packets = SetProtectionAllocation(
372         num_media_packets, num_fec_packets, num_imp_packets);
373   }
374 
375   int num_fec_remaining = num_fec_packets - num_fec_for_imp_packets;
376   // Done with setting protection type and allocation
377 
378   //
379   // Generate sub_mask1
380   //
381   if (num_fec_for_imp_packets > 0) {
382     ImportantPacketProtection(num_fec_for_imp_packets, num_imp_packets,
383                               num_mask_bytes, packet_mask, mask_table);
384   }
385 
386   //
387   // Generate sub_mask2
388   //
389   if (num_fec_remaining > 0) {
390     RemainingPacketProtection(num_media_packets, num_fec_remaining,
391                               num_fec_for_imp_packets, num_mask_bytes, mode,
392                               packet_mask, mask_table);
393   }
394 }
395 
396 // This algorithm is tailored to look up data in the |kPacketMaskRandomTbl| and
397 // |kPacketMaskBurstyTbl| tables. These tables only cover fec code for up to 12
398 // media packets. Starting from 13 media packets, the fec code will be generated
399 // at runtime. The format of those arrays is that they're essentially a 3
400 // dimensional array with the following dimensions: * media packet
401 //   * Size for kPacketMaskRandomTbl: 12
402 //   * Size for kPacketMaskBurstyTbl: 12
403 // * fec index
404 //   * Size for both random and bursty table increases from 1 to number of rows.
405 //     (i.e. 1-48, or 1-12 respectively).
406 // * Fec data (what actually gets returned)
407 //   * Size for kPacketMaskRandomTbl: 2 bytes.
408 //     * For all entries: 2 * fec index (1 based)
409 //   * Size for kPacketMaskBurstyTbl: 2 bytes.
410 //     * For all entries: 2 * fec index (1 based)
LookUpInFecTable(const uint8_t * table,int media_packet_index,int fec_index)411 rtc::ArrayView<const uint8_t> LookUpInFecTable(const uint8_t* table,
412                                                int media_packet_index,
413                                                int fec_index) {
414   RTC_DCHECK_LT(media_packet_index, table[0]);
415 
416   // Skip over the table size.
417   const uint8_t* entry = &table[1];
418 
419   uint8_t entry_size_increment = 2;  // 0-16 are 2 byte wide, then changes to 6.
420 
421   // Hop over un-interesting array entries.
422   for (int i = 0; i < media_packet_index; ++i) {
423     if (i == 16)
424       entry_size_increment = 6;
425     uint8_t count = entry[0];
426     ++entry;  // skip over the count.
427     for (int j = 0; j < count; ++j) {
428       entry += entry_size_increment * (j + 1);  // skip over the data.
429     }
430   }
431 
432   if (media_packet_index == 16)
433     entry_size_increment = 6;
434 
435   RTC_DCHECK_LT(fec_index, entry[0]);
436   ++entry;  // Skip over the size.
437 
438   // Find the appropriate data in the second dimension.
439 
440   // Find the specific data we're looking for.
441   for (int i = 0; i < fec_index; ++i)
442     entry += entry_size_increment * (i + 1);  // skip over the data.
443 
444   size_t size = entry_size_increment * (fec_index + 1);
445   return {&entry[0], size};
446 }
447 
GeneratePacketMasks(int num_media_packets,int num_fec_packets,int num_imp_packets,bool use_unequal_protection,PacketMaskTable * mask_table,uint8_t * packet_mask)448 void GeneratePacketMasks(int num_media_packets,
449                          int num_fec_packets,
450                          int num_imp_packets,
451                          bool use_unequal_protection,
452                          PacketMaskTable* mask_table,
453                          uint8_t* packet_mask) {
454   RTC_DCHECK_GT(num_media_packets, 0);
455   RTC_DCHECK_GT(num_fec_packets, 0);
456   RTC_DCHECK_LE(num_fec_packets, num_media_packets);
457   RTC_DCHECK_LE(num_imp_packets, num_media_packets);
458   RTC_DCHECK_GE(num_imp_packets, 0);
459 
460   const int num_mask_bytes = PacketMaskSize(num_media_packets);
461 
462   // Equal-protection for these cases.
463   if (!use_unequal_protection || num_imp_packets == 0) {
464     // Retrieve corresponding mask table directly:for equal-protection case.
465     // Mask = (k,n-k), with protection factor = (n-k)/k,
466     // where k = num_media_packets, n=total#packets, (n-k)=num_fec_packets.
467     rtc::ArrayView<const uint8_t> mask =
468         mask_table->LookUp(num_media_packets, num_fec_packets);
469     memcpy(packet_mask, &mask[0], mask.size());
470   } else {  // UEP case
471     UnequalProtectionMask(num_media_packets, num_fec_packets, num_imp_packets,
472                           num_mask_bytes, packet_mask, mask_table);
473   }  // End of UEP modification
474 }  // End of GetPacketMasks
475 
PacketMaskSize(size_t num_sequence_numbers)476 size_t PacketMaskSize(size_t num_sequence_numbers) {
477   RTC_DCHECK_LE(num_sequence_numbers, 8 * kUlpfecPacketMaskSizeLBitSet);
478   if (num_sequence_numbers > 8 * kUlpfecPacketMaskSizeLBitClear) {
479     return kUlpfecPacketMaskSizeLBitSet;
480   }
481   return kUlpfecPacketMaskSizeLBitClear;
482 }
483 
InsertZeroColumns(int num_zeros,uint8_t * new_mask,int new_mask_bytes,int num_fec_packets,int new_bit_index)484 void InsertZeroColumns(int num_zeros,
485                        uint8_t* new_mask,
486                        int new_mask_bytes,
487                        int num_fec_packets,
488                        int new_bit_index) {
489   for (uint16_t row = 0; row < num_fec_packets; ++row) {
490     const int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
491     const int max_shifts = (7 - (new_bit_index % 8));
492     new_mask[new_byte_index] <<= std::min(num_zeros, max_shifts);
493   }
494 }
495 
CopyColumn(uint8_t * new_mask,int new_mask_bytes,uint8_t * old_mask,int old_mask_bytes,int num_fec_packets,int new_bit_index,int old_bit_index)496 void CopyColumn(uint8_t* new_mask,
497                 int new_mask_bytes,
498                 uint8_t* old_mask,
499                 int old_mask_bytes,
500                 int num_fec_packets,
501                 int new_bit_index,
502                 int old_bit_index) {
503   RTC_CHECK_LT(new_bit_index, 8 * new_mask_bytes);
504 
505   // Copy column from the old mask to the beginning of the new mask and shift it
506   // out from the old mask.
507   for (uint16_t row = 0; row < num_fec_packets; ++row) {
508     int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
509     int old_byte_index = row * old_mask_bytes + old_bit_index / 8;
510     new_mask[new_byte_index] |= ((old_mask[old_byte_index] & 0x80) >> 7);
511     if (new_bit_index % 8 != 7) {
512       new_mask[new_byte_index] <<= 1;
513     }
514     old_mask[old_byte_index] <<= 1;
515   }
516 }
517 
518 }  // namespace internal
519 }  // namespace webrtc
520