Allowing RED decoding for Opus.

BUG=4247
TEST=reproduced and fixed the bug
R=henrik.lundin@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/41809004

Cr-Commit-Position: refs/heads/master@{#8364}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8364 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder.cc b/webrtc/modules/audio_coding/codecs/audio_decoder.cc
index 6114e70..bc6b9c5 100644
--- a/webrtc/modules/audio_coding/codecs/audio_decoder.cc
+++ b/webrtc/modules/audio_coding/codecs/audio_decoder.cc
@@ -37,7 +37,8 @@
 
 int AudioDecoder::ErrorCode() { return 0; }
 
-int AudioDecoder::PacketDuration(const uint8_t* encoded, size_t encoded_len) {
+int AudioDecoder::PacketDuration(const uint8_t* encoded,
+                                 size_t encoded_len) const {
   return kNotImplemented;
 }
 
diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder.h b/webrtc/modules/audio_coding/codecs/audio_decoder.h
index 0b4e20c..30fdc44 100644
--- a/webrtc/modules/audio_coding/codecs/audio_decoder.h
+++ b/webrtc/modules/audio_coding/codecs/audio_decoder.h
@@ -69,7 +69,7 @@
   // Returns the duration in samples of the payload in |encoded| which is
   // |encoded_len| bytes long. Returns kNotImplemented if no duration estimate
   // is available, or -1 in case of an error.
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
 
   // Returns the duration in samples of the redandant payload in |encoded| which
   // is |encoded_len| bytes long. Returns kNotImplemented if no duration
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc
index 19c3956..47474f2 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc
+++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc
@@ -1134,7 +1134,7 @@
 }
 
 int AudioDecoderProxy::PacketDuration(const uint8_t* encoded,
-                                      size_t encoded_len) {
+                                      size_t encoded_len) const {
   CriticalSectionScoped decoder_lock(decoder_lock_.get());
   return decoder_->PacketDuration(encoded, encoded_len);
 }
diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h
index d2caea7..2154ebf 100644
--- a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h
+++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h
@@ -1004,7 +1004,7 @@
                      uint32_t rtp_timestamp,
                      uint32_t arrival_timestamp) override;
   int ErrorCode() override;
-  int PacketDuration(const uint8_t* encoded, size_t encoded_len) override;
+  int PacketDuration(const uint8_t* encoded, size_t encoded_len) const override;
   int PacketDurationRedundant(const uint8_t* encoded,
                               size_t encoded_len) const override;
   bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const override;
diff --git a/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc b/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc
index 66eba12..882c8f3 100644
--- a/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc
+++ b/webrtc/modules/audio_coding/neteq/audio_decoder_impl.cc
@@ -48,7 +48,7 @@
 }
 
 int AudioDecoderPcmU::PacketDuration(const uint8_t* encoded,
-                                     size_t encoded_len) {
+                                     size_t encoded_len) const {
   // One encoded byte per sample per channel.
   return static_cast<int>(encoded_len / channels_);
 }
@@ -64,7 +64,7 @@
 }
 
 int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded,
-                                     size_t encoded_len) {
+                                     size_t encoded_len) const {
   // One encoded byte per sample per channel.
   return static_cast<int>(encoded_len / channels_);
 }
@@ -82,7 +82,7 @@
 }
 
 int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded,
-                                       size_t encoded_len) {
+                                       size_t encoded_len) const {
   // Two encoded byte per sample per channel.
   return static_cast<int>(encoded_len / (2 * channels_));
 }
@@ -148,7 +148,7 @@
 }
 
 int AudioDecoderG722::PacketDuration(const uint8_t* encoded,
-                                     size_t encoded_len) {
+                                     size_t encoded_len) const {
   // 1/2 encoded byte per sample per channel.
   return static_cast<int>(2 * encoded_len / channels_);
 }
