| /* |
| * 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. |
| */ |
| |
| #include "modules/audio_coding/neteq/timestamp_scaler.h" |
| |
| #include "api/audio_codecs/builtin_audio_decoder_factory.h" |
| #include "api/environment/environment.h" |
| #include "api/environment/environment_factory.h" |
| #include "modules/audio_coding/neteq/mock/mock_decoder_database.h" |
| #include "modules/audio_coding/neteq/packet.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::Return; |
| using ::testing::ReturnNull; |
| |
| namespace webrtc { |
| |
| TEST(TimestampScaler, TestNoScaling) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use PCMu, because it doesn't use scaled timestamps. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("pcmu", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 0; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| for (uint32_t timestamp = 0xFFFFFFFF - 5; timestamp != 5; ++timestamp) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, TestNoScalingLargeStep) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use PCMu, because it doesn't use scaled timestamps. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("pcmu", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 0; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| static const uint32_t kStep = 160; |
| uint32_t start_timestamp = 0; |
| // `external_timestamp` will be a large positive value. |
| start_timestamp = start_timestamp - 5 * kStep; |
| for (uint32_t timestamp = start_timestamp; timestamp != 5 * kStep; |
| timestamp += kStep) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(timestamp, scaler.ToExternal(timestamp)); |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, TestG722) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("g722", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| uint32_t external_timestamp = 0xFFFFFFFF - 5; |
| uint32_t internal_timestamp = external_timestamp; |
| for (; external_timestamp != 5; ++external_timestamp) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| internal_timestamp += 2; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, TestG722LargeStep) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("g722", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| static const uint32_t kStep = 320; |
| uint32_t external_timestamp = 0; |
| // `external_timestamp` will be a large positive value. |
| external_timestamp = external_timestamp - 5 * kStep; |
| uint32_t internal_timestamp = external_timestamp; |
| for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| // Internal timestamp should be incremented with twice the step. |
| internal_timestamp += 2 * kStep; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, TestG722WithCng) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info_g722( |
| env, SdpAudioFormat("g722", 8000, 1), std::nullopt, factory.get()); |
| const DecoderDatabase::DecoderInfo info_cng( |
| env, SdpAudioFormat("cn", 16000, 1), std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadTypeG722 = 17; |
| static const uint8_t kRtpPayloadTypeCng = 13; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeG722)) |
| .WillRepeatedly(Return(&info_g722)); |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeCng)) |
| .WillRepeatedly(Return(&info_cng)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| uint32_t external_timestamp = 0xFFFFFFFF - 5; |
| uint32_t internal_timestamp = external_timestamp; |
| bool next_is_cng = false; |
| for (; external_timestamp != 5; ++external_timestamp) { |
| // Alternate between G.722 and CNG every other packet. |
| if (next_is_cng) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadTypeCng)); |
| next_is_cng = false; |
| } else { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadTypeG722)); |
| next_is_cng = true; |
| } |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| internal_timestamp += 2; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| // Make sure that the method ToInternal(Packet* packet) is wired up correctly. |
| // Since it is simply calling the other ToInternal method, we are not doing |
| // as many tests here. |
| TEST(TimestampScaler, TestG722Packet) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("g722", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| uint32_t external_timestamp = 0xFFFFFFFF - 5; |
| uint32_t internal_timestamp = external_timestamp; |
| Packet packet; |
| packet.payload_type = kRtpPayloadType; |
| for (; external_timestamp != 5; ++external_timestamp) { |
| packet.timestamp = external_timestamp; |
| // Scale to internal timestamp. |
| scaler.ToInternal(&packet); |
| EXPECT_EQ(internal_timestamp, packet.timestamp); |
| internal_timestamp += 2; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| // Make sure that the method ToInternal(PacketList* packet_list) is wired up |
| // correctly. Since it is simply calling the ToInternal(Packet* packet) method, |
| // we are not doing as many tests here. |
| TEST(TimestampScaler, TestG722PacketList) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("g722", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| uint32_t external_timestamp = 0xFFFFFFFF - 5; |
| uint32_t internal_timestamp = external_timestamp; |
| PacketList packet_list; |
| { |
| Packet packet1; |
| packet1.payload_type = kRtpPayloadType; |
| packet1.timestamp = external_timestamp; |
| Packet packet2; |
| packet2.payload_type = kRtpPayloadType; |
| packet2.timestamp = external_timestamp + 10; |
| packet_list.push_back(std::move(packet1)); |
| packet_list.push_back(std::move(packet2)); |
| } |
| |
| scaler.ToInternal(&packet_list); |
| EXPECT_EQ(internal_timestamp, packet_list.front().timestamp); |
| packet_list.pop_front(); |
| EXPECT_EQ(internal_timestamp + 20, packet_list.front().timestamp); |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, TestG722Reset) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| // Use G722, which has a factor 2 scaling. |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("g722", 8000, 1), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| uint32_t external_timestamp = 0xFFFFFFFF - 5; |
| uint32_t internal_timestamp = external_timestamp; |
| for (; external_timestamp != 5; ++external_timestamp) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| internal_timestamp += 2; |
| } |
| // Reset the scaler. After this, we expect the internal and external to start |
| // over at the same value again. |
| scaler.Reset(); |
| internal_timestamp = external_timestamp; |
| for (; external_timestamp != 15; ++external_timestamp) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| internal_timestamp += 2; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| // TODO(minyue): This test becomes trivial since Opus does not need a timestamp |
| // scaler. Therefore, this test may be removed in future. There is no harm to |
| // keep it, since it can be taken as a test case for the situation of a trivial |
| // timestamp scaler. |
| TEST(TimestampScaler, TestOpusLargeStep) { |
| const Environment env = CreateEnvironment(); |
| MockDecoderDatabase db; |
| auto factory = CreateBuiltinAudioDecoderFactory(); |
| const DecoderDatabase::DecoderInfo info(env, SdpAudioFormat("opus", 48000, 2), |
| std::nullopt, factory.get()); |
| static const uint8_t kRtpPayloadType = 17; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillRepeatedly(Return(&info)); |
| |
| TimestampScaler scaler(db); |
| // Test both sides of the timestamp wrap-around. |
| static const uint32_t kStep = 960; |
| uint32_t external_timestamp = 0; |
| // `external_timestamp` will be a large positive value. |
| external_timestamp = external_timestamp - 5 * kStep; |
| uint32_t internal_timestamp = external_timestamp; |
| for (; external_timestamp != 5 * kStep; external_timestamp += kStep) { |
| // Scale to internal timestamp. |
| EXPECT_EQ(internal_timestamp, |
| scaler.ToInternal(external_timestamp, kRtpPayloadType)); |
| // Scale back. |
| EXPECT_EQ(external_timestamp, scaler.ToExternal(internal_timestamp)); |
| internal_timestamp += kStep; |
| } |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| TEST(TimestampScaler, Failures) { |
| static const uint8_t kRtpPayloadType = 17; |
| MockDecoderDatabase db; |
| EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) |
| .WillOnce(ReturnNull()); // Return NULL to indicate unknown payload type. |
| |
| TimestampScaler scaler(db); |
| uint32_t timestamp = 4711; // Some number. |
| EXPECT_EQ(timestamp, scaler.ToInternal(timestamp, kRtpPayloadType)); |
| |
| Packet* packet = NULL; |
| scaler.ToInternal(packet); // Should not crash. That's all we can test. |
| |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. |
| } |
| |
| } // namespace webrtc |