Index: webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc |
diff --git a/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc b/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc |
index 3a3469241c9ca26fd708fd9ffdc8a5302f18ce2d..5489fed91008d5eef1b1c91d40af4a7f745e48eb 100644 |
--- a/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc |
+++ b/webrtc/modules/audio_coding/neteq/neteq_impl_unittest.cc |
@@ -1005,4 +1005,245 @@ TEST_F(NetEqImplTest, DecodedPayloadTooShort) { |
EXPECT_CALL(mock_decoder, Die()); |
} |
-} // namespace webrtc |
+ |
+// This test checks the behavior of NetEq when audio decoder fails. |
+TEST_F(NetEqImplTest, DecodingError) { |
+ UseNoMocks(); |
+ CreateInstance(); |
+ |
+ const uint8_t kPayloadType = 17; // Just an arbitrary number. |
+ const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. |
+ const int kSampleRateHz = 8000; |
+ const int kDecoderErrorCode = -97; // Any negative number. |
+ |
+ // We let decoder return 5 ms each time, and therefore, 2 packets make 10 ms. |
+ const size_t kFrameLengthSamples = |
+ static_cast<size_t>(5 * kSampleRateHz / 1000); |
+ |
+ const size_t kPayloadLengthBytes = 1; // This can be arbitrary. |
+ |
+ uint8_t payload[kPayloadLengthBytes] = {0}; |
+ |
+ WebRtcRTPHeader rtp_header; |
+ rtp_header.header.payloadType = kPayloadType; |
+ rtp_header.header.sequenceNumber = 0x1234; |
+ rtp_header.header.timestamp = 0x12345678; |
+ rtp_header.header.ssrc = 0x87654321; |
+ |
+ // Create a mock decoder object. |
+ MockAudioDecoder mock_decoder; |
+ EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return()); |
+ EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1)); |
+ EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _)) |
+ .WillRepeatedly(Return(0)); |
+ EXPECT_CALL(mock_decoder, PacketDuration(_, _)) |
+ .WillRepeatedly(Return(kFrameLengthSamples)); |
+ EXPECT_CALL(mock_decoder, ErrorCode()) |
+ .WillOnce(Return(kDecoderErrorCode)); |
+ EXPECT_CALL(mock_decoder, HasDecodePlc()) |
+ .WillOnce(Return(false)); |
+ int16_t dummy_output[kFrameLengthSamples] = {0}; |
+ |
+ { |
+ InSequence sequence; // Dummy variable. |
+ // Mock decoder works normally the first time. |
+ EXPECT_CALL(mock_decoder, |
+ Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _)) |
+ .Times(3) |
+ .WillRepeatedly( |
+ DoAll(SetArrayArgument<4>(dummy_output, |
+ dummy_output + kFrameLengthSamples), |
+ SetArgPointee<5>(AudioDecoder::kSpeech), |
+ Return(kFrameLengthSamples))) |
+ .RetiresOnSaturation(); |
+ |
+ // Then mock decoder fails. A common reason for failure can be buffer being |
+ // too short |
+ EXPECT_CALL(mock_decoder, |
+ Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _)) |
+ .WillOnce(Return(-1)) |
+ .RetiresOnSaturation(); |
+ |
+ // Mock decoder finally returns to normal. |
+ EXPECT_CALL(mock_decoder, |
+ Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _)) |
+ .Times(2) |
+ .WillRepeatedly( |
+ DoAll(SetArrayArgument<4>(dummy_output, |
+ dummy_output + kFrameLengthSamples), |
+ SetArgPointee<5>(AudioDecoder::kSpeech), |
+ Return(kFrameLengthSamples))); |
+ } |
+ |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B, |
+ kPayloadType, kSampleRateHz)); |
+ |
+ // Insert packets. |
+ for (int i = 0; i < 6; ++i) { |
+ rtp_header.header.sequenceNumber += 1; |
+ rtp_header.header.timestamp += kFrameLengthSamples; |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes, |
+ kReceiveTime)); |
+ } |
+ |
+ // Pull audio. |
+ const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000); |
+ int16_t output[kMaxOutputSize]; |
+ size_t samples_per_channel; |
+ int num_channels; |
+ NetEqOutputType type; |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ EXPECT_EQ(kOutputNormal, type); |
+ |
+ // Pull audio again. Decoder fails. |
+ EXPECT_EQ(NetEq::kFail, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError()); |
+ EXPECT_EQ(kDecoderErrorCode, neteq_->LastDecoderError()); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ // TODO(minyue): should NetEq better give kOutputPLC, since it is actually an |
+ // expansion. |
+ EXPECT_EQ(kOutputNormal, type); |
+ |
+ // Pull audio again, should continue an expansion. |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ EXPECT_EQ(kOutputPLC, type); |
+ |
+ // Pull audio again, should behave normal. |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ EXPECT_EQ(kOutputNormal, type); |
+ |
+ EXPECT_CALL(mock_decoder, Die()); |
+} |
+ |
+// This test checks the behavior of NetEq when audio decoder fails during CNG. |
+TEST_F(NetEqImplTest, DecodingErrorDuringInternalCng) { |
+ UseNoMocks(); |
+ CreateInstance(); |
+ |
+ const uint8_t kPayloadType = 17; // Just an arbitrary number. |
+ const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. |
+ const int kSampleRateHz = 8000; |
+ const int kDecoderErrorCode = -97; // Any negative number. |
+ |
+ // We let decoder return 5 ms each time, and therefore, 2 packets make 10 ms. |
+ const size_t kFrameLengthSamples = |
+ static_cast<size_t>(5 * kSampleRateHz / 1000); |
+ |
+ const size_t kPayloadLengthBytes = 1; // This can be arbitrary. |
+ |
+ uint8_t payload[kPayloadLengthBytes] = {0}; |
+ |
+ WebRtcRTPHeader rtp_header; |
+ rtp_header.header.payloadType = kPayloadType; |
+ rtp_header.header.sequenceNumber = 0x1234; |
+ rtp_header.header.timestamp = 0x12345678; |
+ rtp_header.header.ssrc = 0x87654321; |
+ |
+ // Create a mock decoder object. |
+ MockAudioDecoder mock_decoder; |
+ EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return()); |
+ EXPECT_CALL(mock_decoder, Channels()).WillRepeatedly(Return(1)); |
+ EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _)) |
+ .WillRepeatedly(Return(0)); |
+ EXPECT_CALL(mock_decoder, PacketDuration(_, _)) |
+ .WillRepeatedly(Return(kFrameLengthSamples)); |
+ EXPECT_CALL(mock_decoder, ErrorCode()) |
+ .WillOnce(Return(kDecoderErrorCode)); |
+ int16_t dummy_output[kFrameLengthSamples] = {0}; |
+ |
+ { |
+ InSequence sequence; // Dummy variable. |
+ // Mock decoder works normally the first 2 times. |
+ EXPECT_CALL(mock_decoder, |
+ Decode(_, kPayloadLengthBytes, kSampleRateHz, _, _, _)) |
+ .Times(2) |
+ .WillRepeatedly( |
+ DoAll(SetArrayArgument<4>(dummy_output, |
+ dummy_output + kFrameLengthSamples), |
+ SetArgPointee<5>(AudioDecoder::kComfortNoise), |
+ Return(kFrameLengthSamples))) |
+ .RetiresOnSaturation(); |
+ |
+ // Then mock decoder fails. A common reason for failure can be buffer being |
+ // too short |
+ EXPECT_CALL(mock_decoder, Decode(nullptr, 0, kSampleRateHz, _, _, _)) |
+ .WillOnce(Return(-1)) |
+ .RetiresOnSaturation(); |
+ |
+ // Mock decoder finally returns to normal. |
+ EXPECT_CALL(mock_decoder, Decode(nullptr, 0, kSampleRateHz, _, _, _)) |
+ .Times(2) |
+ .WillRepeatedly( |
+ DoAll(SetArrayArgument<4>(dummy_output, |
+ dummy_output + kFrameLengthSamples), |
+ SetArgPointee<5>(AudioDecoder::kComfortNoise), |
+ Return(kFrameLengthSamples))); |
+ } |
+ |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->RegisterExternalDecoder(&mock_decoder, kDecoderPCM16B, |
+ kPayloadType, kSampleRateHz)); |
+ |
+ // Insert 2 packets. This will make netEq into codec internal CNG mode. |
+ for (int i = 0; i < 2; ++i) { |
+ rtp_header.header.sequenceNumber += 1; |
+ rtp_header.header.timestamp += kFrameLengthSamples; |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->InsertPacket(rtp_header, payload, kPayloadLengthBytes, |
+ kReceiveTime)); |
+ } |
+ |
+ // Pull audio. |
+ const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000); |
+ int16_t output[kMaxOutputSize]; |
+ size_t samples_per_channel; |
+ int num_channels; |
+ NetEqOutputType type; |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ EXPECT_EQ(kOutputCNG, type); |
+ |
+ // Pull audio again. Decoder fails. |
+ EXPECT_EQ(NetEq::kFail, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(NetEq::kDecoderErrorCode, neteq_->LastError()); |
+ EXPECT_EQ(kDecoderErrorCode, neteq_->LastDecoderError()); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ // TODO(minyue): should NetEq better give kOutputPLC, since it is actually an |
+ // expansion. |
+ EXPECT_EQ(kOutputCNG, type); |
+ |
+ // Pull audio again, should resume codec CNG. |
+ EXPECT_EQ(NetEq::kOK, |
+ neteq_->GetAudio(kMaxOutputSize, output, &samples_per_channel, |
+ &num_channels, &type)); |
+ EXPECT_EQ(kMaxOutputSize, samples_per_channel); |
+ EXPECT_EQ(1, num_channels); |
+ EXPECT_EQ(kOutputCNG, type); |
+ |
+ EXPECT_CALL(mock_decoder, Die()); |
+} |
+ |
+}// namespace webrtc |