Files
video-v1/vav2/platforms/android/tests/unit-tests/src/MediaCodecAV1DecoderTest.cpp
2025-10-13 22:55:54 +09:00

415 lines
13 KiB
C++

#include <gtest/gtest.h>
#include <android/log.h>
#include "Decoder/MediaCodecAV1Decoder.h"
#include "Decoder/VideoDecoderFactory.h"
#include "Common/VideoTypes.h"
#define LOG_TAG "MediaCodecAV1DecoderTest"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
using namespace VavCore;
class MediaCodecAV1DecoderTest : public ::testing::Test {
protected:
void SetUp() override {
LOGI("Setting up MediaCodecAV1DecoderTest");
decoder = std::make_unique<MediaCodecAV1Decoder>();
}
void TearDown() override {
LOGI("Tearing down MediaCodecAV1DecoderTest");
// NOTE: MediaCodecAV1Decoder destructor will call Cleanup() automatically
// Calling Cleanup() here would cause double-cleanup and potential crashes
if (decoder) {
decoder.reset();
}
}
std::unique_ptr<MediaCodecAV1Decoder> decoder;
};
// Test 1: Basic initialization and cleanup
TEST_F(MediaCodecAV1DecoderTest, InitializationAndCleanup) {
LOGI("Test: InitializationAndCleanup");
ASSERT_NE(decoder, nullptr) << "Decoder should be created";
// Cleanup should not crash
decoder->Cleanup();
SUCCEED() << "Decoder initialization and cleanup successful";
}
// Test 2: Get available codecs
TEST_F(MediaCodecAV1DecoderTest, GetAvailableCodecs) {
LOGI("Test: GetAvailableCodecs");
std::vector<std::string> codecs = decoder->GetAvailableCodecs();
LOGI("Found %zu MediaCodec AV1 codecs", codecs.size());
for (const auto& codec : codecs) {
LOGI(" - %s", codec.c_str());
}
// On most Android devices, there should be at least one AV1 codec
// But we allow 0 for older devices
EXPECT_GE(codecs.size(), 0) << "Should have 0 or more codecs available";
if (codecs.size() > 0) {
SUCCEED() << "Found " << codecs.size() << " AV1 codec(s)";
} else {
GTEST_SKIP() << "No AV1 codecs available on this device (API < 29 or no hardware support)";
}
}
// Test 3: Initialize with valid metadata
TEST_F(MediaCodecAV1DecoderTest, InitializeWithValidMetadata) {
LOGI("Test: InitializeWithValidMetadata");
// Check if any codecs are available
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for initialization test";
}
VideoMetadata metadata;
metadata.width = 1920;
metadata.height = 1080;
metadata.frame_rate = 30.0;
metadata.codec_name = "av01";
bool success = decoder->Initialize(metadata);
if (success) {
SUCCEED() << "Decoder initialized successfully with 1920x1080@30fps";
} else {
// Initialization might fail on emulators or devices without proper support
LOGI("Decoder initialization failed (may be expected on emulator)");
}
}
// Test 4: Initialize with invalid dimensions (should fail gracefully)
TEST_F(MediaCodecAV1DecoderTest, InitializeWithInvalidDimensions) {
LOGI("Test: InitializeWithInvalidDimensions");
VideoMetadata metadata;
metadata.width = 0; // Invalid width
metadata.height = 0; // Invalid height
metadata.frame_rate = 30.0;
metadata.codec_name = "av01";
bool success = decoder->Initialize(metadata);
EXPECT_FALSE(success) << "Initialization should fail with invalid dimensions";
if (!success) {
SUCCEED() << "Correctly rejected invalid dimensions";
}
}
// Test 5: Decode frame without initialization (should fail)
TEST_F(MediaCodecAV1DecoderTest, DecodeFrameWithoutInitialization) {
LOGI("Test: DecodeFrameWithoutInitialization");
// Try to decode without initializing
std::vector<uint8_t> dummyData(100, 0);
VideoFrame frame;
bool success = decoder->DecodeFrame(dummyData.data(), dummyData.size(), frame);
EXPECT_FALSE(success) << "DecodeFrame should fail without initialization";
if (!success) {
SUCCEED() << "Correctly rejected decode attempt without initialization";
}
}
// Test 6: Test reset functionality
TEST_F(MediaCodecAV1DecoderTest, ResetFunctionality) {
LOGI("Test: ResetFunctionality");
// Initialize decoder
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for reset test";
}
VideoMetadata metadata;
metadata.width = 1280;
metadata.height = 720;
metadata.frame_rate = 30.0;
metadata.codec_name = "av01";
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test reset without successful initialization";
}
// Reset should not crash
decoder->Reset();
SUCCEED() << "Decoder reset successful";
}
// Test 7: Test flush functionality
TEST_F(MediaCodecAV1DecoderTest, FlushFunctionality) {
LOGI("Test: FlushFunctionality");
// Initialize decoder
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for flush test";
}
VideoMetadata metadata;
metadata.width = 1280;
metadata.height = 720;
metadata.frame_rate = 30.0;
metadata.codec_name = "av01";
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test flush without successful initialization";
}
// Flush should not crash
decoder->Flush();
SUCCEED() << "Decoder flush successful";
}
// Test 8: Test decoder statistics (skipped - GetStatistics not implemented)
TEST_F(MediaCodecAV1DecoderTest, DecoderStatistics) {
LOGI("Test: DecoderStatistics");
// Note: GetStatistics() method not yet implemented in MediaCodecAV1Decoder
GTEST_SKIP() << "GetStatistics() method not yet implemented";
}
// Test 9: Get decoder name (skipped - GetName not in interface)
TEST_F(MediaCodecAV1DecoderTest, GetDecoderName) {
LOGI("Test: GetDecoderName");
// Note: GetName() is not part of IVideoDecoder interface
GTEST_SKIP() << "GetName() method not in IVideoDecoder interface";
}
// Test 10: Multiple initialize/cleanup cycles
TEST_F(MediaCodecAV1DecoderTest, MultipleInitializeCleanupCycles) {
LOGI("Test: MultipleInitializeCleanupCycles");
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for cycle test";
}
VideoMetadata metadata;
metadata.width = 1280;
metadata.height = 720;
metadata.frame_rate = 30.0;
metadata.codec_name = "av01";
// Perform 3 cycles
for (int i = 0; i < 3; i++) {
LOGI("Cycle %d: Initializing...", i + 1);
bool initSuccess = decoder->Initialize(metadata);
if (initSuccess) {
LOGI("Cycle %d: Cleaning up...", i + 1);
decoder->Cleanup();
} else {
LOGI("Cycle %d: Initialization failed (may be expected)", i + 1);
}
}
SUCCEED() << "Multiple initialize/cleanup cycles completed";
}
// Test 11: Supports codec type (skipped - SupportsCodec not in interface)
TEST_F(MediaCodecAV1DecoderTest, SupportsCodecType) {
LOGI("Test: SupportsCodecType");
// Note: SupportsCodec() is not part of MediaCodecAV1Decoder interface
GTEST_SKIP() << "SupportsCodec() method not in MediaCodecAV1Decoder interface";
}
// Test 12: Hardware acceleration detection
TEST_F(MediaCodecAV1DecoderTest, HardwareAccelerationDetection) {
LOGI("Test: HardwareAccelerationDetection");
bool isHardwareAccelerated = decoder->IsHardwareAccelerated();
LOGI("Hardware acceleration: %s", isHardwareAccelerated ? "YES" : "NO");
// This is informational, not an assertion
if (isHardwareAccelerated) {
SUCCEED() << "Decoder reports hardware acceleration available";
} else {
SUCCEED() << "Decoder reports software decoding (may be emulator)";
}
}
// Test 13: Async mode support and initialization
TEST_F(MediaCodecAV1DecoderTest, AsyncModeSupport) {
LOGI("Test: AsyncModeSupport");
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for async mode test";
}
// Initialize decoder first
VideoMetadata metadata;
metadata.width = 1920;
metadata.height = 1080;
metadata.frame_rate = 30.0;
metadata.codec_type = VideoCodecType::AV1;
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test async mode without successful initialization";
}
// Check async mode support
bool supportsAsync = decoder->SupportsAsyncMode();
LOGI("Async mode supported: %s", supportsAsync ? "YES" : "NO");
// On API 29+, async mode should be supported
EXPECT_TRUE(supportsAsync) << "Async mode should be supported on API 29+";
SUCCEED() << "Async mode support verified";
}
// Test 14: Async mode enable/disable cycle
TEST_F(MediaCodecAV1DecoderTest, AsyncModeEnableDisableCycle) {
LOGI("Test: AsyncModeEnableDisableCycle");
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for async mode cycle test";
}
// Initialize decoder
VideoMetadata metadata;
metadata.width = 1920;
metadata.height = 1080;
metadata.frame_rate = 30.0;
metadata.codec_type = VideoCodecType::AV1;
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test async mode cycle without successful initialization";
}
if (!decoder->SupportsAsyncMode()) {
GTEST_SKIP() << "Device doesn't support async mode";
}
// Async mode should be enabled during initialization
// Try to disable and re-enable
bool disableSuccess = decoder->EnableAsyncMode(false);
LOGI("Async mode disable: %s", disableSuccess ? "SUCCESS" : "FAILED");
bool enableSuccess = decoder->EnableAsyncMode(true);
LOGI("Async mode enable: %s", enableSuccess ? "SUCCESS" : "FAILED");
EXPECT_TRUE(enableSuccess) << "Should be able to re-enable async mode";
SUCCEED() << "Async mode enable/disable cycle completed";
}
// Test 15: SetVulkanDevice and MediaCodec reconfiguration
TEST_F(MediaCodecAV1DecoderTest, VulkanDeviceReconfiguration) {
LOGI("Test: VulkanDeviceReconfiguration");
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available for Vulkan reconfiguration test";
}
// Initialize decoder
VideoMetadata metadata;
metadata.width = 1920;
metadata.height = 1080;
metadata.frame_rate = 30.0;
metadata.codec_type = VideoCodecType::AV1;
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test Vulkan reconfiguration without successful initialization";
}
// Create dummy Vulkan handles (null pointers for testing)
// In real scenario, these would be actual Vulkan objects
void* vk_device = reinterpret_cast<void*>(0x1234);
void* vk_instance = reinterpret_cast<void*>(0x5678);
void* vk_physical_device = reinterpret_cast<void*>(0x9ABC);
// SetVulkanDevice should trigger MediaCodec reconfiguration
// This will internally call:
// 1. CleanupAsyncMode()
// 2. AMediaCodec_stop()
// 3. AMediaCodec_configure() with ImageReader surface
// 4. InitializeAsyncMode() + EnableAsyncMode()
// 5. AMediaCodec_start()
bool vulkanSuccess = decoder->SetVulkanDevice(vk_device, vk_instance, vk_physical_device);
LOGI("SetVulkanDevice result: %s", vulkanSuccess ? "SUCCESS" : "FAILED");
// Note: This may fail on emulators or devices without proper Vulkan support
// The important thing is that it doesn't crash
if (vulkanSuccess) {
SUCCEED() << "Vulkan device set successfully - MediaCodec reconfigured";
} else {
LOGI("Vulkan device setup failed (expected on emulator)");
SUCCEED() << "Vulkan reconfiguration handled gracefully";
}
}
// Test 16: Async callbacks persistence after MediaCodec reconfiguration
TEST_F(MediaCodecAV1DecoderTest, AsyncCallbacksPersistenceAfterReconfiguration) {
LOGI("Test: AsyncCallbacksPersistenceAfterReconfiguration");
auto codecs = decoder->GetAvailableCodecs();
if (codecs.empty()) {
GTEST_SKIP() << "No AV1 codecs available";
}
// Initialize decoder
VideoMetadata metadata;
metadata.width = 1920;
metadata.height = 1080;
metadata.frame_rate = 30.0;
metadata.codec_type = VideoCodecType::AV1;
bool initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test without successful initialization";
}
if (!decoder->SupportsAsyncMode()) {
GTEST_SKIP() << "Device doesn't support async mode";
}
// Verify async mode is active
bool asyncActive1 = decoder->SupportsAsyncMode();
LOGI("Async mode active before reconfiguration: %s", asyncActive1 ? "YES" : "NO");
// Simulate reconfiguration by calling Reset which internally may reconfigure
decoder->Reset();
// Re-initialize
initSuccess = decoder->Initialize(metadata);
if (!initSuccess) {
GTEST_SKIP() << "Cannot test without re-initialization";
}
// Verify async mode is still active after reconfiguration
bool asyncActive2 = decoder->SupportsAsyncMode();
LOGI("Async mode active after reconfiguration: %s", asyncActive2 ? "YES" : "NO");
EXPECT_TRUE(asyncActive2) << "Async mode should persist after reconfiguration";
SUCCEED() << "Async callbacks persistence verified";
}