From 1765d6e0c962d2b89897c0278f969084f632d181 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Tue, 25 Oct 2011 14:50:16 -0700 Subject: [PATCH] Stagefright: push blank buffers when tearing down This change makes OMXCodec push RGB 565 buffers filled with black to an ANativeWindow when tearing down after decoding to protected gralloc buffers. This allows the OMX tear down to zero out any protected buffers that were used without the possibility that the buffer is still being used by SurfaceFlinger or HWComposer. Bug: 5483222 Change-Id: I8acedd81a7bb67dfdc2fd15733e3375b6ce8d560 --- include/media/stagefright/OMXCodec.h | 1 + media/libstagefright/OMXCodec.cpp | 160 +++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 0d5a726386..c21d19d458 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -277,6 +277,7 @@ private: status_t queueBufferToNativeWindow(BufferInfo *info); status_t cancelBufferToNativeWindow(BufferInfo *info); BufferInfo* dequeueBufferFromNativeWindow(); + status_t pushBlankBuffersToNativeWindow(); status_t freeBuffersOnPort( OMX_U32 portIndex, bool onlyThoseWeOwn = false); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 00d414ce39..b20bfcb73a 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -2001,6 +2001,157 @@ OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { return bufInfo; } +status_t OMXCodec::pushBlankBuffersToNativeWindow() { + status_t err = NO_ERROR; + ANativeWindowBuffer* anb = NULL; + int numBufs = 0; + int minUndequeuedBufs = 0; + + // We need to reconnect to the ANativeWindow as a CPU client to ensure that + // no frames get dropped by SurfaceFlinger assuming that these are video + // frames. + err = native_window_api_disconnect(mNativeWindow.get(), + NATIVE_WINDOW_API_MEDIA); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: api_disconnect failed: %s (%d)", + strerror(-err), -err); + return err; + } + + err = native_window_api_connect(mNativeWindow.get(), + NATIVE_WINDOW_API_CPU); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: api_connect failed: %s (%d)", + strerror(-err), -err); + return err; + } + + err = native_window_set_scaling_mode(mNativeWindow.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1, + HAL_PIXEL_FORMAT_RGBX_8888); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + err = native_window_set_usage(mNativeWindow.get(), + GRALLOC_USAGE_SW_WRITE_OFTEN); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: set_usage failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + err = mNativeWindow->query(mNativeWindow.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query " + "failed: %s (%d)", strerror(-err), -err); + goto error; + } + + numBufs = minUndequeuedBufs + 1; + err = native_window_set_buffer_count(mNativeWindow.get(), numBufs); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + // We push numBufs + 1 buffers to ensure that we've drawn into the same + // buffer twice. This should guarantee that the buffer has been displayed + // on the screen and then been replaced, so an previous video frames are + // guaranteed NOT to be currently displayed. + for (int i = 0; i < numBufs + 1; i++) { + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &anb); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + sp buf(new GraphicBuffer(anb, false)); + err = mNativeWindow->lockBuffer(mNativeWindow.get(), + buf->getNativeBuffer()); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: lockBuffer failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + // Fill the buffer with the a 1x1 checkerboard pattern ;) + uint32_t* img = NULL; + err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: lock failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + *img = 0; + + err = buf->unlock(); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: unlock failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + err = mNativeWindow->queueBuffer(mNativeWindow.get(), + buf->getNativeBuffer()); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: queueBuffer failed: %s (%d)", + strerror(-err), -err); + goto error; + } + + anb = NULL; + } + +error: + + if (err != NO_ERROR) { + // Clean up after an error. + if (anb != NULL) { + mNativeWindow->cancelBuffer(mNativeWindow.get(), anb); + } + + native_window_api_disconnect(mNativeWindow.get(), + NATIVE_WINDOW_API_CPU); + native_window_api_connect(mNativeWindow.get(), + NATIVE_WINDOW_API_MEDIA); + + return err; + } else { + // Clean up after success. + err = native_window_api_disconnect(mNativeWindow.get(), + NATIVE_WINDOW_API_CPU); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: api_disconnect failed: %s (%d)", + strerror(-err), -err); + return err; + } + + err = native_window_api_connect(mNativeWindow.get(), + NATIVE_WINDOW_API_MEDIA); + if (err != NO_ERROR) { + LOGE("error pushing blank frames: api_connect failed: %s (%d)", + strerror(-err), -err); + return err; + } + + return NO_ERROR; + } +} + int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) { CHECK(mIsEncoder); @@ -2589,6 +2740,15 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexOutput] = ENABLED; + if ((mFlags & kEnableGrallocUsageProtected) && + mNativeWindow != NULL) { + // We push enough 1x1 blank buffers to ensure that one of + // them has made it to the display. This allows the OMX + // component teardown to zero out any protected buffers + // without the risk of scanning out one of those buffers. + pushBlankBuffersToNativeWindow(); + } + setState(IDLE_TO_LOADED); } break; -- 2.11.0