284 lines
8.8 KiB
C
284 lines
8.8 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: ESPRESSIF MIT
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "esp_timer.h"
|
|
#include "driver/i2c_master.h"
|
|
#include "linux/videodev2.h"
|
|
#include "esp_video_init.h"
|
|
#include "esp_video_ioctl.h"
|
|
#include "app_camera.h"
|
|
|
|
#define MEMORY_TYPE V4L2_MEMORY_MMAP
|
|
#define BUFFER_COUNT 2
|
|
#define CAPTURE_SECONDS 3
|
|
|
|
static i2c_master_bus_handle_t i2c_handle = NULL;
|
|
|
|
static const char *TAG = "example";
|
|
|
|
#if CONFIG_EXAMPLE_ENABLE_MIPI_CSI_CAM_SENSOR
|
|
static esp_video_init_csi_config_t csi_config[] = {
|
|
{
|
|
.sccb_config = {
|
|
.init_sccb = true,
|
|
.i2c_config = {
|
|
.port = CONFIG_EXAMPLE_MIPI_CSI_SCCB_I2C_PORT,
|
|
.scl_pin = CONFIG_EXAMPLE_MIPI_CSI_SCCB_I2C_SCL_PIN,
|
|
.sda_pin = CONFIG_EXAMPLE_MIPI_CSI_SCCB_I2C_SDA_PIN,
|
|
},
|
|
.freq = CONFIG_EXAMPLE_MIPI_CSI_SCCB_I2C_FREQ,
|
|
},
|
|
.reset_pin = CONFIG_EXAMPLE_MIPI_CSI_CAM_SENSOR_RESET_PIN,
|
|
.pwdn_pin = CONFIG_EXAMPLE_MIPI_CSI_CAM_SENSOR_PWDN_PIN,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_EXAMPLE_ENABLE_DVP_CAM_SENSOR
|
|
static const esp_video_init_dvp_config_t dvp_config[] = {
|
|
{
|
|
.sccb_config = {
|
|
.init_sccb = true,
|
|
.i2c_config = {
|
|
.port = CONFIG_EXAMPLE_DVP_SCCB_I2C_PORT,
|
|
.scl_pin = CONFIG_EXAMPLE_DVP_SCCB_I2C_SCL_PIN,
|
|
.sda_pin = CONFIG_EXAMPLE_DVP_SCCB_I2C_SDA_PIN,
|
|
},
|
|
.freq = CONFIG_EXAMPLE_DVP_SCCB_I2C_FREQ,
|
|
},
|
|
.reset_pin = CONFIG_EXAMPLE_DVP_CAM_SENSOR_RESET_PIN,
|
|
.pwdn_pin = CONFIG_EXAMPLE_DVP_CAM_SENSOR_PWDN_PIN,
|
|
.dvp_pin = {
|
|
.data_width = CAM_CTLR_DATA_WIDTH_8,
|
|
.data_io = {
|
|
CONFIG_EXAMPLE_DVP_D0_PIN, CONFIG_EXAMPLE_DVP_D1_PIN, CONFIG_EXAMPLE_DVP_D2_PIN, CONFIG_EXAMPLE_DVP_D3_PIN,
|
|
CONFIG_EXAMPLE_DVP_D4_PIN, CONFIG_EXAMPLE_DVP_D5_PIN, CONFIG_EXAMPLE_DVP_D6_PIN, CONFIG_EXAMPLE_DVP_D7_PIN,
|
|
},
|
|
.vsync_io = CONFIG_EXAMPLE_DVP_VSYNC_PIN,
|
|
.de_io = CONFIG_EXAMPLE_DVP_DE_PIN,
|
|
.pclk_io = CONFIG_EXAMPLE_DVP_PCLK_PIN,
|
|
.xclk_io = CONFIG_EXAMPLE_DVP_XCLK_PIN,
|
|
},
|
|
.xclk_freq = CONFIG_EXAMPLE_DVP_XCLK_FREQ,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
|
|
|
|
static esp_err_t camera_capture_stream(void)
|
|
{
|
|
int fd;
|
|
esp_err_t ret;
|
|
int fmt_index = 0;
|
|
uint32_t frame_size;
|
|
uint32_t frame_count;
|
|
struct v4l2_buffer buf;
|
|
uint8_t *buffer[BUFFER_COUNT];
|
|
struct v4l2_format init_format;
|
|
struct v4l2_requestbuffers req;
|
|
struct v4l2_capability capability;
|
|
|
|
const int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
fd = open("/dev/video0", O_RDONLY);
|
|
if (fd < 0) {
|
|
ESP_LOGE(TAG, "failed to open device");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if (ioctl(fd, VIDIOC_QUERYCAP, &capability)) {
|
|
ESP_LOGE(TAG, "failed to get capability");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "version: %d.%d.%d", (uint16_t)(capability.version >> 16),
|
|
(uint8_t)(capability.version >> 8),
|
|
(uint8_t)capability.version);
|
|
ESP_LOGI(TAG, "driver: %s", capability.driver);
|
|
ESP_LOGI(TAG, "card: %s", capability.card);
|
|
ESP_LOGI(TAG, "bus: %s", capability.bus_info);
|
|
ESP_LOGI(TAG, "capabilities:");
|
|
if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
|
ESP_LOGI(TAG, "\tVIDEO_CAPTURE");
|
|
}
|
|
if (capability.capabilities & V4L2_CAP_READWRITE) {
|
|
ESP_LOGI(TAG, "\tREADWRITE");
|
|
}
|
|
if (capability.capabilities & V4L2_CAP_ASYNCIO) {
|
|
ESP_LOGI(TAG, "\tASYNCIO");
|
|
}
|
|
if (capability.capabilities & V4L2_CAP_STREAMING) {
|
|
ESP_LOGI(TAG, "\tSTREAMING");
|
|
}
|
|
if (capability.capabilities & V4L2_CAP_META_OUTPUT) {
|
|
ESP_LOGI(TAG, "\tMETA_OUTPUT");
|
|
}
|
|
if (capability.capabilities & V4L2_CAP_DEVICE_CAPS) {
|
|
ESP_LOGI(TAG, "device capabilities:");
|
|
if (capability.device_caps & V4L2_CAP_VIDEO_CAPTURE) {
|
|
ESP_LOGI(TAG, "\tVIDEO_CAPTURE");
|
|
}
|
|
if (capability.device_caps & V4L2_CAP_READWRITE) {
|
|
ESP_LOGI(TAG, "\tREADWRITE");
|
|
}
|
|
if (capability.device_caps & V4L2_CAP_ASYNCIO) {
|
|
ESP_LOGI(TAG, "\tASYNCIO");
|
|
}
|
|
if (capability.device_caps & V4L2_CAP_STREAMING) {
|
|
ESP_LOGI(TAG, "\tSTREAMING");
|
|
}
|
|
if (capability.device_caps & V4L2_CAP_META_OUTPUT) {
|
|
ESP_LOGI(TAG, "\tMETA_OUTPUT");
|
|
}
|
|
}
|
|
|
|
/* Set custom register configuration */
|
|
if (ioctl(fd, VIDIOC_S_SENSOR_FMT, &custom_format_info) != 0) {
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
memset(&init_format, 0, sizeof(struct v4l2_format));
|
|
init_format.type = type;
|
|
if (ioctl(fd, VIDIOC_G_FMT, &init_format) != 0) {
|
|
ESP_LOGE(TAG, "failed to get format");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
struct v4l2_fmtdesc fmtdesc = {
|
|
.index = fmt_index++,
|
|
.type = type,
|
|
};
|
|
|
|
ESP_LOGI(TAG, "Capture %s format frames for %d seconds:", (char *)fmtdesc.description, CAPTURE_SECONDS);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.count = BUFFER_COUNT;
|
|
req.type = type;
|
|
req.memory = MEMORY_TYPE;
|
|
if (ioctl(fd, VIDIOC_REQBUFS, &req) != 0) {
|
|
ESP_LOGE(TAG, "failed to require buffer");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
for (int i = 0; i < BUFFER_COUNT; i++) {
|
|
struct v4l2_buffer buf;
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
buf.type = type;
|
|
buf.memory = MEMORY_TYPE;
|
|
buf.index = i;
|
|
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) != 0) {
|
|
ESP_LOGE(TAG, "failed to query buffer");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
buffer[i] = (uint8_t *)mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, fd, buf.m.offset);
|
|
if (!buffer[i]) {
|
|
ESP_LOGE(TAG, "failed to map buffer");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
if (ioctl(fd, VIDIOC_QBUF, &buf) != 0) {
|
|
ESP_LOGE(TAG, "failed to queue video frame");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
}
|
|
|
|
if (ioctl(fd, VIDIOC_STREAMON, &type) != 0) {
|
|
ESP_LOGE(TAG, "failed to start stream");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
frame_count = 0;
|
|
frame_size = 0;
|
|
int64_t start_time_us = esp_timer_get_time();
|
|
while (esp_timer_get_time() - start_time_us < (CAPTURE_SECONDS * 1000 * 1000)) {
|
|
memset(&buf, 0, sizeof(buf));
|
|
buf.type = type;
|
|
buf.memory = MEMORY_TYPE;
|
|
if (ioctl(fd, VIDIOC_DQBUF, &buf) != 0) {
|
|
ESP_LOGE(TAG, "failed to receive video frame");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
frame_size += buf.bytesused;
|
|
|
|
if (ioctl(fd, VIDIOC_QBUF, &buf) != 0) {
|
|
ESP_LOGE(TAG, "failed to queue video frame");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
frame_count++;
|
|
}
|
|
|
|
if (ioctl(fd, VIDIOC_STREAMOFF, &type) != 0) {
|
|
ESP_LOGE(TAG, "failed to stop stream");
|
|
ret = ESP_FAIL;
|
|
goto exit_0;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "\twidth: %" PRIu32, init_format.fmt.pix.width);
|
|
ESP_LOGI(TAG, "\theight: %" PRIu32, init_format.fmt.pix.height);
|
|
ESP_LOGI(TAG, "\tsize: %" PRIu32, frame_size / frame_count);
|
|
ESP_LOGI(TAG, "\tFPS: %" PRIu32, frame_count / CAPTURE_SECONDS);
|
|
|
|
// bsp_display_lock(0);
|
|
// lv_label_ins_text(label,LV_LABEL_POS_LAST,"Camera initial #00ff00 Success#\n");
|
|
// bsp_display_unlock();
|
|
ret = ESP_OK;
|
|
|
|
exit_0:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
void app_camera(void)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
i2c_master_get_bus_handle(1, &i2c_handle);
|
|
csi_config[0].sccb_config.init_sccb = false;
|
|
csi_config[0].sccb_config.i2c_handle = i2c_handle;
|
|
|
|
static const esp_video_init_config_t cam_config = {
|
|
.csi = csi_config,
|
|
};
|
|
|
|
ret = esp_video_init(&cam_config);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Camera init failed with error 0x%x", ret);
|
|
// bsp_display_lock(0);
|
|
// lv_label_ins_text(label,LV_LABEL_POS_LAST,"Camera initial #ff0000 Failed#\n");
|
|
// bsp_display_unlock();
|
|
return;
|
|
}
|
|
|
|
ret = camera_capture_stream();
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Camera capture stream failed with error 0x%x", ret);
|
|
return;
|
|
}
|
|
}
|
|
|