@@ -260,6 +260,11 @@
 int AudioDecoderOpus::DecodeRedundant(const uint8_t* encoded,
                                       size_t encoded_len, int16_t* decoded,
                                       SpeechType* speech_type) {
+  if (!PacketHasFec(encoded, encoded_len)) {
+    // This packet is a RED packet.
+    return Decode(encoded, encoded_len, decoded, speech_type);
+  }
+
   int16_t temp_type = 1;  // Default is speech.
   int16_t ret = WebRtcOpus_DecodeFec(dec_state_, encoded,
                                      static_cast<int16_t>(encoded_len), decoded,
@@ -275,13 +280,18 @@
 }
 
 int AudioDecoderOpus::PacketDuration(const uint8_t* encoded,
-                                     size_t encoded_len) {
+                                     size_t encoded_len) const {
   return WebRtcOpus_DurationEst(dec_state_,
                                 encoded, static_cast<int>(encoded_len));
 }
 
 int AudioDecoderOpus::PacketDurationRedundant(const uint8_t* encoded,
                                               size_t encoded_len) const {
+  if (!PacketHasFec(encoded, encoded_len)) {
+    // This packet is a RED packet.
+    return PacketDuration(encoded, encoded_len);
+  }
+
   return WebRtcOpus_FecDurationEst(encoded, static_cast<int>(encoded_len));
 }
 
diff --git a/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h b/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h
index 8e1ba7e..57bd522 100644
--- a/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h
+++ b/webrtc/modules/audio_coding/neteq/audio_decoder_impl.h
@@ -40,7 +40,7 @@
   virtual int Decode(const uint8_t* encoded, size_t encoded_len,
                      int16_t* decoded, SpeechType* speech_type);
   virtual int Init() { return 0; }
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU);
@@ -52,7 +52,7 @@
   virtual int Decode(const uint8_t* encoded, size_t encoded_len,
                      int16_t* decoded, SpeechType* speech_type);
   virtual int Init() { return 0; }
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA);
@@ -89,7 +89,7 @@
   virtual int Decode(const uint8_t* encoded, size_t encoded_len,
                      int16_t* decoded, SpeechType* speech_type);
   virtual int Init() { return 0; }
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B);
@@ -133,7 +133,7 @@
                      int16_t* decoded, SpeechType* speech_type);
   virtual bool HasDecodePlc() const { return false; }
   virtual int Init();
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
 
  private:
   G722DecInst* dec_state_;
@@ -174,7 +174,7 @@
   virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len,
                               int16_t* decoded, SpeechType* speech_type);
   virtual int Init();
-  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len);
+  virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
   virtual int PacketDurationRedundant(const uint8_t* encoded,
                                       size_t encoded_len) const;
   virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const;
diff --git a/webrtc/modules/audio_coding/neteq/payload_splitter.cc b/webrtc/modules/audio_coding/neteq/payload_splitter.cc
index 118556b..c19375b 100644
--- a/webrtc/modules/audio_coding/neteq/payload_splitter.cc
+++ b/webrtc/modules/audio_coding/neteq/payload_splitter.cc
@@ -151,8 +151,11 @@
     switch (info->codec_type) {
       case kDecoderOpus:
       case kDecoderOpus_2ch: {
-        Packet* new_packet = new Packet;
+        // The main payload of this packet should be decoded as a primary
+        // payload, even if it comes as a secondary payload in a RED packet.
+        packet->primary = true;
 
+        Packet* new_packet = new Packet;
         new_packet->header = packet->header;
         int duration = decoder->
             PacketDurationRedundant(packet->payload, packet->payload_length);
diff --git a/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc b/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc
index d397a07..085e76f 100644
--- a/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/payload_splitter_unittest.cc
@@ -32,6 +32,28 @@
 static const uint16_t kSequenceNumber = 0;
 static const uint32_t kBaseTimestamp = 0x12345678;
 
+// A possible Opus packet that contains FEC is the following.
+// The frame is 20 ms in duration.
+//
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x|                             |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                             |
+// |                    Compressed frame 1 (N-2 bytes)...          :
+// :                                                               |
+// |                                                               |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+void CreateOpusFecPayload(uint8_t* payload, size_t payload_length,
+                          uint8_t payload_value) {
+  if (payload_length < 2) {
+    return;
+  }
+  payload[0] = 0x08;
+  payload[1] = 0x40;
+  memset(&payload[2], payload_value, payload_length - 2);
+}
+
 // RED headers (according to RFC 2198):
 //
 //    0                   1                   2                   3
@@ -52,7 +74,8 @@
 // "behind" the the previous payload.
 Packet* CreateRedPayload(size_t num_payloads,
                          uint8_t* payload_types,
-                         int timestamp_offset) {
+                         int timestamp_offset,
+                         bool embed_opus_fec = false) {
   Packet* packet = new Packet;
   packet->header.payloadType = kRedPayloadType;
   packet->header.timestamp = kBaseTimestamp;
@@ -84,52 +107,34 @@
   }
   for (size_t i = 0; i < num_payloads; ++i) {
     // Write |i| to all bytes in each payload.
-    memset(payload_ptr, static_cast<int>(i), kPayloadLength);
+    if (embed_opus_fec) {
+      CreateOpusFecPayload(payload_ptr, kPayloadLength,
+                           static_cast<uint8_t>(i));
+    } else {
+      memset(payload_ptr, static_cast<int>(i), kPayloadLength);
+    }
     payload_ptr += kPayloadLength;
   }
   packet->payload = payload;
   return packet;
 }
 
