blob: 1c4d34068c4aede543ff3f5321b445e2d203f08c [file] [log] [blame]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jan Sebechlebsky4be2bd02024-02-26 18:35:18 +010017#include "system/camera_metadata.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010018#define LOG_TAG "VirtualCameraRenderThread"
19#include "VirtualCameraRenderThread.h"
20
21#include <chrono>
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +010022#include <cstdint>
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010023#include <cstring>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010024#include <future>
25#include <memory>
26#include <mutex>
27#include <thread>
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010028#include <vector>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010029
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010030#include "Exif.h"
Jan Sebechlebsky9ae496f2023-12-05 15:56:28 +010031#include "GLES/gl.h"
Biswarup Pal8ad8bc52024-02-08 13:41:44 +000032#include "VirtualCameraDevice.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010033#include "VirtualCameraSessionContext.h"
34#include "aidl/android/hardware/camera/common/Status.h"
35#include "aidl/android/hardware/camera/device/BufferStatus.h"
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010036#include "aidl/android/hardware/camera/device/CameraBlob.h"
37#include "aidl/android/hardware/camera/device/CameraBlobId.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010038#include "aidl/android/hardware/camera/device/CameraMetadata.h"
39#include "aidl/android/hardware/camera/device/CaptureResult.h"
40#include "aidl/android/hardware/camera/device/ErrorCode.h"
41#include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h"
42#include "aidl/android/hardware/camera/device/NotifyMsg.h"
43#include "aidl/android/hardware/camera/device/ShutterMsg.h"
44#include "aidl/android/hardware/camera/device/StreamBuffer.h"
45#include "android-base/thread_annotations.h"
46#include "android/binder_auto_utils.h"
47#include "android/hardware_buffer.h"
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +010048#include "ui/GraphicBuffer.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010049#include "util/EglFramebuffer.h"
50#include "util/JpegUtil.h"
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010051#include "util/MetadataUtil.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010052#include "util/TestPatternHelper.h"
53#include "util/Util.h"
54#include "utils/Errors.h"
55
56namespace android {
57namespace companion {
58namespace virtualcamera {
59
60using ::aidl::android::hardware::camera::common::Status;
61using ::aidl::android::hardware::camera::device::BufferStatus;
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010062using ::aidl::android::hardware::camera::device::CameraBlob;
63using ::aidl::android::hardware::camera::device::CameraBlobId;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010064using ::aidl::android::hardware::camera::device::CameraMetadata;
65using ::aidl::android::hardware::camera::device::CaptureResult;
66using ::aidl::android::hardware::camera::device::ErrorCode;
67using ::aidl::android::hardware::camera::device::ErrorMsg;
68using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
69using ::aidl::android::hardware::camera::device::NotifyMsg;
70using ::aidl::android::hardware::camera::device::ShutterMsg;
71using ::aidl::android::hardware::camera::device::Stream;
72using ::aidl::android::hardware::camera::device::StreamBuffer;
73using ::aidl::android::hardware::graphics::common::PixelFormat;
74using ::android::base::ScopedLockAssertion;
75
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010076using ::android::hardware::camera::common::helper::ExifUtils;
77
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010078namespace {
79
80using namespace std::chrono_literals;
81
82static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
83
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +010084// See REQUEST_PIPELINE_DEPTH in CaptureResult.java.
85// This roughly corresponds to frame latency, we set to
86// documented minimum of 2.
87static constexpr uint8_t kPipelineDepth = 2;
88
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010089static constexpr size_t kJpegThumbnailBufferSize = 32 * 1024; // 32 KiB
90
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010091CameraMetadata createCaptureResultMetadata(
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +010092 const std::chrono::nanoseconds timestamp,
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +010093 const RequestSettings& requestSettings,
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +010094 const Resolution reportedSensorSize) {
Vadim Caen11dfd932024-03-05 09:57:20 +010095 // All of the keys used in the response needs to be referenced in
96 // availableResultKeys in CameraCharacteristics (see initCameraCharacteristics
97 // in VirtualCameraDevice.cc).
98 MetadataBuilder builder =
Jan Sebechlebskyc3e1a632024-02-06 14:19:05 +010099 MetadataBuilder()
Jan Sebechlebsky4be2bd02024-02-26 18:35:18 +0100100 .setAberrationCorrectionMode(
101 ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF)
Vadim Caen11dfd932024-03-05 09:57:20 +0100102 .setControlAeAvailableAntibandingModes(
103 {ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF})
104 .setControlAeAntibandingMode(ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF)
105 .setControlAeExposureCompensation(0)
106 .setControlAeLockAvailable(false)
107 .setControlAeLock(ANDROID_CONTROL_AE_LOCK_OFF)
Jan Sebechlebskyc3e1a632024-02-06 14:19:05 +0100108 .setControlAeMode(ANDROID_CONTROL_AE_MODE_ON)
109 .setControlAePrecaptureTrigger(
110 ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
Vadim Caen11dfd932024-03-05 09:57:20 +0100111 .setControlAeState(ANDROID_CONTROL_AE_STATE_INACTIVE)
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100112 .setControlAfMode(ANDROID_CONTROL_AF_MODE_OFF)
Vadim Caen11dfd932024-03-05 09:57:20 +0100113 .setControlAfTrigger(ANDROID_CONTROL_AF_TRIGGER_IDLE)
114 .setControlAfState(ANDROID_CONTROL_AF_STATE_INACTIVE)
Jan Sebechlebskyc3e1a632024-02-06 14:19:05 +0100115 .setControlAwbMode(ANDROID_CONTROL_AWB_MODE_AUTO)
Vadim Caen11dfd932024-03-05 09:57:20 +0100116 .setControlAwbLock(ANDROID_CONTROL_AWB_LOCK_OFF)
117 .setControlAwbState(ANDROID_CONTROL_AWB_STATE_INACTIVE)
118 .setControlCaptureIntent(requestSettings.captureIntent)
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100119 .setControlEffectMode(ANDROID_CONTROL_EFFECT_MODE_OFF)
120 .setControlMode(ANDROID_CONTROL_MODE_AUTO)
Vadim Caen11dfd932024-03-05 09:57:20 +0100121 .setControlSceneMode(ANDROID_CONTROL_SCENE_MODE_DISABLED)
122 .setControlVideoStabilizationMode(
123 ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF)
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100124 .setCropRegion(0, 0, reportedSensorSize.width,
125 reportedSensorSize.height)
126 .setFaceDetectMode(ANDROID_STATISTICS_FACE_DETECT_MODE_OFF)
Jan Sebechlebskyc3e1a632024-02-06 14:19:05 +0100127 .setFlashState(ANDROID_FLASH_STATE_UNAVAILABLE)
Vadim Caen11dfd932024-03-05 09:57:20 +0100128 .setFlashMode(ANDROID_FLASH_MODE_OFF)
Biswarup Pal8ad8bc52024-02-08 13:41:44 +0000129 .setFocalLength(VirtualCameraDevice::kFocalLength)
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100130 .setJpegQuality(requestSettings.jpegQuality)
131 .setJpegThumbnailSize(requestSettings.thumbnailResolution.width,
132 requestSettings.thumbnailResolution.height)
133 .setJpegThumbnailQuality(requestSettings.thumbnailJpegQuality)
Vadim Caen11dfd932024-03-05 09:57:20 +0100134 .setLensOpticalStabilizationMode(
135 ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF)
Jan Sebechlebsky4be2bd02024-02-26 18:35:18 +0100136 .setNoiseReductionMode(ANDROID_NOISE_REDUCTION_MODE_OFF)
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100137 .setPipelineDepth(kPipelineDepth)
Jan Sebechlebskyc3e1a632024-02-06 14:19:05 +0100138 .setSensorTimestamp(timestamp)
Vadim Caen11dfd932024-03-05 09:57:20 +0100139 .setStatisticsHotPixelMapMode(
140 ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF)
141 .setStatisticsLensShadingMapMode(
142 ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF)
143 .setStatisticsSceneFlicker(ANDROID_STATISTICS_SCENE_FLICKER_NONE);
144
145 if (requestSettings.fpsRange.has_value()) {
146 builder.setControlAeTargetFpsRange(requestSettings.fpsRange.value());
147 }
148
149 std::unique_ptr<CameraMetadata> metadata = builder.build();
150
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100151 if (metadata == nullptr) {
152 ALOGE("%s: Failed to build capture result metadata", __func__);
153 return CameraMetadata();
154 }
155 return std::move(*metadata);
156}
157
158NotifyMsg createShutterNotifyMsg(int frameNumber,
159 std::chrono::nanoseconds timestamp) {
160 NotifyMsg msg;
161 msg.set<NotifyMsg::Tag::shutter>(ShutterMsg{
162 .frameNumber = frameNumber,
163 .timestamp = timestamp.count(),
164 });
165 return msg;
166}
167
168NotifyMsg createBufferErrorNotifyMsg(int frameNumber, int streamId) {
169 NotifyMsg msg;
170 msg.set<NotifyMsg::Tag::error>(ErrorMsg{.frameNumber = frameNumber,
171 .errorStreamId = streamId,
172 .errorCode = ErrorCode::ERROR_BUFFER});
173 return msg;
174}
175
176NotifyMsg createRequestErrorNotifyMsg(int frameNumber) {
177 NotifyMsg msg;
178 msg.set<NotifyMsg::Tag::error>(ErrorMsg{
Jan Sebechlebskyb0d8cab2023-11-28 10:55:04 +0100179 .frameNumber = frameNumber,
180 // errorStreamId needs to be set to -1 for ERROR_REQUEST
181 // (not tied to specific stream).
182 .errorStreamId = -1,
183 .errorCode = ErrorCode::ERROR_REQUEST});
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100184 return msg;
185}
186
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100187std::shared_ptr<EglFrameBuffer> allocateTemporaryFramebuffer(
188 EGLDisplay eglDisplay, const uint width, const int height) {
189 const AHardwareBuffer_Desc desc{
190 .width = static_cast<uint32_t>(width),
191 .height = static_cast<uint32_t>(height),
192 .layers = 1,
193 .format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
194 .usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
195 AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
196 .rfu0 = 0,
197 .rfu1 = 0};
198
199 AHardwareBuffer* hwBufferPtr;
200 int status = AHardwareBuffer_allocate(&desc, &hwBufferPtr);
201 if (status != NO_ERROR) {
202 ALOGE(
203 "%s: Failed to allocate hardware buffer for temporary framebuffer: %d",
204 __func__, status);
205 return nullptr;
206 }
207
208 return std::make_shared<EglFrameBuffer>(
209 eglDisplay,
210 std::shared_ptr<AHardwareBuffer>(hwBufferPtr, AHardwareBuffer_release));
211}
212
213bool isYuvFormat(const PixelFormat pixelFormat) {
214 switch (static_cast<android_pixel_format_t>(pixelFormat)) {
215 case HAL_PIXEL_FORMAT_YCBCR_422_I:
216 case HAL_PIXEL_FORMAT_YCBCR_422_SP:
217 case HAL_PIXEL_FORMAT_Y16:
218 case HAL_PIXEL_FORMAT_YV12:
219 case HAL_PIXEL_FORMAT_YCBCR_420_888:
220 return true;
221 default:
222 return false;
223 }
224}
225
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100226std::vector<uint8_t> createExif(
227 Resolution imageSize, const std::vector<uint8_t>& compressedThumbnail = {}) {
228 std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
229 exifUtils->initialize();
230 exifUtils->setImageWidth(imageSize.width);
231 exifUtils->setImageHeight(imageSize.height);
232 // TODO(b/324383963) Set Make/Model and orientation.
233
234 std::vector<uint8_t> app1Data;
235
236 size_t thumbnailDataSize = compressedThumbnail.size();
237 const void* thumbnailData =
238 thumbnailDataSize > 0
239 ? reinterpret_cast<const void*>(compressedThumbnail.data())
240 : nullptr;
241
242 if (!exifUtils->generateApp1(thumbnailData, thumbnailDataSize)) {
243 ALOGE("%s: Failed to generate APP1 segment for EXIF metadata", __func__);
244 return app1Data;
245 }
246
247 const uint8_t* data = exifUtils->getApp1Buffer();
248 const size_t size = exifUtils->getApp1Length();
249
250 app1Data.insert(app1Data.end(), data, data + size);
251 return app1Data;
252}
253
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100254} // namespace
255
256CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
257 sp<Fence> fence)
258 : mStreamId(streamId), mBufferId(bufferId), mFence(fence) {
259}
260
261int CaptureRequestBuffer::getStreamId() const {
262 return mStreamId;
263}
264
265int CaptureRequestBuffer::getBufferId() const {
266 return mBufferId;
267}
268
269sp<Fence> CaptureRequestBuffer::getFence() const {
270 return mFence;
271}
272
273VirtualCameraRenderThread::VirtualCameraRenderThread(
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100274 VirtualCameraSessionContext& sessionContext,
275 const Resolution inputSurfaceSize, const Resolution reportedSensorSize,
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100276 std::shared_ptr<ICameraDeviceCallback> cameraDeviceCallback, bool testMode)
277 : mCameraDeviceCallback(cameraDeviceCallback),
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100278 mInputSurfaceSize(inputSurfaceSize),
279 mReportedSensorSize(reportedSensorSize),
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100280 mTestMode(testMode),
281 mSessionContext(sessionContext) {
282}
283
284VirtualCameraRenderThread::~VirtualCameraRenderThread() {
285 stop();
286 if (mThread.joinable()) {
287 mThread.join();
288 }
289}
290
291ProcessCaptureRequestTask::ProcessCaptureRequestTask(
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100292 int frameNumber, const std::vector<CaptureRequestBuffer>& requestBuffers,
293 const RequestSettings& requestSettings)
294 : mFrameNumber(frameNumber),
295 mBuffers(requestBuffers),
296 mRequestSettings(requestSettings) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100297}
298
299int ProcessCaptureRequestTask::getFrameNumber() const {
300 return mFrameNumber;
301}
302
303const std::vector<CaptureRequestBuffer>& ProcessCaptureRequestTask::getBuffers()
304 const {
305 return mBuffers;
306}
307
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100308const RequestSettings& ProcessCaptureRequestTask::getRequestSettings() const {
309 return mRequestSettings;
310}
311
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100312void VirtualCameraRenderThread::enqueueTask(
313 std::unique_ptr<ProcessCaptureRequestTask> task) {
314 std::lock_guard<std::mutex> lock(mLock);
315 mQueue.emplace_back(std::move(task));
316 mCondVar.notify_one();
317}
318
319void VirtualCameraRenderThread::flush() {
320 std::lock_guard<std::mutex> lock(mLock);
Jan Sebechlebskyb0d8cab2023-11-28 10:55:04 +0100321 while (!mQueue.empty()) {
322 std::unique_ptr<ProcessCaptureRequestTask> task = std::move(mQueue.front());
323 mQueue.pop_front();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100324 flushCaptureRequest(*task);
325 }
326}
327
328void VirtualCameraRenderThread::start() {
329 mThread = std::thread(&VirtualCameraRenderThread::threadLoop, this);
330}
331
332void VirtualCameraRenderThread::stop() {
333 {
334 std::lock_guard<std::mutex> lock(mLock);
335 mPendingExit = true;
336 mCondVar.notify_one();
337 }
338}
339
340sp<Surface> VirtualCameraRenderThread::getInputSurface() {
341 return mInputSurfacePromise.get_future().get();
342}
343
344std::unique_ptr<ProcessCaptureRequestTask>
345VirtualCameraRenderThread::dequeueTask() {
346 std::unique_lock<std::mutex> lock(mLock);
347 // Clang's thread safety analysis doesn't perform alias analysis,
348 // so it doesn't support moveable std::unique_lock.
349 //
350 // Lock assertion below is basically explicit declaration that
351 // the lock is held in this scope, which is true, since it's only
352 // released during waiting inside mCondVar.wait calls.
353 ScopedLockAssertion lockAssertion(mLock);
354
355 mCondVar.wait(lock, [this]() REQUIRES(mLock) {
356 return mPendingExit || !mQueue.empty();
357 });
358 if (mPendingExit) {
359 return nullptr;
360 }
361 std::unique_ptr<ProcessCaptureRequestTask> task = std::move(mQueue.front());
362 mQueue.pop_front();
363 return task;
364}
365
366void VirtualCameraRenderThread::threadLoop() {
367 ALOGV("Render thread starting");
368
369 mEglDisplayContext = std::make_unique<EglDisplayContext>();
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100370 mEglTextureYuvProgram =
371 std::make_unique<EglTextureProgram>(EglTextureProgram::TextureFormat::YUV);
372 mEglTextureRgbProgram = std::make_unique<EglTextureProgram>(
373 EglTextureProgram::TextureFormat::RGBA);
Jan Sebechlebskybb01c1d2024-02-12 11:41:37 +0100374 mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(
375 mInputSurfaceSize.width, mInputSurfaceSize.height);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100376 mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
377
378 while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) {
379 processCaptureRequest(*task);
380 }
381
382 ALOGV("Render thread exiting");
383}
384
385void VirtualCameraRenderThread::processCaptureRequest(
386 const ProcessCaptureRequestTask& request) {
387 const std::chrono::nanoseconds timestamp =
388 std::chrono::duration_cast<std::chrono::nanoseconds>(
389 std::chrono::steady_clock::now().time_since_epoch());
390
391 CaptureResult captureResult;
392 captureResult.fmqResultSize = 0;
393 captureResult.frameNumber = request.getFrameNumber();
Jan Sebechlebskyb0d8cab2023-11-28 10:55:04 +0100394 // Partial result needs to be set to 1 when metadata are present.
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100395 captureResult.partialResult = 1;
396 captureResult.inputBuffer.streamId = -1;
397 captureResult.physicalCameraMetadata.resize(0);
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100398 captureResult.result = createCaptureResultMetadata(
399 timestamp, request.getRequestSettings(), mReportedSensorSize);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100400
401 const std::vector<CaptureRequestBuffer>& buffers = request.getBuffers();
402 captureResult.outputBuffers.resize(buffers.size());
403
404 if (mTestMode) {
405 // In test mode let's just render something to the Surface ourselves.
406 renderTestPatternYCbCr420(mEglSurfaceTexture->getSurface(),
407 request.getFrameNumber());
408 }
409
410 mEglSurfaceTexture->updateTexture();
411
412 for (int i = 0; i < buffers.size(); ++i) {
413 const CaptureRequestBuffer& reqBuffer = buffers[i];
414 StreamBuffer& resBuffer = captureResult.outputBuffers[i];
415 resBuffer.streamId = reqBuffer.getStreamId();
416 resBuffer.bufferId = reqBuffer.getBufferId();
417 resBuffer.status = BufferStatus::OK;
418
419 const std::optional<Stream> streamConfig =
420 mSessionContext.getStreamConfig(reqBuffer.getStreamId());
421
422 if (!streamConfig.has_value()) {
423 resBuffer.status = BufferStatus::ERROR;
424 continue;
425 }
426
427 auto status = streamConfig->format == PixelFormat::BLOB
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100428 ? renderIntoBlobStreamBuffer(
429 reqBuffer.getStreamId(), reqBuffer.getBufferId(),
430 request.getRequestSettings(), reqBuffer.getFence())
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100431 : renderIntoImageStreamBuffer(reqBuffer.getStreamId(),
432 reqBuffer.getBufferId(),
433 reqBuffer.getFence());
434 if (!status.isOk()) {
435 resBuffer.status = BufferStatus::ERROR;
436 }
437 }
438
439 std::vector<NotifyMsg> notifyMsg{
440 createShutterNotifyMsg(request.getFrameNumber(), timestamp)};
441 for (const StreamBuffer& resBuffer : captureResult.outputBuffers) {
442 if (resBuffer.status != BufferStatus::OK) {
443 notifyMsg.push_back(createBufferErrorNotifyMsg(request.getFrameNumber(),
444 resBuffer.streamId));
445 }
446 }
447
448 auto status = mCameraDeviceCallback->notify(notifyMsg);
449 if (!status.isOk()) {
450 ALOGE("%s: notify call failed: %s", __func__,
451 status.getDescription().c_str());
452 return;
453 }
454
455 std::vector<::aidl::android::hardware::camera::device::CaptureResult>
456 captureResults(1);
457 captureResults[0] = std::move(captureResult);
458
459 status = mCameraDeviceCallback->processCaptureResult(captureResults);
460 if (!status.isOk()) {
461 ALOGE("%s: processCaptureResult call failed: %s", __func__,
462 status.getDescription().c_str());
463 return;
464 }
465
Vadim Caen324fcfb2024-03-21 16:49:08 +0100466 ALOGV("%s: Successfully called processCaptureResult", __func__);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100467}
468
469void VirtualCameraRenderThread::flushCaptureRequest(
470 const ProcessCaptureRequestTask& request) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100471 CaptureResult captureResult;
472 captureResult.fmqResultSize = 0;
473 captureResult.frameNumber = request.getFrameNumber();
474 captureResult.inputBuffer.streamId = -1;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100475
476 const std::vector<CaptureRequestBuffer>& buffers = request.getBuffers();
477 captureResult.outputBuffers.resize(buffers.size());
478
479 for (int i = 0; i < buffers.size(); ++i) {
480 const CaptureRequestBuffer& reqBuffer = buffers[i];
481 StreamBuffer& resBuffer = captureResult.outputBuffers[i];
482 resBuffer.streamId = reqBuffer.getStreamId();
483 resBuffer.bufferId = reqBuffer.getBufferId();
484 resBuffer.status = BufferStatus::ERROR;
485 sp<Fence> fence = reqBuffer.getFence();
486 if (fence != nullptr && fence->isValid()) {
487 resBuffer.releaseFence.fds.emplace_back(fence->dup());
488 }
489 }
490
491 auto status = mCameraDeviceCallback->notify(
492 {createRequestErrorNotifyMsg(request.getFrameNumber())});
493 if (!status.isOk()) {
494 ALOGE("%s: notify call failed: %s", __func__,
495 status.getDescription().c_str());
496 return;
497 }
498
499 std::vector<::aidl::android::hardware::camera::device::CaptureResult>
500 captureResults(1);
501 captureResults[0] = std::move(captureResult);
502
503 status = mCameraDeviceCallback->processCaptureResult(captureResults);
504 if (!status.isOk()) {
505 ALOGE("%s: processCaptureResult call failed: %s", __func__,
506 status.getDescription().c_str());
507 }
508}
509
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100510std::vector<uint8_t> VirtualCameraRenderThread::createThumbnail(
511 const Resolution resolution, const int quality) {
512 if (resolution.width == 0 || resolution.height == 0) {
513 ALOGV("%s: Skipping thumbnail creation, zero size requested", __func__);
514 return {};
515 }
516
517 ALOGV("%s: Creating thumbnail with size %d x %d, quality %d", __func__,
518 resolution.width, resolution.height, quality);
519 std::shared_ptr<EglFrameBuffer> framebuffer = allocateTemporaryFramebuffer(
520 mEglDisplayContext->getEglDisplay(), resolution.width, resolution.height);
521 if (framebuffer == nullptr) {
522 ALOGE(
523 "Failed to allocate temporary framebuffer for JPEG thumbnail "
524 "compression");
525 return {};
526 }
527
528 // TODO(b/324383963) Add support for letterboxing if the thumbnail size
529 // doesn't correspond
530 // to input texture aspect ratio.
531 if (!renderIntoEglFramebuffer(*framebuffer).isOk()) {
532 ALOGE(
533 "Failed to render input texture into temporary framebuffer for JPEG "
534 "thumbnail");
535 return {};
536 }
537
538 std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
539 GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
540
541 if (gBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_YCbCr_420_888) {
542 // This should never happen since we're allocating the temporary buffer
543 // with YUV420 layout above.
544 ALOGE("%s: Cannot compress non-YUV buffer (pixelFormat %d)", __func__,
545 gBuffer->getPixelFormat());
546 return {};
547 }
548
Jan Sebechlebsky43543222024-02-16 12:50:32 +0100549 YCbCrLockGuard yCbCrLock(inHwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
550 if (yCbCrLock.getStatus() != NO_ERROR) {
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100551 ALOGE("%s: Failed to lock graphic buffer while generating thumbnail: %d",
Jan Sebechlebsky43543222024-02-16 12:50:32 +0100552 __func__, yCbCrLock.getStatus());
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100553 return {};
554 }
555
556 std::vector<uint8_t> compressedThumbnail;
557 compressedThumbnail.resize(kJpegThumbnailBufferSize);
558 ALOGE("%s: Compressing thumbnail %d x %d", __func__, gBuffer->getWidth(),
559 gBuffer->getHeight());
Jan Sebechlebsky43543222024-02-16 12:50:32 +0100560 std::optional<size_t> compressedSize = compressJpeg(
561 gBuffer->getWidth(), gBuffer->getHeight(), quality, *yCbCrLock, {},
562 compressedThumbnail.size(), compressedThumbnail.data());
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100563 if (!compressedSize.has_value()) {
564 ALOGE("%s: Failed to compress jpeg thumbnail", __func__);
565 return {};
566 }
567 compressedThumbnail.resize(compressedSize.value());
568 return compressedThumbnail;
569}
570
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100571ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoBlobStreamBuffer(
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100572 const int streamId, const int bufferId,
573 const RequestSettings& requestSettings, sp<Fence> fence) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100574 std::shared_ptr<AHardwareBuffer> hwBuffer =
575 mSessionContext.fetchHardwareBuffer(streamId, bufferId);
Jan Sebechlebsky9ae496f2023-12-05 15:56:28 +0100576 if (hwBuffer == nullptr) {
577 ALOGE("%s: Failed to fetch hardware buffer %d for streamId %d", __func__,
578 bufferId, streamId);
579 return cameraStatus(Status::INTERNAL_ERROR);
580 }
581
582 std::optional<Stream> stream = mSessionContext.getStreamConfig(streamId);
583 if (!stream.has_value()) {
584 ALOGE("%s, failed to fetch information about stream %d", __func__, streamId);
585 return cameraStatus(Status::INTERNAL_ERROR);
586 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100587
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100588 ALOGV("%s: Rendering JPEG with size %d x %d, quality %d", __func__,
589 stream->width, stream->height, requestSettings.jpegQuality);
590
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100591 // Let's create YUV framebuffer and render the surface into this.
592 // This will take care about rescaling as well as potential format conversion.
593 std::shared_ptr<EglFrameBuffer> framebuffer = allocateTemporaryFramebuffer(
594 mEglDisplayContext->getEglDisplay(), stream->width, stream->height);
595 if (framebuffer == nullptr) {
596 ALOGE("Failed to allocate temporary framebuffer for JPEG compression");
597 return cameraStatus(Status::INTERNAL_ERROR);
598 }
599
600 // Render into temporary framebuffer.
601 ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer);
602 if (!status.isOk()) {
603 ALOGE("Failed to render input texture into temporary framebuffer");
604 return status;
605 }
606
Jan Sebechlebsky43543222024-02-16 12:50:32 +0100607 PlanesLockGuard planesLock(hwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
608 fence);
609 if (planesLock.getStatus() != OK) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100610 return cameraStatus(Status::INTERNAL_ERROR);
611 }
612
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100613 std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
614 GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
615
Jan Sebechlebsky5c789e42024-02-29 16:32:17 +0100616 if (gBuffer == nullptr) {
617 ALOGE(
618 "%s: Encountered invalid temporary buffer while rendering JPEG "
619 "into BLOB stream",
620 __func__);
621 return cameraStatus(Status::INTERNAL_ERROR);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100622 }
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100623
Jan Sebechlebsky5c789e42024-02-29 16:32:17 +0100624 if (gBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_YCbCr_420_888) {
625 // This should never happen since we're allocating the temporary buffer
626 // with YUV420 layout above.
627 ALOGE("%s: Cannot compress non-YUV buffer (pixelFormat %d)", __func__,
628 gBuffer->getPixelFormat());
629 return cameraStatus(Status::INTERNAL_ERROR);
630 }
631
632 YCbCrLockGuard yCbCrLock(inHwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
633 if (yCbCrLock.getStatus() != OK) {
634 return cameraStatus(Status::INTERNAL_ERROR);
635 }
636
637 std::vector<uint8_t> app1ExifData =
638 createExif(Resolution(stream->width, stream->height),
639 createThumbnail(requestSettings.thumbnailResolution,
640 requestSettings.thumbnailJpegQuality));
641 std::optional<size_t> compressedSize = compressJpeg(
642 gBuffer->getWidth(), gBuffer->getHeight(), requestSettings.jpegQuality,
643 *yCbCrLock, app1ExifData, stream->bufferSize - sizeof(CameraBlob),
644 (*planesLock).planes[0].data);
645
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100646 if (!compressedSize.has_value()) {
647 ALOGE("%s: Failed to compress JPEG image", __func__);
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100648 return cameraStatus(Status::INTERNAL_ERROR);
649 }
650
651 CameraBlob cameraBlob{
652 .blobId = CameraBlobId::JPEG,
653 .blobSizeBytes = static_cast<int32_t>(compressedSize.value())};
654
Jan Sebechlebsky43543222024-02-16 12:50:32 +0100655 memcpy(reinterpret_cast<uint8_t*>((*planesLock).planes[0].data) +
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100656 (stream->bufferSize - sizeof(cameraBlob)),
657 &cameraBlob, sizeof(cameraBlob));
658
Jan Sebechlebsky4ce32082024-02-14 16:02:11 +0100659 ALOGV("%s: Successfully compressed JPEG image, resulting size %zu B",
660 __func__, compressedSize.value());
661
662 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100663}
664
665ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoImageStreamBuffer(
666 int streamId, int bufferId, sp<Fence> fence) {
667 ALOGV("%s", __func__);
668
669 const std::chrono::nanoseconds before =
670 std::chrono::duration_cast<std::chrono::nanoseconds>(
671 std::chrono::steady_clock::now().time_since_epoch());
672
673 // Render test pattern using EGL.
674 std::shared_ptr<EglFrameBuffer> framebuffer =
675 mSessionContext.fetchOrCreateEglFramebuffer(
676 mEglDisplayContext->getEglDisplay(), streamId, bufferId);
677 if (framebuffer == nullptr) {
678 ALOGE(
679 "%s: Failed to get EGL framebuffer corresponding to buffer id "
680 "%d for streamId %d",
681 __func__, bufferId, streamId);
682 return cameraStatus(Status::ILLEGAL_ARGUMENT);
683 }
684
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100685 ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer, fence);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100686
687 const std::chrono::nanoseconds after =
688 std::chrono::duration_cast<std::chrono::nanoseconds>(
689 std::chrono::steady_clock::now().time_since_epoch());
690
691 ALOGV("Rendering to buffer %d, stream %d took %lld ns", bufferId, streamId,
692 after.count() - before.count());
693
694 return ndk::ScopedAStatus::ok();
695}
696
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100697ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoEglFramebuffer(
698 EglFrameBuffer& framebuffer, sp<Fence> fence) {
699 ALOGV("%s", __func__);
700 // Wait for fence to clear.
701 if (fence != nullptr && fence->isValid()) {
702 status_t ret = fence->wait(kAcquireFenceTimeout.count());
703 if (ret != 0) {
704 ALOGE("Timeout while waiting for the acquire fence for buffer");
705 return cameraStatus(Status::INTERNAL_ERROR);
706 }
707 }
708
709 mEglDisplayContext->makeCurrent();
710 framebuffer.beforeDraw();
711
712 sp<GraphicBuffer> textureBuffer = mEglSurfaceTexture->getCurrentBuffer();
713 if (textureBuffer == nullptr) {
714 // If there's no current buffer, nothing was written to the surface and
715 // texture is not initialized yet. Let's render the framebuffer black
716 // instead of rendering the texture.
717 glClearColor(0.0f, 0.5f, 0.5f, 0.0f);
718 glClear(GL_COLOR_BUFFER_BIT);
719 } else {
720 const bool renderSuccess =
721 isYuvFormat(static_cast<PixelFormat>(textureBuffer->getPixelFormat()))
Jan Sebechlebsky99492e32023-12-20 09:49:45 +0100722 ? mEglTextureYuvProgram->draw(
723 mEglSurfaceTexture->getTextureId(),
724 mEglSurfaceTexture->getTransformMatrix())
725 : mEglTextureRgbProgram->draw(
726 mEglSurfaceTexture->getTextureId(),
727 mEglSurfaceTexture->getTransformMatrix());
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100728 if (!renderSuccess) {
729 ALOGE("%s: Failed to render texture", __func__);
730 return cameraStatus(Status::INTERNAL_ERROR);
731 }
732 }
733 framebuffer.afterDraw();
734
735 return ndk::ScopedAStatus::ok();
736}
737
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100738} // namespace virtualcamera
739} // namespace companion
740} // namespace android