• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace android {
25 
26 // std::min is not constexpr in C++11
27 template<typename T>
MIN(const T & a,const T & b)28 constexpr 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.
32 static const size_t kSharedMemoryThreshold = MIN(
33         (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
34 
MediaBufferGroup(size_t growthLimit)35 MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
36     mGrowthLimit(growthLimit) {
37 }
38 
MediaBufferGroup(size_t buffers,size_t buffer_size,size_t growthLimit)39 MediaBufferGroup::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 
~MediaBufferGroup()83 MediaBufferGroup::~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 
add_buffer(MediaBuffer * buffer)108 void 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 
has_buffers()129 bool 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 
acquire_buffer(MediaBuffer ** out,bool nonBlocking,size_t requestedSize)141 status_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 
signalBufferReturned(MediaBuffer *)201 void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
202     mCondition.signal();
203 }
204 
205 }  // namespace android
206