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