blob: 5e324821bbe191c20d2e0ad715fcc63025dc139b [file] [log] [blame]
/*
* 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 <android/hardware_buffer.h>
#include <com_android_graphics_libgui_flags.h>
#include <com_android_internal_camera_flags.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
#include <system/window.h>
#include <vndk/window.h>
#include <gtest/gtest.h>
#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<BufferItemConsumer>, sp<Surface>> createConsumerAndSurface() {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(kConsumerUsage);
return {consumer, consumer->getSurface()};
#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
return {sp<BufferItemConsumer>::make(consumer, kConsumerUsage), sp<Surface>::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<Camera3StreamSplitter>::make();
#else
mSplitter = sp<DeprecatedCamera3StreamSplitter>::make();
#endif // USE_NEW_STREAM_SPLITTER
}
protected:
#if USE_NEW_STREAM_SPLITTER
sp<Camera3StreamSplitter> mSplitter;
#else
sp<DeprecatedCamera3StreamSplitter> 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<sp<GraphicBuffer>>&) override {};
uint32_t mNumBuffersReleased = 0;
};
class TestConsumerListener : public BufferItemConsumer::FrameAvailableListener {
public:
TestConsumerListener(const wp<BufferItemConsumer>& consumer) : mConsumer(consumer) {}
virtual void onFrameAvailable(const BufferItem&) {
sp<BufferItemConsumer> 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<BufferItemConsumer> mConsumer;
uint32_t mNumBuffersAcquired = 0;
};
} // namespace
TEST_F(Camera3StreamSplitterTest, WithoutSurfaces_NoBuffersConsumed) {
sp<Surface> consumer;
EXPECT_EQ(OK, mSplitter->connect({}, kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth,
kHeight, kFormat, &consumer, kDynamicRangeProfile));
sp<TestSurfaceListener> surfaceListener = sp<TestSurfaceListener>::make();
EXPECT_EQ(OK, consumer->connect(NATIVE_WINDOW_API_CAMERA, surfaceListener, false));
sp<GraphicBuffer> 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<TestConsumerListener> consumerListener1 =
sp<TestConsumerListener>::make(bufferItemConsumer1);
bufferItemConsumer1->setFrameAvailableListener(consumerListener1);
constexpr auto kSurfaceId2 = 2;
auto [bufferItemConsumer2, surface2] = createConsumerAndSurface();
sp<TestConsumerListener> consumerListener2 =
sp<TestConsumerListener>::make(bufferItemConsumer2);
bufferItemConsumer2->setFrameAvailableListener(consumerListener2);
//
// Connect it to the splitter, get the input surface, and set it up:
//
sp<Surface> inputSurface;
EXPECT_EQ(OK, mSplitter->connect({{kSurfaceId1, surface1}, {kSurfaceId2, surface2}},
kConsumerUsage, kProducerUsage, kHalMaxBuffers, kWidth,
kHeight, kFormat, &inputSurface, kDynamicRangeProfile));
sp<TestSurfaceListener> surfaceListener = sp<TestSurfaceListener>::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<GraphicBuffer> 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);
}