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