390 lines
12 KiB
C++
390 lines
12 KiB
C++
#include <gtest/gtest.h>
|
||
#include "pipeline/input/video_reader.hpp"
|
||
#include "test_base.hpp"
|
||
#include <filesystem>
|
||
#include <fstream>
|
||
#include <thread>
|
||
#include <chrono>
|
||
#include <opencv2/imgproc.hpp>
|
||
|
||
using namespace pipeline;
|
||
|
||
class VideoReaderTest : public test::TestBase {
|
||
protected:
|
||
void SetUp() override {
|
||
test::TestBase::SetUp(); // 调用基类的SetUp
|
||
// 创建测试视频文件
|
||
createTestVideo("test_video.mp4");
|
||
}
|
||
|
||
void TearDown() override {
|
||
// 清理测试文件
|
||
std::filesystem::remove("test_video.mp4");
|
||
}
|
||
|
||
// 创建测试视频文件
|
||
void createTestVideo(const std::string& filename) {
|
||
// 创建一个简单的视频文件用于测试
|
||
cv::VideoWriter writer(filename,
|
||
cv::VideoWriter::fourcc('m', 'p', '4', 'v'),
|
||
30.0, cv::Size(640, 480));
|
||
|
||
// 写入100帧测试数据
|
||
for (int i = 0; i < 100; ++i) {
|
||
cv::Mat frame(480, 640, CV_8UC3, cv::Scalar(0, 0, 0));
|
||
// 在每一帧上写入帧号,用于验证
|
||
cv::putText(frame, std::to_string(i), cv::Point(50, 50),
|
||
cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 255, 255));
|
||
writer.write(frame);
|
||
}
|
||
writer.release();
|
||
}
|
||
};
|
||
|
||
// 测试基本的打开/关闭功能
|
||
TEST_F(VideoReaderTest, OpenClose) {
|
||
VideoReader reader;
|
||
|
||
// 测试打开不存在的文件
|
||
EXPECT_FALSE(reader.open("nonexistent.mp4"));
|
||
EXPECT_FALSE(reader.isOpened());
|
||
|
||
// 测试打开有效文件
|
||
EXPECT_TRUE(reader.open("test_video.mp4"));
|
||
EXPECT_TRUE(reader.isOpened());
|
||
EXPECT_EQ(reader.getFilePath(), "test_video.mp4");
|
||
EXPECT_EQ(reader.getTotalFrames(), 100);
|
||
EXPECT_NEAR(reader.getOriginalFps(), 30.0, 0.1);
|
||
|
||
// 测试关闭
|
||
reader.close();
|
||
EXPECT_FALSE(reader.isOpened());
|
||
}
|
||
|
||
// 测试帧读取功能
|
||
TEST_F(VideoReaderTest, ReadFrames) {
|
||
VideoReader reader;
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
int frame_count = 0;
|
||
|
||
// 读取所有帧
|
||
while (reader.read(frame)) {
|
||
EXPECT_FALSE(frame.empty());
|
||
EXPECT_EQ(frame.cols, 640);
|
||
EXPECT_EQ(frame.rows, 480);
|
||
frame_count++;
|
||
}
|
||
|
||
EXPECT_EQ(frame_count, 100);
|
||
}
|
||
|
||
// 测试帧率控制
|
||
TEST_F(VideoReaderTest, FrameRateControl) {
|
||
VideoReader::Config config;
|
||
config.target_fps = 10.0f; // 设置较低的帧率以便测试
|
||
VideoReader reader(config);
|
||
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
auto start_time = std::chrono::steady_clock::now();
|
||
int frame_count = 0;
|
||
|
||
// 读取10帧并检查时间
|
||
for (int i = 0; i < 10 && reader.read(frame); ++i) {
|
||
frame_count++;
|
||
}
|
||
|
||
auto end_time = std::chrono::steady_clock::now();
|
||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
|
||
|
||
// 验证帧率控制(允许10%的误差)
|
||
float expected_duration = (frame_count / config.target_fps) * 1000;
|
||
EXPECT_NEAR(duration, expected_duration, expected_duration * 0.1);
|
||
}
|
||
|
||
// 测试循环播放功能
|
||
TEST_F(VideoReaderTest, LoopPlayback) {
|
||
VideoReader::Config config;
|
||
config.loop_playback = true;
|
||
VideoReader reader(config);
|
||
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
int frame_count = 0;
|
||
|
||
// 读取超过一个循环的帧数
|
||
for (int i = 0; i < 150 && reader.read(frame); ++i) {
|
||
frame_count++;
|
||
}
|
||
|
||
// 验证是否正确循环(应该超过总帧数)
|
||
EXPECT_GT(frame_count, 100);
|
||
}
|
||
|
||
// 测试帧定位功能
|
||
TEST_F(VideoReaderTest, FrameSeeking) {
|
||
VideoReader reader;
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 测试有效的帧定位
|
||
EXPECT_TRUE(reader.seek(50));
|
||
EXPECT_EQ(reader.getCurrentFrame(), 50);
|
||
|
||
// 测试无效的帧定位
|
||
EXPECT_FALSE(reader.seek(-1));
|
||
EXPECT_FALSE(reader.seek(100));
|
||
|
||
// 测试定位后的读取
|
||
cv::Mat frame;
|
||
ASSERT_TRUE(reader.read(frame));
|
||
EXPECT_FALSE(frame.empty());
|
||
}
|
||
|
||
// 测试重启功能
|
||
TEST_F(VideoReaderTest, Restart) {
|
||
VideoReader reader;
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 读取一些帧
|
||
cv::Mat frame;
|
||
for (int i = 0; i < 50 && reader.read(frame); ++i) {}
|
||
|
||
// 测试重启
|
||
reader.restart();
|
||
EXPECT_EQ(reader.getCurrentFrame(), 0);
|
||
|
||
// 验证可以继续读取
|
||
EXPECT_TRUE(reader.read(frame));
|
||
EXPECT_FALSE(frame.empty());
|
||
}
|
||
|
||
// 测试错误处理
|
||
TEST_F(VideoReaderTest, ErrorHandling) {
|
||
VideoReader reader;
|
||
|
||
// 测试打开损坏的视频文件
|
||
{
|
||
std::ofstream bad_file("corrupt.mp4", std::ios::binary);
|
||
bad_file << "This is not a valid MP4 file";
|
||
bad_file.close();
|
||
|
||
EXPECT_FALSE(reader.open("corrupt.mp4"));
|
||
std::filesystem::remove("corrupt.mp4");
|
||
}
|
||
|
||
// 测试在未打开状态下的操作
|
||
cv::Mat frame;
|
||
EXPECT_FALSE(reader.read(frame));
|
||
EXPECT_FALSE(reader.seek(0));
|
||
|
||
// 测试在关闭后的操作
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
reader.close();
|
||
EXPECT_FALSE(reader.read(frame));
|
||
EXPECT_FALSE(reader.seek(0));
|
||
}
|
||
|
||
// 测试性能
|
||
TEST_F(VideoReaderTest, Performance) {
|
||
VideoReader reader;
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
const int num_frames = 100;
|
||
|
||
// 测试连续读取性能
|
||
auto start = std::chrono::high_resolution_clock::now();
|
||
for (int i = 0; i < num_frames && reader.read(frame); ++i) {
|
||
EXPECT_FALSE(frame.empty());
|
||
}
|
||
auto end = std::chrono::high_resolution_clock::now();
|
||
|
||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||
float fps = static_cast<float>(num_frames) * 1000.0f / duration;
|
||
|
||
// 验证性能是否在合理范围内(至少应该达到原始帧率的50%)
|
||
EXPECT_GT(fps, reader.getOriginalFps() * 0.5f);
|
||
|
||
// 输出性能数据
|
||
std::cout << "Performance Test Results:" << std::endl;
|
||
std::cout << "Frames processed: " << num_frames << std::endl;
|
||
std::cout << "Total time: " << duration << "ms" << std::endl;
|
||
std::cout << "Average FPS: " << fps << std::endl;
|
||
}
|
||
|
||
// 测试内存使用
|
||
TEST_F(VideoReaderTest, MemoryUsage) {
|
||
VideoReader reader;
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
const int num_iterations = 10;
|
||
|
||
// 测试多次读取是否存在内存泄漏
|
||
for (int i = 0; i < num_iterations; ++i) {
|
||
reader.restart();
|
||
while (reader.read(frame)) {
|
||
// 验证帧是否正确释放
|
||
EXPECT_FALSE(frame.empty());
|
||
frame.release();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 测试异步读取功能
|
||
TEST_F(VideoReaderTest, AsyncReading) {
|
||
VideoReader::Config config;
|
||
config.async_reading = true;
|
||
config.buffer_size = 10;
|
||
VideoReader reader(config);
|
||
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 等待帧队列填充
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
// 验证帧队列状态
|
||
EXPECT_TRUE(reader.hasFrames());
|
||
EXPECT_GT(reader.getQueueSize(), 0);
|
||
EXPECT_EQ(reader.getQueueCapacity(), 10);
|
||
|
||
// 读取一些帧
|
||
cv::Mat frame;
|
||
int frame_count = 0;
|
||
while (frame_count < 20 && reader.read(frame)) {
|
||
EXPECT_FALSE(frame.empty());
|
||
frame_count++;
|
||
}
|
||
|
||
EXPECT_GT(frame_count, 0);
|
||
}
|
||
|
||
// 测试帧队列满和空的情况
|
||
TEST_F(VideoReaderTest, QueueBoundaries) {
|
||
VideoReader::Config config;
|
||
config.async_reading = true;
|
||
config.buffer_size = 5; // 设置较小的缓冲区以便测试边界条件
|
||
VideoReader reader(config);
|
||
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 等待队列填满
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
// 验证队列已填充
|
||
EXPECT_GT(reader.getQueueSize(), 0);
|
||
|
||
// 快速读取所有帧
|
||
cv::Mat frame;
|
||
std::vector<cv::Mat> frames;
|
||
while (reader.read(frame)) {
|
||
frames.push_back(frame.clone());
|
||
}
|
||
|
||
// 验证读取到了正确数量的帧
|
||
EXPECT_GT(frames.size(), 0);
|
||
}
|
||
|
||
// 测试异步读取的性能
|
||
TEST_F(VideoReaderTest, AsyncPerformance) {
|
||
// 同步读取测试
|
||
{
|
||
VideoReader::Config sync_config;
|
||
sync_config.async_reading = false;
|
||
VideoReader sync_reader(sync_config);
|
||
|
||
ASSERT_TRUE(sync_reader.open("test_video.mp4"));
|
||
|
||
cv::Mat frame;
|
||
auto start = std::chrono::high_resolution_clock::now();
|
||
int frame_count = 0;
|
||
while (frame_count < 50 && sync_reader.read(frame)) {
|
||
frame_count++;
|
||
}
|
||
auto sync_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||
std::chrono::high_resolution_clock::now() - start).count();
|
||
|
||
// 异步读取测试
|
||
VideoReader::Config async_config;
|
||
async_config.async_reading = true;
|
||
async_config.buffer_size = 30;
|
||
VideoReader async_reader(async_config);
|
||
|
||
ASSERT_TRUE(async_reader.open("test_video.mp4"));
|
||
|
||
// 等待队列填充
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
start = std::chrono::high_resolution_clock::now();
|
||
frame_count = 0;
|
||
while (frame_count < 50 && async_reader.read(frame)) {
|
||
frame_count++;
|
||
}
|
||
auto async_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||
std::chrono::high_resolution_clock::now() - start).count();
|
||
|
||
std::cout << "Performance comparison:" << std::endl;
|
||
std::cout << "Sync reading time: " << sync_duration << "ms" << std::endl;
|
||
std::cout << "Async reading time: " << async_duration << "ms" << std::endl;
|
||
|
||
// 异步读取应该更快
|
||
EXPECT_LT(async_duration, sync_duration);
|
||
}
|
||
}
|
||
|
||
// 测试异步读取时的错误恢复
|
||
TEST_F(VideoReaderTest, AsyncErrorRecovery) {
|
||
VideoReader::Config config;
|
||
config.async_reading = true;
|
||
config.buffer_size = 10;
|
||
VideoReader reader(config);
|
||
|
||
// 先正常打开文件
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 等待一些帧被读取
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
// 关闭视频
|
||
reader.close();
|
||
EXPECT_FALSE(reader.hasFrames());
|
||
EXPECT_EQ(reader.getQueueSize(), 0);
|
||
|
||
// 等待资源完全释放
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||
|
||
// 重新打开视频
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 等待一些帧被读取
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
// 验证可以继续读取
|
||
cv::Mat frame;
|
||
EXPECT_TRUE(reader.read(frame));
|
||
EXPECT_FALSE(frame.empty());
|
||
|
||
// 再次关闭以确保清理正确
|
||
reader.close();
|
||
}
|
||
|
||
// 测试在析构时正确清理资源
|
||
TEST_F(VideoReaderTest, CleanupTest) {
|
||
{
|
||
VideoReader::Config config;
|
||
config.async_reading = true;
|
||
VideoReader reader(config);
|
||
ASSERT_TRUE(reader.open("test_video.mp4"));
|
||
|
||
// 等待一些帧被读取
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||
|
||
// reader 将在这里被销毁
|
||
}
|
||
// 验证没有内存泄漏或其他资源问题
|
||
// 注意:这主要依赖于内存检查工具
|
||
}
|