blob: f67b8ad11395559e148ccbc5ba954f9d54e0ca07 [file] [log] [blame]
/*
* 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 <log/log.h>
#include <android-base/properties.h>
#include <system/camera_metadata.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <gralloc_cb_bp.h>
#include <qemu_pipe_bp.h>
#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#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<BufferUsage>(static_cast<uint64_t>(a) | static_cast<uint64_t>(b));
}
constexpr bool usageTest(const BufferUsage a, const BufferUsage b) {
return (static_cast<uint64_t>(a) & static_cast<uint64_t>(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<uint16_t> 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<uint16_t> 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<uint8_t*>(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<PixelFormat, BufferUsage, Dataspace, int32_t>
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<int>(PixelFormat::RGBA_8888), 1,
static_cast<uint64_t>(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<int64_t, int64_t, CameraMetadata,
std::vector<StreamBuffer>, std::vector<DelayedStreamBuffer>>
FakeRotatingCamera::processCaptureRequest(CameraMetadata metadataUpdate,
Span<CachedStreamBuffer*> csbs) {
CameraMetadata resultMetadata = metadataUpdate.metadata.empty() ?
updateCaptureResultMetadata() :
applyMetadata(std::move(metadataUpdate));
const size_t csbsSize = csbs.size();
std::vector<StreamBuffer> outputBuffers;
std::vector<DelayedStreamBuffer> 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<StreamInfo>();
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<StreamBuffer>* outputBuffers,
std::vector<DelayedStreamBuffer>* 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<const uint32_t*>(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<uint8_t> nv21data = captureFrameForCompressing(si, renderParams);
const Rect<uint16_t> 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<uint8_t>
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<uint32_t>(BufferUsage::CPU_READ_OFTEN),
{si.size.width, si.size.height}, &rgba) != NO_ERROR) {
return {};
}
std::vector<uint8_t> 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<const uint32_t*>(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<uint16_t> 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<GraphicBuffer>::make(
rgbaBuffer, GraphicBuffer::WRAP_HANDLE, si.size.width,
si.size.height, static_cast<int>(si.pixelFormat), 1,
static_cast<uint64_t>(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<const camera_metadata_t*>(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<camera_metadata_enum_android_control_af_mode_t>(entry.data.i32[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<camera_metadata_enum_android_control_af_trigger_t>(entry.data.i32[0]);
const auto af = mAFStateMachine(afMode, afTrigger);
CameraMetadataMap m = parseCameraMetadataMap(metadata);
m[ANDROID_CONTROL_AE_STATE] = ANDROID_CONTROL_AE_STATE_CONVERGED;
m[ANDROID_CONTROL_AF_STATE] = af.first;
m[ANDROID_CONTROL_AWB_STATE] = ANDROID_CONTROL_AWB_STATE_CONVERGED;
m[ANDROID_FLASH_STATE] = ANDROID_FLASH_STATE_UNAVAILABLE;
m[ANDROID_LENS_APERTURE] = getDefaultAperture();
m[ANDROID_LENS_FOCUS_DISTANCE] = af.second;
m[ANDROID_LENS_STATE] = ANDROID_LENS_STATE_STATIONARY;
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] = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
std::optional<CameraMetadata> maybeSerialized =
serializeCameraMetadataMap(m);
if (maybeSerialized) {
mCaptureResultMetadata = std::move(maybeSerialized.value());
}
{ // reset ANDROID_CONTROL_AF_TRIGGER to IDLE
camera_metadata_t* const raw =
reinterpret_cast<camera_metadata_t*>(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<camera_metadata_t*>(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<const std::pair<int32_t, int32_t>> FakeRotatingCamera::getTargetFpsRanges() const {
// ordered to satisfy testPreviewFpsRangeByCamera
static const std::pair<int32_t, int32_t> targetFpsRanges[] = {
{kMinFPS, kMedFPS},
{kMedFPS, kMedFPS},
{kMinFPS, kMaxFPS},
{kMaxFPS, kMaxFPS},
};
return targetFpsRanges;
}
Span<const Rect<uint16_t>> FakeRotatingCamera::getAvailableThumbnailSizes() const {
static const Rect<uint16_t> availableThumbnailSizes[] = {
{0, 0},
{11 * 4, 9 * 4},
{16 * 4, 9 * 4},
{4 * 16, 3 * 16},
};
return availableThumbnailSizes;
}
bool FakeRotatingCamera::isBackFacing() const {
return mIsBackFacing;
}
Span<const float> FakeRotatingCamera::getAvailableFocalLength() const {
static const float availableFocalLengths[] = {
kDefaultFocalLength
};
return availableFocalLengths;
}
std::tuple<int32_t, int32_t, int32_t> FakeRotatingCamera::getMaxNumOutputStreams() const {
return {
0, // raw
2, // processed
1, // jpeg
};
}
Span<const PixelFormat> 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<uint16_t> FakeRotatingCamera::getSensorSize() const {
return {1920, 1080};
}
std::pair<int64_t, int64_t> FakeRotatingCamera::getSensorExposureTimeRange() const {
return {kMinSensorExposureTimeNs, kMaxSensorExposureTimeNs};
}
int64_t FakeRotatingCamera::getSensorMaxFrameDuration() const {
return kMaxFrameDurationNs;
}
Span<const Rect<uint16_t>> FakeRotatingCamera::getSupportedResolutions() const {
static const Rect<uint16_t> supportedResolutions[] = {
{176, 144},
{320, 240},
{640, 480},
{1024, 576},
{1280, 720},
{1600, 900},
{1920, 1080},
};
return supportedResolutions;
}
std::pair<int32_t, int32_t> 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