81 lines
3.0 KiB
C++
81 lines
3.0 KiB
C++
#include <gtest/gtest.h>
|
|
#include <web_server/src/H264_Encoder.hpp>
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
namespace aethera::web {
|
|
namespace {
|
|
std::vector<std::byte> test_pattern(std::uint32_t width,
|
|
std::uint32_t height) {
|
|
std::vector<std::byte> result(
|
|
static_cast<std::size_t>(width) * height * 4U);
|
|
for (std::uint32_t y = 0; y < height; ++y) {
|
|
for (std::uint32_t x = 0; x < width; ++x) {
|
|
const auto offset =
|
|
(static_cast<std::size_t>(y) * width + x) * 4U;
|
|
result[offset] =
|
|
std::byte{static_cast<std::uint8_t>(x)};
|
|
result[offset + 1U] =
|
|
std::byte{static_cast<std::uint8_t>(y)};
|
|
result[offset + 2U] =
|
|
std::byte{static_cast<std::uint8_t>(x ^ y)};
|
|
result[offset + 3U] = std::byte{255};
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
TEST(H264_Encoder, Rejects_Invalid_Input_Before_FFmpeg) {
|
|
H264_Encoder encoder(30.0);
|
|
const auto pixels = test_pattern(320, 180);
|
|
EXPECT_THROW(static_cast<void>(encoder.encode(
|
|
pixels, 319, 180, Video_Pixel_Layout::rgba,
|
|
1, std::chrono::microseconds{33'333})),
|
|
std::invalid_argument);
|
|
EXPECT_THROW(static_cast<void>(encoder.encode(
|
|
std::span<const std::byte>{pixels}.first(
|
|
pixels.size() - 1U),
|
|
320, 180, Video_Pixel_Layout::rgba,
|
|
1, std::chrono::microseconds{33'333})),
|
|
std::invalid_argument);
|
|
}
|
|
|
|
TEST(H264_Encoder, Libx264_Produces_Ordered_Annex_B_Access_Units) {
|
|
/* 四个 720x420 Plot 组成的一行真实 Gallery 图集。 */
|
|
constexpr std::uint32_t width{2'880};
|
|
constexpr std::uint32_t height{420};
|
|
H264_Encoder encoder(30.0);
|
|
auto pixels = test_pattern(width, height);
|
|
std::optional<Encoded_Video_Frame> first;
|
|
std::uint64_t produced{};
|
|
for (std::uint64_t sequence = 1; sequence <= 4; ++sequence) {
|
|
pixels[0] = std::byte{static_cast<std::uint8_t>(sequence)};
|
|
auto encoded = encoder.encode(
|
|
pixels, width, height, Video_Pixel_Layout::rgba, sequence,
|
|
std::chrono::microseconds{
|
|
static_cast<std::int64_t>(sequence * 33'333)});
|
|
if (!encoded) continue;
|
|
if (!first) first = encoded;
|
|
EXPECT_EQ(encoded->sequence, sequence);
|
|
EXPECT_EQ(encoded->backend, Video_Encoder_Backend::libx264);
|
|
EXPECT_FALSE(encoded->annex_b.empty());
|
|
++produced;
|
|
}
|
|
ASSERT_TRUE(first);
|
|
EXPECT_TRUE(first->key_frame);
|
|
ASSERT_GE(first->annex_b.size(), 4U);
|
|
EXPECT_EQ(first->annex_b[0], std::byte{0});
|
|
EXPECT_EQ(first->annex_b[1], std::byte{0});
|
|
EXPECT_TRUE(first->annex_b[2] == std::byte{1} ||
|
|
(first->annex_b[2] == std::byte{0} &&
|
|
first->annex_b[3] == std::byte{1}));
|
|
EXPECT_EQ(produced, 4U);
|
|
EXPECT_EQ(video_encoder_backend_name(first->backend), "libx264");
|
|
EXPECT_EQ(h264_profile_level_id(), "640033");
|
|
}
|
|
}
|