Release Vulpes OS 2.7-preview.24
This commit is contained in:
parent
49572b6b45
commit
97ef99f827
135 changed files with 7717 additions and 2524 deletions
214
tundra/probes/beatles-camera-worker.cpp
Normal file
214
tundra/probes/beatles-camera-worker.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// CameraService worker for the Nokia 2720 Flip. The parent broker drops this
|
||||
// process to Android's camera UID before exec. Gaia talks to it over a local
|
||||
// socket; it never receives Binder or elevated credentials.
|
||||
#include <camera/Camera.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <gui/BufferQueue.h>
|
||||
#include <gui/CpuConsumer.h>
|
||||
#include <gui/GraphicBufferAlloc.h>
|
||||
#include <hardware/gralloc.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/String16.h>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
namespace {
|
||||
bool write_all(int fd, const void *data, size_t size) {
|
||||
const char *p = static_cast<const char *>(data);
|
||||
while (size) {
|
||||
ssize_t n = write(fd, p, size);
|
||||
if (n <= 0) return false;
|
||||
p += n; size -= static_cast<size_t>(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool read_line(int fd, std::string *line) {
|
||||
line->clear();
|
||||
char c;
|
||||
while (line->size() < 2048) {
|
||||
ssize_t n = read(fd, &c, 1);
|
||||
if (n <= 0) return false;
|
||||
if (c == '\n') return true;
|
||||
line->push_back(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool send_json(int fd, const std::string &json, bool frame=false,
|
||||
uint32_t sequence=0, const std::vector<uint8_t> *bytes=nullptr) {
|
||||
uint32_t length = static_cast<uint32_t>(json.size());
|
||||
if (frame) {
|
||||
uint32_t header[2] = {sequence, bytes ? static_cast<uint32_t>(bytes->size()) : 0};
|
||||
return write_all(fd, header, sizeof(header)) && (!bytes || write_all(fd,bytes->data(),bytes->size()));
|
||||
}
|
||||
return write_all(fd,&length,sizeof(length)) && write_all(fd,json.data(),json.size());
|
||||
}
|
||||
|
||||
class CameraWorker : public android::CameraListener {
|
||||
public:
|
||||
explicit CameraWorker(int fd) : fd_(fd), camera_id_(0), sequence_(0), stopping_(false) {}
|
||||
void notify(int32_t type, int32_t first, int32_t second) override {
|
||||
fprintf(stderr,"camera notify type=%d first=%d second=%d\n",type,first,second);
|
||||
}
|
||||
void postData(int32_t type, const android::sp<android::IMemory> &data,
|
||||
camera_frame_metadata_t *) override {
|
||||
if (data == nullptr) return;
|
||||
if (type == CAMERA_MSG_COMPRESSED_IMAGE) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
jpeg_.assign(static_cast<const uint8_t *>(data->pointer()),
|
||||
static_cast<const uint8_t *>(data->pointer()) + data->size());
|
||||
ready_.notify_all();
|
||||
return;
|
||||
}
|
||||
if (type != CAMERA_MSG_PREVIEW_FRAME) return;
|
||||
const size_t expected = 320u * 240u * 3u / 2u;
|
||||
if (data->size() < expected) return;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frame_.assign(static_cast<const uint8_t *>(data->pointer()),
|
||||
static_cast<const uint8_t *>(data->pointer()) + expected);
|
||||
++sequence_;
|
||||
if(sequence_==1) fprintf(stderr,"camera first frame bytes=%zu uid=%d\n",frame_.size(),getuid());
|
||||
}
|
||||
ready_.notify_all();
|
||||
}
|
||||
void postDataTimestamp(nsecs_t, int32_t,
|
||||
const android::sp<android::IMemory> &) override {}
|
||||
|
||||
int run() {
|
||||
android::ProcessState::self()->startThreadPool();
|
||||
std::string line;
|
||||
while (read_line(fd_, &line)) {
|
||||
if (line == "start" || line == "start 0") {
|
||||
if (!start()) { send_json(fd_,"{\"ready\":false,\"error\":\"CAMERA_START_FAILED\"}"); continue; }
|
||||
if (!send_json(fd_,"{\"ready\":true,\"width\":320,\"height\":240,\"format\":\"nv21\",\"orientation\":270,\"flashModes\":[\"off\",\"auto\",\"on\",\"torch\"],\"pictureSizes\":[{\"width\":1600,\"height\":1200}],\"focusModes\":[\"fixed\"],\"maxFocusAreas\":0,\"zoomRatios\":[1] }")) return 1;
|
||||
} else if (line == "frame") {
|
||||
std::vector<uint8_t> frame;
|
||||
uint32_t seq;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
const uint32_t previous = delivered_;
|
||||
ready_.wait_for(lock,std::chrono::seconds(2),[&]{return sequence_>previous || stopping_;});
|
||||
if (sequence_<=previous || frame_.empty()) { if(!send_json(fd_,"",true,sequence_,nullptr)) return 1; continue; }
|
||||
seq=sequence_; delivered_=seq; frame=frame_;
|
||||
}
|
||||
if (!send_json(fd_,"",true,seq,&frame)) return 1;
|
||||
} else if (line == "stop") {
|
||||
stop(); send_json(fd_,"{\"ok\":true}"); return 0;
|
||||
} else if (line.find("\"action\":\"flash\"") != std::string::npos) {
|
||||
const size_t key=line.find("\"mode\":\"");
|
||||
const size_t begin=key==std::string::npos?key:key+8;
|
||||
const size_t end=begin==std::string::npos?begin:line.find('"',begin);
|
||||
if(begin==std::string::npos || end==std::string::npos) {
|
||||
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_INVALID_FLASH_MODE\"}");continue;
|
||||
}
|
||||
const std::string mode=line.substr(begin,end-begin);
|
||||
android::CameraParameters params(camera_->getParameters());
|
||||
params.set(android::CameraParameters::KEY_FLASH_MODE,mode.c_str());
|
||||
const android::status_t status=camera_->setParameters(params.flatten());
|
||||
if(status==android::NO_ERROR) {
|
||||
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
|
||||
camera_->startPreview();
|
||||
send_json(fd_,"{\"ok\":true,\"result\":true}");
|
||||
}
|
||||
else send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_FLASH_UNAVAILABLE\"}");
|
||||
} else if (line.find("\"action\":\"zoom\"") != std::string::npos) {
|
||||
// This module reports a single zoom ratio (1x), so expose a stable
|
||||
// no-op instead of asking Camera1 to select an unsupported index.
|
||||
send_json(fd_,"{\"ok\":true,\"result\":{\"zoom\":1}}");
|
||||
} else if (line.find("\"action\":\"picture\"") != std::string::npos) {
|
||||
{ std::lock_guard<std::mutex> lock(mutex_); jpeg_.clear(); }
|
||||
const android::status_t status=camera_->takePicture(CAMERA_MSG_COMPRESSED_IMAGE);
|
||||
std::vector<uint8_t> jpeg;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
ready_.wait_for(lock,std::chrono::seconds(12),[&]{return !jpeg_.empty() || stopping_;});
|
||||
jpeg=jpeg_;
|
||||
}
|
||||
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
|
||||
camera_->startPreview();
|
||||
if(status!=android::NO_ERROR || jpeg.empty()) {
|
||||
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_CAPTURE_FAILED\"}");continue;
|
||||
}
|
||||
char reply[128];
|
||||
std::snprintf(reply,sizeof(reply),"{\"ok\":true,\"jpegLength\":%zu}",jpeg.size());
|
||||
if(!send_json(fd_,reply) || !write_all(fd_,jpeg.data(),jpeg.size())) return 1;
|
||||
} else {
|
||||
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_UNSUPPORTED_COMMAND\"}");
|
||||
}
|
||||
}
|
||||
stop(); return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool start() {
|
||||
if (camera_.get() != nullptr) stop();
|
||||
fprintf(stderr,"camera count=%d uid=%d\n",android::Camera::getNumberOfCameras(),getuid());
|
||||
camera_ = android::Camera::connect(camera_id_,android::String16("gonk.camera"),android::Camera::USE_CALLING_UID);
|
||||
if (camera_.get() == nullptr) {fprintf(stderr,"camera connect failed\n");return false;}
|
||||
android::CameraParameters params(camera_->getParameters());
|
||||
params.setPreviewSize(320,240); params.setPictureSize(1600,1200);
|
||||
params.setPreviewFormat("yuv420sp"); params.set("recording-hint","false");
|
||||
android::status_t status=camera_->setParameters(params.flatten());
|
||||
fprintf(stderr,"camera set parameters=%d\n",status);
|
||||
if (status != android::NO_ERROR) { stop(); return false; }
|
||||
android::sp<android::IGraphicBufferProducer> producer;
|
||||
android::sp<android::IGraphicBufferConsumer> consumer;
|
||||
android::sp<android::GraphicBufferAlloc> allocator = new android::GraphicBufferAlloc();
|
||||
android::BufferQueue::createBufferQueue(&producer,&consumer,allocator);
|
||||
consumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER);
|
||||
consumer->setDefaultBufferSize(320,240);
|
||||
consumer_ = new android::CpuConsumer(consumer,4);
|
||||
consumer_->setName(android::String8("Vulpes Camera"));
|
||||
status=camera_->setPreviewTarget(producer);
|
||||
fprintf(stderr,"camera set target=%d\n",status);
|
||||
if (status != android::NO_ERROR) { stop(); return false; }
|
||||
camera_->setListener(this);
|
||||
// The broker consumes copied Camera1 preview frames rather than video metadata.
|
||||
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
|
||||
status=camera_->startPreview(); fprintf(stderr,"camera start preview=%d\n",status);
|
||||
if (status != android::NO_ERROR) { stop(); return false; }
|
||||
stopping_=false; delivered_=sequence_;
|
||||
consumer_thread_=std::thread([this]{
|
||||
while(!stopping_) {
|
||||
android::CpuConsumer::LockedBuffer buffer;
|
||||
if(consumer_->lockNextBuffer(&buffer)==android::NO_ERROR) consumer_->unlockBuffer(buffer);
|
||||
else usleep(10000);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
void stop() {
|
||||
{ std::lock_guard<std::mutex> lock(mutex_); stopping_=true; }
|
||||
ready_.notify_all();
|
||||
if (camera_.get() != nullptr) { camera_->stopPreview(); camera_->disconnect(); camera_.clear(); }
|
||||
if(consumer_thread_.joinable()) consumer_thread_.join();
|
||||
consumer_.clear();
|
||||
}
|
||||
int fd_; int camera_id_;
|
||||
android::sp<android::Camera> camera_;
|
||||
android::sp<android::CpuConsumer> consumer_;
|
||||
std::mutex mutex_; std::condition_variable ready_;
|
||||
std::thread consumer_thread_;
|
||||
std::vector<uint8_t> frame_, jpeg_; uint32_t sequence_, delivered_; std::atomic<bool> stopping_;
|
||||
};
|
||||
}
|
||||
|
||||
int main() {
|
||||
setvbuf(stderr,nullptr,_IONBF,0);
|
||||
android::sp<CameraWorker> worker = new CameraWorker(STDIN_FILENO);
|
||||
return worker->run();
|
||||
}
|
||||
Loading…
Reference in a new issue