rpicamera: add rpiCameraMode parameter (#1111)
This commit is contained in:
@@ -67,6 +67,7 @@ type PathConf struct {
|
||||
RPICameraEV float64 `json:"rpiCameraEV"`
|
||||
RPICameraROI string `json:"rpiCameraROI"`
|
||||
RPICameraTuningFile string `json:"rpiCameraTuningFile"`
|
||||
RPICameraMode string `json:"rpiCameraMode"`
|
||||
RPICameraFPS int `json:"rpiCameraFPS"`
|
||||
RPICameraIDRPeriod int `json:"rpiCameraIDRPeriod"`
|
||||
RPICameraBitrate int `json:"rpiCameraBitrate"`
|
||||
|
||||
@@ -104,6 +104,7 @@ func newSourceStatic(
|
||||
EV: conf.RPICameraEV,
|
||||
ROI: conf.RPICameraROI,
|
||||
TuningFile: conf.RPICameraTuningFile,
|
||||
Mode: conf.RPICameraMode,
|
||||
FPS: conf.RPICameraFPS,
|
||||
IDRPeriod: conf.RPICameraIDRPeriod,
|
||||
Bitrate: conf.RPICameraBitrate,
|
||||
|
||||
@@ -26,7 +26,9 @@ OBJS = \
|
||||
camera.o \
|
||||
encoder.o \
|
||||
main.o \
|
||||
parameters.o
|
||||
parameters.o \
|
||||
roi.o \
|
||||
sensor_mode.o
|
||||
|
||||
all: exe
|
||||
|
||||
|
||||
@@ -46,11 +46,32 @@ const char *camera_get_error() {
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
// https://github.com/raspberrypi/libcamera-apps/blob/dd97618a25523c2c4aa58f87af5f23e49aa6069c/core/libcamera_app.cpp#L42
|
||||
static libcamera::PixelFormat mode_to_pixel_format(sensor_mode_t *mode) {
|
||||
static std::vector<std::pair<std::pair<int, bool>, libcamera::PixelFormat>> table = {
|
||||
{ {8, false}, libcamera::formats::SBGGR8 },
|
||||
{ {8, true}, libcamera::formats::SBGGR8 },
|
||||
{ {10, false}, libcamera::formats::SBGGR10 },
|
||||
{ {10, true}, libcamera::formats::SBGGR10_CSI2P },
|
||||
{ {12, false}, libcamera::formats::SBGGR12 },
|
||||
{ {12, true}, libcamera::formats::SBGGR12_CSI2P },
|
||||
};
|
||||
|
||||
auto it = std::find_if(table.begin(), table.end(), [&mode] (auto &m) {
|
||||
return mode->bit_depth == m.first.first && mode->packed == m.first.second; });
|
||||
if (it != table.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return libcamera::formats::SBGGR12_CSI2P;
|
||||
}
|
||||
|
||||
struct CameraPriv {
|
||||
parameters_t *params;
|
||||
camera_frame_cb frame_cb;
|
||||
std::unique_ptr<CameraManager> camera_manager;
|
||||
std::shared_ptr<Camera> camera;
|
||||
Stream *video_stream;
|
||||
std::unique_ptr<FrameBufferAllocator> allocator;
|
||||
std::vector<std::unique_ptr<Request>> requests;
|
||||
};
|
||||
@@ -87,6 +108,8 @@ bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **ca
|
||||
return false;
|
||||
}
|
||||
|
||||
setenv("LIBCAMERA_RPI_TUNING_FILE", params->tuning_file, 1);
|
||||
|
||||
ret = camp->camera->acquire();
|
||||
if (ret != 0) {
|
||||
set_error("Camera.acquire() failed");
|
||||
@@ -94,21 +117,32 @@ bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **ca
|
||||
}
|
||||
|
||||
StreamRoles stream_roles = { StreamRole::VideoRecording };
|
||||
if (params->mode != NULL) {
|
||||
stream_roles.push_back(StreamRole::Raw);
|
||||
}
|
||||
|
||||
std::unique_ptr<CameraConfiguration> conf = camp->camera->generateConfiguration(stream_roles);
|
||||
if (conf == NULL) {
|
||||
set_error("Camera.generateConfiguration() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamConfiguration &stream_conf = conf->at(0);
|
||||
stream_conf.pixelFormat = formats::YUV420;
|
||||
stream_conf.bufferCount = params->buffer_count;
|
||||
stream_conf.size.width = params->width;
|
||||
stream_conf.size.height = params->height;
|
||||
StreamConfiguration &video_stream_conf = conf->at(0);
|
||||
video_stream_conf.pixelFormat = formats::YUV420;
|
||||
video_stream_conf.bufferCount = params->buffer_count;
|
||||
video_stream_conf.size.width = params->width;
|
||||
video_stream_conf.size.height = params->height;
|
||||
if (params->width >= 1280 || params->height >= 720) {
|
||||
stream_conf.colorSpace = libcamera::ColorSpace::Rec709;
|
||||
video_stream_conf.colorSpace = libcamera::ColorSpace::Rec709;
|
||||
} else {
|
||||
stream_conf.colorSpace = libcamera::ColorSpace::Smpte170m;
|
||||
video_stream_conf.colorSpace = libcamera::ColorSpace::Smpte170m;
|
||||
}
|
||||
|
||||
if (params->mode != NULL) {
|
||||
StreamConfiguration &raw_stream_conf = conf->at(1);
|
||||
raw_stream_conf.size = libcamera::Size(params->mode->width, params->mode->height);
|
||||
raw_stream_conf.pixelFormat = mode_to_pixel_format(params->mode);
|
||||
raw_stream_conf.bufferCount = video_stream_conf.bufferCount;
|
||||
}
|
||||
|
||||
conf->transform = Transform::Identity;
|
||||
@@ -119,8 +153,6 @@ bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **ca
|
||||
conf->transform = Transform::VFlip * conf->transform;
|
||||
}
|
||||
|
||||
setenv("LIBCAMERA_RPI_TUNING_FILE", params->tuning_file, 1);
|
||||
|
||||
CameraConfiguration::Status vstatus = conf->validate();
|
||||
if (vstatus == CameraConfiguration::Invalid) {
|
||||
set_error("StreamConfiguration.validate() failed");
|
||||
@@ -133,23 +165,23 @@ bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **ca
|
||||
return false;
|
||||
}
|
||||
|
||||
Stream *stream = stream_conf.stream();
|
||||
camp->video_stream = video_stream_conf.stream();
|
||||
|
||||
camp->allocator = std::make_unique<FrameBufferAllocator>(camp->camera);
|
||||
res = camp->allocator->allocate(stream);
|
||||
res = camp->allocator->allocate(camp->video_stream);
|
||||
if (res < 0) {
|
||||
set_error("allocate() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::unique_ptr<FrameBuffer> &buffer : camp->allocator->buffers(stream)) {
|
||||
for (const std::unique_ptr<FrameBuffer> &buffer : camp->allocator->buffers(camp->video_stream)) {
|
||||
std::unique_ptr<Request> request = camp->camera->createRequest((uint64_t)camp.get());
|
||||
if (request == NULL) {
|
||||
set_error("createRequest() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
int res = request->addBuffer(stream, buffer.get());
|
||||
int res = request->addBuffer(camp->video_stream, buffer.get());
|
||||
if (res != 0) {
|
||||
set_error("addBuffer() failed");
|
||||
return false;
|
||||
@@ -185,14 +217,14 @@ static void on_request_complete(Request *request) {
|
||||
camp->camera->queueRequest(request);
|
||||
}
|
||||
|
||||
int camera_get_stride(camera_t *cam) {
|
||||
int camera_get_mode_stride(camera_t *cam) {
|
||||
CameraPriv *camp = (CameraPriv *)cam;
|
||||
return (*camp->camera->streams().begin())->configuration().stride;
|
||||
return camp->video_stream->configuration().stride;
|
||||
}
|
||||
|
||||
int camera_get_colorspace(camera_t *cam) {
|
||||
int camera_get_mode_colorspace(camera_t *cam) {
|
||||
CameraPriv *camp = (CameraPriv *)cam;
|
||||
return get_v4l2_colorspace((*camp->camera->streams().begin())->configuration().colorSpace);
|
||||
return get_v4l2_colorspace(camp->video_stream->configuration().colorSpace);
|
||||
}
|
||||
|
||||
bool camera_start(camera_t *cam) {
|
||||
@@ -271,21 +303,13 @@ bool camera_start(camera_t *cam) {
|
||||
|
||||
ctrls.set(controls::ExposureValue, camp->params->ev);
|
||||
|
||||
if (strlen(camp->params->roi) != 0) {
|
||||
float vals[4];
|
||||
int i = 0;
|
||||
char *token = strtok((char *)camp->params->roi, ",");
|
||||
while (token != NULL) {
|
||||
vals[i++] = atof(token);
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
if (camp->params->roi != NULL) {
|
||||
Rectangle sensor_area = camp->camera->properties().get(libcamera::properties::ScalerCropMaximum);
|
||||
Rectangle crop(
|
||||
vals[0] * sensor_area.width,
|
||||
vals[1] * sensor_area.height,
|
||||
vals[2] * sensor_area.width,
|
||||
vals[3] * sensor_area.height);
|
||||
camp->params->roi->x * sensor_area.width,
|
||||
camp->params->roi->y * sensor_area.height,
|
||||
camp->params->roi->width * sensor_area.width,
|
||||
camp->params->roi->height * sensor_area.height);
|
||||
crop.translateBy(sensor_area.topLeft());
|
||||
ctrls.set(controls::ScalerCrop, crop);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef __CAMERA_H__
|
||||
#define __CAMERA_H__
|
||||
|
||||
typedef void camera_t;
|
||||
|
||||
typedef void (*camera_frame_cb)(int buffer_fd, uint64_t size, uint64_t timestamp);
|
||||
@@ -8,10 +11,12 @@ extern "C" {
|
||||
|
||||
const char *camera_get_error();
|
||||
bool camera_create(parameters_t *params, camera_frame_cb frame_cb, camera_t **cam);
|
||||
int camera_get_stride(camera_t *cam);
|
||||
int camera_get_colorspace(camera_t *cam);
|
||||
int camera_get_mode_stride(camera_t *cam);
|
||||
int camera_get_mode_colorspace(camera_t *cam);
|
||||
bool camera_start(camera_t *cam);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "parameters.h"
|
||||
#include "encoder.h"
|
||||
|
||||
#define DEVICE "/dev/video11"
|
||||
|
||||
char errbuf[256];
|
||||
|
||||
static void set_error(const char *format, ...) {
|
||||
@@ -58,7 +60,7 @@ static void *output_thread(void *userdata) {
|
||||
buf.m.planes = planes;
|
||||
int res = ioctl(encp->fd, VIDIOC_DQBUF, &buf);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "output_thread(): ioctl() failed\n");
|
||||
fprintf(stderr, "output_thread(): ioctl(VIDIOC_DQBUF) failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ static void *output_thread(void *userdata) {
|
||||
buf.m.planes[0].length = length;
|
||||
int res = ioctl(encp->fd, VIDIOC_QBUF, &buf);
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "output_thread(): ioctl() failed\n");
|
||||
fprintf(stderr, "output_thread(): ioctl(VIDIOC_QBUF) failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -102,7 +104,7 @@ bool encoder_create(parameters_t *params, int stride, int colorspace, encoder_ou
|
||||
*enc = malloc(sizeof(encoder_priv_t));
|
||||
encoder_priv_t *encp = (encoder_priv_t *)(*enc);
|
||||
|
||||
encp->fd = open("/dev/video11", O_RDWR, 0);
|
||||
encp->fd = open(DEVICE, O_RDWR, 0);
|
||||
if (encp->fd < 0) {
|
||||
set_error("unable to open device");
|
||||
return false;
|
||||
@@ -254,7 +256,7 @@ bool encoder_create(parameters_t *params, int stride, int colorspace, encoder_ou
|
||||
|
||||
res = ioctl(encp->fd, VIDIOC_QBUF, &buffer);
|
||||
if (res != 0) {
|
||||
set_error("ioctl() failed");
|
||||
set_error("ioctl(VIDIOC_QBUF) failed");
|
||||
free(encp->capture_buffers);
|
||||
close(encp->fd);
|
||||
return false;
|
||||
@@ -309,7 +311,7 @@ void encoder_encode(encoder_t *enc, int buffer_fd, size_t size, int64_t timestam
|
||||
buf.m.planes[0].length = size;
|
||||
int res = ioctl(encp->fd, VIDIOC_QBUF, &buf);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "encoder_encode(): ioctl() failed\n");
|
||||
fprintf(stderr, "encoder_encode(): ioctl(VIDIOC_QBUF) failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef __ENCODER_H__
|
||||
#define __ENCODER_H__
|
||||
|
||||
typedef void encoder_t;
|
||||
|
||||
typedef void (*encoder_output_cb)(const uint8_t *buf, uint64_t size);
|
||||
@@ -5,3 +8,5 @@ typedef void (*encoder_output_cb)(const uint8_t *buf, uint64_t size);
|
||||
const char *encoder_get_error();
|
||||
bool encoder_create(parameters_t *params, int stride, int colorspace, encoder_output_cb output_cb, encoder_t **enc);
|
||||
void encoder_encode(encoder_t *enc, int buffer_fd, size_t size, int64_t timestamp_us);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -72,9 +72,13 @@ int main() {
|
||||
pthread_mutex_init(&pipe_mutex, NULL);
|
||||
pthread_mutex_lock(&pipe_mutex);
|
||||
|
||||
parameters_load(¶ms);
|
||||
bool ok = parameters_load(¶ms);
|
||||
if (!ok) {
|
||||
pipe_write_error(pipe_fd, "parameters_load(): %s", parameters_get_error());
|
||||
return 5;
|
||||
}
|
||||
|
||||
bool ok = camera_create(
|
||||
ok = camera_create(
|
||||
¶ms,
|
||||
on_frame,
|
||||
&cam);
|
||||
@@ -85,8 +89,8 @@ int main() {
|
||||
|
||||
ok = encoder_create(
|
||||
¶ms,
|
||||
camera_get_stride(cam),
|
||||
camera_get_colorspace(cam),
|
||||
camera_get_mode_stride(cam),
|
||||
camera_get_mode_colorspace(cam),
|
||||
on_encoder_output,
|
||||
&enc);
|
||||
if (!ok) {
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "parameters.h"
|
||||
|
||||
void parameters_load(parameters_t *params) {
|
||||
char errbuf[256];
|
||||
|
||||
static void set_error(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(errbuf, 256, format, args);
|
||||
}
|
||||
|
||||
const char *parameters_get_error() {
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
bool parameters_load(parameters_t *params) {
|
||||
params->camera_id = atoi(getenv("CAMERA_ID"));
|
||||
params->width = atoi(getenv("WIDTH"));
|
||||
params->height = atoi(getenv("HEIGHT"));
|
||||
@@ -23,8 +37,29 @@ void parameters_load(parameters_t *params) {
|
||||
params->metering = getenv("METERING");
|
||||
params->gain = atof(getenv("GAIN"));
|
||||
params->ev = atof(getenv("EV"));
|
||||
params->roi = getenv("ROI");
|
||||
|
||||
if (strlen(getenv("ROI")) != 0) {
|
||||
bool ok = roi_load(getenv("ROI"), ¶ms->roi);
|
||||
if (!ok) {
|
||||
set_error("invalid ROI");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
params->roi = NULL;
|
||||
}
|
||||
|
||||
params->tuning_file = getenv("TUNING_FILE");
|
||||
|
||||
if (strlen(getenv("MODE")) != 0) {
|
||||
bool ok = sensor_mode_load(getenv("MODE"), ¶ms->mode);
|
||||
if (!ok) {
|
||||
set_error("invalid sensor mode");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
params->mode = NULL;
|
||||
}
|
||||
|
||||
params->fps = atoi(getenv("FPS"));
|
||||
params->idr_period = atoi(getenv("IDR_PERIOD"));
|
||||
params->bitrate = atoi(getenv("BITRATE"));
|
||||
@@ -49,4 +84,6 @@ void parameters_load(parameters_t *params) {
|
||||
|
||||
params->buffer_count = 3;
|
||||
params->capture_buffer_count = params->buffer_count * 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
#ifndef __PARAMETERS_H__
|
||||
#define __PARAMETERS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "roi.h"
|
||||
#include "sensor_mode.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned int camera_id;
|
||||
unsigned int width;
|
||||
@@ -15,8 +23,9 @@ typedef struct {
|
||||
const char *metering;
|
||||
float gain;
|
||||
float ev;
|
||||
const char *roi;
|
||||
roi_t *roi;
|
||||
const char *tuning_file;
|
||||
sensor_mode_t *mode;
|
||||
unsigned int fps;
|
||||
unsigned int idr_period;
|
||||
unsigned int bitrate;
|
||||
@@ -32,8 +41,11 @@ typedef struct {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void parameters_load(parameters_t *params);
|
||||
const char *parameters_get_error();
|
||||
bool parameters_load(parameters_t *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "roi.h"
|
||||
|
||||
bool roi_load(const char *encoded, roi_t **mode) {
|
||||
float vals[4];
|
||||
int i = 0;
|
||||
char *token = strtok((char *)encoded, ",");
|
||||
while (token != NULL) {
|
||||
vals[i] = atof(token);
|
||||
if (vals[i] < 0 || vals[i] > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i++;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
if (i != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*mode = malloc(sizeof(roi_t));
|
||||
(*mode)->x = vals[0];
|
||||
(*mode)->y = vals[1];
|
||||
(*mode)->width = vals[2];
|
||||
(*mode)->height = vals[3];
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef __ROI_H__
|
||||
#define __ROI_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
} roi_t;
|
||||
|
||||
bool roi_load(const char *encoded, roi_t **mode);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sensor_mode.h"
|
||||
|
||||
bool sensor_mode_load(const char *encoded, sensor_mode_t **mode) {
|
||||
*mode = malloc(sizeof(sensor_mode_t));
|
||||
|
||||
char p;
|
||||
int n = sscanf(encoded, "%u:%u:%u:%c", &((*mode)->width), &((*mode)->height), &((*mode)->bit_depth), &p);
|
||||
if (n < 2) {
|
||||
free(*mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n < 4) {
|
||||
(*mode)->packed = true;
|
||||
} else if (toupper(p) == 'P') {
|
||||
(*mode)->packed = true;
|
||||
} else if (toupper(p) == 'U') {
|
||||
(*mode)->packed = false;
|
||||
}
|
||||
|
||||
if (n < 3) {
|
||||
(*mode)->bit_depth = 12;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef __SENSOR_MODE_H__
|
||||
#define __SENSOR_MODE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int bit_depth;
|
||||
bool packed;
|
||||
} sensor_mode_t;
|
||||
|
||||
bool sensor_mode_load(const char *encoded, sensor_mode_t **mode);
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@ type Params struct {
|
||||
EV float64
|
||||
ROI string
|
||||
TuningFile string
|
||||
Mode string
|
||||
FPS int
|
||||
IDRPeriod int
|
||||
Bitrate int
|
||||
|
||||
@@ -60,6 +60,7 @@ func New(
|
||||
"EV=" + strconv.FormatFloat(params.EV, 'f', -1, 64),
|
||||
"ROI=" + params.ROI,
|
||||
"TUNING_FILE=" + params.TuningFile,
|
||||
"MODE=" + params.Mode,
|
||||
"FPS=" + strconv.FormatInt(int64(params.FPS), 10),
|
||||
"IDR_PERIOD=" + strconv.FormatInt(int64(params.IDRPeriod), 10),
|
||||
"BITRATE=" + strconv.FormatInt(int64(params.Bitrate), 10),
|
||||
|
||||
@@ -264,10 +264,13 @@ paths:
|
||||
rpiCameraGain: 0
|
||||
# EV compensation of the image [-10, 10]
|
||||
rpiCameraEV: 0
|
||||
# Region of interst in format x,y,width,height
|
||||
# Region of interest, in format x,y,width,height
|
||||
rpiCameraROI:
|
||||
# tuning file
|
||||
rpiCameraTuningFile:
|
||||
# sensor mode, in format [width]:[height]:[bit-depth]:[packing]
|
||||
# bit-depth and packing are optional.
|
||||
rpiCameraMode:
|
||||
# frames per second
|
||||
rpiCameraFPS: 30
|
||||
# period between IDR frames
|
||||
|
||||
Reference in New Issue
Block a user