213 lines
9.5 KiB
C++
213 lines
9.5 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// RAM-only diagnostic reproducing Gecko's Android 6 GonkBufferQueue path.
|
|
#include <camera/Camera.h>
|
|
#include <camera/CameraParameters.h>
|
|
#include <system/camera.h>
|
|
#include <binder/ProcessState.h>
|
|
#include <gui/GraphicBufferAlloc.h>
|
|
#include <gui/IConsumerListener.h>
|
|
#include <ui/Fence.h>
|
|
#include <ui/GraphicBuffer.h>
|
|
#include <utils/String16.h>
|
|
#include <utils/Mutex.h>
|
|
#include <utils/Vector.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <inttypes.h>
|
|
#include <utils/Timers.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define ANDROID_VERSION 23
|
|
#include "GonkBufferQueueLL.h"
|
|
#include "GonkBufferQueueProducer.h"
|
|
|
|
class CameraEvents : public android::CameraListener {
|
|
public:
|
|
void notify(int32_t type, int32_t ext1, int32_t ext2) override {
|
|
fprintf(stderr, "camera_notify=%d,%d,%d\n", type, ext1, ext2);
|
|
}
|
|
void postData(int32_t type, const android::sp<android::IMemory>& memory,
|
|
camera_frame_metadata_t*) override {
|
|
if (memory == nullptr) return;
|
|
const size_t size = memory->size();
|
|
const unsigned char* bytes = static_cast<const unsigned char*>(memory->pointer());
|
|
unsigned int low = 255, high = 0;
|
|
uint64_t sum = 0;
|
|
const size_t ySize = size < 640u * 480u ? size : 640u * 480u;
|
|
for (size_t i = 0; i < ySize; ++i) {
|
|
if (bytes[i] < low) low = bytes[i];
|
|
if (bytes[i] > high) high = bytes[i];
|
|
sum += bytes[i];
|
|
}
|
|
fprintf(stderr, "camera_data=%d size=%zu y=%u..%u mean=%.2f\n",
|
|
type, size, low, high, ySize ? (double)sum / ySize : 0);
|
|
static bool saved = false;
|
|
if (type == CAMERA_MSG_COMPRESSED_IMAGE) {
|
|
FILE* jpeg = fopen("/data/local/tmp/vulpes-camera-callback.jpg", "wb");
|
|
if (jpeg) { fwrite(bytes, 1, size, jpeg); fclose(jpeg); chmod("/data/local/tmp/vulpes-camera-callback.jpg", 0644); }
|
|
fprintf(stderr, "captured_jpeg=%zu\n", size);
|
|
} else if (!saved) {
|
|
FILE* output = fopen("/data/local/tmp/vulpes-camera-callback.yuv", "wb");
|
|
if (output) { fwrite(bytes, 1, size, output); fclose(output); chmod("/data/local/tmp/vulpes-camera-callback.yuv", 0644); saved = true; }
|
|
}
|
|
}
|
|
void postDataTimestamp(nsecs_t, int32_t type,
|
|
const android::sp<android::IMemory>&) override {
|
|
fprintf(stderr, "camera_timestamp_data=%d\n", type);
|
|
}
|
|
};
|
|
|
|
class QueueListener : public android::ConsumerListener {
|
|
public:
|
|
void onFrameAvailable(const android::BufferItem&) override {}
|
|
void onFrameReplaced(const android::BufferItem&) override {}
|
|
void onBuffersReleased() override {}
|
|
void onSidebandStreamChanged() override {}
|
|
};
|
|
|
|
int main() {
|
|
setvbuf(stderr, nullptr, _IONBF, 0);
|
|
android::ProcessState::self()->startThreadPool();
|
|
const int count = android::Camera::getNumberOfCameras();
|
|
fprintf(stderr, "camera_count=%d uid=%d\n", count, getuid());
|
|
if (count < 1) return 2;
|
|
|
|
android::sp<android::Camera> camera = android::Camera::connect(
|
|
// Match GonkCameraHwMgr.cpp exactly. CameraService/AppOps on this
|
|
// firmware treats the client package as part of camera authorization.
|
|
0, android::String16("gonk.camera"),
|
|
android::Camera::USE_CALLING_UID);
|
|
if (camera == nullptr) return 3;
|
|
android::CameraParameters params(camera->getParameters());
|
|
android::Vector<android::Size> previews, pictures;
|
|
params.getSupportedPreviewSizes(previews);
|
|
params.getSupportedPictureSizes(pictures);
|
|
android::Size picture = {0, 0};
|
|
for (size_t i = 0; i < pictures.size(); ++i)
|
|
if (pictures[i].width * pictures[i].height > picture.width * picture.height)
|
|
picture = pictures[i];
|
|
android::Size preview = {0, 0};
|
|
uint64_t bestDelta = UINT64_MAX;
|
|
for (size_t i = 0; i < previews.size(); ++i) {
|
|
const android::Size candidate = previews[i];
|
|
if (picture.width && candidate.width * picture.height != picture.width * candidate.height)
|
|
continue;
|
|
const uint64_t area = (uint64_t)candidate.width * candidate.height;
|
|
const uint64_t delta = area > 640u * 480u ? area - 640u * 480u : 640u * 480u - area;
|
|
if (delta < bestDelta) { preview = candidate; bestDelta = delta; }
|
|
}
|
|
if (!preview.width && previews.size()) preview = previews[0];
|
|
if (!preview.width || !picture.width) {
|
|
fprintf(stderr, "no supported preview/picture size (preview=%zu picture=%zu)\n",
|
|
previews.size(), pictures.size());
|
|
camera->disconnect();
|
|
return 4;
|
|
}
|
|
params.setPreviewSize(preview.width, preview.height);
|
|
params.setPictureSize(picture.width, picture.height);
|
|
// Gecko applies its recording hint after selecting still/preview sizes and
|
|
// before starting the preview. Keep the probe's Camera1 state in step.
|
|
params.set("recording-hint", "false");
|
|
android::String8 configured = params.flatten();
|
|
android::status_t status = camera->setParameters(configured);
|
|
fprintf(stderr, "set_parameters=%d requested_preview=%dx%d picture=%dx%d\n",
|
|
status, preview.width, preview.height, picture.width, picture.height);
|
|
if (status != android::NO_ERROR) { camera->disconnect(); return 4; }
|
|
params.unflatten(camera->getParameters());
|
|
int width = 0, height = 0;
|
|
params.getPreviewSize(&width, &height);
|
|
int pictureWidth = 0, pictureHeight = 0;
|
|
params.getPictureSize(&pictureWidth, &pictureHeight);
|
|
fprintf(stderr, "preview=%dx%d picture=%dx%d format=%s\n", width, height,
|
|
pictureWidth, pictureHeight, params.getPreviewFormat());
|
|
|
|
android::sp<android::IGraphicBufferProducer> producer;
|
|
android::sp<android::IGonkGraphicBufferConsumer> consumer;
|
|
android::sp<android::GraphicBufferAlloc> allocator =
|
|
new android::GraphicBufferAlloc();
|
|
android::GonkBufferQueue::createBufferQueue(&producer, &consumer, allocator);
|
|
status = static_cast<android::GonkBufferQueueProducer*>(producer.get())
|
|
->setSynchronousMode(false);
|
|
fprintf(stderr, "async_producer=%d\n", status);
|
|
status = consumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
|
|
fprintf(stderr, "cpu_read_usage=%d\n", status);
|
|
status = consumer->setMaxAcquiredBufferCount(4);
|
|
fprintf(stderr, "max_acquired=%d\n", status);
|
|
android::sp<QueueListener> listener = new QueueListener();
|
|
android::sp<android::IConsumerListener> proxy =
|
|
new android::GonkBufferQueue::ProxyConsumerListener(listener);
|
|
status = consumer->consumerConnect(proxy, false);
|
|
fprintf(stderr, "consumer_connect=%d\n", status);
|
|
android::sp<CameraEvents> cameraEvents = new CameraEvents();
|
|
camera->setListener(cameraEvents);
|
|
status = camera->setPreviewTarget(producer);
|
|
fprintf(stderr, "set_preview_target=%d\n", status);
|
|
camera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
|
|
fprintf(stderr, "preview_callback_flags=%d\n", CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
|
|
const android::status_t focusEvents = camera->sendCommand(
|
|
CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG, 1, 0);
|
|
fprintf(stderr, "focus_move_events=%d\n", focusEvents);
|
|
status = camera->startPreview();
|
|
fprintf(stderr, "start_preview=%d\n", status);
|
|
if (status) return 4;
|
|
|
|
unsigned acquired = 0;
|
|
bool saved = false;
|
|
const int64_t deadline = systemTime(SYSTEM_TIME_MONOTONIC) + 5000000000LL;
|
|
while (systemTime(SYSTEM_TIME_MONOTONIC) < deadline) {
|
|
android::IGonkGraphicBufferConsumer::BufferItem item;
|
|
status = consumer->acquireBuffer(&item, 0);
|
|
if (status != android::NO_ERROR) { usleep(20000); continue; }
|
|
++acquired;
|
|
android::sp<android::GraphicBuffer> buffer = item.mGraphicBuffer;
|
|
if (item.mFence != nullptr) item.mFence->waitForever("vulpes-gonk-camera");
|
|
void* pixels = nullptr;
|
|
if (buffer != nullptr &&
|
|
buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels) == android::NO_ERROR &&
|
|
pixels != nullptr) {
|
|
const size_t bytes = (size_t)buffer->getStride() * buffer->getHeight() * 3 / 2;
|
|
const unsigned char* y = static_cast<const unsigned char*>(pixels);
|
|
unsigned int minimum = 255, maximum = 0;
|
|
uint64_t sum = 0;
|
|
const size_t yBytes = (size_t)buffer->getStride() * buffer->getHeight();
|
|
for (size_t i = 0; i < yBytes; ++i) {
|
|
if (y[i] < minimum) minimum = y[i];
|
|
if (y[i] > maximum) maximum = y[i];
|
|
sum += y[i];
|
|
}
|
|
fprintf(stderr, "frame=%u number=%llu timestamp=%llu y=%u..%u mean=%.2f format=%d\n",
|
|
acquired, (unsigned long long)item.mFrameNumber, (unsigned long long)item.mTimestamp, minimum, maximum,
|
|
(double)sum / yBytes, buffer->format);
|
|
if (!saved || acquired % 10 == 0) {
|
|
FILE* output = fopen("/data/local/tmp/vulpes-camera-gonkqueue.nv21", "wb");
|
|
if (output) {
|
|
fwrite(pixels, 1, bytes, output);
|
|
fclose(output);
|
|
chmod("/data/local/tmp/vulpes-camera-gonkqueue.nv21", 0644);
|
|
FILE* meta = fopen("/data/local/tmp/vulpes-camera-gonkqueue.txt", "w");
|
|
if (meta) {
|
|
fprintf(meta, "%u %u %u %d %zu %u %u %u %u\n", buffer->getWidth(),
|
|
buffer->getHeight(), buffer->getStride(), buffer->format, bytes,
|
|
acquired, minimum, maximum, (unsigned)(sum / yBytes));
|
|
fclose(meta);
|
|
chmod("/data/local/tmp/vulpes-camera-gonkqueue.txt", 0644);
|
|
}
|
|
saved = true;
|
|
}
|
|
}
|
|
buffer->unlock();
|
|
}
|
|
consumer->releaseBuffer(item.mBuf, item.mFrameNumber, android::Fence::NO_FENCE);
|
|
}
|
|
fprintf(stderr, "acquired=%u saved=%d; taking_still\n", acquired, saved);
|
|
status = camera->takePicture(CAMERA_MSG_SHUTTER | CAMERA_MSG_COMPRESSED_IMAGE);
|
|
fprintf(stderr, "take_picture=%d\n", status);
|
|
sleep(3);
|
|
fprintf(stderr, "stopping_preview\n");
|
|
camera->stopPreview();
|
|
fprintf(stderr, "camera_stopped; disconnecting\n");
|
|
camera->disconnect();
|
|
consumer->consumerDisconnect();
|
|
fprintf(stderr, "done\n");
|
|
return saved ? 0 : 5;
|
|
}
|