GraphicBufferSource.cpp revision bdfd4885aa4d7b3f3f591118927a3f4aec593096
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#include <media/stagefright/foundation/AMessage.h>
26
27#include <media/hardware/MetadataBufferType.h>
28#include <ui/GraphicBuffer.h>
29
30namespace android {
31
32static const bool EXTRA_CHECK = true;
33
34
35GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
36        uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) :
37    mInitCheck(UNKNOWN_ERROR),
38    mNodeInstance(nodeInstance),
39    mExecuting(false),
40    mSuspended(false),
41    mNumFramesAvailable(0),
42    mEndOfStream(false),
43    mEndOfStreamSent(false),
44    mRepeatAfterUs(-1ll),
45    mRepeatLastFrameGeneration(0),
46    mLatestSubmittedBufferId(-1),
47    mLatestSubmittedBufferFrameNum(0),
48    mLatestSubmittedBufferUseCount(0),
49    mRepeatBufferDeferred(false) {
50
51    ALOGV("GraphicBufferSource w=%u h=%u c=%u",
52            bufferWidth, bufferHeight, bufferCount);
53
54    if (bufferWidth == 0 || bufferHeight == 0) {
55        ALOGE("Invalid dimensions %ux%u", bufferWidth, bufferHeight);
56        mInitCheck = BAD_VALUE;
57        return;
58    }
59
60    String8 name("GraphicBufferSource");
61
62    mBufferQueue = new BufferQueue();
63    mBufferQueue->setConsumerName(name);
64    mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
65    mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
66            GRALLOC_USAGE_HW_TEXTURE);
67
68    mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount);
69    if (mInitCheck != NO_ERROR) {
70        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
71                bufferCount, mInitCheck);
72        return;
73    }
74
75    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
76    // reference once the ctor ends, as that would cause the refcount of 'this'
77    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
78    // that's what we create.
79    wp<BufferQueue::ConsumerListener> listener = static_cast<BufferQueue::ConsumerListener*>(this);
80    sp<BufferQueue::ProxyConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
81
82    mInitCheck = mBufferQueue->consumerConnect(proxy, false);
83    if (mInitCheck != NO_ERROR) {
84        ALOGE("Error connecting to BufferQueue: %s (%d)",
85                strerror(-mInitCheck), mInitCheck);
86        return;
87    }
88
89    CHECK(mInitCheck == NO_ERROR);
90}
91
92GraphicBufferSource::~GraphicBufferSource() {
93    ALOGV("~GraphicBufferSource");
94    if (mBufferQueue != NULL) {
95        status_t err = mBufferQueue->consumerDisconnect();
96        if (err != NO_ERROR) {
97            ALOGW("consumerDisconnect failed: %d", err);
98        }
99    }
100}
101
102void GraphicBufferSource::omxExecuting() {
103    Mutex::Autolock autoLock(mMutex);
104    ALOGV("--> executing; avail=%d, codec vec size=%zd",
105            mNumFramesAvailable, mCodecBuffers.size());
106    CHECK(!mExecuting);
107    mExecuting = true;
108
109    // Start by loading up as many buffers as possible.  We want to do this,
110    // rather than just submit the first buffer, to avoid a degenerate case:
111    // if all BQ buffers arrive before we start executing, and we only submit
112    // one here, the other BQ buffers will just sit until we get notified
113    // that the codec buffer has been released.  We'd then acquire and
114    // submit a single additional buffer, repeatedly, never using more than
115    // one codec buffer simultaneously.  (We could instead try to submit
116    // all BQ buffers whenever any codec buffer is freed, but if we get the
117    // initial conditions right that will never be useful.)
118    while (mNumFramesAvailable) {
119        if (!fillCodecBuffer_l()) {
120            ALOGV("stop load with frames available (codecAvail=%d)",
121                    isCodecBufferAvailable_l());
122            break;
123        }
124    }
125
126    ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
127
128    // If EOS has already been signaled, and there are no more frames to
129    // submit, try to send EOS now as well.
130    if (mEndOfStream && mNumFramesAvailable == 0) {
131        submitEndOfInputStream_l();
132    }
133
134    if (mRepeatAfterUs > 0ll && mLooper == NULL) {
135        mReflector = new AHandlerReflector<GraphicBufferSource>(this);
136
137        mLooper = new ALooper;
138        mLooper->registerHandler(mReflector);
139        mLooper->start();
140
141        if (mLatestSubmittedBufferId >= 0) {
142            sp<AMessage> msg =
143                new AMessage(kWhatRepeatLastFrame, mReflector->id());
144
145            msg->setInt32("generation", ++mRepeatLastFrameGeneration);
146            msg->post(mRepeatAfterUs);
147        }
148    }
149}
150
151void GraphicBufferSource::omxLoaded(){
152    Mutex::Autolock autoLock(mMutex);
153    if (!mExecuting) {
154        // This can happen if something failed very early.
155        ALOGW("Dropped back down to Loaded without Executing");
156    }
157
158    if (mLooper != NULL) {
159        mLooper->unregisterHandler(mReflector->id());
160        mReflector.clear();
161
162        mLooper->stop();
163        mLooper.clear();
164    }
165
166    ALOGV("--> loaded; avail=%d eos=%d eosSent=%d",
167            mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
168
169    // Codec is no longer executing.  Discard all codec-related state.
170    mCodecBuffers.clear();
171    // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries
172    //       are null; complain if not
173
174    mExecuting = false;
175}
176
177void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
178    Mutex::Autolock autoLock(mMutex);
179
180    if (mExecuting) {
181        // This should never happen -- buffers can only be allocated when
182        // transitioning from "loaded" to "idle".
183        ALOGE("addCodecBuffer: buffer added while executing");
184        return;
185    }
186
187    ALOGV("addCodecBuffer h=%p size=%lu p=%p",
188            header, header->nAllocLen, header->pBuffer);
189    CodecBuffer codecBuffer;
190    codecBuffer.mHeader = header;
191    mCodecBuffers.add(codecBuffer);
192}
193
194void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
195    Mutex::Autolock autoLock(mMutex);
196
197    CHECK(mExecuting);  // could this happen if app stop()s early?
198
199    int cbi = findMatchingCodecBuffer_l(header);
200    if (cbi < 0) {
201        // This should never happen.
202        ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
203        return;
204    }
205
206    ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p",
207            header, header->nAllocLen, header->nFilledLen,
208            header->pBuffer);
209    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
210
211    // header->nFilledLen may not be the original value, so we can't compare
212    // that to zero to see of this was the EOS buffer.  Instead we just
213    // see if the GraphicBuffer reference was null, which should only ever
214    // happen for EOS.
215    if (codecBuffer.mGraphicBuffer == NULL) {
216        CHECK(mEndOfStream && mEndOfStreamSent);
217        // No GraphicBuffer to deal with, no additional input or output is
218        // expected, so just return.
219        return;
220    }
221
222    if (EXTRA_CHECK) {
223        // Pull the graphic buffer handle back out of the buffer, and confirm
224        // that it matches expectations.
225        OMX_U8* data = header->pBuffer;
226        buffer_handle_t bufferHandle;
227        memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t));
228        if (bufferHandle != codecBuffer.mGraphicBuffer->handle) {
229            // should never happen
230            ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
231                    bufferHandle, codecBuffer.mGraphicBuffer->handle);
232            CHECK(!"codecBufferEmptied: mismatched buffer");
233        }
234    }
235
236    // Find matching entry in our cached copy of the BufferQueue slots.
237    // If we find a match, release that slot.  If we don't, the BufferQueue
238    // has dropped that GraphicBuffer, and there's nothing for us to release.
239    int id = codecBuffer.mBuf;
240    if (mBufferSlot[id] != NULL &&
241        mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
242        ALOGV("cbi %d matches bq slot %d, handle=%p",
243                cbi, id, mBufferSlot[id]->handle);
244
245        if (id == mLatestSubmittedBufferId) {
246            CHECK_GT(mLatestSubmittedBufferUseCount--, 0);
247        } else {
248            mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber,
249                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
250        }
251    } else {
252        ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
253                cbi);
254    }
255
256    // Mark the codec buffer as available by clearing the GraphicBuffer ref.
257    codecBuffer.mGraphicBuffer = NULL;
258
259    if (mNumFramesAvailable) {
260        // Fill this codec buffer.
261        CHECK(!mEndOfStreamSent);
262        ALOGV("buffer freed, %d frames avail (eos=%d)",
263                mNumFramesAvailable, mEndOfStream);
264        fillCodecBuffer_l();
265    } else if (mEndOfStream) {
266        // No frames available, but EOS is pending, so use this buffer to
267        // send that.
268        ALOGV("buffer freed, EOS pending");
269        submitEndOfInputStream_l();
270    } else if (mRepeatBufferDeferred) {
271        bool success = repeatLatestSubmittedBuffer_l();
272        if (success) {
273            ALOGV("deferred repeatLatestSubmittedBuffer_l SUCCESS");
274        } else {
275            ALOGV("deferred repeatLatestSubmittedBuffer_l FAILURE");
276        }
277        mRepeatBufferDeferred = false;
278    }
279
280    return;
281}
282
283void GraphicBufferSource::suspend(bool suspend) {
284    Mutex::Autolock autoLock(mMutex);
285
286    if (suspend) {
287        mSuspended = true;
288
289        while (mNumFramesAvailable > 0) {
290            BufferQueue::BufferItem item;
291            status_t err = mBufferQueue->acquireBuffer(&item, 0);
292
293            if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
294                // shouldn't happen.
295                ALOGW("suspend: frame was not available");
296                break;
297            } else if (err != OK) {
298                ALOGW("suspend: acquireBuffer returned err=%d", err);
299                break;
300            }
301
302            --mNumFramesAvailable;
303
304            mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
305                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
306        }
307        return;
308    }
309
310    mSuspended = false;
311
312    if (mExecuting && mNumFramesAvailable == 0 && mRepeatBufferDeferred) {
313        if (repeatLatestSubmittedBuffer_l()) {
314            ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l SUCCESS");
315
316            mRepeatBufferDeferred = false;
317        } else {
318            ALOGV("suspend/deferred repeatLatestSubmittedBuffer_l FAILURE");
319        }
320    }
321}
322
323bool GraphicBufferSource::fillCodecBuffer_l() {
324    CHECK(mExecuting && mNumFramesAvailable > 0);
325
326    if (mSuspended) {
327        return false;
328    }
329
330    int cbi = findAvailableCodecBuffer_l();
331    if (cbi < 0) {
332        // No buffers available, bail.
333        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
334                mNumFramesAvailable);
335        return false;
336    }
337
338    ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
339            mNumFramesAvailable);
340    BufferQueue::BufferItem item;
341    status_t err = mBufferQueue->acquireBuffer(&item, 0);
342    if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
343        // shouldn't happen
344        ALOGW("fillCodecBuffer_l: frame was not available");
345        return false;
346    } else if (err != OK) {
347        // now what? fake end-of-stream?
348        ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
349        return false;
350    }
351
352    mNumFramesAvailable--;
353
354    // Wait for it to become available.
355    err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l");
356    if (err != OK) {
357        ALOGW("failed to wait for buffer fence: %d", err);
358        // keep going
359    }
360
361    // If this is the first time we're seeing this buffer, add it to our
362    // slot table.
363    if (item.mGraphicBuffer != NULL) {
364        ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
365        mBufferSlot[item.mBuf] = item.mGraphicBuffer;
366    }
367
368    err = submitBuffer_l(item, cbi);
369    if (err != OK) {
370        ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
371        mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
372                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
373    } else {
374        ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
375        setLatestSubmittedBuffer_l(item);
376    }
377
378    return true;
379}
380
381bool GraphicBufferSource::repeatLatestSubmittedBuffer_l() {
382    CHECK(mExecuting && mNumFramesAvailable == 0);
383
384    if (mLatestSubmittedBufferId < 0 || mSuspended) {
385        return false;
386    }
387    if (mBufferSlot[mLatestSubmittedBufferId] == NULL) {
388        // This can happen if the remote side disconnects, causing
389        // onBuffersReleased() to NULL out our copy of the slots.  The
390        // buffer is gone, so we have nothing to show.
391        //
392        // To be on the safe side we try to release the buffer.
393        ALOGD("repeatLatestSubmittedBuffer_l: slot was NULL");
394        mBufferQueue->releaseBuffer(
395                mLatestSubmittedBufferId,
396                mLatestSubmittedBufferFrameNum,
397                EGL_NO_DISPLAY,
398                EGL_NO_SYNC_KHR,
399                Fence::NO_FENCE);
400        mLatestSubmittedBufferId = -1;
401        mLatestSubmittedBufferFrameNum = 0;
402        return false;
403    }
404
405    int cbi = findAvailableCodecBuffer_l();
406    if (cbi < 0) {
407        // No buffers available, bail.
408        ALOGV("repeatLatestSubmittedBuffer_l: no codec buffers.");
409        return false;
410    }
411
412    BufferQueue::BufferItem item;
413    item.mBuf = mLatestSubmittedBufferId;
414    item.mFrameNumber = mLatestSubmittedBufferFrameNum;
415
416    status_t err = submitBuffer_l(item, cbi);
417
418    if (err != OK) {
419        return false;
420    }
421
422    ++mLatestSubmittedBufferUseCount;
423
424    return true;
425}
426
427void GraphicBufferSource::setLatestSubmittedBuffer_l(
428        const BufferQueue::BufferItem &item) {
429    ALOGV("setLatestSubmittedBuffer_l");
430
431    if (mLatestSubmittedBufferId >= 0) {
432        if (mLatestSubmittedBufferUseCount == 0) {
433            mBufferQueue->releaseBuffer(
434                    mLatestSubmittedBufferId,
435                    mLatestSubmittedBufferFrameNum,
436                    EGL_NO_DISPLAY,
437                    EGL_NO_SYNC_KHR,
438                    Fence::NO_FENCE);
439        }
440    }
441
442    mLatestSubmittedBufferId = item.mBuf;
443    mLatestSubmittedBufferFrameNum = item.mFrameNumber;
444    mLatestSubmittedBufferUseCount = 1;
445    mRepeatBufferDeferred = false;
446
447    if (mReflector != NULL) {
448        sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector->id());
449        msg->setInt32("generation", ++mRepeatLastFrameGeneration);
450        msg->post(mRepeatAfterUs);
451    }
452}
453
454status_t GraphicBufferSource::signalEndOfInputStream() {
455    Mutex::Autolock autoLock(mMutex);
456    ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d",
457            mExecuting, mNumFramesAvailable, mEndOfStream);
458
459    if (mEndOfStream) {
460        ALOGE("EOS was already signaled");
461        return INVALID_OPERATION;
462    }
463
464    // Set the end-of-stream flag.  If no frames are pending from the
465    // BufferQueue, and a codec buffer is available, and we're executing,
466    // we initiate the EOS from here.  Otherwise, we'll let
467    // codecBufferEmptied() (or omxExecuting) do it.
468    //
469    // Note: if there are no pending frames and all codec buffers are
470    // available, we *must* submit the EOS from here or we'll just
471    // stall since no future events are expected.
472    mEndOfStream = true;
473
474    if (mExecuting && mNumFramesAvailable == 0) {
475        submitEndOfInputStream_l();
476    }
477
478    return OK;
479}
480
481status_t GraphicBufferSource::submitBuffer_l(
482        const BufferQueue::BufferItem &item, int cbi) {
483    ALOGV("submitBuffer_l cbi=%d", cbi);
484    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
485    codecBuffer.mGraphicBuffer = mBufferSlot[item.mBuf];
486    codecBuffer.mBuf = item.mBuf;
487    codecBuffer.mFrameNumber = item.mFrameNumber;
488
489    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
490    CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t));
491    OMX_U8* data = header->pBuffer;
492    const OMX_U32 type = kMetadataBufferTypeGrallocSource;
493    buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle;
494    memcpy(data, &type, 4);
495    memcpy(data + 4, &handle, sizeof(buffer_handle_t));
496
497    status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
498            4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
499            item.mTimestamp / 1000);
500    if (err != OK) {
501        ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
502        codecBuffer.mGraphicBuffer = NULL;
503        return err;
504    }
505
506    ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p",
507            header, header->pBuffer, handle);
508    return OK;
509}
510
511void GraphicBufferSource::submitEndOfInputStream_l() {
512    CHECK(mEndOfStream);
513    if (mEndOfStreamSent) {
514        ALOGV("EOS already sent");
515        return;
516    }
517
518    int cbi = findAvailableCodecBuffer_l();
519    if (cbi < 0) {
520        ALOGV("submitEndOfInputStream_l: no codec buffers available");
521        return;
522    }
523
524    // We reject any additional incoming graphic buffers, so there's no need
525    // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
526    // in-use.
527    CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
528
529    OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
530    if (EXTRA_CHECK) {
531        // Guard against implementations that don't check nFilledLen.
532        size_t fillLen = 4 + sizeof(buffer_handle_t);
533        CHECK(header->nAllocLen >= fillLen);
534        OMX_U8* data = header->pBuffer;
535        memset(data, 0xcd, fillLen);
536    }
537
538    uint64_t timestamp = 0; // does this matter?
539
540    status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0,
541            /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
542            timestamp);
543    if (err != OK) {
544        ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
545    } else {
546        ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
547                header, cbi);
548        mEndOfStreamSent = true;
549    }
550}
551
552int GraphicBufferSource::findAvailableCodecBuffer_l() {
553    CHECK(mCodecBuffers.size() > 0);
554
555    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
556        if (mCodecBuffers[i].mGraphicBuffer == NULL) {
557            return i;
558        }
559    }
560    return -1;
561}
562
563int GraphicBufferSource::findMatchingCodecBuffer_l(
564        const OMX_BUFFERHEADERTYPE* header) {
565    for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
566        if (mCodecBuffers[i].mHeader == header) {
567            return i;
568        }
569    }
570    return -1;
571}
572
573// BufferQueue::ConsumerListener callback
574void GraphicBufferSource::onFrameAvailable() {
575    Mutex::Autolock autoLock(mMutex);
576
577    ALOGV("onFrameAvailable exec=%d avail=%d",
578            mExecuting, mNumFramesAvailable);
579
580    if (mEndOfStream || mSuspended) {
581        if (mEndOfStream) {
582            // This should only be possible if a new buffer was queued after
583            // EOS was signaled, i.e. the app is misbehaving.
584
585            ALOGW("onFrameAvailable: EOS is set, ignoring frame");
586        } else {
587            ALOGV("onFrameAvailable: suspended, ignoring frame");
588        }
589
590        BufferQueue::BufferItem item;
591        status_t err = mBufferQueue->acquireBuffer(&item, 0);
592        if (err == OK) {
593            mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
594                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
595        }
596        return;
597    }
598
599    mNumFramesAvailable++;
600
601    mRepeatBufferDeferred = false;
602    ++mRepeatLastFrameGeneration;
603
604    if (mExecuting) {
605        fillCodecBuffer_l();
606    }
607}
608
609// BufferQueue::ConsumerListener callback
610void GraphicBufferSource::onBuffersReleased() {
611    Mutex::Autolock lock(mMutex);
612
613    uint32_t slotMask;
614    if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) {
615        ALOGW("onBuffersReleased: unable to get released buffer set");
616        slotMask = 0xffffffff;
617    }
618
619    ALOGV("onBuffersReleased: 0x%08x", slotMask);
620
621    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
622        if ((slotMask & 0x01) != 0) {
623            mBufferSlot[i] = NULL;
624        }
625        slotMask >>= 1;
626    }
627}
628
629status_t GraphicBufferSource::setRepeatPreviousFrameDelayUs(
630        int64_t repeatAfterUs) {
631    Mutex::Autolock autoLock(mMutex);
632
633    if (mExecuting || repeatAfterUs <= 0ll) {
634        return INVALID_OPERATION;
635    }
636
637    mRepeatAfterUs = repeatAfterUs;
638
639    return OK;
640}
641
642void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) {
643    switch (msg->what()) {
644        case kWhatRepeatLastFrame:
645        {
646            Mutex::Autolock autoLock(mMutex);
647
648            int32_t generation;
649            CHECK(msg->findInt32("generation", &generation));
650
651            if (generation != mRepeatLastFrameGeneration) {
652                // stale
653                break;
654            }
655
656            if (!mExecuting || mNumFramesAvailable > 0) {
657                break;
658            }
659
660            bool success = repeatLatestSubmittedBuffer_l();
661
662            if (success) {
663                ALOGV("repeatLatestSubmittedBuffer_l SUCCESS");
664            } else {
665                ALOGV("repeatLatestSubmittedBuffer_l FAILURE");
666                mRepeatBufferDeferred = true;
667            }
668            break;
669        }
670
671        default:
672            TRESPASS();
673    }
674}
675
676}  // namespace android
677