|
|
@@ -0,0 +1,419 @@
|
|
|
+#include "code/codec/codec_audio_encoder.h"
|
|
|
+#include "code/base/logger.h"
|
|
|
+#include "code/base/media_common.h"
|
|
|
+#include <iostream>
|
|
|
+#include <vector>
|
|
|
+#include <memory>
|
|
|
+#include <chrono>
|
|
|
+#include <fstream>
|
|
|
+#include <algorithm>
|
|
|
+#include <cmath>
|
|
|
+
|
|
|
+using namespace av;
|
|
|
+using namespace av::codec;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 音频编码器测试类
|
|
|
+ */
|
|
|
+class AudioEncoderTester {
|
|
|
+public:
|
|
|
+ AudioEncoderTester() {
|
|
|
+ Logger::instance().setLevel(LogLevel::DEBUG);
|
|
|
+ Logger::instance().info("=== 音频编码器测试套件 ===");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试基本音频编码功能
|
|
|
+ */
|
|
|
+ bool testBasicAudioEncoding() {
|
|
|
+ Logger::instance().info("[测试] 基本音频编码功能...");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 创建音频编码器
|
|
|
+ auto encoder = AudioEncoderFactory::create("aac");
|
|
|
+ if (!encoder) {
|
|
|
+ Logger::instance().error("[失败] 无法创建AAC编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置编码参数
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.codecName = "aac";
|
|
|
+ params.bitRate = 128000;
|
|
|
+ params.sampleRate = 44100;
|
|
|
+ params.channels = 2;
|
|
|
+ params.channelLayout = AV_CHANNEL_LAYOUT_STEREO;
|
|
|
+ params.sampleFormat = AV_SAMPLE_FMT_FLTP;
|
|
|
+ params.frameSize = 1024;
|
|
|
+
|
|
|
+ // 打开编码器
|
|
|
+ ErrorCode result = encoder->open(params);
|
|
|
+ if (result != ErrorCode::OK) {
|
|
|
+ Logger::instance().errorf("[失败] 打开编码器失败: {}", static_cast<int>(result));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建测试音频帧
|
|
|
+ AVFramePtr frame = createTestAudioFrame(params);
|
|
|
+ if (!frame) {
|
|
|
+ Logger::instance().error("[失败] 创建测试音频帧失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 编码音频帧
|
|
|
+ std::vector<AVPacketPtr> packets;
|
|
|
+ result = encoder->encode(frame, packets);
|
|
|
+ if (result != ErrorCode::OK) {
|
|
|
+ Logger::instance().errorf("[失败] 编码音频帧失败: {}", static_cast<int>(result));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().infof("[成功] 编码产生了 {} 个数据包", packets.size());
|
|
|
+
|
|
|
+ // 关闭编码器
|
|
|
+ encoder->close();
|
|
|
+
|
|
|
+ Logger::instance().info("[成功] 基本音频编码测试通过");
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf("[异常] {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试多种音频编码器
|
|
|
+ */
|
|
|
+ bool testMultipleEncoders() {
|
|
|
+ Logger::instance().info("[测试] 多种音频编码器支持...");
|
|
|
+
|
|
|
+ auto supportedEncoders = AudioEncoder::getSupportedEncoders();
|
|
|
+ Logger::instance().infof("支持的编码器数量: {}", supportedEncoders.size());
|
|
|
+
|
|
|
+ if (supportedEncoders.empty()) {
|
|
|
+ Logger::instance().warning("[警告] 没有找到支持的音频编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ int successCount = 0;
|
|
|
+ // 只测试几个主要的编码器,避免实验性编码器导致的问题
|
|
|
+ std::vector<std::string> testEncoders = {"aac", "libmp3lame", "libopus", "flac"};
|
|
|
+
|
|
|
+ for (const auto& codecName : testEncoders) {
|
|
|
+ // 检查编码器是否在支持列表中
|
|
|
+ if (std::find(supportedEncoders.begin(), supportedEncoders.end(), codecName) == supportedEncoders.end()) {
|
|
|
+ Logger::instance().infof("跳过不支持的编码器: {}", codecName);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().infof("测试编码器: {}", codecName);
|
|
|
+
|
|
|
+ try {
|
|
|
+ auto encoder = AudioEncoderFactory::create(codecName);
|
|
|
+ if (!encoder) {
|
|
|
+ Logger::instance().errorf(" [失败] 无法创建编码器: {}", codecName);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.codecName = codecName;
|
|
|
+ params.bitRate = 128000;
|
|
|
+ params.sampleRate = 44100;
|
|
|
+ params.channels = 2;
|
|
|
+ params.channelLayout = AV_CHANNEL_LAYOUT_STEREO;
|
|
|
+ params.sampleFormat = AV_SAMPLE_FMT_FLTP;
|
|
|
+
|
|
|
+ ErrorCode result = encoder->open(params);
|
|
|
+ if (result == ErrorCode::OK) {
|
|
|
+ Logger::instance().infof(" [成功] {} 编码器打开成功", codecName);
|
|
|
+ successCount++;
|
|
|
+ } else {
|
|
|
+ Logger::instance().errorf(" [失败] {} 编码器打开失败: {}", codecName, static_cast<int>(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保编码器正确关闭
|
|
|
+ encoder->close();
|
|
|
+ encoder.reset(); // 释放编码器资源
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf(" [异常] 测试编码器 {} 时发生异常: {}", codecName, e.what());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().infof("成功测试的编码器: {}/{}", successCount, testEncoders.size());
|
|
|
+ return successCount > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试音频重采样功能
|
|
|
+ */
|
|
|
+ bool testAudioResampling() {
|
|
|
+ Logger::instance().info("[测试] 音频重采样功能...");
|
|
|
+
|
|
|
+ try {
|
|
|
+ AudioResampler resampler;
|
|
|
+
|
|
|
+ // 设置重采样参数:从48kHz立体声转换为44.1kHz立体声
|
|
|
+ AVChannelLayout srcLayout = AV_CHANNEL_LAYOUT_STEREO;
|
|
|
+ AVChannelLayout dstLayout = AV_CHANNEL_LAYOUT_STEREO;
|
|
|
+
|
|
|
+ bool result = resampler.init(srcLayout, AV_SAMPLE_FMT_FLTP, 48000,
|
|
|
+ dstLayout, AV_SAMPLE_FMT_FLTP, 44100);
|
|
|
+ if (!result) {
|
|
|
+ Logger::instance().error("[失败] 初始化重采样器失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建测试音频帧
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.sampleRate = 48000;
|
|
|
+ params.channels = 2;
|
|
|
+ params.sampleFormat = AV_SAMPLE_FMT_FLTP;
|
|
|
+
|
|
|
+ AVFramePtr srcFrame = createTestAudioFrame(params);
|
|
|
+ if (!srcFrame) {
|
|
|
+ Logger::instance().error("[失败] 创建源音频帧失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行重采样
|
|
|
+ AVFramePtr dstFrame = resampler.resample(srcFrame);
|
|
|
+ if (!dstFrame) {
|
|
|
+ Logger::instance().error("[失败] 音频重采样失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().infof("[成功] 重采样: {}Hz -> {}Hz", srcFrame->sample_rate, dstFrame->sample_rate);
|
|
|
+ Logger::instance().infof("[成功] 样本数: {} -> {}", srcFrame->nb_samples, dstFrame->nb_samples);
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf("[异常] {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试编码器工厂
|
|
|
+ */
|
|
|
+ bool testEncoderFactory() {
|
|
|
+ Logger::instance().info("[测试] 编码器工厂功能...");
|
|
|
+
|
|
|
+ // 测试创建最佳编码器
|
|
|
+ auto bestEncoder = AudioEncoderFactory::createBest();
|
|
|
+ if (!bestEncoder) {
|
|
|
+ Logger::instance().error("[失败] 无法创建最佳编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Logger::instance().info("[成功] 创建最佳编码器");
|
|
|
+
|
|
|
+ // 测试创建无损编码器
|
|
|
+ auto losslessEncoder = AudioEncoderFactory::createLossless();
|
|
|
+ if (!losslessEncoder) {
|
|
|
+ Logger::instance().error("[失败] 无法创建无损编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Logger::instance().info("[成功] 创建无损编码器");
|
|
|
+
|
|
|
+ // 测试推荐编码器
|
|
|
+ std::string recommended = AudioEncoder::getRecommendedEncoder();
|
|
|
+ if (recommended.empty()) {
|
|
|
+ Logger::instance().error("[失败] 无法获取推荐编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Logger::instance().infof("[成功] 推荐编码器: {}", recommended);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试编码器状态管理
|
|
|
+ */
|
|
|
+ bool testEncoderStateManagement() {
|
|
|
+ Logger::instance().info("[测试] 编码器状态管理...");
|
|
|
+
|
|
|
+ try {
|
|
|
+ auto encoder = AudioEncoderFactory::create("aac");
|
|
|
+ if (!encoder) {
|
|
|
+ Logger::instance().error("[失败] 无法创建编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测试初始状态
|
|
|
+ if (encoder->getState() != CodecState::IDLE) {
|
|
|
+ Logger::instance().error("[失败] 初始状态不正确");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测试打开编码器
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.codecName = "aac";
|
|
|
+ params.bitRate = 128000;
|
|
|
+ params.sampleRate = 44100;
|
|
|
+ params.channels = 2;
|
|
|
+
|
|
|
+ ErrorCode result = encoder->open(params);
|
|
|
+ if (result != ErrorCode::OK) {
|
|
|
+ Logger::instance().error("[失败] 打开编码器失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (encoder->getState() != CodecState::OPENED) {
|
|
|
+ Logger::instance().error("[失败] 打开后状态不正确");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测试关闭编码器
|
|
|
+ encoder->close();
|
|
|
+ if (encoder->getState() != CodecState::CLOSED) {
|
|
|
+ Logger::instance().error("[失败] 关闭后状态不正确");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().info("[成功] 编码器状态管理测试通过");
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf("[异常] {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 测试错误处理
|
|
|
+ */
|
|
|
+ bool testErrorHandling() {
|
|
|
+ Logger::instance().info("[测试] 错误处理...");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 测试无效编码器名称
|
|
|
+ auto encoder = AudioEncoderFactory::create("invalid_codec");
|
|
|
+ if (encoder) {
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.codecName = "invalid_codec";
|
|
|
+ ErrorCode result = encoder->open(params);
|
|
|
+ if (result == ErrorCode::OK) {
|
|
|
+ Logger::instance().error("[失败] 应该拒绝无效编码器");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测试无效参数
|
|
|
+ encoder = AudioEncoderFactory::create("aac");
|
|
|
+ if (encoder) {
|
|
|
+ AudioEncoderParams params;
|
|
|
+ params.codecName = "aac";
|
|
|
+ params.sampleRate = -1; // 无效采样率
|
|
|
+ params.channels = 0; // 无效声道数
|
|
|
+
|
|
|
+ ErrorCode result = encoder->open(params);
|
|
|
+ if (result == ErrorCode::OK) {
|
|
|
+ Logger::instance().error("[失败] 应该拒绝无效参数");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger::instance().info("[成功] 错误处理测试通过");
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf("[异常] {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 运行所有测试
|
|
|
+ */
|
|
|
+ bool runAllTests() {
|
|
|
+ Logger::instance().info("开始运行音频编码器测试套件...");
|
|
|
+
|
|
|
+ bool allPassed = true;
|
|
|
+
|
|
|
+ allPassed &= testBasicAudioEncoding();
|
|
|
+ allPassed &= testMultipleEncoders();
|
|
|
+ allPassed &= testAudioResampling();
|
|
|
+ allPassed &= testEncoderFactory();
|
|
|
+ allPassed &= testEncoderStateManagement();
|
|
|
+ allPassed &= testErrorHandling();
|
|
|
+
|
|
|
+ Logger::instance().info("=== 测试结果 ===");
|
|
|
+ if (allPassed) {
|
|
|
+ Logger::instance().info("[成功] 所有音频编码器测试通过!");
|
|
|
+ } else {
|
|
|
+ Logger::instance().error("[失败] 部分测试失败!");
|
|
|
+ }
|
|
|
+
|
|
|
+ return allPassed;
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ /**
|
|
|
+ * 创建测试音频帧
|
|
|
+ */
|
|
|
+ AVFramePtr createTestAudioFrame(const AudioEncoderParams& params) {
|
|
|
+ AVFramePtr frame = makeAVFrame();
|
|
|
+ if (!frame) {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ frame->format = params.sampleFormat;
|
|
|
+ frame->sample_rate = params.sampleRate;
|
|
|
+ frame->nb_samples = params.frameSize > 0 ? params.frameSize : 1024;
|
|
|
+
|
|
|
+ // 设置声道布局
|
|
|
+ if (params.channelLayout.nb_channels > 0) {
|
|
|
+ av_channel_layout_copy(&frame->ch_layout, ¶ms.channelLayout);
|
|
|
+ } else {
|
|
|
+ av_channel_layout_default(&frame->ch_layout, params.channels);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分配音频缓冲区
|
|
|
+ int ret = av_frame_get_buffer(frame.get(), 0);
|
|
|
+ if (ret < 0) {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充测试数据(简单的正弦波)
|
|
|
+ fillTestAudioData(frame.get());
|
|
|
+
|
|
|
+ return frame;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 填充测试音频数据
|
|
|
+ */
|
|
|
+ void fillTestAudioData(AVFrame* frame) {
|
|
|
+ if (frame->format == AV_SAMPLE_FMT_FLTP) {
|
|
|
+ // 浮点平面格式
|
|
|
+ for (int ch = 0; ch < frame->ch_layout.nb_channels; ch++) {
|
|
|
+ float* data = reinterpret_cast<float*>(frame->data[ch]);
|
|
|
+ for (int i = 0; i < frame->nb_samples; i++) {
|
|
|
+ // 生成440Hz正弦波
|
|
|
+ double t = static_cast<double>(i) / frame->sample_rate;
|
|
|
+ data[i] = static_cast<float>(0.5 * sin(2.0 * M_PI * 440.0 * t));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (frame->format == AV_SAMPLE_FMT_S16) {
|
|
|
+ // 16位整数格式
|
|
|
+ int16_t* data = reinterpret_cast<int16_t*>(frame->data[0]);
|
|
|
+ for (int i = 0; i < frame->nb_samples * frame->ch_layout.nb_channels; i++) {
|
|
|
+ double t = static_cast<double>(i / frame->ch_layout.nb_channels) / frame->sample_rate;
|
|
|
+ data[i] = static_cast<int16_t>(16384 * sin(2.0 * M_PI * 440.0 * t));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+int main() {
|
|
|
+ try {
|
|
|
+ AudioEncoderTester tester;
|
|
|
+ bool success = tester.runAllTests();
|
|
|
+ return success ? 0 : 1;
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ Logger::instance().errorf("测试程序异常: {}", e.what());
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+}
|