WIP
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user