This commit is contained in:
2025-10-15 02:25:11 +09:00
parent 6f9238e00d
commit 90d273c8e6
3 changed files with 20 additions and 80 deletions

View File

@@ -464,10 +464,14 @@ bool MediaCodecAsyncHandler::ProcessAsyncOutputFrame(int32_t output_index, AMedi
// Step 1: Release MediaCodec buffer to ImageReader surface (render=true)
// This triggers MediaCodec to render the frame to ImageReader's Surface
LogInfo("ProcessAsyncOutputFrame: Releasing output buffer to ImageReader (render=true)...");
LogInfo("ProcessAsyncOutputFrame: About to call releaseOutputBuffer with index=" +
std::to_string(output_index) + ", codec=0x" +
std::to_string(reinterpret_cast<uintptr_t>(m_codec)));
media_status_t status = AMediaCodec_releaseOutputBuffer(m_codec, output_index, true);
LogInfo("ProcessAsyncOutputFrame: releaseOutputBuffer returned status=" + std::to_string(status));
LogInfo("ProcessAsyncOutputFrame: releaseOutputBuffer COMPLETED with status=" +
std::to_string(status) + " (0=AMEDIA_OK)");
if (status != AMEDIA_OK) {
LogError("ProcessAsyncOutputFrame: Failed to release output buffer: " + std::to_string(status));

View File

@@ -38,8 +38,6 @@ MediaCodecSurfaceManager::MediaCodecSurfaceManager()
, m_video_height(0)
, m_java_vm(nullptr)
, m_jni_env(nullptr)
, m_looper_running(false)
, m_looper(nullptr)
, m_initialized(false) {
}
@@ -526,73 +524,28 @@ bool MediaCodecSurfaceManager::SetupImageReader(uint32_t width, uint32_t height)
LogInfo("AImageReader created successfully (format=PRIVATE, maxImages=3)");
// CRITICAL FIX: AImageReader callbacks require ALooper to dispatch events
// Android NDK AImageReader_setImageListener uses the calling thread's Looper
// If no Looper exists, callbacks are never invoked!
// Solution: Create dedicated handler thread with Looper
// CRITICAL: Android NDK AImageReader callbacks are invoked on a dedicated internal thread
// managed by the system - NO Looper or Handler thread is required!
// The listener can be set from any thread, and callbacks will be dispatched automatically.
// Reference: https://developer.android.com/ndk/reference/group/media#aimagereader_setimagelist
LogInfo("Starting handler thread with ALooper for ImageReader callbacks...");
LogInfo("Setting native image listener (callback dispatched on system thread)...");
// Start looper thread
m_looper_running = true;
m_looper_thread = std::thread([this]() {
// Prepare Looper on this thread (required for AImageReader callbacks!)
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
if (!looper) {
LogError("Handler thread: Failed to prepare ALooper");
m_looper_running = false;
return;
}
// Set native image listener (NO JAVA, NO LOOPER NEEDED!)
AImageReader_ImageListener listener{
.context = this,
.onImageAvailable = OnImageAvailableStatic
};
m_looper = looper;
LogInfo("Handler thread: ALooper prepared successfully");
// Register image listener on THIS thread (with Looper)
AImageReader_ImageListener listener{
.context = this,
.onImageAvailable = OnImageAvailableStatic
};
media_status_t status = AImageReader_setImageListener(m_image_reader, &listener);
if (status != AMEDIA_OK) {
LogError("Handler thread: Failed to set image listener: " + std::to_string(status));
m_looper_running = false;
return;
}
LogInfo("Handler thread: Native image listener registered (callback: OnImageAvailableStatic)");
// Run Looper event loop (blocks until m_looper_running = false)
LogInfo("Handler thread: Starting Looper event loop...");
while (m_looper_running) {
// Poll for events with 100ms timeout
// This allows callbacks to be dispatched while checking m_looper_running periodically
int result = ALooper_pollAll(100, nullptr, nullptr, nullptr);
// result: ALOOPER_POLL_WAKE (-1), ALOOPER_POLL_CALLBACK (-2),
// ALOOPER_POLL_TIMEOUT (-3), ALOOPER_POLL_ERROR (-4), or >= 0 (fd)
if (result == ALOOPER_POLL_ERROR) {
LogError("Handler thread: ALooper_pollAll error");
break;
}
}
LogInfo("Handler thread: Looper event loop exited");
});
// Wait briefly for thread to initialize
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (!m_looper_running) {
LogError("Handler thread failed to start");
if (m_looper_thread.joinable()) {
m_looper_thread.join();
}
status = AImageReader_setImageListener(m_image_reader, &listener);
if (status != AMEDIA_OK) {
LogError("Failed to set image listener: " + std::to_string(status));
AImageReader_delete(m_image_reader);
m_image_reader = nullptr;
return false;
}
LogInfo("Handler thread started successfully with Looper");
LogInfo("Native image listener registered (callback: OnImageAvailableStatic)");
// Get ANativeWindow from AImageReader (NO JAVA!)
ANativeWindow* nativeWindow = nullptr;
@@ -1052,19 +1005,6 @@ JNIEnv* MediaCodecSurfaceManager::GetJNIEnv() const {
void MediaCodecSurfaceManager::CleanupJNI() {
// PHASE 2: Native AImageReader cleanup (replaces JNI DeleteGlobalRef)
// Stop looper thread before cleaning up ImageReader
if (m_looper_running) {
LogInfo("Stopping handler thread...");
m_looper_running = false;
if (m_looper_thread.joinable()) {
m_looper_thread.join();
LogInfo("Handler thread joined successfully");
}
m_looper = nullptr;
}
// Release current image before cleaning up ImageReader
if (m_current_image) {
ReleaseImage();

View File

@@ -165,15 +165,11 @@ private:
JNIEnv* m_jni_env;
// Synchronization for OnImageAvailableCallback (Phase 2)
// AImageReader callbacks are dispatched on system-managed thread automatically
mutable std::mutex m_image_mutex;
std::condition_variable m_image_cv;
std::atomic<bool> m_image_available{false};
// Handler thread for AImageReader callbacks (Looper required)
std::thread m_looper_thread;
std::atomic<bool> m_looper_running{false};
void* m_looper; // ALooper* (opaque pointer to avoid header dependency)
// Initialization state
bool m_initialized;
};