Add -snapshot.height
, -stream.height
and -video.height
This commit is contained in:

committed by
Kamil Trzcinski

parent
c18561c9f8
commit
ca08adb62f
@ -43,13 +43,15 @@ void camera_close(camera_t **camerap)
|
||||
for (int i = MAX_DEVICES; i-- > 0; ) {
|
||||
link_t *link = &camera->links[i];
|
||||
|
||||
if (link->callbacks.on_buffer) {
|
||||
link->callbacks.on_buffer(NULL);
|
||||
link->callbacks.on_buffer = NULL;
|
||||
}
|
||||
if (link->callbacks.buf_lock) {
|
||||
buffer_lock_capture(link->callbacks.buf_lock, NULL);
|
||||
link->callbacks.buf_lock = NULL;
|
||||
for (int j = 0; j < link->n_callbacks; j++) {
|
||||
if (link->callbacks[j].on_buffer) {
|
||||
link->callbacks[j].on_buffer(NULL);
|
||||
link->callbacks[j].on_buffer = NULL;
|
||||
}
|
||||
if (link->callbacks[j].buf_lock) {
|
||||
buffer_lock_capture(link->callbacks[j].buf_lock, NULL);
|
||||
link->callbacks[j].buf_lock = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,10 +88,10 @@ void camera_capture_add_output(camera_t *camera, buffer_list_t *capture, buffer_
|
||||
link->sinks[nsinks] = output;
|
||||
}
|
||||
|
||||
void camera_capture_set_callbacks(camera_t *camera, buffer_list_t *capture, link_callbacks_t callbacks)
|
||||
void camera_capture_add_callbacks(camera_t *camera, buffer_list_t *capture, link_callbacks_t callbacks)
|
||||
{
|
||||
link_t *link = camera_ensure_capture(camera, capture);
|
||||
link->callbacks = callbacks;
|
||||
link->callbacks[link->n_callbacks++] = callbacks;
|
||||
|
||||
if (callbacks.buf_lock) {
|
||||
callbacks.buf_lock->buf_list = capture;
|
||||
@ -107,12 +109,10 @@ int camera_set_params(camera_t *camera)
|
||||
}
|
||||
|
||||
// Set some defaults
|
||||
for (int i = 0; i < 2; i++) {
|
||||
device_set_option_list(camera->legacy_isp[i], camera->options.isp.options);
|
||||
device_set_option_list(camera->codec_jpeg[i], camera->options.jpeg.options);
|
||||
device_set_option_string(camera->codec_h264[i], "repeat_sequence_header", "1"); // required for force key support
|
||||
device_set_option_list(camera->codec_h264[i], camera->options.h264.options);
|
||||
}
|
||||
device_set_option_list(camera->codec_snapshot, camera->options.snapshot.options);
|
||||
device_set_option_list(camera->codec_stream, camera->options.stream.options);
|
||||
device_set_option_string(camera->codec_video, "repeat_sequence_header", "1"); // required for force key support
|
||||
device_set_option_list(camera->codec_video, camera->options.video.options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "device/device.h"
|
||||
|
||||
#define MAX_DEVICES 20
|
||||
#define MAX_RESCALLERS 4
|
||||
#define MAX_HTTP_METHODS 20
|
||||
|
||||
#define CAMERA_DEVICE_CAMERA 0
|
||||
@ -14,6 +15,12 @@ typedef enum {
|
||||
CAMERA_LIBCAMERA
|
||||
} camera_type_t;
|
||||
|
||||
typedef struct camera_output_options_s {
|
||||
bool disabled;
|
||||
unsigned height;
|
||||
char options[CAMERA_OPTIONS_LENGTH];
|
||||
} camera_output_options_t;
|
||||
|
||||
typedef struct camera_options_s {
|
||||
char path[256];
|
||||
unsigned width, height, format;
|
||||
@ -40,13 +47,9 @@ typedef struct camera_options_s {
|
||||
char options[CAMERA_OPTIONS_LENGTH];
|
||||
} isp;
|
||||
|
||||
struct {
|
||||
char options[CAMERA_OPTIONS_LENGTH];
|
||||
} jpeg;
|
||||
|
||||
struct {
|
||||
char options[CAMERA_OPTIONS_LENGTH];
|
||||
} h264;
|
||||
camera_output_options_t snapshot;
|
||||
camera_output_options_t stream;
|
||||
camera_output_options_t video;
|
||||
} camera_options_t;
|
||||
|
||||
typedef struct camera_s {
|
||||
@ -60,9 +63,10 @@ typedef struct camera_s {
|
||||
device_t *camera;
|
||||
device_t *decoder; // decode JPEG/H264 into YUVU
|
||||
device_t *isp;
|
||||
device_t *legacy_isp[2];
|
||||
device_t *codec_jpeg[2]; // encode YUVU into JPEG
|
||||
device_t *codec_h264[2]; // encode YUVU into H264
|
||||
device_t *rescallers[3];
|
||||
device_t *codec_snapshot;
|
||||
device_t *codec_stream;
|
||||
device_t *codec_video;
|
||||
};
|
||||
};
|
||||
|
||||
@ -81,12 +85,13 @@ int camera_run(camera_t *camera);
|
||||
|
||||
link_t *camera_ensure_capture(camera_t *camera, buffer_list_t *capture);
|
||||
void camera_capture_add_output(camera_t *camera, buffer_list_t *capture, buffer_list_t *output);
|
||||
void camera_capture_set_callbacks(camera_t *camera, buffer_list_t *capture, link_callbacks_t callbacks);
|
||||
void camera_capture_add_callbacks(camera_t *camera, buffer_list_t *capture, link_callbacks_t callbacks);
|
||||
|
||||
int camera_configure_input(camera_t *camera);
|
||||
int camera_configure_decoder(camera_t *camera, buffer_list_t *src_capture);
|
||||
int camera_configure_output_rescaler(camera_t *camera, buffer_list_t *src_capture, float high_div, float low_div);
|
||||
int camera_configure_output(camera_t *camera, buffer_list_t *src_capture, int res);
|
||||
int camera_configure_pipeline(camera_t *camera, buffer_list_t *camera_capture);
|
||||
|
||||
int camera_configure_isp(camera_t *camera, buffer_list_t *src, float high_div, float low_div);
|
||||
int camera_configure_legacy_isp(camera_t *camera, buffer_list_t *src, float div, int res);
|
||||
buffer_list_t *camera_configure_isp(camera_t *camera, buffer_list_t *src_capture);
|
||||
buffer_list_t *camera_configure_decoder(camera_t *camera, buffer_list_t *src_capture);
|
||||
unsigned camera_rescaller_align_size(unsigned target_height);
|
||||
buffer_list_t *camera_configure_rescaller(camera_t *camera, buffer_list_t *src_capture, const char *name, unsigned target_height, unsigned formats[]);
|
||||
int camera_configure_output(camera_t *camera, const char *name, unsigned target_height, unsigned formats[], link_callbacks_t callbacks, device_t **device);
|
||||
|
81
device/camera/camera_decoder.c
Normal file
81
device/camera/camera_decoder.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "camera.h"
|
||||
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "util/http/http.h"
|
||||
#include "output/rtsp/rtsp.h"
|
||||
#include "output/output.h"
|
||||
|
||||
static unsigned decoder_formats[] =
|
||||
{
|
||||
// best quality
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
|
||||
// medium quality
|
||||
V4L2_PIX_FMT_YUV420,
|
||||
V4L2_PIX_FMT_NV12,
|
||||
|
||||
// low quality
|
||||
V4L2_PIX_FMT_YVU420,
|
||||
V4L2_PIX_FMT_NV21,
|
||||
0
|
||||
};
|
||||
|
||||
static void decoder_debug_on_buffer(buffer_t *buf)
|
||||
{
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
static int index = 0;
|
||||
|
||||
char path[256];
|
||||
sprintf(path, "/tmp/decoder_capture.%d.%s", index++ % 10, fourcc_to_string(buf->buf_list->fmt.format).buf);
|
||||
|
||||
FILE *fp = fopen(path, "wb");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(buf->start, 1, buf->used, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static link_callbacks_t decoder_debug_callbacks = {
|
||||
.name = "DECODER-DEBUG-CAPTURE",
|
||||
.on_buffer = decoder_debug_on_buffer
|
||||
};
|
||||
|
||||
buffer_list_t *camera_configure_decoder(camera_t *camera, buffer_list_t *src_capture)
|
||||
{
|
||||
unsigned chosen_format = 0;
|
||||
device_info_t *device = device_list_find_m2m_formats(camera->device_list, src_capture->fmt.format, decoder_formats, &chosen_format);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find '%s' decoder", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_video_force_key(camera->camera);
|
||||
|
||||
camera->decoder = device_v4l2_open("DECODER", device->path);
|
||||
|
||||
buffer_list_t *decoder_output = device_open_buffer_list_output(
|
||||
camera->decoder, src_capture);
|
||||
buffer_list_t *decoder_capture = device_open_buffer_list_capture(
|
||||
camera->decoder, NULL, decoder_output, 0, 0, chosen_format, true);
|
||||
|
||||
if (getenv("CAMERA_DECODER_DEBUG")) {
|
||||
camera_capture_add_callbacks(camera, decoder_capture, decoder_debug_callbacks);
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, decoder_output);
|
||||
|
||||
return decoder_capture;
|
||||
}
|
@ -32,47 +32,13 @@ static int camera_configure_input_v4l2(camera_t *camera)
|
||||
camera->camera->opts.allow_dma = false;
|
||||
}
|
||||
|
||||
buffer_list_t *camera_capture = device_open_buffer_list(camera->camera, true, camera->options.width, camera->options.height, camera->options.format, 0, camera->options.nbufs, true);
|
||||
buffer_list_t *camera_capture = device_open_buffer_list(camera->camera, true,
|
||||
camera->options.width, camera->options.height, camera->options.format, 0, camera->options.nbufs, true);
|
||||
if (!camera_capture) {
|
||||
return -1;
|
||||
}
|
||||
camera_capture->do_timestamps = true;
|
||||
|
||||
if (camera->options.fps > 0) {
|
||||
camera_capture->fmt.interval_us = 1000 * 1000 / camera->options.fps;
|
||||
}
|
||||
|
||||
switch (camera_capture->fmt.format) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_YVYU:
|
||||
case V4L2_PIX_FMT_VYUY:
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
if (camera->options.high_res_factor > 1) {
|
||||
// Use ISP, as there are two resolutions
|
||||
return camera_configure_isp(camera, camera_capture,
|
||||
camera->options.high_res_factor, camera->options.low_res_factor);
|
||||
} else {
|
||||
// Use direct approach, as there's likely low frequently used low resolution
|
||||
return camera_configure_output_rescaler(camera, camera_capture,
|
||||
camera->options.high_res_factor, camera->options.low_res_factor);
|
||||
}
|
||||
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
case V4L2_PIX_FMT_H264:
|
||||
return camera_configure_decoder(camera, camera_capture);
|
||||
|
||||
case V4L2_PIX_FMT_SRGGB10P:
|
||||
return camera_configure_isp(camera, camera_capture,
|
||||
camera->options.high_res_factor, camera->options.low_res_factor);
|
||||
|
||||
default:
|
||||
LOG_INFO(camera, "Unsupported camera format=%s",
|
||||
fourcc_to_string(camera_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
return camera_configure_pipeline(camera, camera_capture);
|
||||
}
|
||||
|
||||
static int camera_configure_input_libcamera(camera_t *camera)
|
||||
@ -89,8 +55,8 @@ static int camera_configure_input_libcamera(camera_t *camera)
|
||||
buffer_list_t *camera_capture = device_open_buffer_list(
|
||||
camera->camera,
|
||||
true,
|
||||
camera->options.width / camera->options.high_res_factor,
|
||||
camera->options.height / camera->options.high_res_factor,
|
||||
camera->options.width,
|
||||
camera->options.height,
|
||||
camera->options.format,
|
||||
0,
|
||||
camera->options.nbufs,
|
||||
@ -99,14 +65,8 @@ static int camera_configure_input_libcamera(camera_t *camera)
|
||||
if (!camera_capture) {
|
||||
return -1;
|
||||
}
|
||||
camera_capture->do_timestamps = true;
|
||||
|
||||
if (camera->options.fps > 0) {
|
||||
camera_capture->fmt.interval_us = 1000 * 1000 / camera->options.fps;
|
||||
}
|
||||
|
||||
return camera_configure_output_rescaler(camera, camera_capture,
|
||||
1.0, camera->options.low_res_factor / camera->options.high_res_factor);
|
||||
return camera_configure_pipeline(camera, camera_capture);
|
||||
}
|
||||
|
||||
int camera_configure_input(camera_t *camera)
|
||||
|
@ -10,67 +10,16 @@
|
||||
#include "device/buffer_list.h"
|
||||
#include "util/http/http.h"
|
||||
|
||||
int camera_configure_output_rescaler2(camera_t *camera, buffer_list_t *src_capture, float div, int res);
|
||||
|
||||
int camera_configure_isp(camera_t *camera, buffer_list_t *src_capture, float high_div, float low_div)
|
||||
buffer_list_t *camera_configure_isp(camera_t *camera, buffer_list_t *src_capture)
|
||||
{
|
||||
camera->isp = device_v4l2_open("ISP", "/dev/video13");
|
||||
|
||||
buffer_list_t *isp_output = device_open_buffer_list_output(
|
||||
camera->isp, src_capture);
|
||||
buffer_list_t *isp_capture = device_open_buffer_list_capture2(
|
||||
camera->isp, "/dev/video14", isp_output, high_div, V4L2_PIX_FMT_YUYV, true);
|
||||
|
||||
camera_capture_add_output(camera, src_capture, isp_output);
|
||||
|
||||
if (camera_configure_output(camera, isp_capture, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 1
|
||||
return camera_configure_output_rescaler2(camera, isp_capture, low_div, 1);
|
||||
#else
|
||||
if (low_div > 1) {
|
||||
// TODO: Currently we cannot pull the data at the same time from /dev/video14 and /dev/video15
|
||||
// if only one path (consuming /dev/video15) is activated
|
||||
buffer_list_t *isp_lowres_capture = device_open_buffer_list_capture2(
|
||||
camera->isp, "/dev/video15", isp_output, low_div, V4L2_PIX_FMT_YUYV, true);
|
||||
|
||||
if (camera_configure_output(camera, isp_lowres_capture, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *isp_names[2] = {
|
||||
"ISP",
|
||||
"ISP-LOW"
|
||||
};
|
||||
|
||||
int camera_configure_legacy_isp(camera_t *camera, buffer_list_t *src_capture, float div, int res)
|
||||
{
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_YUYV);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find ISP to scale from '%s' to 'YUYV'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera->legacy_isp[res] = device_v4l2_open(isp_names[res], device->path);
|
||||
|
||||
buffer_list_t *isp_output = device_open_buffer_list_output(
|
||||
camera->legacy_isp[res], src_capture);
|
||||
buffer_list_t *isp_capture = device_open_buffer_list_capture(
|
||||
camera->legacy_isp[res], isp_output, div, V4L2_PIX_FMT_YUYV, true);
|
||||
camera->isp, "/dev/video14", isp_output, 0, 0, V4L2_PIX_FMT_YUYV, true);
|
||||
|
||||
camera_capture_add_output(camera, src_capture, isp_output);
|
||||
|
||||
if (camera_configure_output(camera, isp_capture, res) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return isp_capture;
|
||||
}
|
||||
|
@ -12,196 +12,94 @@
|
||||
#include "output/rtsp/rtsp.h"
|
||||
#include "output/output.h"
|
||||
|
||||
static const char *jpeg_names[2] = {
|
||||
"JPEG",
|
||||
"JPEG-LOW"
|
||||
};
|
||||
|
||||
static link_callbacks_t jpeg_callbacks[2] = {
|
||||
{ .name = "JPEG-CAPTURE", .buf_lock = &http_jpeg },
|
||||
{ .name = "JPEG-LOW-CAPTURE", .buf_lock = &http_jpeg_lowres }
|
||||
};
|
||||
|
||||
static const char *h264_names[2] = {
|
||||
"H264",
|
||||
"H264-LOW"
|
||||
};
|
||||
|
||||
static link_callbacks_t h264_callbacks[2] = {
|
||||
{ .name = "H264-CAPTURE", .buf_lock = &http_h264 },
|
||||
{ .name = "H264-LOW-CAPTURE", .buf_lock = &http_h264_lowres }
|
||||
};
|
||||
|
||||
static int camera_configure_h264_output(camera_t *camera, buffer_list_t *src_capture, int res)
|
||||
static bool camera_output_matches_capture(buffer_list_t *capture, unsigned target_height, unsigned formats[])
|
||||
{
|
||||
if (src_capture->fmt.format == V4L2_PIX_FMT_H264) {
|
||||
camera_capture_set_callbacks(camera, src_capture, h264_callbacks[res]);
|
||||
return 0;
|
||||
if (target_height && capture->fmt.height != target_height && capture->fmt.height != camera_rescaller_align_size(target_height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_H264);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find H264 encoder to convert from '%s'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
for (int i = 0; formats[i]; i++) {
|
||||
if (formats[i] == capture->fmt.format)
|
||||
return true;
|
||||
}
|
||||
|
||||
camera->codec_h264[res] = device_v4l2_open(h264_names[res], device->path);
|
||||
|
||||
buffer_list_t *output = device_open_buffer_list_output(camera->codec_h264[res], src_capture);
|
||||
buffer_list_t *capture = device_open_buffer_list_capture(camera->codec_h264[res], output, 1.0, V4L2_PIX_FMT_H264, true);
|
||||
|
||||
if (!capture) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, output);
|
||||
camera_capture_set_callbacks(camera, capture, h264_callbacks[res]);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int camera_configure_jpeg_output(camera_t *camera, buffer_list_t *src_capture, int res)
|
||||
static buffer_list_t *camera_find_capture(camera_t *camera, unsigned target_height, unsigned formats[])
|
||||
{
|
||||
if (src_capture->fmt.format == V4L2_PIX_FMT_MJPEG || src_capture->fmt.format == V4L2_PIX_FMT_JPEG) {
|
||||
camera_capture_set_callbacks(camera, src_capture, jpeg_callbacks[res]);
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||
if (!camera->devices[i])
|
||||
continue;
|
||||
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_JPEG);
|
||||
device_t *device = camera->devices[i];
|
||||
for (int j = 0; j < device->n_capture_list; j++) {
|
||||
buffer_list_t *capture_list = device->capture_lists[j];
|
||||
|
||||
if (!device) {
|
||||
device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_MJPEG);
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find JPEG encoder to convert from '%s'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera->codec_jpeg[res] = device_v4l2_open(jpeg_names[res], device->path);
|
||||
|
||||
buffer_list_t *output = device_open_buffer_list_output(camera->codec_jpeg[res], src_capture);
|
||||
buffer_list_t *capture = device_open_buffer_list_capture(camera->codec_jpeg[res], output, 1.0, V4L2_PIX_FMT_JPEG, true);
|
||||
|
||||
if (!capture) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, output);
|
||||
camera_capture_set_callbacks(camera, capture, jpeg_callbacks[res]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int camera_configure_output(camera_t *camera, buffer_list_t *src_capture, int res)
|
||||
{
|
||||
if (camera_configure_h264_output(camera, src_capture, res) < 0 ||
|
||||
camera_configure_jpeg_output(camera, src_capture, res) < 0) {
|
||||
return -1;
|
||||
if (camera_output_matches_capture(capture_list, target_height, formats)) {
|
||||
return capture_list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int camera_configure_output_rescaler2(camera_t *camera, buffer_list_t *src_capture, float div, int res)
|
||||
static unsigned rescalled_formats[] =
|
||||
{
|
||||
if (div > 1) {
|
||||
return camera_configure_legacy_isp(camera, src_capture, div, res);
|
||||
} else if (div > 0) {
|
||||
return camera_configure_output(camera, src_capture, 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// best quality
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
|
||||
int camera_configure_output_rescaler(camera_t *camera, buffer_list_t *src_capture, float high_div, float low_div)
|
||||
{
|
||||
if (camera_configure_output_rescaler2(camera, src_capture, high_div, 0) < 0 ||
|
||||
camera_configure_output_rescaler2(camera, src_capture, low_div, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
// medium quality
|
||||
V4L2_PIX_FMT_YUV420,
|
||||
V4L2_PIX_FMT_NV12,
|
||||
|
||||
return 0;
|
||||
}
|
||||
// low quality
|
||||
V4L2_PIX_FMT_NV21,
|
||||
V4L2_PIX_FMT_YVU420,
|
||||
|
||||
static void decoder_debug_on_buffer(buffer_t *buf) {
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
static int index = 0;
|
||||
|
||||
char path[256];
|
||||
sprintf(path, "/tmp/decoder_capture.%d.%s", index++ % 10, fourcc_to_string(buf->buf_list->fmt.format).buf);
|
||||
|
||||
FILE *fp = fopen(path, "wb");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(buf->start, 1, buf->used, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static link_callbacks_t decoder_debug_callbacks = {
|
||||
.name = "DECODER-DEBUG-CAPTURE",
|
||||
.on_buffer = decoder_debug_on_buffer
|
||||
0
|
||||
};
|
||||
|
||||
int camera_configure_decoder(camera_t *camera, buffer_list_t *src_capture)
|
||||
int camera_configure_output(camera_t *camera, const char *name, unsigned target_height, unsigned formats[], link_callbacks_t callbacks, device_t **device)
|
||||
{
|
||||
unsigned decode_formats[] = {
|
||||
// best quality
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
buffer_list_t *src_capture = camera_find_capture(camera, target_height, formats);
|
||||
if (src_capture) {
|
||||
camera_capture_add_callbacks(camera, src_capture, callbacks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// medium quality
|
||||
V4L2_PIX_FMT_YUV420,
|
||||
V4L2_PIX_FMT_NV12,
|
||||
src_capture = camera_find_capture(camera, target_height, rescalled_formats);
|
||||
if (!src_capture) {
|
||||
// Try to find re-scallabe output
|
||||
src_capture = camera_find_capture(camera, 0, rescalled_formats);
|
||||
if (src_capture) {
|
||||
src_capture = camera_configure_rescaller(camera, src_capture, name, target_height, rescalled_formats);
|
||||
}
|
||||
}
|
||||
|
||||
if (!src_capture) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// low quality
|
||||
V4L2_PIX_FMT_NV21,
|
||||
V4L2_PIX_FMT_YVU420,
|
||||
0
|
||||
};
|
||||
unsigned chosen_format = 0;
|
||||
device_info_t *device = device_list_find_m2m_formats(camera->device_list, src_capture->fmt.format, decode_formats, &chosen_format);
|
||||
device_info_t *device_info = device_list_find_m2m_formats(camera->device_list, src_capture->fmt.format, formats, &chosen_format);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find '%s' decoder", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
if (!device_info) {
|
||||
LOG_INFO(camera, "Cannot find encoder to convert from '%s'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_video_force_key(camera->camera);
|
||||
*device = device_v4l2_open(name, device_info->path);
|
||||
|
||||
camera->decoder = device_v4l2_open("DECODER", device->path);
|
||||
buffer_list_t *output = device_open_buffer_list_output(*device, src_capture);
|
||||
buffer_list_t *capture = device_open_buffer_list_capture(*device, NULL, output, 0, 0, chosen_format, true);
|
||||
|
||||
buffer_list_t *decoder_output = device_open_buffer_list_output(
|
||||
camera->decoder, src_capture);
|
||||
buffer_list_t *decoder_capture = device_open_buffer_list_capture(
|
||||
camera->decoder, decoder_output, 1.0, chosen_format, true);
|
||||
|
||||
if (getenv("CAMERA_DECODER_DEBUG")) {
|
||||
camera_capture_set_callbacks(camera, decoder_capture, decoder_debug_callbacks);
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, decoder_output);
|
||||
|
||||
if (camera->options.high_res_factor <= 1 && (src_capture->fmt.format == V4L2_PIX_FMT_JPEG || src_capture->fmt.format == V4L2_PIX_FMT_MJPEG)) {
|
||||
camera_capture_set_callbacks(camera, src_capture, jpeg_callbacks[0]);
|
||||
|
||||
if (camera_configure_h264_output(camera, decoder_capture, 0) < 0)
|
||||
return -1;
|
||||
} else if (camera->options.high_res_factor <= 1 && src_capture->fmt.format == V4L2_PIX_FMT_H264) {
|
||||
camera_capture_set_callbacks(camera, src_capture, h264_callbacks[0]);
|
||||
|
||||
if (camera_configure_jpeg_output(camera, decoder_capture, 0) < 0)
|
||||
return -1;
|
||||
} else if (camera_configure_output_rescaler2(camera, decoder_capture, camera->options.high_res_factor, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (camera->options.low_res_factor > 1 && camera_configure_output_rescaler2(camera, decoder_capture, camera->options.low_res_factor, 1) < 0) {
|
||||
if (!capture) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, output);
|
||||
camera_capture_add_callbacks(camera, capture, callbacks);
|
||||
return 0;
|
||||
}
|
||||
|
85
device/camera/camera_pipeline.c
Normal file
85
device/camera/camera_pipeline.c
Normal file
@ -0,0 +1,85 @@
|
||||
#include "camera.h"
|
||||
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "util/http/http.h"
|
||||
#include "output/output.h"
|
||||
|
||||
static unsigned snapshot_formats[] =
|
||||
{
|
||||
V4L2_PIX_FMT_MJPEG,
|
||||
V4L2_PIX_FMT_JPEG,
|
||||
0
|
||||
};
|
||||
|
||||
static link_callbacks_t snapshot_callbacks =
|
||||
{
|
||||
.name = "SNAPSHOT-CAPTURE",
|
||||
.buf_lock = &snapshot_lock
|
||||
};
|
||||
|
||||
static link_callbacks_t stream_callbacks =
|
||||
{
|
||||
.name = "STREAM-CAPTURE",
|
||||
.buf_lock = &stream_lock
|
||||
};
|
||||
|
||||
static unsigned video_formats[] =
|
||||
{
|
||||
V4L2_PIX_FMT_H264,
|
||||
0
|
||||
};
|
||||
|
||||
static link_callbacks_t video_callbacks =
|
||||
{
|
||||
.name = "VIDEO-CAPTURE",
|
||||
.buf_lock = &video_lock
|
||||
};
|
||||
|
||||
int camera_configure_pipeline(camera_t *camera, buffer_list_t *capture)
|
||||
{
|
||||
if (capture) {
|
||||
capture->do_timestamps = true;
|
||||
|
||||
if (camera->options.fps > 0) {
|
||||
capture->fmt.interval_us = 1000 * 1000 / camera->options.fps;
|
||||
}
|
||||
|
||||
switch (capture->fmt.format) {
|
||||
case V4L2_PIX_FMT_SRGGB10P:
|
||||
case V4L2_PIX_FMT_SGRBG10P:
|
||||
case V4L2_PIX_FMT_SRGGB10:
|
||||
case V4L2_PIX_FMT_SGRBG10:
|
||||
camera_configure_isp(camera, capture);
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
case V4L2_PIX_FMT_H264:
|
||||
camera_configure_decoder(camera, capture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!camera->options.snapshot.disabled && camera_configure_output(
|
||||
camera, "SNAPSHOT", camera->options.snapshot.height, snapshot_formats, snapshot_callbacks, &camera->codec_snapshot) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!camera->options.stream.disabled && camera_configure_output(
|
||||
camera, "STREAM", camera->options.stream.height, snapshot_formats, stream_callbacks, &camera->codec_stream) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!camera->options.video.disabled && camera_configure_output(
|
||||
camera, "VIDEO", camera->options.video.height, video_formats, video_callbacks, &camera->codec_video) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
77
device/camera/camera_rescaller.c
Normal file
77
device/camera/camera_rescaller.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "camera.h"
|
||||
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "util/http/http.h"
|
||||
|
||||
unsigned camera_rescaller_align_size(unsigned size)
|
||||
{
|
||||
return (size + 31) / 32 * 32;
|
||||
}
|
||||
|
||||
buffer_list_t *camera_try_rescaller(camera_t *camera, buffer_list_t *src_capture, const char *name, unsigned target_height, unsigned target_format)
|
||||
{
|
||||
device_info_t *device_info = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, target_format);
|
||||
|
||||
if (!device_info) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (target_height > src_capture->fmt.height) {
|
||||
LOG_INFO(src_capture, "Upscaling from %dp to %dp does not make sense.",
|
||||
src_capture->fmt.height, target_height);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
target_height = camera_rescaller_align_size(target_height);
|
||||
unsigned target_width = target_height * src_capture->fmt.width / src_capture->fmt.height;
|
||||
target_width = camera_rescaller_align_size(target_width);
|
||||
|
||||
char name2[256];
|
||||
sprintf(name2, "RESCALLER:%s", name);
|
||||
|
||||
device_t *device = device_v4l2_open(name2, device_info->path);
|
||||
|
||||
buffer_list_t *rescaller_output = device_open_buffer_list_output(
|
||||
device, src_capture);
|
||||
buffer_list_t *rescaller_capture = device_open_buffer_list_capture(
|
||||
device, NULL, rescaller_output,
|
||||
target_width, target_height, target_format, true);
|
||||
|
||||
if (!rescaller_capture) {
|
||||
device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
camera_capture_add_output(camera, src_capture, rescaller_output);
|
||||
return rescaller_capture;
|
||||
}
|
||||
|
||||
buffer_list_t *camera_configure_rescaller(camera_t *camera, buffer_list_t *src_capture, const char *name, unsigned target_height, unsigned formats[])
|
||||
{
|
||||
int rescallers = 0;
|
||||
for ( ; rescallers < MAX_RESCALLERS && camera->rescallers[rescallers]; rescallers++);
|
||||
if (rescallers == MAX_RESCALLERS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer_list_t *rescaller_capture = NULL; // camera_try_rescaller(camera, src_capture, name, target_height, src_capture->fmt.format);
|
||||
|
||||
for (int i = 0; !rescaller_capture && formats[i]; i++) {
|
||||
rescaller_capture = camera_try_rescaller(camera, src_capture, name, target_height, formats[i]);
|
||||
}
|
||||
|
||||
if (!rescaller_capture) {
|
||||
LOG_INFO(src_capture, "Cannot find rescaller to scale from '%s' to 'YUYV'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
camera->rescallers[rescallers] = rescaller_capture->dev;
|
||||
return rescaller_capture;
|
||||
}
|
Reference in New Issue
Block a user