GraphicBufferSource.cpp revision d030447b617105b31bf3013e5e4b39d422b53b77
1/*
2 * Copyright (C) 2013 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 "GraphicBufferSource"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <GraphicBufferSource.h>
22
23#include <OMX_Core.h>
24#include <media/stagefright/foundation/ADebug.h>
25
26#include <MetadataBufferType.h>
27#include <ui/GraphicBuffer.h>
28
29namespace android {
30
31static const bool EXTRA_CHECK = true;
32
33
34GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
35        uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) :
36    mInitCheck(UNKNOWN_ERROR),
37    mNodeInstance(nodeInstance),
38    mExecuting(false),
39    mNumFramesAvailable(0),
40    mEndOfStream(false),
41    mEndOfStreamSent(false) {
42
43    ALOGV("GraphicBufferSource w=%u h=%u c=%u",
44            bufferWidth, bufferHeight, bufferCount);
45
46    if (bufferWidth == 0 || bufferHeight == 0) {
47        ALOGE("Invalid dimensions %ux%u", bufferWidth, bufferHeight);
48        mInitCheck = BAD_VALUE;
49        return;
50    }
51
52    String8 name("GraphicBufferSource");
53
54    mBufferQueue = new BufferQueue(true);
55    mBufferQueue->setConsumerName(name);
56    mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
57    mBufferQueue->setSynchronousMode(true);
58    mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
59            GRALLOC_USAGE_HW_TEXTURE);
60
61    mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount);
62    if (mInitCheck != NO_ERROR) {
63        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
64                bufferCount, mInitCheck);
65        return;
66    }
67
68    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
69    // reference once the ctor ends, as that would cause the refcount of 'this'
70    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
71    // that's what we create.
72    wp<BufferQueue::ConsumerListener> listener;
73    listener = static_cast<BufferQueue::ConsumerListener*>(this);
74
75    sp<BufferQueue::ConsumerListener> proxy;
76    proxy = new BufferQueue::ProxyConsumerListener(listener);
77
78    mInitCheck = mBufferQueue->consumerConnect(proxy);
79    if (mInitCheck != NO_ERROR) {
80        ALOGE("Error connecting to BufferQueue: %s (%d)",
81                strerror(-mInitCheck), mInitCheck);
82        return;
83    }
84
85    CHECK(mInitCheck == NO_ERROR);
86}
87
88GraphicBufferSource::~GraphicBufferSource() {
89    ALOGV("~GraphicBufferSource");
90    if (mBufferQueue != NULL) {
91        status_t err = mBufferQueue->consumerDisconnect();
92        if (err != NO_ERROR) {
93            ALOGW("consumerDisconnect failed: %d", err);
94        }
95    }
96}
97
98void GraphicBufferSource::omxExecuting() {
99    Mutex::Autolock autoLock(mMutex);
100    ALOGV("--> executing; avail=%d, codec vec size=%zd",
101            mNumFramesAvailable, mCodecBuffers.size());
102    CHECK(!mExecuting);
103    mExecuting = true;
104
105    // Start by loading up as many buffers as possible.  We want to do this,
106    // rather than just submit the first buffer, to avoid a degenerate case:
107    // if all BQ buffers arrive before we start executing, and we only submit
108    // one here, the other BQ buffers will just sit until we get notified
109    // that the codec buffer has been released.  We'd then acquire and
110    // submit a single additional buffer, repeatedly, never using more than
111    // one codec buffer simultaneously.  (We could instead try to submit
112    // all BQ buffers whenever any codec buffer is freed, but if we get the
113    // initial conditions right that will never be useful.)
114    while (mNumFramesAvailable) {
115        if (!fillCodecBuffer_l()) {
116            ALOGV("stop load with frames available (codecAvail=%d)",
117                    isCodecBufferAvailable_l());
118            break;
119        }
120    }
121
122    ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
123
124    // If EOS has already been signaled, and there are no more frames to
125    // submit, try to send EOS now as well.
126    if (mEndOfStream && mNumFramesAvailable == 0) {
127        submitEndOfInputStream_l();
128    }
129}
130
131void GraphicBufferSource::omxLoaded(){
132    Mutex::Autolock autoLock(mMutex);
133    ALOGV("--> loaded");
134    CHECK(mExecuting);
135
136    ALOGV("Dropped down to loaded, avail=%d eos=%d eosSent=%d",
137            mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
138
139    // Codec is no longer executing.  Discard all codec-related state.
140    mCodecBuffers.clear();
141    // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries
142    //       are null; complain if not
143
144    mExecuting = false;
145}
146
147void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
148    Mutex::Autolock autoLock(mMutex);
149
150    if (mExecuting) {
151        // This should never happen -- buffers can only be allocated when
152        // transitioning from "loaded" to "idle".
153        ALOGE("addCodecBuffer: buffer added while executing");
154        return;
155    }
156
157    ALOGV("addCodecBuffer h=%p size=%lu p=%p",
158            header, header->nAllocLen, header->pBuffer);
159    CodecBuffer codecBuffer;
160    codecBuffer.mHeader = header;
161    mCodecBuffers.add(codecBuffer);
162}
163
164void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
165    Mutex::Autolock autoLock(mMutex);
166
167    CHECK(mExecuting);  // could this happen if app stop()s early?
168
169    int cbi = findMatchingCodecBuffer_l(header);
170    if (cbi < 0) {
171        // This should never happen.
172        ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
173        return;
174    }
175
176    ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p",
177            header, header->nAllocLen, header->nFilledLen,
178            header->pBuffer);
179    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
180
181    // header->nFilledLen may not be the original value, so we can't compare
182    // that to zero to see of this was the EOS buffer.  Instead we just
183    // see if the GraphicBuffer reference was null, which should only ever
184    // happen for EOS.
185    if (codecBuffer.mGraphicBuffer == NULL) {
186        CHECK(mEndOfStream && mEndOfStreamSent);
187        // No GraphicBuffer to deal with, no additional input or output is
188        // expected, so just return.
189        return;
190    }
191
192    if (EXTRA_CHECK) {
193        // Pull the graphic buffer handle back out of the buffer, and confirm
194        // that it matches expectations.
195        OMX_U8* data = header->pBuffer;
196        buffer_handle_t bufferHandle;
197        memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t));
198        if (bufferHandle != codecBuffer.mGraphicBuffer->handle) {
199            // should never happen
200            ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
201                    bufferHandle, codecBuffer.mGraphicBuffer->handle);
202            CHECK(!"codecBufferEmptied: mismatched buffer");
203        }
204    }
205
206    // Find matching entry in our cached copy of the BufferQueue slots.
207    // If we find a match, release that slot.  If we don't, the BufferQueue
208    // has dropped that GraphicBuffer, and there's nothing for us to release.
209    int id = codecBuffer.mBuf;
210    if (mBufferSlot[id] != NULL &&
211        mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
212        ALOGV("cbi %d matches bq slot %d, handle=%p",
213                cbi, id, mBufferSlot[id]->handle);
214
215        mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber,
216                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
217    } else {
218        ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
219                cbi);
220    }
221
222    // Mark the codec buffer as available by clearing the GraphicBuffer ref.
223    codecBuffer.mGraphicBuffer = NULL;
224
225    if (mNumFramesAvailable) {
226        // Fill this codec buffer.
227        CHECK(!mEndOfStreamSent);
228        ALOGV("buffer freed, %d frames avail (eos=%d)",
229                mNumFramesAvailable, mEndOfStream);
230        fillCodecBuffer_l();
231    } else if (mEndOfStream) {
232        // No frames available, but EOS is pending, so use this buffer to
233        // send that.
234        ALOGV("buffer freed, EOS pending");
235        submitEndOfInputStream_l();
236    }
237    return;
238}
239
240bool GraphicBufferSource::fillCodecBuffer_l() {
241    CHECK(mExecuting && mNumFramesAvailable > 0);
242
243    int cbi = findAvailableCodecBuffer_l();
244    if (cbi < 0) {
245        // No buffers available, bail.
246        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
247                mNumFramesAvailable);
248        return false;
249    }
250
251    ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
252            mNumFramesAvailable);
253    BufferQueue::BufferItem item;
254    status_t err = mBufferQueue->acquireBuffer(&item);
255    if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
256        // shouldn't happen
257        ALOGW("fillCodecBuffer_l: frame was not available");
258        return false;
259    } else if (err != OK) {
260        // now what? fake end-of-stream?
261        ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
262        return false;
263    }
264
265    mNumFramesAvailable--;
266
267    // Wait for it to become available.
268    err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l");
269    if (err != OK) {
270        ALOGW("failed to wait for buffer fence: %d", err);
271        // keep going
272    }
273
274    // If this is the first time we're seeing this buffer, add it to our
275    // slot table.
276    if (item.mGraphicBuffer != NULL) {
277        ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
278        mBufferSlot[item.mBuf] = item.mGraphicBuffer;
279    }
280
281    err = submitBuffer_l(item, cbi);
282    if (err != OK) {
283        ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
284        mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
285                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
286    } else {
287        ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
288    }
289
290    return true;
291}
292
293status_t GraphicBufferSource::signalEndOfInputStream() {
294    Mutex::Autolock autoLock(mMutex);
295    ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d",
296            mExecuting, mNumFramesAvailable, mEndOfStream);
297
298    if (mEndOfStream) {
299        ALOGE("EOS was already signaled");
300        return INVALID_OPERATION;
301    }
302
303    // Set the end-of-stream flag.  If no frames are pending from the
304    // BufferQueue, and a codec buffer is available, and we're executing,
305    // we initiate the EOS from here.  Otherwise, we'll let
306    // codecBufferEmptied() (or omxExecuting) do it.
307    //
308    // Note: if there are no pending frames and all codec buffers are
309    // available, we *must* submit the EOS from here or we'll just
310    // stall since no future events are expected.
311    mEndOfStream = true;
312
313    if (mExecuting && mNumFramesAvailable == 0) {
314        submitEndOfInputStream_l();
315    }
316
317    return OK;
318}
319
320status_t GraphicBufferSource::submitBuffer_l(
321        const BufferQueue::BufferItem &item, int cbi) {
322    ALOGV("submitBuffer_l cbi=%d", cbi);
323    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
324    codecBuffer.mGraphicBuffer = mBufferSlot[item.mBuf];
325    codecBuffer.mBuf = item.mBuf;
326    codecBuffer.mFrameNumber = item.mFrameNumber;
327
328    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
329    CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t));
330    OMX_U8* data = header->pBuffer;
331    const OMX_U32 type = kMetadataBufferTypeGrallocSource;
332    buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle;
333    memcpy(data, &type, 4);
334    memcpy(data + 4, &handle, sizeof(buffer_handle_t));
335
336    status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
337            4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
338            item.mTimestamp / 1000);
339    if (err != OK) {
340        ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
341        codecBuffer.mGraphicBuffer = NULL;
342        return err;
343    }
344
345    ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p",
346            header, header->pBuffer, handle);
347    return OK;
348}
349
350void GraphicBufferSource::submitEndOfInputStream_l() {
351    CHECK(mEndOfStream);
352    if (mEndOfStreamSent) {
353        ALOGV("EOS already sent");
354        return;
355    }
356
357    int cbi = findAvailableCodecBuffer_l();
358    if (cbi < 0) {
359        ALOGV("submitEndOfInputStream_l: no codec buffers available");
360        return;
361    }
362
363    // We reject any additional incoming graphic buffers, so there's no need
364    // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
365    // in-use.
366    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
367
368    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
369    if (EXTRA_CHECK) {
370        // Guard against implementations that don't check nFilledLen.
371        size_t fillLen = 4 + sizeof(buffer_handle_t);
372        CHECK(header->nAllocLen >= fillLen);
373        OMX_U8* data = header->pBuffer;
374        memset(data, 0xcd, fillLen);
375    }
376
377    uint64_t timestamp = 0; // does this matter?
378
379    status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0,
380            /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
381            timestamp);
382    if (err != OK) {
383        ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
384    } else {
385        ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
386                header, cbi);
387        mEndOfStreamSent = true;
388    }
389}
390
391int GraphicBufferSource::findAvailableCodecBuffer_l() {
392    CHECK(mCodecBuffers.size() > 0);
393
394    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
395        if (mCodecBuffers[i].mGraphicBuffer == NULL) {
396            return i;
397        }
398    }
399    return -1;
400}
401
402int GraphicBufferSource::findMatchingCodecBuffer_l(
403        const OMX_BUFFERHEADERTYPE* header) {
404    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
405        if (mCodecBuffers[i].mHeader == header) {
406            return i;
407        }
408    }
409    return -1;
410}
411
412// BufferQueue::ConsumerListener callback
413void GraphicBufferSource::onFrameAvailable() {
414    Mutex::Autolock autoLock(mMutex);
415
416    ALOGV("onFrameAvailable exec=%d avail=%d",
417            mExecuting, mNumFramesAvailable);
418
419    if (mEndOfStream) {
420        // This should only be possible if a new buffer was queued after
421        // EOS was signaled, i.e. the app is misbehaving.
422        ALOGW("onFrameAvailable: EOS is set, ignoring frame");
423
424        BufferQueue::BufferItem item;
425        status_t err = mBufferQueue->acquireBuffer(&item);
426        if (err == OK) {
427            mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
428                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
429        }
430        return;
431    }
432
433    mNumFramesAvailable++;
434
435    if (mExecuting) {
436        fillCodecBuffer_l();
437    }
438}
439
440// BufferQueue::ConsumerListener callback
441void GraphicBufferSource::onBuffersReleased() {
442    Mutex::Autolock lock(mMutex);
443
444    uint32_t slotMask;
445    if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) {
446        ALOGW("onBuffersReleased: unable to get released buffer set");
447        slotMask = 0xffffffff;
448    }
449
450    ALOGV("onBuffersReleased: 0x%08x", slotMask);
451
452    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
453        if ((slotMask & 0x01) != 0) {
454            mBufferSlot[i] = NULL;
455        }
456        slotMask >>= 1;
457    }
458}
459
460}  // namespace android
461