2025-09-29 02:42:26 +09:00
|
|
|
#include "TestFramework.h"
|
2025-09-30 19:54:29 +09:00
|
|
|
#include "Decoder/MediaCodecAV1Decoder.h"
|
2025-09-29 02:42:26 +09:00
|
|
|
#include "Common/VideoTypes.h"
|
|
|
|
|
#include <android/hardware_buffer.h>
|
|
|
|
|
#include <sys/system_properties.h>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#ifndef PROP_VALUE_MAX
|
|
|
|
|
#define PROP_VALUE_MAX 92
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace VavCoreTest {
|
|
|
|
|
|
|
|
|
|
bool TestVulkanAvailability(std::string& error_msg) {
|
|
|
|
|
LOGI("Testing Vulkan availability...");
|
|
|
|
|
|
|
|
|
|
// Test Android API level requirement
|
|
|
|
|
char sdk_version[PROP_VALUE_MAX] = {};
|
|
|
|
|
int api_level = 29; // Default
|
|
|
|
|
|
|
|
|
|
if (__system_property_get("ro.build.version.sdk", sdk_version) > 0) {
|
|
|
|
|
api_level = std::atoi(sdk_version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGI("Android API Level: %d", api_level);
|
|
|
|
|
|
|
|
|
|
if (api_level < 29) {
|
|
|
|
|
LOGW("Android API %d < 29, Vulkan AHardwareBuffer integration not fully supported", api_level);
|
|
|
|
|
LOGI("⚠️ Vulkan availability test skipped (API level too low)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test AHardwareBuffer availability (API 26+)
|
|
|
|
|
if (api_level < 26) {
|
|
|
|
|
LOGW("Android API %d < 26, AHardwareBuffer not available", api_level);
|
|
|
|
|
error_msg = "AHardwareBuffer requires API 26+";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGI("✅ Vulkan availability requirements met");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TestAHardwareBufferCreation(std::string& error_msg) {
|
|
|
|
|
LOGI("Testing AHardwareBuffer creation...");
|
|
|
|
|
|
|
|
|
|
// Check Android API level
|
|
|
|
|
char sdk_version[PROP_VALUE_MAX] = {};
|
|
|
|
|
int api_level = 29;
|
|
|
|
|
|
|
|
|
|
if (__system_property_get("ro.build.version.sdk", sdk_version) > 0) {
|
|
|
|
|
api_level = std::atoi(sdk_version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (api_level < 26) {
|
|
|
|
|
LOGW("AHardwareBuffer requires API 26+, current API: %d", api_level);
|
|
|
|
|
LOGI("⚠️ AHardwareBuffer creation test skipped (API level too low)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test AHardwareBuffer allocation
|
|
|
|
|
AHardwareBuffer_Desc desc = {};
|
|
|
|
|
desc.width = 1920;
|
|
|
|
|
desc.height = 1080;
|
|
|
|
|
desc.layers = 1;
|
|
|
|
|
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; // RGBA format (YV12 not available in NDK)
|
|
|
|
|
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
|
|
|
|
|
AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
|
|
|
|
|
|
|
|
|
|
AHardwareBuffer* buffer = nullptr;
|
|
|
|
|
int result = AHardwareBuffer_allocate(&desc, &buffer);
|
|
|
|
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
LOGW("AHardwareBuffer allocation failed with code: %d", result);
|
|
|
|
|
LOGI("⚠️ AHardwareBuffer creation test skipped (allocation failed)");
|
|
|
|
|
return true; // Not a hard failure - device might not support this format
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_ASSERT_NOT_NULL(buffer, "AHardwareBuffer allocation returned null");
|
|
|
|
|
|
|
|
|
|
LOGI("AHardwareBuffer created successfully: %p", buffer);
|
|
|
|
|
|
|
|
|
|
// Test buffer description retrieval
|
|
|
|
|
AHardwareBuffer_Desc retrieved_desc;
|
|
|
|
|
AHardwareBuffer_describe(buffer, &retrieved_desc);
|
|
|
|
|
|
|
|
|
|
TEST_ASSERT_EQ(desc.width, retrieved_desc.width, "Buffer width mismatch");
|
|
|
|
|
TEST_ASSERT_EQ(desc.height, retrieved_desc.height, "Buffer height mismatch");
|
|
|
|
|
TEST_ASSERT_EQ(desc.format, retrieved_desc.format, "Buffer format mismatch");
|
|
|
|
|
|
|
|
|
|
LOGI("Buffer description verified: %ux%u, format: 0x%x",
|
|
|
|
|
retrieved_desc.width, retrieved_desc.height, retrieved_desc.format);
|
|
|
|
|
|
|
|
|
|
// Clean up
|
|
|
|
|
AHardwareBuffer_release(buffer);
|
|
|
|
|
|
|
|
|
|
LOGI("✅ AHardwareBuffer creation test passed");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TestAndroidMediaCodecVulkanSetup(std::string& error_msg) {
|
2025-09-30 19:54:29 +09:00
|
|
|
LOGI("Testing MediaCodecAV1Decoder Vulkan setup...");
|
2025-09-29 02:42:26 +09:00
|
|
|
|
|
|
|
|
// Create decoder instance
|
2025-09-30 19:54:29 +09:00
|
|
|
auto decoder = std::make_unique<VavCore::MediaCodecAV1Decoder>();
|
|
|
|
|
TEST_ASSERT_NOT_NULL(decoder.get(), "Failed to create MediaCodecAV1Decoder");
|
2025-09-29 02:42:26 +09:00
|
|
|
|
|
|
|
|
// Test video metadata
|
|
|
|
|
VavCore::VideoMetadata metadata;
|
|
|
|
|
metadata.codec_type = VavCore::VideoCodecType::AV1;
|
|
|
|
|
metadata.width = 1920;
|
|
|
|
|
metadata.height = 1080;
|
|
|
|
|
metadata.frame_rate = 30.0;
|
|
|
|
|
|
|
|
|
|
// Initialize decoder
|
|
|
|
|
bool init_result = decoder->Initialize(metadata);
|
|
|
|
|
if (!init_result) {
|
|
|
|
|
LOGW("Decoder initialization failed - likely no hardware AV1 support");
|
|
|
|
|
LOGI("⚠️ AndroidMediaCodec Vulkan setup test skipped (no AV1 hardware support)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGI("Decoder initialized successfully");
|
|
|
|
|
|
|
|
|
|
// Test hardware acceleration check
|
|
|
|
|
bool is_hw_accelerated = decoder->IsHardwareAccelerated();
|
|
|
|
|
LOGI("Hardware acceleration: %s", is_hw_accelerated ? "YES" : "NO");
|
|
|
|
|
|
|
|
|
|
if (!is_hw_accelerated) {
|
|
|
|
|
LOGW("No hardware acceleration available - Vulkan image output not supported");
|
|
|
|
|
LOGI("⚠️ AndroidMediaCodec Vulkan setup test skipped (no hardware acceleration)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test Vulkan device setup (mock objects for testing)
|
|
|
|
|
void* mock_vk_device = reinterpret_cast<void*>(0x12345678); // Mock Vulkan device
|
|
|
|
|
void* mock_vk_instance = reinterpret_cast<void*>(0x87654321); // Mock Vulkan instance
|
|
|
|
|
|
|
|
|
|
bool vulkan_result = decoder->SetVulkanDevice(mock_vk_device, mock_vk_instance);
|
|
|
|
|
TEST_ASSERT(vulkan_result, "Failed to set Vulkan device");
|
|
|
|
|
|
|
|
|
|
LOGI("Vulkan device set successfully");
|
|
|
|
|
|
|
|
|
|
// Test Vulkan image creation
|
|
|
|
|
bool image_result = decoder->CreateVulkanImage(mock_vk_device, mock_vk_instance);
|
|
|
|
|
if (!image_result) {
|
|
|
|
|
LOGW("Vulkan image creation failed - this might be expected on some devices");
|
|
|
|
|
LOGI("⚠️ Vulkan image creation test skipped (creation failed)");
|
|
|
|
|
return true; // Not a hard failure
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGI("Vulkan image created successfully");
|
|
|
|
|
|
|
|
|
|
// Test optimal surface type for Vulkan
|
|
|
|
|
VavCoreSurfaceType optimal_type = decoder->GetOptimalSurfaceType();
|
|
|
|
|
LOGI("Optimal surface type: %d", static_cast<int>(optimal_type));
|
|
|
|
|
|
|
|
|
|
// Verify Vulkan image is supported
|
|
|
|
|
bool supports_vulkan = decoder->SupportsSurfaceType(VAVCORE_SURFACE_VULKAN_IMAGE);
|
|
|
|
|
LOGI("Vulkan image surface supported: %s", supports_vulkan ? "YES" : "NO");
|
|
|
|
|
|
|
|
|
|
// Test Godot integration info for Vulkan
|
|
|
|
|
std::string godot_info = decoder->GetGodotIntegrationInfo();
|
|
|
|
|
LOGI("Godot integration info: %s", godot_info.c_str());
|
|
|
|
|
|
|
|
|
|
bool is_optimal_for_godot = decoder->IsOptimalForGodot();
|
|
|
|
|
LOGI("Optimal for Godot: %s", is_optimal_for_godot ? "YES" : "NO");
|
|
|
|
|
|
|
|
|
|
// Clean up
|
|
|
|
|
decoder->Cleanup();
|
|
|
|
|
|
|
|
|
|
LOGI("✅ AndroidMediaCodec Vulkan setup test passed");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TestVulkanDecodeToSurface(std::string& error_msg) {
|
|
|
|
|
LOGI("Testing Vulkan decode to surface...");
|
|
|
|
|
|
|
|
|
|
// Create decoder instance
|
2025-09-30 19:54:29 +09:00
|
|
|
auto decoder = std::make_unique<VavCore::MediaCodecAV1Decoder>();
|
|
|
|
|
TEST_ASSERT_NOT_NULL(decoder.get(), "Failed to create MediaCodecAV1Decoder");
|
2025-09-29 02:42:26 +09:00
|
|
|
|
|
|
|
|
// Test video metadata
|
|
|
|
|
VavCore::VideoMetadata metadata;
|
|
|
|
|
metadata.codec_type = VavCore::VideoCodecType::AV1;
|
|
|
|
|
metadata.width = 1280;
|
|
|
|
|
metadata.height = 720;
|
|
|
|
|
metadata.frame_rate = 30.0;
|
|
|
|
|
|
|
|
|
|
// Initialize decoder
|
|
|
|
|
bool init_result = decoder->Initialize(metadata);
|
|
|
|
|
if (!init_result) {
|
|
|
|
|
LOGW("Decoder initialization failed - skipping Vulkan decode to surface test");
|
|
|
|
|
LOGI("⚠️ Vulkan decode to surface test skipped (no AV1 hardware support)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check hardware acceleration
|
|
|
|
|
if (!decoder->IsHardwareAccelerated()) {
|
|
|
|
|
LOGW("No hardware acceleration - skipping Vulkan decode to surface test");
|
|
|
|
|
LOGI("⚠️ Vulkan decode to surface test skipped (no hardware acceleration)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set Vulkan device
|
|
|
|
|
void* mock_vk_device = reinterpret_cast<void*>(0x12345678);
|
|
|
|
|
void* mock_vk_instance = reinterpret_cast<void*>(0x87654321);
|
|
|
|
|
|
|
|
|
|
bool vulkan_result = decoder->SetVulkanDevice(mock_vk_device, mock_vk_instance);
|
|
|
|
|
TEST_ASSERT(vulkan_result, "Failed to set Vulkan device");
|
|
|
|
|
|
|
|
|
|
// Create Vulkan image
|
|
|
|
|
bool image_result = decoder->CreateVulkanImage(mock_vk_device, mock_vk_instance);
|
|
|
|
|
if (!image_result) {
|
|
|
|
|
LOGW("Vulkan image creation failed - skipping decode to surface test");
|
|
|
|
|
LOGI("⚠️ Vulkan decode to surface test skipped (image creation failed)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test DecodeToSurface with dummy data
|
|
|
|
|
uint8_t dummy_packet[] = { 0x12, 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x01 }; // Minimal AV1 OBU
|
|
|
|
|
size_t packet_size = sizeof(dummy_packet);
|
|
|
|
|
|
|
|
|
|
VavCore::VideoFrame output_frame;
|
|
|
|
|
bool decode_result = decoder->DecodeToSurface(
|
|
|
|
|
dummy_packet, packet_size,
|
|
|
|
|
VAVCORE_SURFACE_VULKAN_IMAGE,
|
|
|
|
|
mock_vk_device, // Mock VkImage
|
|
|
|
|
output_frame
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Note: This will likely fail with real dummy data, but we're testing the API flow
|
|
|
|
|
if (!decode_result) {
|
|
|
|
|
LOGW("DecodeToSurface failed with dummy data - this is expected");
|
|
|
|
|
} else {
|
|
|
|
|
LOGI("DecodeToSurface succeeded with dummy data");
|
|
|
|
|
|
|
|
|
|
// Verify frame metadata
|
|
|
|
|
TEST_ASSERT_EQ(1280, output_frame.width, "Incorrect frame width");
|
|
|
|
|
TEST_ASSERT_EQ(720, output_frame.height, "Incorrect frame height");
|
|
|
|
|
|
|
|
|
|
LOGI("Frame metadata: %dx%d", output_frame.width, output_frame.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGI("✅ Vulkan decode to surface test completed");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TestVulkanSurfaceTypeOptimization(std::string& error_msg) {
|
|
|
|
|
LOGI("Testing Vulkan surface type optimization...");
|
|
|
|
|
|
|
|
|
|
// Create decoder instance
|
2025-09-30 19:54:29 +09:00
|
|
|
auto decoder = std::make_unique<VavCore::MediaCodecAV1Decoder>();
|
|
|
|
|
TEST_ASSERT_NOT_NULL(decoder.get(), "Failed to create MediaCodecAV1Decoder");
|
2025-09-29 02:42:26 +09:00
|
|
|
|
|
|
|
|
// Test video metadata
|
|
|
|
|
VavCore::VideoMetadata metadata;
|
|
|
|
|
metadata.codec_type = VavCore::VideoCodecType::AV1;
|
|
|
|
|
metadata.width = 1920;
|
|
|
|
|
metadata.height = 1080;
|
|
|
|
|
metadata.frame_rate = 60.0;
|
|
|
|
|
|
|
|
|
|
// Initialize decoder
|
|
|
|
|
bool init_result = decoder->Initialize(metadata);
|
|
|
|
|
if (!init_result) {
|
|
|
|
|
LOGW("Decoder initialization failed - skipping surface type optimization test");
|
|
|
|
|
LOGI("⚠️ Vulkan surface type optimization test skipped (no AV1 hardware support)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test all Vulkan-related surface types
|
|
|
|
|
std::vector<std::pair<VavCoreSurfaceType, std::string>> surface_types = {
|
|
|
|
|
{ VAVCORE_SURFACE_VULKAN_IMAGE, "VULKAN_IMAGE" },
|
|
|
|
|
{ VAVCORE_SURFACE_ANDROID_HARDWARE_BUFFER, "ANDROID_HARDWARE_BUFFER" },
|
|
|
|
|
{ VAVCORE_SURFACE_OPENGL_ES_TEXTURE, "OPENGL_ES_TEXTURE" },
|
|
|
|
|
{ VAVCORE_SURFACE_ANDROID_NATIVE_WINDOW, "ANDROID_NATIVE_WINDOW" },
|
|
|
|
|
{ VAVCORE_SURFACE_CPU, "CPU" }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const auto& surface_type_pair : surface_types) {
|
|
|
|
|
VavCoreSurfaceType type = surface_type_pair.first;
|
|
|
|
|
const std::string& name = surface_type_pair.second;
|
|
|
|
|
|
|
|
|
|
bool supported = decoder->SupportsSurfaceType(type);
|
|
|
|
|
LOGI("Surface type %s: %s", name.c_str(), supported ? "SUPPORTED" : "NOT SUPPORTED");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test optimal surface type selection
|
|
|
|
|
VavCoreSurfaceType optimal_type = decoder->GetOptimalSurfaceType();
|
|
|
|
|
LOGI("Optimal surface type for this device: %d", static_cast<int>(optimal_type));
|
|
|
|
|
|
|
|
|
|
// Test Godot-specific optimization
|
|
|
|
|
bool is_optimal_for_godot = decoder->IsOptimalForGodot();
|
|
|
|
|
LOGI("Optimal for Godot integration: %s", is_optimal_for_godot ? "YES" : "NO");
|
|
|
|
|
|
|
|
|
|
// Get detailed Godot integration information
|
|
|
|
|
std::string godot_info = decoder->GetGodotIntegrationInfo();
|
|
|
|
|
LOGI("Godot integration details: %s", godot_info.c_str());
|
|
|
|
|
|
|
|
|
|
// Verify that at least CPU surface is supported (fallback)
|
|
|
|
|
bool cpu_supported = decoder->SupportsSurfaceType(VAVCORE_SURFACE_CPU);
|
|
|
|
|
TEST_ASSERT(cpu_supported, "CPU surface should always be supported");
|
|
|
|
|
|
|
|
|
|
LOGI("✅ Vulkan surface type optimization test passed");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test registration function
|
|
|
|
|
void RegisterVulkanImageTests(TestFramework& framework) {
|
|
|
|
|
framework.RegisterTest("Vulkan_Availability", TestVulkanAvailability);
|
|
|
|
|
framework.RegisterTest("Vulkan_AHardwareBuffer", TestAHardwareBufferCreation);
|
|
|
|
|
framework.RegisterTest("Vulkan_MediaCodecSetup", TestAndroidMediaCodecVulkanSetup);
|
|
|
|
|
framework.RegisterTest("Vulkan_DecodeToSurface", TestVulkanDecodeToSurface);
|
|
|
|
|
framework.RegisterTest("Vulkan_SurfaceOptimization", TestVulkanSurfaceTypeOptimization);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace VavCoreTest
|