/* * Copyright (C) 2023 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 FAILURE_DEBUG_PREFIX "FakeRotatingCamera" #include #include #include #include #include #include #include #include #define GL_GLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #undef EGL_EGLEXT_PROTOTYPES #undef GL_GLEXT_PROTOTYPES #include "acircles_pattern_512_512.h" #include "converters.h" #include "debug.h" #include "FakeRotatingCamera.h" #include "jpeg.h" #include "metadata_utils.h" #include "utils.h" #include "yuv.h" namespace android { namespace hardware { namespace camera { namespace provider { namespace implementation { namespace hw { using base::unique_fd; namespace { constexpr char kClass[] = "FakeRotatingCamera"; constexpr int kMinFPS = 2; constexpr int kMedFPS = 15; constexpr int kMaxFPS = 30; constexpr int64_t kOneSecondNs = 1000000000; constexpr float kDefaultFocalLength = 2.8; constexpr int64_t kMinFrameDurationNs = kOneSecondNs / kMaxFPS; constexpr int64_t kMaxFrameDurationNs = kOneSecondNs / kMinFPS; constexpr int64_t kDefaultFrameDurationNs = kOneSecondNs / kMedFPS; constexpr int64_t kDefaultSensorExposureTimeNs = kOneSecondNs / 100; constexpr int64_t kMinSensorExposureTimeNs = kDefaultSensorExposureTimeNs / 100; constexpr int64_t kMaxSensorExposureTimeNs = kDefaultSensorExposureTimeNs * 10; constexpr int32_t kDefaultJpegQuality = 85; constexpr BufferUsage usageOr(const BufferUsage a, const BufferUsage b) { return static_cast(static_cast(a) | static_cast(b)); } constexpr bool usageTest(const BufferUsage a, const BufferUsage b) { return (static_cast(a) & static_cast(b)) != 0; } constexpr uint16_t toR5G6B5(float r, float g, float b) { return uint16_t(b * 31) | (uint16_t(g * 63) << 5) | (uint16_t(r * 31) << 11); } constexpr uint32_t toR8G8B8A8(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return uint32_t(r) | (uint32_t(g) << 8) | (uint32_t(b) << 16) | (uint32_t(a) << 24); } constexpr double degrees2rad(const double degrees) { return degrees * M_PI / 180.0; } // This texture is useful to debug camera orientation and image aspect ratio abc3d::AutoTexture loadTestPatternTextureA() { constexpr uint16_t B = toR5G6B5(.4, .4, .4); constexpr uint16_t R = toR5G6B5( 1, .1, .1); static const uint16_t texels[] = { B, R, R, R, R, R, B, B, R, B, B, B, B, B, R, B, B, B, B, B, B, B, R, B, B, R, R, R, R, R, B, B, R, B, B, B, B, B, R, B, R, B, B, B, B, B, R, B, R, B, B, B, B, B, R, B, B, R, R, R, R, R, B, R, }; abc3d::AutoTexture tex(GL_TEXTURE_2D, GL_RGB, 8, 8, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return tex; } // This texture is useful to debug camera dataspace abc3d::AutoTexture loadTestPatternTextureColors() { static const uint32_t texels[] = { toR8G8B8A8(32, 0, 0, 255), toR8G8B8A8(64, 0, 0, 255), toR8G8B8A8(96, 0, 0, 255), toR8G8B8A8(128, 0, 0, 255), toR8G8B8A8(160, 0, 0, 255), toR8G8B8A8(192, 0, 0, 255), toR8G8B8A8(224, 0, 0, 255), toR8G8B8A8(255, 0, 0, 255), toR8G8B8A8(32, 32, 0, 255), toR8G8B8A8(64, 64, 0, 255), toR8G8B8A8(96, 96, 0, 255), toR8G8B8A8(128, 128, 0, 255), toR8G8B8A8(160, 160, 0, 255), toR8G8B8A8(192, 192, 0, 255), toR8G8B8A8(224, 224, 0, 255), toR8G8B8A8(255, 255, 0, 255), toR8G8B8A8(0, 32, 0, 255), toR8G8B8A8(0, 64, 0, 255), toR8G8B8A8(0, 96, 0, 255), toR8G8B8A8(0, 128, 0, 255), toR8G8B8A8(0, 160, 0, 255), toR8G8B8A8(0, 192, 0, 255), toR8G8B8A8(0, 224, 0, 255), toR8G8B8A8(0, 255, 0, 255), toR8G8B8A8(0, 32, 32, 255), toR8G8B8A8(0, 64, 64, 255), toR8G8B8A8(0, 96, 96, 255), toR8G8B8A8(0, 128, 128, 255), toR8G8B8A8(0, 160, 160, 255), toR8G8B8A8(0, 192, 192, 255), toR8G8B8A8(0, 224, 224, 255), toR8G8B8A8(0, 255, 255, 255), toR8G8B8A8(0, 0, 32, 255), toR8G8B8A8(0, 0, 64, 255), toR8G8B8A8(0, 0, 96, 255), toR8G8B8A8(0, 0, 128, 255), toR8G8B8A8(0, 0, 160, 255), toR8G8B8A8(0, 0, 192, 255), toR8G8B8A8(0, 0, 224, 255), toR8G8B8A8(0, 0, 255, 255), toR8G8B8A8(32, 0, 32, 255), toR8G8B8A8(64, 0, 64, 255), toR8G8B8A8(96, 0, 96, 255), toR8G8B8A8(128, 0, 128, 255), toR8G8B8A8(160, 0, 160, 255), toR8G8B8A8(192, 0, 192, 255), toR8G8B8A8(224, 0, 224, 255), toR8G8B8A8(255, 0, 255, 255), toR8G8B8A8(32, 128, 0, 255), toR8G8B8A8(64, 128, 0, 255), toR8G8B8A8(96, 128, 0, 255), toR8G8B8A8(255, 255, 255, 255), toR8G8B8A8(160, 128, 0, 255), toR8G8B8A8(192, 128, 0, 255), toR8G8B8A8(224, 128, 0, 255), toR8G8B8A8(255, 128, 0, 255), toR8G8B8A8(0, 0, 0, 255), toR8G8B8A8(32, 32, 32, 255), toR8G8B8A8(64, 64, 64, 255), toR8G8B8A8(96, 96, 96, 255), toR8G8B8A8(128, 128, 128, 255), toR8G8B8A8(160, 160, 160, 255), toR8G8B8A8(192, 192, 192, 255), toR8G8B8A8(224, 224, 224, 255), }; abc3d::AutoTexture tex(GL_TEXTURE_2D, GL_RGBA, 8, 8, GL_RGBA, GL_UNSIGNED_BYTE, texels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return tex; } // This texture is used to pass CtsVerifier abc3d::AutoTexture loadTestPatternTextureAcircles() { constexpr uint16_t kPalette[] = { toR5G6B5(0, 0, 0), toR5G6B5(.25, .25, .25), toR5G6B5(.5, .5, .5), toR5G6B5(1, 1, 0), toR5G6B5(1, 1, 1), }; std::vector texels; texels.reserve(kAcirclesPatternWidth * kAcirclesPatternWidth); auto i = std::begin(kAcirclesPatternRLE); const auto end = std::end(kAcirclesPatternRLE); while (i < end) { const unsigned x = *i; ++i; unsigned n; uint16_t color; if (x & 1) { n = (x >> 3) + 1; color = kPalette[(x >> 1) & 3]; } else { if (x & 2) { n = ((unsigned(*i) << 6) | (x >> 2)) + 1; ++i; } else { n = (x >> 2) + 1; } color = kPalette[4]; } texels.insert(texels.end(), n, color); } abc3d::AutoTexture tex(GL_TEXTURE_2D, GL_RGB, kAcirclesPatternWidth, kAcirclesPatternWidth, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texels.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); return tex; } abc3d::AutoTexture loadTestPatternTexture() { std::string valueStr = base::GetProperty("vendor.qemu.FakeRotatingCamera.scene", ""); if (valueStr.empty()) { valueStr = base::GetProperty("ro.boot.qemu.FakeRotatingCamera.scene", ""); } if (strcmp(valueStr.c_str(), "a") == 0) { return loadTestPatternTextureA(); } else if (strcmp(valueStr.c_str(), "colors") == 0) { return loadTestPatternTextureColors(); } else { return loadTestPatternTextureAcircles(); } } bool compressNV21IntoJpeg(const Rect imageSize, const uint8_t* nv21data, const CameraMetadata& metadata, const native_handle_t* jpegBuffer, const size_t jpegBufferSize) { const android_ycbcr imageYcbcr = yuv::NV21init(imageSize.width, imageSize.height, const_cast(nv21data)); return HwCamera::compressJpeg(imageSize, imageYcbcr, metadata, jpegBuffer, jpegBufferSize); } } // namespace FakeRotatingCamera::FakeRotatingCamera(const bool isBackFacing) : mIsBackFacing(isBackFacing) , mAFStateMachine(200, 1, 2) {} FakeRotatingCamera::~FakeRotatingCamera() { closeImpl(true); } std::tuple FakeRotatingCamera::overrideStreamParams(const PixelFormat format, const BufferUsage usage, const Dataspace dataspace) const { constexpr BufferUsage kRgbaExtraUsage = usageOr(BufferUsage::CAMERA_OUTPUT, BufferUsage::GPU_RENDER_TARGET); constexpr BufferUsage kYuvExtraUsage = usageOr(BufferUsage::CAMERA_OUTPUT, BufferUsage::CPU_WRITE_OFTEN); constexpr BufferUsage kBlobExtraUsage = usageOr(BufferUsage::CAMERA_OUTPUT, BufferUsage::CPU_WRITE_OFTEN); switch (format) { case PixelFormat::YCBCR_420_888: return {PixelFormat::YCBCR_420_888, usageOr(usage, kYuvExtraUsage), Dataspace::JFIF, (usageTest(usage, BufferUsage::VIDEO_ENCODER) ? 8 : 4)}; case PixelFormat::IMPLEMENTATION_DEFINED: if (usageTest(usage, BufferUsage::VIDEO_ENCODER)) { return {PixelFormat::YCBCR_420_888, usageOr(usage, kYuvExtraUsage), Dataspace::JFIF, 8}; } else { return {PixelFormat::RGBA_8888, usageOr(usage, kRgbaExtraUsage), Dataspace::UNKNOWN, 4}; } case PixelFormat::RGBA_8888: return {PixelFormat::RGBA_8888, usageOr(usage, kRgbaExtraUsage), Dataspace::UNKNOWN, (usageTest(usage, BufferUsage::VIDEO_ENCODER) ? 8 : 4)}; case PixelFormat::BLOB: switch (dataspace) { case Dataspace::JFIF: return {PixelFormat::BLOB, usageOr(usage, kBlobExtraUsage), Dataspace::JFIF, 4}; // JPEG default: return {format, usage, dataspace, FAILURE(kErrorBadDataspace)}; } default: return {format, usage, dataspace, FAILURE(kErrorBadFormat)}; } } bool FakeRotatingCamera::configure(const CameraMetadata& sessionParams, size_t nStreams, const Stream* streams, const HalStream* halStreams) { closeImpl(false); applyMetadata(sessionParams); if (!mQemuChannel.ok()) { static const char kPipeName[] = "FakeRotatingCameraSensor"; mQemuChannel.reset(qemu_pipe_open_ns(NULL, kPipeName, O_RDWR)); if (!mQemuChannel.ok()) { ALOGE("%s:%s:%d qemu_pipe_open_ns failed for '%s'", kClass, __func__, __LINE__, kPipeName); return FAILURE(false); } } const abc3d::EglCurrentContext currentContext = initOpenGL(); if (!currentContext.ok()) { return FAILURE(false); } LOG_ALWAYS_FATAL_IF(!mStreamInfoCache.empty()); for (; nStreams > 0; --nStreams, ++streams, ++halStreams) { const int32_t id = streams->id; LOG_ALWAYS_FATAL_IF(halStreams->id != id); StreamInfo& si = mStreamInfoCache[id]; si.usage = halStreams->producerUsage; si.size.width = streams->width; si.size.height = streams->height; si.pixelFormat = halStreams->overrideFormat; si.blobBufferSize = streams->bufferSize; if (si.pixelFormat != PixelFormat::RGBA_8888) { const native_handle_t* buffer; GraphicBufferAllocator& gba = GraphicBufferAllocator::get(); uint32_t stride; if (gba.allocate(si.size.width, si.size.height, static_cast(PixelFormat::RGBA_8888), 1, static_cast(usageOr(BufferUsage::GPU_RENDER_TARGET, usageOr(BufferUsage::CPU_READ_OFTEN, BufferUsage::CAMERA_OUTPUT))), &buffer, &stride, kClass) == NO_ERROR) { si.rgbaBuffer.reset(buffer); } else { mStreamInfoCache.clear(); return FAILURE(false); } } } return true; } void FakeRotatingCamera::close() { closeImpl(true); } abc3d::EglCurrentContext FakeRotatingCamera::initOpenGL() { if (mGlProgram.ok()) { return mEglContext.getCurrentContext(); } abc3d::EglContext context; abc3d::EglCurrentContext currentContext = context.init(); if (!currentContext.ok()) { return abc3d::EglCurrentContext(); } abc3d::AutoTexture testPatternTexture = loadTestPatternTexture(); if (!testPatternTexture.ok()) { return abc3d::EglCurrentContext(); } const char kVertexShaderStr[] = R"CODE( attribute vec4 a_position; attribute vec2 a_texCoord; uniform mat4 u_pvmMatrix; varying vec2 v_texCoord; void main() { gl_Position = u_pvmMatrix * a_position; v_texCoord = a_texCoord; } )CODE"; abc3d::AutoShader vertexShader; if (!vertexShader.compile(GL_VERTEX_SHADER, kVertexShaderStr)) { return abc3d::EglCurrentContext(); } const char kFragmentShaderStr[] = R"CODE( precision mediump float; varying vec2 v_texCoord; uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, v_texCoord); } )CODE"; abc3d::AutoShader fragmentShader; if (!fragmentShader.compile(GL_FRAGMENT_SHADER, kFragmentShaderStr)) { return abc3d::EglCurrentContext(); } abc3d::AutoProgram program; if (!program.link(vertexShader.get(), fragmentShader.get())) { return abc3d::EglCurrentContext(); } const GLint programAttrPositionLoc = program.getAttribLocation("a_position"); if (programAttrPositionLoc < 0) { return abc3d::EglCurrentContext(); } const GLint programAttrTexCoordLoc = program.getAttribLocation("a_texCoord"); if (programAttrTexCoordLoc < 0) { return abc3d::EglCurrentContext(); } const GLint programUniformTextureLoc = program.getUniformLocation("u_texture"); if (programUniformTextureLoc < 0) { return abc3d::EglCurrentContext(); } const GLint programUniformPvmMatrixLoc = program.getUniformLocation("u_pvmMatrix"); if (programUniformPvmMatrixLoc < 0) { return abc3d::EglCurrentContext(); } mEglContext = std::move(context); mGlTestPatternTexture = std::move(testPatternTexture); mGlProgramAttrPositionLoc = programAttrPositionLoc; mGlProgramAttrTexCoordLoc = programAttrTexCoordLoc; mGlProgramUniformTextureLoc = programUniformTextureLoc; mGlProgramUniformPvmMatrixLoc = programUniformPvmMatrixLoc; mGlProgram = std::move(program); return std::move(currentContext); } void FakeRotatingCamera::closeImpl(const bool everything) { { const abc3d::EglCurrentContext currentContext = mEglContext.getCurrentContext(); LOG_ALWAYS_FATAL_IF(!mStreamInfoCache.empty() && !currentContext.ok()); mStreamInfoCache.clear(); if (everything) { mGlProgram.clear(); mGlTestPatternTexture.clear(); } } if (everything) { mEglContext.clear(); mQemuChannel.reset(); } } std::tuple, std::vector> FakeRotatingCamera::processCaptureRequest(CameraMetadata metadataUpdate, Span csbs) { CameraMetadata resultMetadata = metadataUpdate.metadata.empty() ? updateCaptureResultMetadata() : applyMetadata(std::move(metadataUpdate)); const size_t csbsSize = csbs.size(); std::vector outputBuffers; std::vector delayedOutputBuffers; outputBuffers.reserve(csbsSize); const abc3d::EglCurrentContext currentContext = mEglContext.getCurrentContext(); if (!currentContext.ok()) { goto fail; } RenderParams renderParams; { SensorValues sensorValues; if (readSensors(&sensorValues)) { static_assert(sizeof(renderParams.cameraParams.rotXYZ3) == sizeof(sensorValues.rotation)); memcpy(renderParams.cameraParams.rotXYZ3, sensorValues.rotation, sizeof(sensorValues.rotation)); } else { goto fail; } constexpr double kR = 5.0; float* pos3 = renderParams.cameraParams.pos3; pos3[0] = -kR * sin(sensorValues.rotation[0]) * sin(sensorValues.rotation[1]); pos3[1] = -kR * sin(sensorValues.rotation[0]) * cos(sensorValues.rotation[1]); pos3[2] = kR * cos(sensorValues.rotation[0]); } for (size_t i = 0; i < csbsSize; ++i) { CachedStreamBuffer* csb = csbs[i]; LOG_ALWAYS_FATAL_IF(!csb); // otherwise mNumBuffersInFlight will be hard const StreamInfo* si = csb->getStreamInfo(); if (!si) { const auto sii = mStreamInfoCache.find(csb->getStreamId()); if (sii == mStreamInfoCache.end()) { ALOGE("%s:%s:%d could not find stream=%d in the cache", kClass, __func__, __LINE__, csb->getStreamId()); } else { si = &sii->second; csb->setStreamInfo(si); } } if (si) { captureFrame(*si, renderParams, csb, &outputBuffers, &delayedOutputBuffers); } else { outputBuffers.push_back(csb->finish(false)); } } return make_tuple(mFrameDurationNs, kDefaultSensorExposureTimeNs, std::move(resultMetadata), std::move(outputBuffers), std::move(delayedOutputBuffers)); fail: for (size_t i = 0; i < csbsSize; ++i) { CachedStreamBuffer* csb = csbs[i]; LOG_ALWAYS_FATAL_IF(!csb); // otherwise mNumBuffersInFlight will be hard outputBuffers.push_back(csb->finish(false)); } return make_tuple(FAILURE(-1), 0, std::move(resultMetadata), std::move(outputBuffers), std::move(delayedOutputBuffers)); } void FakeRotatingCamera::captureFrame(const StreamInfo& si, const RenderParams& renderParams, CachedStreamBuffer* csb, std::vector* outputBuffers, std::vector* delayedOutputBuffers) const { switch (si.pixelFormat) { case PixelFormat::RGBA_8888: outputBuffers->push_back(csb->finish(captureFrameRGBA(si, renderParams, csb))); break; case PixelFormat::YCBCR_420_888: outputBuffers->push_back(csb->finish(captureFrameYUV(si, renderParams, csb))); break; case PixelFormat::BLOB: delayedOutputBuffers->push_back(captureFrameJpeg(si, renderParams, csb)); break; default: ALOGE("%s:%s:%d: unexpected pixelFormat=%" PRIx32, kClass, __func__, __LINE__, static_cast(si.pixelFormat)); outputBuffers->push_back(csb->finish(false)); break; } } bool FakeRotatingCamera::captureFrameRGBA(const StreamInfo& si, const RenderParams& renderParams, CachedStreamBuffer* csb) const { if (!csb->waitAcquireFence(mFrameDurationNs / 2000000)) { return FAILURE(false); } return renderIntoRGBA(si, renderParams, csb->getBuffer()); } bool FakeRotatingCamera::captureFrameYUV(const StreamInfo& si, const RenderParams& renderParams, CachedStreamBuffer* csb) const { LOG_ALWAYS_FATAL_IF(!si.rgbaBuffer); if (!renderIntoRGBA(si, renderParams, si.rgbaBuffer.get())) { return false; } if (!csb->waitAcquireFence(mFrameDurationNs / 2000000)) { return false; } void* rgba = nullptr; if (GraphicBufferMapper::get().lock( si.rgbaBuffer.get(), static_cast(BufferUsage::CPU_READ_OFTEN), {si.size.width, si.size.height}, &rgba) != NO_ERROR) { return FAILURE(false); } android_ycbcr ycbcr; if (GraphicBufferMapper::get().lockYCbCr( csb->getBuffer(), static_cast(BufferUsage::CPU_WRITE_OFTEN), {si.size.width, si.size.height}, &ycbcr) != NO_ERROR) { LOG_ALWAYS_FATAL_IF(GraphicBufferMapper::get().unlock(si.rgbaBuffer.get()) != NO_ERROR); return FAILURE(false); } const bool converted = conv::rgba2yuv(si.size.width, si.size.height, static_cast(rgba), ycbcr); LOG_ALWAYS_FATAL_IF(GraphicBufferMapper::get().unlock(csb->getBuffer()) != NO_ERROR); LOG_ALWAYS_FATAL_IF(GraphicBufferMapper::get().unlock(si.rgbaBuffer.get()) != NO_ERROR); return converted; } DelayedStreamBuffer FakeRotatingCamera::captureFrameJpeg(const StreamInfo& si, const RenderParams& renderParams, CachedStreamBuffer* csb) const { std::vector nv21data = captureFrameForCompressing(si, renderParams); const Rect imageSize = si.size; const uint32_t jpegBufferSize = si.blobBufferSize; const int64_t frameDurationNs = mFrameDurationNs; CameraMetadata metadata = mCaptureResultMetadata; return [csb, imageSize, nv21data = std::move(nv21data), metadata = std::move(metadata), jpegBufferSize, frameDurationNs](const bool ok) -> StreamBuffer { StreamBuffer sb; if (ok && !nv21data.empty() && csb->waitAcquireFence(frameDurationNs / 1000000)) { sb = csb->finish(compressNV21IntoJpeg(imageSize, nv21data.data(), metadata, csb->getBuffer(), jpegBufferSize)); } else { sb = csb->finish(false); } return sb; }; } std::vector FakeRotatingCamera::captureFrameForCompressing(const StreamInfo& si, const RenderParams& renderParams) const { if (!renderIntoRGBA(si, renderParams, si.rgbaBuffer.get())) { return {}; } void* rgba = nullptr; if (GraphicBufferMapper::get().lock( si.rgbaBuffer.get(), static_cast(BufferUsage::CPU_READ_OFTEN), {si.size.width, si.size.height}, &rgba) != NO_ERROR) { return {}; } std::vector nv21data(yuv::NV21size(si.size.width, si.size.height)); const android_ycbcr ycbcr = yuv::NV21init(si.size.width, si.size.height, nv21data.data()); const bool converted = conv::rgba2yuv(si.size.width, si.size.height, static_cast(rgba), ycbcr); LOG_ALWAYS_FATAL_IF(GraphicBufferMapper::get().unlock(si.rgbaBuffer.get()) != NO_ERROR); if (converted) { return nv21data; } else { return {}; } } bool FakeRotatingCamera::drawScene(const Rect imageSize, const RenderParams& renderParams, const bool isHardwareBuffer) const { float pvMatrix44[16]; { float projectionMatrix44[16]; float viewMatrix44[16]; // This matrix takes into account specific behaviors below: // * The Y axis if rendering int0 AHardwareBuffer goes down while it // goes up everywhere else (e.g. when rendering to `EGLSurface`). // * We set `sensorOrientation=90` because a lot of places in Android and // 3Ps assume this and don't work properly with `sensorOrientation=0`. const float workaroundMatrix44[16] = { 0, (isHardwareBuffer ? -1.0f : 1.0f), 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; { constexpr double kNear = 1.0; constexpr double kFar = 10.0; // We use `height` to calculate `right` because the image is 90degrees // rotated (sensorOrientation=90). const double right = kNear * (.5 * getSensorSize().height / getSensorDPI() / getDefaultFocalLength()); const double top = right / imageSize.width * imageSize.height; abc3d::frustum(pvMatrix44, -right, right, -top, top, kNear, kFar); } abc3d::mulM44(projectionMatrix44, pvMatrix44, workaroundMatrix44); { const auto& cam = renderParams.cameraParams; abc3d::lookAtXyzRot(viewMatrix44, cam.pos3, cam.rotXYZ3); } abc3d::mulM44(pvMatrix44, projectionMatrix44, viewMatrix44); } glViewport(0, 0, imageSize.width, imageSize.height); const bool result = drawSceneImpl(pvMatrix44); glFinish(); return result; } bool FakeRotatingCamera::drawSceneImpl(const float pvMatrix44[]) const { constexpr float kX = 0; constexpr float kY = 0; constexpr float kZ = 0; constexpr float kS = 1; const GLfloat vVertices[] = { -kS + kX, kY, kZ - kS, // Position 0 0, 0, // TexCoord 0 -kS + kX, kY, kZ + kS, // Position 1 0, 1, // TexCoord 1 kS + kX, kY, kZ + kS, // Position 2 1, 1, // TexCoord 2 kS + kX, kY, kZ - kS, // Position 3 1, 0 // TexCoord 3 }; static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; glClearColor(0.2, 0.3, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mGlProgram.get()); glVertexAttribPointer(mGlProgramAttrPositionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[0]); glEnableVertexAttribArray(mGlProgramAttrPositionLoc); glVertexAttribPointer(mGlProgramAttrTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]); glEnableVertexAttribArray(mGlProgramAttrTexCoordLoc); glUniformMatrix4fv(mGlProgramUniformPvmMatrixLoc, 1, true, pvMatrix44); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mGlTestPatternTexture.get()); glUniform1i(mGlProgramUniformTextureLoc, 0); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); return true; } bool FakeRotatingCamera::renderIntoRGBA(const StreamInfo& si, const RenderParams& renderParams, const native_handle_t* rgbaBuffer) const { const cb_handle_t* const cb = cb_handle_t::from(rgbaBuffer); if (!cb) { return FAILURE(false); } const auto gb = sp::make( rgbaBuffer, GraphicBuffer::WRAP_HANDLE, si.size.width, si.size.height, static_cast(si.pixelFormat), 1, static_cast(si.usage), cb->stride); const EGLClientBuffer clientBuf = eglGetNativeClientBufferANDROID(gb->toAHardwareBuffer()); if (!clientBuf) { return FAILURE(false); } const abc3d::AutoImageKHR eglImage(mEglContext.getDisplay(), clientBuf); if (!eglImage.ok()) { return false; } abc3d::AutoTexture fboTex(GL_TEXTURE_2D); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage.get()); abc3d::AutoFrameBuffer fbo; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex.get(), 0); // drawing into EGLClientBuffer is Y-flipped on Android return drawScene(si.size, renderParams, true); } bool FakeRotatingCamera::readSensors(SensorValues* vals) { static const char kReadCommand[] = "get"; uint32_t len = sizeof(kReadCommand); if (qemu_pipe_write_fully(mQemuChannel.get(), &len, sizeof(len))) { return FAILURE(false); } if (qemu_pipe_write_fully(mQemuChannel.get(), &kReadCommand[0], sizeof(kReadCommand))) { return FAILURE(false); } if (qemu_pipe_read_fully(mQemuChannel.get(), &len, sizeof(len))) { return FAILURE(false); } if (len != sizeof(*vals)) { return FAILURE(false); } if (qemu_pipe_read_fully(mQemuChannel.get(), vals, len)) { return FAILURE(false); } return true; } CameraMetadata FakeRotatingCamera::applyMetadata(const CameraMetadata& metadata) { const camera_metadata_t* const raw = reinterpret_cast(metadata.metadata.data()); mFrameDurationNs = getFrameDuration(raw, kDefaultFrameDurationNs, kMinFrameDurationNs, kMaxFrameDurationNs); camera_metadata_ro_entry_t entry; const camera_metadata_enum_android_control_af_mode_t afMode = find_camera_metadata_ro_entry(raw, ANDROID_CONTROL_AF_MODE, &entry) ? ANDROID_CONTROL_AF_MODE_OFF : static_cast(entry.data.u8[0]); const camera_metadata_enum_android_control_af_trigger_t afTrigger = find_camera_metadata_ro_entry(raw, ANDROID_CONTROL_AF_TRIGGER, &entry) ? ANDROID_CONTROL_AF_TRIGGER_IDLE : static_cast(entry.data.u8[0]); const auto af = mAFStateMachine(afMode, afTrigger); CameraMetadataMap m = parseCameraMetadataMap(metadata); m[ANDROID_CONTROL_AE_STATE] = uint8_t(ANDROID_CONTROL_AE_STATE_CONVERGED); m[ANDROID_CONTROL_AF_STATE] = uint8_t(af.first); m[ANDROID_CONTROL_AWB_STATE] = uint8_t(ANDROID_CONTROL_AWB_STATE_CONVERGED); m[ANDROID_FLASH_STATE] = uint8_t(ANDROID_FLASH_STATE_UNAVAILABLE); m[ANDROID_LENS_APERTURE] = getDefaultAperture(); m[ANDROID_LENS_FOCUS_DISTANCE] = af.second; m[ANDROID_LENS_STATE] = uint8_t(getAfLensState(af.first)); m[ANDROID_REQUEST_PIPELINE_DEPTH] = uint8_t(4); m[ANDROID_SENSOR_FRAME_DURATION] = mFrameDurationNs; m[ANDROID_SENSOR_EXPOSURE_TIME] = kDefaultSensorExposureTimeNs; m[ANDROID_SENSOR_SENSITIVITY] = getDefaultSensorSensitivity(); m[ANDROID_SENSOR_TIMESTAMP] = int64_t(0); m[ANDROID_SENSOR_ROLLING_SHUTTER_SKEW] = kMinSensorExposureTimeNs; m[ANDROID_STATISTICS_SCENE_FLICKER] = uint8_t(ANDROID_STATISTICS_SCENE_FLICKER_NONE); std::optional maybeSerialized = serializeCameraMetadataMap(m); if (maybeSerialized) { mCaptureResultMetadata = std::move(maybeSerialized.value()); } { // reset ANDROID_CONTROL_AF_TRIGGER to IDLE camera_metadata_t* const raw = reinterpret_cast(mCaptureResultMetadata.metadata.data()); camera_metadata_ro_entry_t entry; const auto newTriggerValue = ANDROID_CONTROL_AF_TRIGGER_IDLE; if (find_camera_metadata_ro_entry(raw, ANDROID_CONTROL_AF_TRIGGER, &entry)) { return mCaptureResultMetadata; } else if (entry.data.i32[0] == newTriggerValue) { return mCaptureResultMetadata; } else { CameraMetadata result = mCaptureResultMetadata; if (update_camera_metadata_entry(raw, entry.index, &newTriggerValue, 1, nullptr)) { ALOGW("%s:%s:%d: update_camera_metadata_entry(ANDROID_CONTROL_AF_TRIGGER) " "failed", kClass, __func__, __LINE__); } return result; } } } CameraMetadata FakeRotatingCamera::updateCaptureResultMetadata() { camera_metadata_t* const raw = reinterpret_cast(mCaptureResultMetadata.metadata.data()); const auto af = mAFStateMachine(); camera_metadata_ro_entry_t entry; if (find_camera_metadata_ro_entry(raw, ANDROID_CONTROL_AF_STATE, &entry)) { ALOGW("%s:%s:%d: find_camera_metadata_ro_entry(ANDROID_CONTROL_AF_STATE) failed", kClass, __func__, __LINE__); } else if (update_camera_metadata_entry(raw, entry.index, &af.first, 1, nullptr)) { ALOGW("%s:%s:%d: update_camera_metadata_entry(ANDROID_CONTROL_AF_STATE) failed", kClass, __func__, __LINE__); } if (find_camera_metadata_ro_entry(raw, ANDROID_LENS_FOCUS_DISTANCE, &entry)) { ALOGW("%s:%s:%d: find_camera_metadata_ro_entry(ANDROID_LENS_FOCUS_DISTANCE) failed", kClass, __func__, __LINE__); } else if (update_camera_metadata_entry(raw, entry.index, &af.second, 1, nullptr)) { ALOGW("%s:%s:%d: update_camera_metadata_entry(ANDROID_LENS_FOCUS_DISTANCE) failed", kClass, __func__, __LINE__); } return metadataCompact(mCaptureResultMetadata); } //////////////////////////////////////////////////////////////////////////////// Span> FakeRotatingCamera::getTargetFpsRanges() const { // ordered to satisfy testPreviewFpsRangeByCamera static const std::pair targetFpsRanges[] = { {kMinFPS, kMedFPS}, {kMedFPS, kMedFPS}, {kMinFPS, kMaxFPS}, {kMaxFPS, kMaxFPS}, }; return targetFpsRanges; } Span> FakeRotatingCamera::getAvailableThumbnailSizes() const { static const Rect availableThumbnailSizes[] = { {0, 0}, {11 * 4, 9 * 4}, {16 * 4, 9 * 4}, {4 * 16, 3 * 16}, }; return availableThumbnailSizes; } bool FakeRotatingCamera::isBackFacing() const { return mIsBackFacing; } Span FakeRotatingCamera::getAvailableFocalLength() const { static const float availableFocalLengths[] = { kDefaultFocalLength }; return availableFocalLengths; } std::tuple FakeRotatingCamera::getMaxNumOutputStreams() const { return { 0, // raw 2, // processed 1, // jpeg }; } Span FakeRotatingCamera::getSupportedPixelFormats() const { static const PixelFormat supportedPixelFormats[] = { PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888, PixelFormat::RGBA_8888, PixelFormat::BLOB, }; return {supportedPixelFormats}; } int64_t FakeRotatingCamera::getMinFrameDurationNs() const { return kMinFrameDurationNs; } Rect FakeRotatingCamera::getSensorSize() const { return {1920, 1080}; } uint8_t FakeRotatingCamera::getSensorColorFilterArrangement() const { return ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB; } std::pair FakeRotatingCamera::getSensorExposureTimeRange() const { return {kMinSensorExposureTimeNs, kMaxSensorExposureTimeNs}; } int64_t FakeRotatingCamera::getSensorMaxFrameDuration() const { return kMaxFrameDurationNs; } Span> FakeRotatingCamera::getSupportedResolutions() const { static const Rect supportedResolutions[] = { {176, 144}, {320, 240}, {640, 480}, {1024, 576}, {1280, 720}, {1600, 900}, {1920, 1080}, }; return supportedResolutions; } std::pair FakeRotatingCamera::getDefaultTargetFpsRange(const RequestTemplate tpl) const { switch (tpl) { case RequestTemplate::PREVIEW: case RequestTemplate::VIDEO_RECORD: case RequestTemplate::VIDEO_SNAPSHOT: return {kMaxFPS, kMaxFPS}; default: return {kMinFPS, kMaxFPS}; } } int64_t FakeRotatingCamera::getDefaultSensorExpTime() const { return kDefaultSensorExposureTimeNs; } int64_t FakeRotatingCamera::getDefaultSensorFrameDuration() const { return kMinFrameDurationNs; } float FakeRotatingCamera::getDefaultFocalLength() const { return kDefaultFocalLength; } } // namespace hw } // namespace implementation } // namespace provider } // namespace camera } // namespace hardware } // namespace android