#include #include #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(); } 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 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 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 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(0x1234); void* vk_instance = reinterpret_cast(0x5678); void* vk_physical_device = reinterpret_cast(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"; }