1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "MediaBufferGroup" 18#include <utils/Log.h> 19 20#include <media/stagefright/foundation/ADebug.h> 21#include <media/stagefright/MediaBuffer.h> 22#include <media/stagefright/MediaBufferGroup.h> 23 24namespace android { 25 26// std::min is not constexpr in C++11 27template<typename T> 28constexpr T MIN(const T &a, const T &b) { return a <= b ? a : b; } 29 30// MediaBufferGroup may create shared memory buffers at a 31// smaller threshold than an isolated new MediaBuffer. 32static const size_t kSharedMemoryThreshold = MIN( 33 (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024)); 34 35MediaBufferGroup::MediaBufferGroup(size_t growthLimit) : 36 mGrowthLimit(growthLimit) { 37} 38 39MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit) 40 : mGrowthLimit(growthLimit) { 41 42 if (mGrowthLimit > 0 && buffers > mGrowthLimit) { 43 ALOGW("Preallocated buffers %zu > growthLimit %zu, increasing growthLimit", 44 buffers, mGrowthLimit); 45 mGrowthLimit = buffers; 46 } 47 48 if (buffer_size >= kSharedMemoryThreshold) { 49 ALOGD("creating MemoryDealer"); 50 // Using a single MemoryDealer is efficient for a group of shared memory objects. 51 // This loop guarantees that we use shared memory (no fallback to malloc). 52 53 size_t alignment = MemoryDealer::getAllocationAlignment(); 54 size_t augmented_size = buffer_size + sizeof(MediaBuffer::SharedControl); 55 size_t total = (augmented_size + alignment - 1) / alignment * alignment * buffers; 56 sp<MemoryDealer> memoryDealer = new MemoryDealer(total, "MediaBufferGroup"); 57 58 for (size_t i = 0; i < buffers; ++i) { 59 sp<IMemory> mem = memoryDealer->allocate(augmented_size); 60 if (mem.get() == nullptr || mem->pointer() == nullptr) { 61 ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size); 62 break; 63 } 64 MediaBuffer *buffer = new MediaBuffer(mem); 65 buffer->getSharedControl()->clear(); 66 add_buffer(buffer); 67 } 68 return; 69 } 70 71 // Non-shared memory allocation. 72 for (size_t i = 0; i < buffers; ++i) { 73 MediaBuffer *buffer = new MediaBuffer(buffer_size); 74 if (buffer->data() == nullptr) { 75 delete buffer; // don't call release, it's not properly formed 76 ALOGW("Only allocated %zu malloc buffers of size %zu", i, buffer_size); 77 break; 78 } 79 add_buffer(buffer); 80 } 81} 82 83MediaBufferGroup::~MediaBufferGroup() { 84 for (MediaBuffer *buffer : mBuffers) { 85 if (buffer->refcount() != 0) { 86 const int localRefcount = buffer->localRefcount(); 87 const int remoteRefcount = buffer->remoteRefcount(); 88 89 // Fatal if we have a local refcount. 90 LOG_ALWAYS_FATAL_IF(localRefcount != 0, 91 "buffer(%p) localRefcount %d != 0, remoteRefcount %d", 92 buffer, localRefcount, remoteRefcount); 93 94 // Log an error if we have a remaining remote refcount, 95 // as the remote process may have died or may have inappropriate behavior. 96 // The shared memory associated with the MediaBuffer will 97 // automatically be reclaimed when there are no remaining fds 98 // associated with it. 99 ALOGE("buffer(%p) has residual remoteRefcount %d", 100 buffer, remoteRefcount); 101 } 102 // gracefully delete. 103 buffer->setObserver(nullptr); 104 buffer->release(); 105 } 106} 107 108void MediaBufferGroup::add_buffer(MediaBuffer *buffer) { 109 Mutex::Autolock autoLock(mLock); 110 111 // if we're above our growth limit, release buffers if we can 112 for (auto it = mBuffers.begin(); 113 mGrowthLimit > 0 114 && mBuffers.size() >= mGrowthLimit 115 && it != mBuffers.end();) { 116 if ((*it)->refcount() == 0) { 117 (*it)->setObserver(nullptr); 118 (*it)->release(); 119 it = mBuffers.erase(it); 120 } else { 121 ++it; 122 } 123 } 124 125 buffer->setObserver(this); 126 mBuffers.emplace_back(buffer); 127} 128 129bool MediaBufferGroup::has_buffers() { 130 if (mBuffers.size() < mGrowthLimit) { 131 return true; // We can add more buffers internally. 132 } 133 for (MediaBuffer *buffer : mBuffers) { 134 if (buffer->refcount() == 0) { 135 return true; 136 } 137 } 138 return false; 139} 140 141status_t MediaBufferGroup::acquire_buffer( 142 MediaBuffer **out, bool nonBlocking, size_t requestedSize) { 143 Mutex::Autolock autoLock(mLock); 144 for (;;) { 145 size_t smallest = requestedSize; 146 MediaBuffer *buffer = nullptr; 147 auto free = mBuffers.end(); 148 for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) { 149 if ((*it)->refcount() == 0) { 150 const size_t size = (*it)->size(); 151 if (size >= requestedSize) { 152 buffer = *it; 153 break; 154 } 155 if (size < smallest) { 156 smallest = size; // always free the smallest buf 157 free = it; 158 } 159 } 160 } 161 if (buffer == nullptr 162 && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) { 163 // We alloc before we free so failure leaves group unchanged. 164 const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ? 165 requestedSize * 3 / 2 : requestedSize; 166 buffer = new MediaBuffer(allocateSize); 167 if (buffer->data() == nullptr) { 168 ALOGE("Allocation failure for size %zu", allocateSize); 169 delete buffer; // Invalid alloc, prefer not to call release. 170 buffer = nullptr; 171 } else { 172 buffer->setObserver(this); 173 if (free != mBuffers.end()) { 174 ALOGV("reallocate buffer, requested size %zu vs available %zu", 175 requestedSize, (*free)->size()); 176 (*free)->setObserver(nullptr); 177 (*free)->release(); 178 *free = buffer; // in-place replace 179 } else { 180 ALOGV("allocate buffer, requested size %zu", requestedSize); 181 mBuffers.emplace_back(buffer); 182 } 183 } 184 } 185 if (buffer != nullptr) { 186 buffer->add_ref(); 187 buffer->reset(); 188 *out = buffer; 189 return OK; 190 } 191 if (nonBlocking) { 192 *out = nullptr; 193 return WOULD_BLOCK; 194 } 195 // All buffers are in use, block until one of them is returned. 196 mCondition.wait(mLock); 197 } 198 // Never gets here. 199} 200 201void MediaBufferGroup::signalBufferReturned(MediaBuffer *) { 202 Mutex::Autolock autoLock(mLock); 203 mCondition.signal(); 204} 205 206} // namespace android 207