| #include <private/dvr/buffer_hub_queue_producer.h> |
| |
| #include <base/logging.h> |
| #include <gui/IProducerListener.h> |
| #include <gui/Surface.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace android { |
| namespace dvr { |
| |
| namespace { |
| |
| // Default dimensions before setDefaultBufferSize is called by the consumer. |
| constexpr uint32_t kDefaultWidth = 1; |
| constexpr uint32_t kDefaultHeight = 1; |
| |
| // Default format before setDefaultBufferFormat is called by the consumer. |
| constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888; |
| constexpr int kDefaultConsumerUsageBits = 0; |
| |
| // Default transform hint before setTransformHint is called by the consumer. |
| constexpr uint32_t kDefaultTransformHint = 0; |
| |
| constexpr int kTestApi = NATIVE_WINDOW_API_CPU; |
| constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL; |
| constexpr int kTestApiInvalid = 0xDEADBEEF; |
| constexpr int kTestProducerUsageBits = 0; |
| constexpr bool kTestControlledByApp = true; |
| |
| // Builder pattern to slightly vary *almost* correct input |
| // -- avoids copying and pasting |
| struct QueueBufferInputBuilder { |
| IGraphicBufferProducer::QueueBufferInput build() { |
| return IGraphicBufferProducer::QueueBufferInput( |
| mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode, |
| mTransform, mFence); |
| } |
| |
| QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { |
| this->mTimestamp = timestamp; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { |
| this->mIsAutoTimestamp = isAutoTimestamp; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) { |
| this->mDataSpace = dataSpace; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setCrop(Rect crop) { |
| this->mCrop = crop; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setScalingMode(int scalingMode) { |
| this->mScalingMode = scalingMode; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setTransform(uint32_t transform) { |
| this->mTransform = transform; |
| return *this; |
| } |
| |
| QueueBufferInputBuilder& setFence(sp<Fence> fence) { |
| this->mFence = fence; |
| return *this; |
| } |
| |
| private: |
| int64_t mTimestamp{1384888611}; |
| bool mIsAutoTimestamp{false}; |
| android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN}; |
| Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)}; |
| int mScalingMode{0}; |
| uint32_t mTransform{0}; |
| sp<Fence> mFence{Fence::NO_FENCE}; |
| }; |
| |
| // This is a test that covers our implementation of bufferhubqueue-based |
| // IGraphicBufferProducer. |
| class BufferHubQueueProducerTest : public ::testing::Test { |
| protected: |
| virtual void SetUp() { |
| const ::testing::TestInfo* const testInfo = |
| ::testing::UnitTest::GetInstance()->current_test_info(); |
| ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(), |
| testInfo->name()); |
| |
| mProducer = BufferHubQueueProducer::Create(); |
| ASSERT_TRUE(mProducer != nullptr); |
| mSurface = new Surface(mProducer, true); |
| ASSERT_TRUE(mSurface != nullptr); |
| } |
| |
| // Connect to a producer in a 'correct' fashion. |
| void ConnectProducer() { |
| IGraphicBufferProducer::QueueBufferOutput output; |
| // Can connect the first time. |
| ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi, |
| kTestControlledByApp, &output)); |
| } |
| |
| // Dequeue a buffer in a 'correct' fashion. |
| // Precondition: Producer is connected. |
| void DequeueBuffer(int* outSlot) { |
| sp<Fence> fence; |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence)); |
| } |
| |
| void DequeueBuffer(int* outSlot, sp<Fence>* outFence) { |
| ASSERT_NE(nullptr, outSlot); |
| ASSERT_NE(nullptr, outFence); |
| |
| int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth, |
| kDefaultHeight, kDefaultFormat, |
| kTestProducerUsageBits, nullptr); |
| // BUFFER_NEEDS_REALLOCATION can be either on or off. |
| ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret); |
| |
| // Slot number should be in boundary. |
| ASSERT_LE(0, *outSlot); |
| ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot); |
| } |
| |
| // Create a generic "valid" input for queueBuffer |
| // -- uses the default buffer format, width, etc. |
| static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { |
| return QueueBufferInputBuilder().build(); |
| } |
| |
| const sp<IProducerListener> kDummyListener{new DummyProducerListener}; |
| |
| sp<BufferHubQueueProducer> mProducer; |
| sp<Surface> mSurface; |
| }; |
| |
| TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| // NULL output returns BAD_VALUE |
| EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, |
| kTestControlledByApp, nullptr)); |
| |
| // Invalid API returns bad value |
| EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid, |
| kTestControlledByApp, &output)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| // Can't connect when there is already a producer connected. |
| IGraphicBufferProducer::QueueBufferOutput output; |
| EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, |
| kTestControlledByApp, &output)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| // Must disconnect with same API number |
| EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther)); |
| // API must not be out of range |
| EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| int32_t value = -1; |
| EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); |
| EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value)); |
| |
| EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); |
| EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value)); |
| |
| EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); |
| EXPECT_EQ(kDefaultFormat, value); |
| |
| EXPECT_EQ(NO_ERROR, |
| mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); |
| EXPECT_LE(0, value); |
| EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, static_cast<size_t>(value)); |
| |
| EXPECT_EQ(NO_ERROR, |
| mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); |
| EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue |
| |
| EXPECT_EQ(NO_ERROR, |
| mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); |
| EXPECT_EQ(kDefaultConsumerUsageBits, value); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| // One past the end of the last 'query' enum value. Update this if we add more |
| // enums. |
| const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1; |
| |
| int value; |
| // What was out of range |
| EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value)); |
| EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value)); |
| EXPECT_EQ(BAD_VALUE, |
| mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value)); |
| |
| // Some enums from window.h are 'invalid' |
| EXPECT_EQ(BAD_VALUE, |
| mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value)); |
| EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value)); |
| EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value)); |
| EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value)); |
| EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); |
| |
| // Value was NULL |
| EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { |
| int slot = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| // Request the buffer (pre-requisite for queueing) |
| sp<GraphicBuffer> buffer; |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| // A generic "valid" input |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| // Queue the buffer back into the BQ |
| ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); |
| |
| EXPECT_EQ(kDefaultWidth, output.width); |
| EXPECT_EQ(kDefaultHeight, output.height); |
| EXPECT_EQ(kDefaultTransformHint, output.transformHint); |
| |
| // BufferHubQueue delivers buffers to consumer immediately. |
| EXPECT_EQ(0u, output.numPendingBuffers); |
| |
| // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to |
| // be a SurfaceFlinger specific optimization. |
| EXPECT_EQ(0u, output.nextFrameNumber); |
| |
| // Buffer was not in the dequeued state |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| // Test invalid slot number |
| TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| // A generic "valid" input |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output)); |
| EXPECT_EQ(BAD_VALUE, |
| mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output)); |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS, |
| input, &output)); |
| } |
| |
| // Slot was not in the dequeued state (all slots start out in Free state) |
| TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output)); |
| } |
| |
| // Slot was enqueued without requesting a buffer |
| TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) { |
| int slot = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| // Test when fence was NULL |
| TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { |
| int slot = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| sp<GraphicBuffer> buffer; |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| sp<Fence> nullFence = NULL; |
| |
| IGraphicBufferProducer::QueueBufferInput input = |
| QueueBufferInputBuilder().setFence(nullFence).build(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| // Test scaling mode was invalid |
| TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { |
| int slot = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| sp<GraphicBuffer> buffer; |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| IGraphicBufferProducer::QueueBufferInput input = |
| QueueBufferInputBuilder().setScalingMode(-1).build(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| |
| input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| // Test crop rect is out of bounds of the buffer dimensions |
| TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { |
| int slot = -1; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| sp<GraphicBuffer> buffer; |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| IGraphicBufferProducer::QueueBufferInput input = |
| QueueBufferInputBuilder() |
| .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1)) |
| .build(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { |
| int slot = -1; |
| sp<Fence> fence; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); |
| |
| // Should be able to cancel buffer after a dequeue. |
| EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { |
| return; |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| int minUndequeuedBuffers; |
| ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, |
| &minUndequeuedBuffers)); |
| |
| const int minBuffers = 1; |
| const int maxBuffers = |
| BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; |
| |
| ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) |
| << "async mode: " << false; |
| ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers)) |
| << "bufferCount: " << minBuffers; |
| |
| // Should now be able to dequeue up to minBuffers times |
| // Should now be able to dequeue up to maxBuffers times |
| int slot = -1; |
| for (int i = 0; i < minBuffers; ++i) { |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| } |
| |
| ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers)); |
| |
| // queue the first buffer to enable max dequeued buffer count checking |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| sp<GraphicBuffer> buffer; |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); |
| |
| sp<Fence> fence; |
| for (int i = 0; i < maxBuffers; ++i) { |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); |
| } |
| |
| // Cancel a buffer, so we can decrease the buffer count |
| ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); |
| |
| // Should now be able to decrease the max dequeued count by 1 |
| ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| |
| int minUndequeuedBuffers; |
| ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, |
| &minUndequeuedBuffers)); |
| |
| const int minBuffers = 1; |
| const int maxBuffers = |
| BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; |
| |
| ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) |
| << "async mode: " << false; |
| // Buffer count was out of range |
| EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0)) |
| << "bufferCount: " << 0; |
| EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1)) |
| << "bufferCount: " << maxBuffers + 1; |
| |
| // Set max dequeue count to 2 |
| ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2)); |
| // Dequeue 2 buffers |
| int slot = -1; |
| sp<Fence> fence; |
| for (int i = 0; i < 2; i++) { |
| ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & |
| (mProducer->dequeueBuffer( |
| &slot, &fence, kDefaultWidth, kDefaultHeight, |
| kDefaultFormat, kTestProducerUsageBits, nullptr))) |
| << "slot: " << slot; |
| } |
| |
| // Client has too many buffers dequeued |
| EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1)) |
| << "bufferCount: " << minBuffers; |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, |
| DisconnectedProducerReturnsError_dequeueBuffer) { |
| int slot = -1; |
| sp<Fence> fence; |
| |
| ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, |
| kDefaultHeight, kDefaultFormat, |
| kTestProducerUsageBits, nullptr)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, |
| DisconnectedProducerReturnsError_requestBuffer) { |
| int slot = -1; |
| sp<GraphicBuffer> buffer; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| |
| // Shouldn't be able to request buffer after disconnect. |
| ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); |
| ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, |
| DisconnectedProducerReturnsError_queueBuffer) { |
| int slot = -1; |
| sp<GraphicBuffer> buffer; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| // A generic "valid" input |
| IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); |
| IGraphicBufferProducer::QueueBufferOutput output; |
| |
| // Shouldn't be able to queue buffer after disconnect. |
| ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); |
| ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); |
| } |
| |
| TEST_F(BufferHubQueueProducerTest, |
| DisconnectedProducerReturnsError_cancelBuffer) { |
| int slot = -1; |
| sp<GraphicBuffer> buffer; |
| |
| ASSERT_NO_FATAL_FAILURE(ConnectProducer()); |
| ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); |
| ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); |
| |
| // Shouldn't be able to cancel buffer after disconnect. |
| ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); |
| ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); |
| } |
| |
| } // namespace |
| |
| } // namespace dvr |
| } // namespace android |