void OmxDecoder::ReleaseMediaResources() { mVideoCodecRequest.DisconnectIfExists(); mMediaResourcePromise.RejectIfExists(true, __func__); ReleaseVideoBuffer(); ReleaseAudioBuffer(); { Mutex::Autolock autoLock(mPendingVideoBuffersLock); MOZ_ASSERT(mPendingRecycleTexutreClients.empty()); // Release all pending recycle TextureClients, if they are not recycled yet. // This should not happen. See Bug 1042308. if (!mPendingRecycleTexutreClients.empty()) { printf_stderr("OmxDecoder::ReleaseMediaResources -- TextureClients are not recycled yet\n"); for (std::set<TextureClient*>::iterator it=mPendingRecycleTexutreClients.begin(); it!=mPendingRecycleTexutreClients.end(); it++) { GrallocTextureData* client = static_cast<GrallocTextureData*>((*it)->GetInternalData()); (*it)->ClearRecycleCallback(); if (client->GetMediaBuffer()) { mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), (*it)->GetAndResetReleaseFenceHandle())); } } mPendingRecycleTexutreClients.clear(); } } { // Free all pending video buffers. Mutex::Autolock autoLock(mSeekLock); ReleaseAllPendingVideoBuffersLocked(); } if (mVideoSource.get()) { mVideoSource->stop(); mVideoSource.clear(); } if (mAudioSource.get()) { mAudioSource->stop(); mAudioSource.clear(); } mNativeWindowClient.clear(); mNativeWindow.clear(); // Reset this variable to make the first seek go to the previous keyframe // when resuming mLastSeekTime = -1; }
void OmxDecoder::RecycleCallbackImp(TextureClient* aClient) { aClient->ClearRecycleCallback(); { Mutex::Autolock autoLock(mPendingVideoBuffersLock); if (mPendingRecycleTexutreClients.find(aClient) == mPendingRecycleTexutreClients.end()) { printf_stderr("OmxDecoder::RecycleCallbackImp -- TextureClient is not pending recycle\n"); return; } mPendingRecycleTexutreClients.erase(aClient); GrallocTextureData* grallocData = static_cast<GrallocTextureData*>(aClient->GetInternalData()); if (grallocData->GetMediaBuffer()) { mPendingVideoBuffers.push(BufferItem(grallocData->GetMediaBuffer(), aClient->GetAndResetReleaseFenceHandle())); } } sp<AMessage> notify = new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id()); // post AMessage to OmxDecoder via ALooper. notify->post(); }
status_t GonkBufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); if ((w && !h) || (!w && h)) { ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } status_t returnFlags(OK); int buf = INVALID_BUFFER_SLOT; { // Scope for the lock Mutex::Autolock lock(mMutex); if (format == 0) { format = mDefaultBufferFormat; } // turn on usage bits the consumer requested usage |= mConsumerUsageBits; int found = -1; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { if (mAbandoned) { ST_LOGE("dequeueBuffer: GonkBufferQueue has been abandoned!"); return NO_INIT; } const int maxBufferCount = getMaxBufferCountLocked(); // Free up any buffers that are in slots beyond the max buffer // count. //for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { // assert(mSlots[i].mBufferState == BufferSlot::FREE); // if (mSlots[i].mGraphicBuffer != NULL) { // freeBufferLocked(i); // returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS; // } //} // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; dequeuedCount = 0; for (int i = 0; i < maxBufferCount; i++) { const int state = mSlots[i].mBufferState; if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } if (state == BufferSlot::FREE) { /* We return the oldest of the free buffers to avoid * stalling the producer if possible. This is because * the consumer may still have pending reads of the * buffers in flight. */ if ((found < 0) || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { found = i; } } } // clients are not allowed to dequeue more than one buffer // if they didn't set a buffer count. if (!mOverrideMaxBufferCount && dequeuedCount) { ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " "setting the buffer count"); return -EINVAL; } // See whether a buffer has been queued since the last // setBufferCount so we know whether to perform the min undequeued // buffers check below. if (mBufferHasBeenQueued) { // make sure the client is not trying to dequeue more buffers // than allowed. const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); const int minUndequeuedCount = getMinUndequeuedBufferCountLocked(); if (newUndequeuedCount < minUndequeuedCount) { ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " "exceeded (dequeued=%d undequeudCount=%d)", minUndequeuedCount, dequeuedCount, newUndequeuedCount); return -EBUSY; } } // If no buffer is found, wait for a buffer to be released or for // the max buffer count to change. tryAgain = found == INVALID_BUFFER_SLOT; if (tryAgain) { mDequeueCondition.wait(mMutex); } } if (found == INVALID_BUFFER_SLOT) { // This should not happen. ST_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } buf = found; *outBuf = found; const bool useDefaultSize = !w && !h; if (useDefaultSize) { // use the default size w = mDefaultWidth; h = mDefaultHeight; } mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); if ((buffer == NULL) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->height) != h) || (uint32_t(buffer->format) != format) || ((uint32_t(buffer->usage) & usage) != usage)) { mSlots[buf].mAcquireCalled = false; mSlots[buf].mGraphicBuffer = NULL; mSlots[buf].mRequestBufferCalled = false; mSlots[buf].mFence = Fence::NO_FENCE; if (mSlots[buf].mTextureClient) { mSlots[buf].mTextureClient->ClearRecycleCallback(); // release TextureClient in ImageBridge thread RefPtr<TextureClientReleaseTask> task = MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient); mSlots[buf].mTextureClient = NULL; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget()); } returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } *outFence = mSlots[buf].mFence; mSlots[buf].mFence = Fence::NO_FENCE; } // end lock scope sp<GraphicBuffer> graphicBuffer; if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { usage |= GraphicBuffer::USAGE_HW_TEXTURE; RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton(); GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w,h), format, gfx::BackendType::NONE, usage, allocator); if (!texData) { ST_LOGE("dequeueBuffer: failed to alloc gralloc buffer"); return -ENOMEM; } RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator); sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer(); { // Scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mTextureClient = textureClient; ST_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, mSlots[buf].mGraphicBuffer->handle); //mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; }
/*static*/ UniquePtr<SharedSurface_Gralloc> SharedSurface_Gralloc::Create(GLContext* prodGL, const GLFormats& formats, const gfx::IntSize& size, bool hasAlpha, layers::TextureFlags flags, LayersIPCChannel* allocator) { GLLibraryEGL* egl = &sEGLLibrary; MOZ_ASSERT(egl); UniquePtr<SharedSurface_Gralloc> ret; DEBUG_PRINT("SharedSurface_Gralloc::Create -------\n"); if (!HasExtensions(egl, prodGL)) return Move(ret); gfxContentType type = hasAlpha ? gfxContentType::COLOR_ALPHA : gfxContentType::COLOR; GrallocTextureData* texData = GrallocTextureData::CreateForGLRendering( size, gfxPlatform::GetPlatform()->Optimal2DFormatForContent(type), allocator ); if (!texData) { return Move(ret); } RefPtr<TextureClient> grallocTC = new TextureClient(texData, flags, allocator); sp<GraphicBuffer> buffer = texData->GetGraphicBuffer(); EGLDisplay display = egl->Display(); EGLClientBuffer clientBuffer = buffer->getNativeBuffer(); EGLint attrs[] = { LOCAL_EGL_NONE, LOCAL_EGL_NONE }; EGLImage image = egl->fCreateImage(display, EGL_NO_CONTEXT, LOCAL_EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); if (!image) { return Move(ret); } prodGL->MakeCurrent(); GLuint prodTex = 0; prodGL->fGenTextures(1, &prodTex); ScopedBindTexture autoTex(prodGL, prodTex); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); prodGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image); egl->fDestroyImage(display, image); ret.reset( new SharedSurface_Gralloc(prodGL, size, hasAlpha, egl, allocator, grallocTC, prodTex) ); DEBUG_PRINT("SharedSurface_Gralloc::Create: success -- surface %p," " GraphicBuffer %p.\n", ret.get(), buffer.get()); return Move(ret); }
status_t GonkNativeWindow::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { if ((w && !h) || (!w && h)) { CNW_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } status_t returnFlags(OK); bool updateFormat = false; bool alloc = false; int buf = INVALID_BUFFER_SLOT; { Mutex::Autolock lock(mMutex); int found = -1; int dequeuedCount = 0; int renderingCount = 0; bool tryAgain = true; CNW_LOGD("dequeueBuffer: E"); while (tryAgain) { if (mAbandoned) { CNW_LOGE("dequeueBuffer: GonkNativeWindow has been abandoned!"); return NO_INIT; } // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; dequeuedCount = 0; renderingCount = 0; for (int i = 0; i < mBufferCount; i++) { const int state = mSlots[i].mBufferState; switch (state) { case BufferSlot::DEQUEUED: CNW_LOGD("dequeueBuffer: slot %d is DEQUEUED\n", i); dequeuedCount++; break; case BufferSlot::RENDERING: CNW_LOGD("dequeueBuffer: slot %d is RENDERING\n", i); renderingCount++; break; case BufferSlot::FREE: CNW_LOGD("dequeueBuffer: slot %d is FREE\n", i); /* We return the oldest of the free buffers to avoid * stalling the producer if possible. This is because * the consumer may still have pending reads of the * buffers in flight. */ if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { found = i; } break; default: CNW_LOGD("dequeueBuffer: slot %d is %d\n", i, state); break; } } // See whether a buffer has been in RENDERING state since the last // setBufferCount so we know whether to perform the // MIN_UNDEQUEUED_BUFFERS check below. if (renderingCount > 0) { // make sure the client is not trying to dequeue more buffers // than allowed. const int avail = mBufferCount - (dequeuedCount + 1); if (avail < MIN_UNDEQUEUED_BUFFERS) { CNW_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " "(dequeued=%d)", MIN_UNDEQUEUED_BUFFERS, dequeuedCount); return -EBUSY; } } // we're in synchronous mode and didn't find a buffer, we need to // wait for some buffers to be consumed tryAgain = (found == INVALID_BUFFER_SLOT); if (tryAgain) { CNW_LOGD("dequeueBuffer: Try again"); mDequeueCondition.wait(mMutex); CNW_LOGD("dequeueBuffer: Now"); } } if (found == INVALID_BUFFER_SLOT) { // This should not happen. CNW_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } buf = found; *outBuf = found; const bool useDefaultSize = !w && !h; if (useDefaultSize) { // use the default size w = mDefaultWidth; h = mDefaultHeight; } updateFormat = (format != 0); if (!updateFormat) { // keep the current (or default) format format = mPixelFormat; } mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer); if ((gbuf == NULL) || ((uint32_t(gbuf->width) != w) || (uint32_t(gbuf->height) != h) || (uint32_t(gbuf->format) != format) || ((uint32_t(gbuf->usage) & usage) != usage))) { mSlots[buf].mGraphicBuffer = NULL; mSlots[buf].mRequestBufferCalled = false; if (mSlots[buf].mTextureClient) { mSlots[buf].mTextureClient->ClearRecycleCallback(); // release TextureClient in ImageBridge thread RefPtr<TextureClientReleaseTask> task = MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient); mSlots[buf].mTextureClient = NULL; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget()); } alloc = true; } } // end lock scope if (alloc) { ClientIPCAllocator* allocator = ImageBridgeChild::GetSingleton(); usage |= GraphicBuffer::USAGE_HW_TEXTURE; GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w, h), format, gfx::BackendType::NONE, usage, allocator); if (!texData) { return -ENOMEM; } RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::DEALLOCATE_CLIENT, allocator); { // Scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { CNW_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } if (updateFormat) { mPixelFormat = format; } mSlots[buf].mGraphicBuffer = texData->GetGraphicBuffer(); mSlots[buf].mTextureClient = textureClient; returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, mSlots[buf].mGraphicBuffer->handle); } } CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, mSlots[buf].mGraphicBuffer->handle ); CNW_LOGD("dequeueBuffer: X"); return returnFlags; }
status_t GonkBufferQueueProducer::dequeueBuffer(int *outSlot, sp<android::Fence> *outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; } // Autolock scope ALOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", async ? "true" : "false", width, height, format, usage); if ((width && !height) || (!width && height)) { ALOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); return BAD_VALUE; } status_t returnFlags = NO_ERROR; // Reset slot *outSlot = GonkBufferQueueCore::INVALID_BUFFER_SLOT; bool attachedByConsumer = false; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; int found; status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, &found, &returnFlags); if (status != NO_ERROR) { return status; } // This should not happen if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { ALOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; attachedByConsumer = mSlots[found].mAttachedByConsumer; const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; } mSlots[found].mBufferState = GonkBufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || (static_cast<uint32_t>(buffer->width) != width) || (static_cast<uint32_t>(buffer->height) != height) || (static_cast<uint32_t>(buffer->format) != format) || ((static_cast<uint32_t>(buffer->usage) & usage) != usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; mSlots[found].mRequestBufferCalled = false; mSlots[found].mFence = Fence::NO_FENCE; if (mSlots[found].mTextureClient) { mSlots[found].mTextureClient->ClearRecycleCallback(); // release TextureClient in ImageBridge thread TextureClientReleaseTask* task = new TextureClientReleaseTask(mSlots[found].mTextureClient); mSlots[found].mTextureClient = NULL; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); } returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { ALOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); } *outFence = mSlots[found].mFence; mSlots[found].mFence = Fence::NO_FENCE; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { ISurfaceAllocator* allocator = ImageBridgeChild::GetSingleton(); usage |= GraphicBuffer::USAGE_HW_TEXTURE; GrallocTextureData* texData = GrallocTextureData::Create(IntSize(width,height), format, gfx::BackendType::NONE, usage, allocator); if (!texData) { ALOGE("dequeueBuffer: failed to alloc gralloc buffer"); return -ENOMEM; } RefPtr<TextureClient> textureClient = TextureClient::CreateWithData( texData, TextureFlags::DEALLOCATE_CLIENT, allocator); sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned"); return NO_INIT; } mSlots[*outSlot].mFrameNumber = UINT32_MAX; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; mSlots[*outSlot].mTextureClient = textureClient; } // Autolock scope } if (attachedByConsumer) { returnFlags |= BUFFER_NEEDS_REALLOCATION; } ALOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); return returnFlags; }