-
-// A possible Opus packet that contains FEC is the following.
-// The frame is 20 ms in duration.
-//
-// 0                   1                   2                   3
-// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x|                             |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                             |
-// |                    Compressed frame 1 (N-2 bytes)...          :
-// :                                                               |
-// |                                                               |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-Packet* CreateOpusFecPacket(uint8_t payload_type, size_t payload_length,
-                            uint8_t payload_value) {
-  Packet* packet = new Packet;
-  packet->header.payloadType = payload_type;
-  packet->header.timestamp = kBaseTimestamp;
-  packet->header.sequenceNumber = kSequenceNumber;
-  packet->payload_length = payload_length;
-  uint8_t* payload = new uint8_t[packet->payload_length];
-  payload[0] = 0x08;
-  payload[1] = 0x40;
-  memset(&payload[2], payload_value, payload_length - 2);
-  packet->payload = payload;
-  return packet;
-}
-
 // Create a packet with all payload bytes set to |payload_value|.
 Packet* CreatePacket(uint8_t payload_type, size_t payload_length,
-                     uint8_t payload_value) {
+                     uint8_t payload_value, bool opus_fec = false) {
   Packet* packet = new Packet;
   packet->header.payloadType = payload_type;
   packet->header.timestamp = kBaseTimestamp;
   packet->header.sequenceNumber = kSequenceNumber;
   packet->payload_length = payload_length;
   uint8_t* payload = new uint8_t[packet->payload_length];
-  memset(payload, payload_value, payload_length);
   packet->payload = payload;
+  if (opus_fec) {
+    CreateOpusFecPayload(packet->payload, packet->payload_length,
+                         payload_value);
+  } else {
+    memset(payload, payload_value, payload_length);
+  }
   return packet;
 }
 
@@ -726,7 +731,7 @@
   decoder_database.RegisterPayload(0, kDecoderOpus);
   decoder_database.RegisterPayload(1, kDecoderPCMu);
 
-  Packet* packet = CreateOpusFecPacket(0, 10, 0xFF);
+  Packet* packet = CreatePacket(0, 10, 0xFF, true);
   packet_list.push_back(packet);
 
   packet = CreatePacket(0, 10, 0); // Non-FEC Opus payload.
@@ -774,4 +779,67 @@
   delete packet;
 }
 
+TEST(FecPayloadSplitter, EmbedFecInRed) {
+  PacketList packet_list;
+  DecoderDatabase decoder_database;
+
+  const int kTimestampOffset = 20 * 48;  // 20 ms * 48 kHz.
+  uint8_t payload_types[] = {0, 0};
+  decoder_database.RegisterPayload(0, kDecoderOpus);
+  Packet* packet = CreateRedPayload(2, payload_types, kTimestampOffset, true);
+  packet_list.push_back(packet);
+
+  PayloadSplitter splitter;
+  EXPECT_EQ(PayloadSplitter::kOK,
+            splitter.SplitRed(&packet_list));
+  EXPECT_EQ(PayloadSplitter::kOK,
+            splitter.SplitFec(&packet_list, &decoder_database));
+
+  EXPECT_EQ(4u, packet_list.size());
+
+  // Check first packet. FEC packet copied from primary payload in RED.
+  packet = packet_list.front();
+  EXPECT_EQ(0, packet->header.payloadType);
+  EXPECT_EQ(kBaseTimestamp - kTimestampOffset, packet->header.timestamp);
+  EXPECT_EQ(kPayloadLength, packet->payload_length);
+  EXPECT_FALSE(packet->primary);
+  EXPECT_EQ(packet->payload[3], 1);
+  delete [] packet->payload;
+  delete packet;
+  packet_list.pop_front();
+
+  // Check second packet. Normal packet copied from primary payload in RED.
+  packet = packet_list.front();
+  EXPECT_EQ(0, packet->header.payloadType);
+  EXPECT_EQ(kBaseTimestamp, packet->header.timestamp);
+  EXPECT_EQ(kPayloadLength, packet->payload_length);
+  EXPECT_TRUE(packet->primary);
+  EXPECT_EQ(packet->payload[3], 1);
+  delete [] packet->payload;
+  delete packet;
+  packet_list.pop_front();
+
+  // Check third packet. FEC packet copied from secondary payload in RED.
+  packet = packet_list.front();
+  EXPECT_EQ(0, packet->header.payloadType);
+  EXPECT_EQ(kBaseTimestamp - 2 * kTimestampOffset, packet->header.timestamp);
+  EXPECT_EQ(kPayloadLength, packet->payload_length);
+  EXPECT_FALSE(packet->primary);
+  EXPECT_EQ(packet->payload[3], 0);
+  delete [] packet->payload;
+  delete packet;
+  packet_list.pop_front();
+
+  // Check fourth packet. Normal packet copied from primary payload in RED.
+  packet = packet_list.front();
+  EXPECT_EQ(0, packet->header.payloadType);
+  EXPECT_EQ(kBaseTimestamp - kTimestampOffset, packet->header.timestamp);
+  EXPECT_EQ(kPayloadLength, packet->payload_length);
+  EXPECT_TRUE(packet->primary);
+  EXPECT_EQ(packet->payload[3], 0);
+  delete [] packet->payload;
+  delete packet;
+  packet_list.pop_front();
+}
+
 }  // namespace webrtc