2025-09-30 19:54:29 +09:00
|
|
|
#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)";
|
|
|
|
|
}
|
2025-10-13 22:55:54 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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";
|
2025-09-30 19:54:29 +09:00
|
|
|
}
|