diff --git a/droidmediabuffer.cpp b/droidmediabuffer.cpp index 64a94eb..947d601 100644 --- a/droidmediabuffer.cpp +++ b/droidmediabuffer.cpp @@ -17,6 +17,7 @@ * Authored by: Mohammed Hassan */ +#include #include "droidmediabuffer.h" #include "private.h" @@ -127,7 +128,15 @@ DroidMediaBuffer *droid_media_buffer_create(uint32_t w, uint32_t h, void droid_media_buffer_destroy(DroidMediaBuffer *buffer) { +#if ANDROID_MAJOR > 5 + if (buffer->m_queue != NULL) { + buffer->m_queue->releaseBufferResources(buffer); + return; + } + ALOGW("Destroying buffer refs but no queue %" PRIxPTR, (uintptr_t)buffer); +#else buffer->decStrong(0); +#endif } void droid_media_buffer_set_user_data(DroidMediaBuffer *buffer, void *data) diff --git a/droidmediacodec.cpp b/droidmediacodec.cpp index 9d75d22..4eec795 100644 --- a/droidmediacodec.cpp +++ b/droidmediacodec.cpp @@ -181,7 +181,7 @@ class Source : public android::MediaSource { for (android::List::iterator iter = m_framesBeingProcessed.buffers.begin(); iter != m_framesBeingProcessed.buffers.end(); iter++) { if (*iter == buffer) { - m_framesBeingProcessed.buffers.erase(iter); + m_framesBeingProcessed.buffers.erase(iter); m_framesBeingProcessed.cond.signal(); m_framesBeingProcessed.lock.unlock(); return; diff --git a/private.cpp b/private.cpp index 4a6af00..8a8a8dd 100644 --- a/private.cpp +++ b/private.cpp @@ -16,7 +16,7 @@ * * Authored by: Mohammed Hassan */ - +#include #include "private.h" #include "droidmediabuffer.h" #if (ANDROID_MAJOR == 4 && ANDROID_MINOR < 4) @@ -63,6 +63,9 @@ void DroidMediaBufferQueueListener::onBuffersReleased() } _DroidMediaBufferQueue::_DroidMediaBufferQueue(const char *name) : +#if ANDROID_MAJOR > 5 + m_unslotted({}), +#endif m_listener(new DroidMediaBufferQueueListener(this)), m_data(0) { @@ -171,8 +174,19 @@ void _DroidMediaBufferQueue::frameAvailable() { DroidMediaBufferSlot &slot = m_slots[slotIndex(item)]; if (item.mGraphicBuffer != NULL) { +#if ANDROID_MAJOR > 5 + if (slot.droidBuffer.get()) { + // It seems the buffers are not recycled. We're manually releasing this previous slot. + ALOGW("Releasing only resources, keeping memory %" PRIxPTR, (uintptr_t)slot.droidBuffer.get()); + slot.mGraphicBuffer = 0; + slot.droidBuffer->m_buffer.clear(); + // instead of clear-ing the entire memory which leads to droid_media_buffer_destroy crashing. + releaseBufferResources(slot.droidBuffer.get()); + // Add the buffer to the list of unslotted buffers that droid_media_buffer_destroy will check and don't do anything. + m_unslotted.emplace(slot.droidBuffer.get(), slot.droidBuffer); + } +#endif static_cast(slot) = item; - slot.droidBuffer = new DroidMediaBuffer(slot, this); // Keep the original reference count for ourselves and give one to the buffer_created @@ -218,18 +232,36 @@ void _DroidMediaBufferQueue::frameAvailable() { } } -void _DroidMediaBufferQueue::buffersReleased() { - for (int i = 0; i < android::BufferQueue::NUM_BUFFER_SLOTS; ++i) { - DroidMediaBufferSlot &slot = m_slots[i]; - slot.droidBuffer.clear(); - slot.mGraphicBuffer = 0; +#if ANDROID_MAJOR > 5 +void _DroidMediaBufferQueue::releaseBufferResources(DroidMediaBuffer *buffer) { + if (m_unslotted.find(buffer) != m_unslotted.end()) { + ALOGI("Unslotted buffer, resources already released %" PRIxPTR, (uintptr_t)buffer); + } else { + buffer->decStrong(0); } +} +#endif +void _DroidMediaBufferQueue::buffersReleased() { android::AutoMutex locker(&m_lock); if (m_data) { m_cb.buffers_released(m_data); } + + // Releasing DroidMediaBuffer memory for slots + for (int i = 0; i < android::BufferQueue::NUM_BUFFER_SLOTS; ++i) { + DroidMediaBufferSlot &slot = m_slots[i]; + slot.droidBuffer.clear(); + slot.mGraphicBuffer = 0; + } +#if ANDROID_MAJOR > 5 + // Releasing DroidMediaBuffer memory for unslotted buffers + for (auto& droidBufferPair : m_unslotted) { + droidBufferPair.second.clear(); + } + m_unslotted.clear(); +#endif } int _DroidMediaBufferQueue::releaseMediaBuffer(int index, EGLDisplay dpy, EGLSyncKHR fence) { diff --git a/private.h b/private.h index 6cd4881..fdde7f7 100644 --- a/private.h +++ b/private.h @@ -33,7 +33,9 @@ #if ANDROID_MAJOR >=5 #include #endif - +#if ANDROID_MAJOR > 5 +#include +#endif struct _DroidMediaBufferQueue; class DroidMediaBufferQueueListener : @@ -81,6 +83,9 @@ struct _DroidMediaBufferQueue : public android::RefBase { void buffersReleased(); +#if ANDROID_MAJOR > 5 + void releaseBufferResources(DroidMediaBuffer *buffer); +#endif private: friend class DroidMediaBufferQueueListener; @@ -97,6 +102,11 @@ struct _DroidMediaBufferQueue : public android::RefBase { DroidMediaBufferSlot m_slots[android::BufferQueue::NUM_BUFFER_SLOTS]; +#if ANDROID_MAJOR > 5 + // Storage for buffers that have their resources cleaned up, but the client still holds a raw reference. + // The first part is used to identify the raw pointer from client, the second to actually clean the memory. + std::unordered_map> m_unslotted; +#endif android::sp m_listener; android::Mutex m_lock; @@ -111,5 +121,5 @@ android::sp droid_media_codec_create_encoder_raw(DroidMedi android::sp looper, #endif android::sp src); - + #endif /* DROID_MEDIA_PRIVATE_H */