| /* | |
| * 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 <cstdint> | |
| #include <utility> | |
| #include "api/audio_codecs/audio_format.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), | |
| 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), | |
| 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), | |
| 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), | |
| 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), factory.get()); | |
| const DecoderDatabase::DecoderInfo info_cng( | |
| env, SdpAudioFormat("cn", 16000, 1), 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), | |
| 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), | |
| 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), | |
| 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), | |
| 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 = nullptr; | |
| scaler.ToInternal(packet); // Should not crash. That's all we can test. | |
| EXPECT_CALL(db, Die()); // Called when database object is deleted. | |
| } | |
| } // namespace webrtc |