/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Camera3StreamSplitterTest" // #define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../device3/Flags.h" #if USE_NEW_STREAM_SPLITTER #include "../device3/Camera3StreamSplitter.h" #else #include "../device3/deprecated/DeprecatedCamera3StreamSplitter.h" #endif // USE_NEW_STREAM_SPLITTER using namespace android; namespace { uint64_t kConsumerUsage = AHARDWAREBUFFER_USAGE_CAMERA_READ; uint64_t kProducerUsage = AHARDWAREBUFFER_USAGE_CAMERA_READ; size_t kHalMaxBuffers = 3; uint32_t kWidth = 640; uint32_t kHeight = 480; PixelFormat kFormat = HAL_PIXEL_FORMAT_YCBCR_420_888; int64_t kDynamicRangeProfile = 0; std::tuple, sp> createConsumerAndSurface() { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) sp consumer = sp::make(kConsumerUsage); return {consumer, consumer->getSurface()}; #else sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); return {sp::make(consumer, kConsumerUsage), sp::make(producer)}; #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } class Camera3StreamSplitterTest : public testing::Test { public: void SetUp() override { #if USE_NEW_STREAM_SPLITTER mSplitter = sp::make(); #else mSplitter = sp::make(); #endif // USE_NEW_STREAM_SPLITTER } protected: #if USE_NEW_STREAM_SPLITTER sp mSplitter; #else sp mSplitter; #endif // USE_NEW_STREAM_SPLITTER }; class TestSurfaceListener : public SurfaceListener { public: virtual void onBufferReleased() override { mNumBuffersReleased++; } virtual bool needsReleaseNotify() { return true; } virtual void onBufferDetached(int) override {} virtual void onBuffersDiscarded(const std::vector>&) override {}; uint32_t mNumBuffersReleased = 0; }; class TestConsumerListener : public BufferItemConsumer::FrameAvailableListener { public: TestConsumerListener(const wp& consumer) : mConsumer(consumer) {} virtual void onFrameAvailable(const BufferItem&) { sp consumer = mConsumer.promote(); EXPECT_NE(nullptr, consumer); BufferItem item; EXPECT_EQ(OK, consumer->acquireBuffer(&item, 0)); mNumBuffersAcquired++; EXPECT_EQ(OK, consumer->releaseBuffer(item, Fence::NO_FENCE)); } virtual void onFrameReplaced(const BufferItem&) {} virtual void onFrameDequeued(const uint64_t) {} virtual void onFrameCancelled(const uint64_t) {} virtual void onFrameDetached(const uint64_t) {} wp mConsumer; uint32_t mNumBuffersAcquired = 0; }; } // namespace TEST_F(Camera3StreamSplitterTest, WithoutSurfaces_NoBuffersConsumed) { sp consumer; EXPECT_EQ(OK, mSplitter->connect({}, kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth, kHeight, kFormat, &consumer, kDynamicRangeProfile)); sp surfaceListener = sp::make(); EXPECT_EQ(OK, consumer->connect(NATIVE_WINDOW_API_CAMERA, surfaceListener, false)); sp buffer = new GraphicBuffer(kWidth, kHeight, kFormat, kProducerUsage); EXPECT_EQ(OK, consumer->attachBuffer(buffer->getNativeBuffer())); // TODO: Do this with the surface itself once the API is available. EXPECT_EQ(OK, ANativeWindow_queueBuffer(consumer.get(), buffer->getNativeBuffer(), /*fenceFd*/ -1)); EXPECT_EQ(0u, surfaceListener->mNumBuffersReleased); } TEST_F(Camera3StreamSplitterTest, TestProcessSingleBuffer) { // // Set up output consumers: // constexpr auto kSurfaceId1 = 1; auto [bufferItemConsumer1, surface1] = createConsumerAndSurface(); sp consumerListener1 = sp::make(bufferItemConsumer1); bufferItemConsumer1->setFrameAvailableListener(consumerListener1); constexpr auto kSurfaceId2 = 2; auto [bufferItemConsumer2, surface2] = createConsumerAndSurface(); sp consumerListener2 = sp::make(bufferItemConsumer2); bufferItemConsumer2->setFrameAvailableListener(consumerListener2); // // Connect it to the splitter, get the input surface, and set it up: // sp inputSurface; EXPECT_EQ(OK, mSplitter->connect({{kSurfaceId1, surface1}, {kSurfaceId2, surface2}}, kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth, kHeight, kFormat, &inputSurface, kDynamicRangeProfile)); sp surfaceListener = sp::make(); EXPECT_EQ(OK, inputSurface->connect(NATIVE_WINDOW_API_CAMERA, surfaceListener, false)); // TODO: Do this with the surface itself once the API is available. EXPECT_EQ(OK, inputSurface->getIGraphicBufferProducer()->allowAllocation(false)); // // Create a buffer to use: // sp singleBuffer = new GraphicBuffer(kWidth, kHeight, kFormat, kProducerUsage); EXPECT_NE(nullptr, singleBuffer); mSplitter->attachBufferToOutputs(singleBuffer->getNativeBuffer(), {kSurfaceId1, kSurfaceId2}); // // Verify that when we attach the buffer, it's processed appropriately: // EXPECT_EQ(OK, inputSurface->attachBuffer(singleBuffer->getNativeBuffer())); EXPECT_EQ(OK, mSplitter->getOnFrameAvailableResult()); // TODO: Do this with the surface itself once the API is available. EXPECT_EQ(OK, ANativeWindow_queueBuffer(inputSurface.get(), singleBuffer->getNativeBuffer(), /*fenceFd*/ -1)); EXPECT_EQ(1u, consumerListener1->mNumBuffersAcquired); EXPECT_EQ(1u, consumerListener2->mNumBuffersAcquired); EXPECT_EQ(1u, surfaceListener->mNumBuffersReleased); }