status_t GraphicBufferSource::signalEndOfInputStream() { Mutex::Autolock autoLock(mMutex); ALOGV("signalEndOfInputStream: exec=%d avail=%zu eos=%d", mExecuting, mNumFramesAvailable, mEndOfStream); if (mEndOfStream) { ALOGE("EOS was already signaled"); return INVALID_OPERATION; } // Set the end-of-stream flag. If no frames are pending from the // BufferQueue, and a codec buffer is available, and we're executing, // we initiate the EOS from here. Otherwise, we'll let // codecBufferEmptied() (or omxExecuting) do it. // // Note: if there are no pending frames and all codec buffers are // available, we *must* submit the EOS from here or we'll just // stall since no future events are expected. mEndOfStream = true; if (mExecuting && mNumFramesAvailable == 0) { submitEndOfInputStream_l(); } return OK; }
void GraphicBufferSource::omxExecuting() { Mutex::Autolock autoLock(mMutex); ALOGV("--> executing; avail=%zu, codec vec size=%zd", mNumFramesAvailable, mCodecBuffers.size()); CHECK(!mExecuting); mExecuting = true; // Start by loading up as many buffers as possible. We want to do this, // rather than just submit the first buffer, to avoid a degenerate case: // if all BQ buffers arrive before we start executing, and we only submit // one here, the other BQ buffers will just sit until we get notified // that the codec buffer has been released. We'd then acquire and // submit a single additional buffer, repeatedly, never using more than // one codec buffer simultaneously. (We could instead try to submit // all BQ buffers whenever any codec buffer is freed, but if we get the // initial conditions right that will never be useful.) while (mNumFramesAvailable) { if (!fillCodecBuffer_l()) { ALOGV("stop load with frames available (codecAvail=%d)", isCodecBufferAvailable_l()); break; } } ALOGV("done loading initial frames, avail=%zu", mNumFramesAvailable); // If EOS has already been signaled, and there are no more frames to // submit, try to send EOS now as well. if (mEndOfStream && mNumFramesAvailable == 0) { submitEndOfInputStream_l(); } if (mRepeatAfterUs > 0ll && mLooper == NULL) { mReflector = new AHandlerReflector<GraphicBufferSource>(this); mLooper = new ALooper; mLooper->registerHandler(mReflector); mLooper->start(); if (mLatestBufferId >= 0) { sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector); msg->setInt32("generation", ++mRepeatLastFrameGeneration); msg->post(mRepeatAfterUs); } } }
void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd) { Mutex::Autolock autoLock(mMutex); if (!mExecuting) { return; } int cbi = findMatchingCodecBuffer_l(header); if (cbi < 0) { // This should never happen. ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); if (fenceFd >= 0) { ::close(fenceFd); } return; } ALOGV("codecBufferEmptied h=%p size=%" PRIu32 " filled=%" PRIu32 " p=%p", header, header->nAllocLen, header->nFilledLen, header->pBuffer); CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); // header->nFilledLen may not be the original value, so we can't compare // that to zero to see of this was the EOS buffer. Instead we just // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { if (!(mEndOfStream && mEndOfStreamSent)) { // This can happen when broken code sends us the same buffer // twice in a row. ALOGE("ERROR: codecBufferEmptied on non-EOS null buffer " "(buffer emptied twice?)"); } // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. if (fenceFd >= 0) { ::close(fenceFd); } return; } if (EXTRA_CHECK && header->nAllocLen >= sizeof(MetadataBufferType)) { // Pull the graphic buffer handle back out of the buffer, and confirm // that it matches expectations. OMX_U8* data = header->pBuffer; MetadataBufferType type = *(MetadataBufferType *)data; if (type == kMetadataBufferTypeGrallocSource && header->nAllocLen >= sizeof(VideoGrallocMetadata)) { VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)data; if (grallocMeta.pHandle != codecBuffer.mGraphicBuffer->handle) { // should never happen ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", grallocMeta.pHandle, codecBuffer.mGraphicBuffer->handle); CHECK(!"codecBufferEmptied: mismatched buffer"); } } else if (type == kMetadataBufferTypeANWBuffer && header->nAllocLen >= sizeof(VideoNativeMetadata)) { VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)data; if (nativeMeta.pBuffer != codecBuffer.mGraphicBuffer->getNativeBuffer()) { // should never happen ALOGE("codecBufferEmptied: buffer is %p, expected %p", nativeMeta.pBuffer, codecBuffer.mGraphicBuffer->getNativeBuffer()); CHECK(!"codecBufferEmptied: mismatched buffer"); } } } // Find matching entry in our cached copy of the BufferQueue slots. // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. int id = codecBuffer.mBuf; sp<Fence> fence = new Fence(fenceFd); if (mBufferSlot[id] != NULL && mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { ALOGV("cbi %d matches bq slot %d, handle=%p", cbi, id, mBufferSlot[id]->handle); if (id == mLatestBufferId) { CHECK_GT(mLatestBufferUseCount--, 0); } else { releaseBuffer(id, codecBuffer.mFrameNumber, mBufferSlot[id], fence); } } else { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); // we will not reuse codec buffer, so there is no need to wait for fence } // Mark the codec buffer as available by clearing the GraphicBuffer ref. codecBuffer.mGraphicBuffer = NULL; if (mNumFramesAvailable) { // Fill this codec buffer. CHECK(!mEndOfStreamSent); ALOGV("buffer freed, %zu frames avail (eos=%d)", mNumFramesAvailable, mEndOfStream); fillCodecBuffer_l(); } else if (mEndOfStream) { // No frames available, but EOS is pending, so use this buffer to // send that. ALOGV("buffer freed, EOS pending"); submitEndOfInputStream_l(); } else if (mRepeatBufferDeferred) { bool success = repeatLatestBuffer_l(); if (success) { ALOGV("deferred repeatLatestBuffer_l SUCCESS"); } else { ALOGV("deferred repeatLatestBuffer_l FAILURE"); } mRepeatBufferDeferred = false; } return; }
void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { Mutex::Autolock autoLock(mMutex); if (!mExecuting) { return; } int cbi = findMatchingCodecBuffer_l(header); if (cbi < 0) { // This should never happen. ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); return; } CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); // header->nFilledLen may not be the original value, so we can't compare // that to zero to see of this was the EOS buffer. Instead we just // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { if (!(mEndOfStream && mEndOfStreamSent)) { // This can happen when broken code sends us the same buffer // twice in a row. ALOGE("ERROR: codecBufferEmptied on non-EOS null buffer " "(buffer emptied twice?)"); } // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. return; } if (EXTRA_CHECK) { // Pull the graphic buffer handle back out of the buffer, and confirm // that it matches expectations. OMX_U8* data = header->pBuffer; buffer_handle_t bufferHandle; memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { // should never happen ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", bufferHandle, codecBuffer.mGraphicBuffer->handle); CHECK(!"codecBufferEmptied: mismatched buffer"); } } // Find matching entry in our cached copy of the BufferQueue slots. // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. int id = codecBuffer.mBuf; if (mBufferSlot[id] != NULL && mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { if (id == mLatestSubmittedBufferId) { CHECK_GT(mLatestSubmittedBufferUseCount--, 0); } else { mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); } } // Mark the codec buffer as available by clearing the GraphicBuffer ref. codecBuffer.mGraphicBuffer = NULL; if (mNumFramesAvailable) { // Fill this codec buffer. CHECK(!mEndOfStreamSent); fillCodecBuffer_l(); } else if (mEndOfStream) { // No frames available, but EOS is pending, so use this buffer to // send that. submitEndOfInputStream_l(); } else if (mRepeatBufferDeferred) { bool success = repeatLatestSubmittedBuffer_l(); mRepeatBufferDeferred = false; } return; }
void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { Mutex::Autolock autoLock(mMutex); CHECK(mExecuting); // could this happen if app stop()s early? int cbi = findMatchingCodecBuffer_l(header); if (cbi < 0) { // This should never happen. ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); return; } ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p", header, header->nAllocLen, header->nFilledLen, header->pBuffer); CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); // header->nFilledLen may not be the original value, so we can't compare // that to zero to see of this was the EOS buffer. Instead we just // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { CHECK(mEndOfStream && mEndOfStreamSent); // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. return; } if (EXTRA_CHECK) { // Pull the graphic buffer handle back out of the buffer, and confirm // that it matches expectations. OMX_U8* data = header->pBuffer; buffer_handle_t bufferHandle; memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { // should never happen ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", bufferHandle, codecBuffer.mGraphicBuffer->handle); CHECK(!"codecBufferEmptied: mismatched buffer"); } } // Find matching entry in our cached copy of the BufferQueue slots. // If we find a match, release that slot. If we don't, the BufferQueue // has dropped that GraphicBuffer, and there's nothing for us to release. // // (We could store "id" in CodecBuffer and avoid the slot search.) int id; for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) { if (mBufferSlot[id] == NULL) { continue; } if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { ALOGV("cbi %d matches bq slot %d, handle=%p", cbi, id, mBufferSlot[id]->handle); mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); break; } } if (id == BufferQueue::NUM_BUFFER_SLOTS) { ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", cbi); } // Mark the codec buffer as available by clearing the GraphicBuffer ref. codecBuffer.mGraphicBuffer = NULL; if (mNumFramesAvailable) { // Fill this codec buffer. CHECK(!mEndOfStreamSent); ALOGV("buffer freed, %d frames avail (eos=%d)", mNumFramesAvailable, mEndOfStream); fillCodecBuffer_l(); } else if (mEndOfStream) { // No frames available, but EOS is pending, so use this buffer to // send that. ALOGV("buffer freed, EOS pending"); submitEndOfInputStream_l(); } return; }