// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/media/audio/capture_service/message_parsing_utils.h"
#include <vector>
#include "base/big_endian.h"
#include "chromecast/media/audio/capture_service/constants.h"
#include "chromecast/media/audio/capture_service/packet_header.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
namespace capture_service {
namespace {
constexpr size_t kFrames = 10;
constexpr size_t kChannels = 2;
constexpr StreamInfo kStreamInfo =
StreamInfo{StreamType::kSoftwareEchoCancelled,
AudioCodec::kPcm,
kChannels,
SampleFormat::PLANAR_FLOAT,
16000,
kFrames};
class PacketHeaderTest
: public testing::TestWithParam<
std::tuple<StreamType, AudioCodec, int, SampleFormat, int, int>> {
protected:
StreamInfo GetStreamInfo() {
StreamInfo info;
info.stream_type = std::get<0>(GetParam());
info.audio_codec = std::get<1>(GetParam());
info.num_channels = std::get<2>(GetParam());
info.sample_format = std::get<3>(GetParam());
info.sample_rate = std::get<4>(GetParam());
info.frames_per_buffer = std::get<5>(GetParam());
return info;
}
};
TEST_P(PacketHeaderTest, HandshakeMessage) {
std::vector<char> data(sizeof(HandshakePacket), 0);
StreamInfo stream_info = GetStreamInfo();
PopulateHandshakeMessage(data.data(), data.size(), stream_info);
StreamInfo info_out;
bool success =
ReadHandshakeMessage(data.data() + sizeof(uint16_t),
data.size() - sizeof(uint16_t), &info_out);
EXPECT_TRUE(success);
EXPECT_EQ(info_out.stream_type, stream_info.stream_type);
EXPECT_EQ(info_out.audio_codec, stream_info.audio_codec);
EXPECT_EQ(info_out.sample_format, stream_info.sample_format);
EXPECT_EQ(info_out.num_channels, stream_info.num_channels);
EXPECT_EQ(info_out.sample_rate, stream_info.sample_rate);
EXPECT_EQ(info_out.frames_per_buffer, stream_info.frames_per_buffer);
}
TEST(MessageParsingUtilsTest, PcmAudioMessage) {
size_t data_size = sizeof(PcmPacketHeader) / sizeof(float);
std::vector<float> data(data_size, 1.0f);
int64_t timestamp_us = 100;
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(float), kStreamInfo.stream_type,
timestamp_us);
int64_t timestamp_out = 0;
bool success = ReadPcmAudioHeader(
reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(float) - sizeof(uint16_t), kStreamInfo,
×tamp_out);
EXPECT_TRUE(success);
EXPECT_EQ(timestamp_out, timestamp_us);
}
TEST(MessageParsingUtilsTest, ValidPlanarFloat) {
size_t data_size =
sizeof(PcmPacketHeader) / sizeof(float) + kFrames * kChannels;
std::vector<float> data(data_size, .0f);
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(float), kStreamInfo.stream_type,
0);
// Fill the last k frames, i.e., the second channel, with 0.5f.
for (size_t i = data_size - kFrames; i < data_size; i++) {
data[i] = .5f;
}
auto audio_bus = ::media::AudioBus::Create(kChannels, kFrames);
bool success = ReadDataToAudioBus(
kStreamInfo, reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(float) - sizeof(uint16_t), audio_bus.get());
EXPECT_TRUE(success);
for (size_t f = 0; f < kFrames; f++) {
EXPECT_FLOAT_EQ(audio_bus->channel(0)[f], .0f);
EXPECT_FLOAT_EQ(audio_bus->channel(1)[f], .5f);
}
}
TEST(MessageParsingUtilsTest, ValidInterleavedInt16) {
size_t data_size =
sizeof(PcmPacketHeader) / sizeof(int16_t) + kFrames * kChannels;
std::vector<int16_t> data(data_size, std::numeric_limits<int16_t>::max());
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(int16_t), kStreamInfo.stream_type,
0);
// Fill the second channel with min().
for (size_t i = sizeof(PcmPacketHeader) / sizeof(int16_t) + 1; i < data_size;
i += 2) {
data[i] = std::numeric_limits<int16_t>::min();
}
auto audio_bus = ::media::AudioBus::Create(kChannels, kFrames);
StreamInfo stream_info = kStreamInfo;
stream_info.sample_format = SampleFormat::INTERLEAVED_INT16;
bool success = ReadDataToAudioBus(
stream_info, reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(int16_t) - sizeof(uint16_t), audio_bus.get());
EXPECT_TRUE(success);
for (size_t f = 0; f < kFrames; f++) {
EXPECT_FLOAT_EQ(audio_bus->channel(0)[f], 1.0f);
EXPECT_FLOAT_EQ(audio_bus->channel(1)[f], -1.0f);
}
}
TEST(MessageParsingUtilsTest, ValidInterleavedInt32) {
size_t data_size =
sizeof(PcmPacketHeader) / sizeof(int32_t) + kFrames * kChannels;
std::vector<int32_t> data(data_size, std::numeric_limits<int32_t>::min());
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(int32_t), kStreamInfo.stream_type,
0);
// Fill the second channel with max().
for (size_t i = sizeof(PcmPacketHeader) / sizeof(int32_t) + 1; i < data_size;
i += 2) {
data[i] = std::numeric_limits<int32_t>::max();
}
auto audio_bus = ::media::AudioBus::Create(kChannels, kFrames);
StreamInfo stream_info = kStreamInfo;
stream_info.sample_format = SampleFormat::INTERLEAVED_INT32;
bool success = ReadDataToAudioBus(
stream_info, reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(int32_t) - sizeof(uint16_t), audio_bus.get());
EXPECT_TRUE(success);
for (size_t f = 0; f < kFrames; f++) {
EXPECT_FLOAT_EQ(audio_bus->channel(0)[f], -1.0f);
EXPECT_FLOAT_EQ(audio_bus->channel(1)[f], 1.0f);
}
}
TEST(MessageParsingUtilsTest, InvalidTypeHandshake) {
std::vector<char> data(sizeof(HandshakePacket), 0);
StreamInfo stream_info = kStreamInfo;
PopulateHandshakeMessage(data.data(), data.size(), stream_info);
*(reinterpret_cast<uint8_t*>(data.data()) +
offsetof(struct HandshakePacket, stream_type)) =
static_cast<uint8_t>(StreamType::kLastType) + 1;
bool success =
ReadHandshakeMessage(data.data() + sizeof(uint16_t),
data.size() - sizeof(uint16_t), &stream_info);
EXPECT_FALSE(success);
}
TEST(MessageParsingUtilsTest, InvalidTypePcmAudio) {
size_t data_size = sizeof(PcmPacketHeader) / sizeof(float);
std::vector<float> data(data_size, 1.0f);
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(float), kStreamInfo.stream_type,
0);
*(reinterpret_cast<uint8_t*>(data.data()) +
offsetof(struct PcmPacketHeader, stream_type)) =
static_cast<uint8_t>(StreamType::kLastType) + 1;
int64_t timestamp_us;
bool success = ReadPcmAudioHeader(
reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(float) - sizeof(uint16_t), kStreamInfo, ×tamp_us);
EXPECT_FALSE(success);
}
TEST(MessageParsingUtilsTest, InvalidCodec) {
std::vector<char> data(sizeof(HandshakePacket), 0);
StreamInfo stream_info = kStreamInfo;
PopulateHandshakeMessage(data.data(), data.size(), stream_info);
*(reinterpret_cast<uint8_t*>(data.data()) +
offsetof(struct HandshakePacket, audio_codec)) =
static_cast<uint8_t>(AudioCodec::kLastCodec) + 1;
bool success =
ReadHandshakeMessage(data.data() + sizeof(uint16_t),
data.size() - sizeof(uint16_t), &stream_info);
EXPECT_FALSE(success);
}
TEST(MessageParsingUtilsTest, InvalidFormat) {
std::vector<char> data(sizeof(HandshakePacket), 0);
StreamInfo stream_info = kStreamInfo;
PopulateHandshakeMessage(data.data(), data.size(), stream_info);
*(reinterpret_cast<uint8_t*>(data.data()) +
offsetof(struct HandshakePacket, sample_format)) =
static_cast<uint8_t>(SampleFormat::LAST_FORMAT) + 1;
bool success =
ReadHandshakeMessage(data.data() + sizeof(uint16_t),
data.size() - sizeof(uint16_t), &stream_info);
EXPECT_FALSE(success);
}
TEST(MessageParsingUtilsTest, InvalidDataLength) {
size_t data_size =
sizeof(PcmPacketHeader) / sizeof(float) + kFrames * kChannels + 1;
std::vector<float> data(data_size, 1.0f);
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()),
data.size() * sizeof(float), kStreamInfo.stream_type,
0);
auto audio_bus = ::media::AudioBus::Create(kChannels, kFrames);
bool success = ReadDataToAudioBus(
kStreamInfo, reinterpret_cast<char*>(data.data()) + sizeof(uint16_t),
data_size * sizeof(float) - sizeof(uint16_t), audio_bus.get());
EXPECT_FALSE(success);
}
TEST(MessageParsingUtilsTest, NotAlignedData) {
size_t data_size =
sizeof(PcmPacketHeader) / sizeof(float) + kFrames * kChannels + 1;
std::vector<float> data(data_size, 1.0f);
PopulatePcmAudioHeader(reinterpret_cast<char*>(data.data()) + 1,
data.size() * sizeof(float) - 1,
kStreamInfo.stream_type, 0);
auto audio_bus = ::media::AudioBus::Create(kChannels, kFrames);
bool success = ReadDataToAudioBus(
kStreamInfo, reinterpret_cast<char*>(data.data()) + 1 + sizeof(uint16_t),
data_size * sizeof(float) - 1 - sizeof(uint16_t), audio_bus.get());
EXPECT_FALSE(success);
}
INSTANTIATE_TEST_SUITE_P(
MessageParsingUtilsTest,
PacketHeaderTest,
testing::Combine(testing::Values(StreamType::kMicRaw,
StreamType::kHardwareEchoRescaled),
testing::Values(AudioCodec::kPcm, AudioCodec::kOpus),
testing::Values(1, 8),
testing::Values(SampleFormat::INTERLEAVED_INT16,
SampleFormat::PLANAR_FLOAT),
testing::Values(16000, 96000),
testing::Values(0, 32761)));
} // namespace
} // namespace capture_service
} // namespace media
} // namespace chromecast