To change image format on the fly you have to perform full re-init camera. Its not clean code, but this is how i did it:
Code: Select all
// camera.c modifications
esp_err_t allocate_fb() {
s_state->fb = (uint8_t*) calloc(s_state->fb_size, 1);
if (s_state->fb == NULL) {
ESP_LOGE(TAG, "Failed to allocate frame buffer");
esp_err_t err = ESP_ERR_NO_MEM;
return err;
}
ESP_LOGI(TAG,
"in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d, quality: %u",
s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel,
s_state->fb_size, s_state->sampling_mode, s_state->width,
s_state->height, s_state->config.jpeg_quality);
ESP_LOGW(TAG, "Allocating frame buffer (%d bytes)", s_state->fb_size);
heap_caps_print_heap_info(MALLOC_CAP_SPIRAM);
return ESP_OK;
}
esp_err_t set_jpeg_quality(int qp) {
if(qp == 0)
qp = s_state->config.jpeg_quality;
else
s_state->config.jpeg_quality = qp;
ESP_LOGI(TAG, "jpeg quality --> %u", qp);
int compression_ratio_bound;
if (qp <= 10) {
compression_ratio_bound = 5;
} else if (qp <= 30) {
compression_ratio_bound = 10;
} else {
compression_ratio_bound = 20;
}
(*s_state->sensor.set_quality)(&s_state->sensor, qp);
size_t equiv_line_count = s_state->height / compression_ratio_bound;
s_state->fb_size = s_state->width * equiv_line_count * 2 /* bpp */;
return ESP_OK;
}
esp_err_t set_frame_size(camera_framesize_t cam_frame_size) {
framesize_t frame_size = (framesize_t) cam_frame_size;
s_state->width = resolution[frame_size][0];
s_state->height = resolution[frame_size][1];
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
ESP_LOGE(TAG, "Failed to set frame size --> %u", frame_size);
esp_err_t err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
return err;
}
ESP_LOGI(TAG, "set frame size --> %u", frame_size);
return ESP_OK;
}
esp_err_t set_pixel_format(camera_pixelformat_t format, int qp) {
if(s_state->fb != NULL) {
free(s_state->fb);
s_state->fb = NULL;
heap_caps_print_heap_info(MALLOC_CAP_SPIRAM);
}
esp_err_t err;
pixformat_t pix_format = (pixformat_t)format;
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
if (pix_format == PIXFORMAT_GRAYSCALE) {
s_state->fb_size = s_state->width * s_state->height * 3;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
s_state->dma_filter = &dma_filter_grayscale_highspeed;
} else {
s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_grayscale;
}
s_state->in_bytes_per_pixel = 2; // camera sends YUYV
s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8
} else if (pix_format == PIXFORMAT_RGB565) {
s_state->fb_size = s_state->width * s_state->height * 3;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
} else {
s_state->sampling_mode = SM_0A00_0B00;
}
s_state->in_bytes_per_pixel = 2; // camera sends RGB565 (2 bytes)
s_state->fb_bytes_per_pixel = 3; // frame buffer stores RGB888
s_state->dma_filter = &dma_filter_rgb565;
ESP_LOGW(TAG, " set RGB565 size --> %u", s_state->fb_size);
} else if (pix_format == PIXFORMAT_JPEG) {
if (s_state->sensor.id.PID != OV2640_PID) {
ESP_LOGE(TAG, "JPEG format is only supported for ov2640");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
set_jpeg_quality(qp);
s_state->dma_filter = &dma_filter_jpeg;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A0B_0B0C;
} else {
s_state->sampling_mode = SM_0A00_0B00;
}
s_state->in_bytes_per_pixel = 2;
s_state->fb_bytes_per_pixel = 2;
ESP_LOGW(TAG, "set JPEG frame size --> %u", s_state->fb_size);
} else {
ESP_LOGE(TAG, "Requested format is not supported");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
allocate_fb();
if(s_state->dma_filter_task != NULL){
vTaskDelete(s_state->dma_filter_task);
vTaskDelay(10/portTICK_PERIOD_MS);
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10,
&s_state->dma_filter_task, 1)) {
ESP_LOGE(TAG, "Failed to create DMA filter task");
err = ESP_ERR_NO_MEM;
goto fail;
}
}
return ESP_OK;
fail:
return err;
}
void reinit_dma() {
dma_desc_deinit();
dma_desc_init();
}
esp_err_t camera_init(camera_config_t* config) {
if (!s_state) {
return ESP_ERR_INVALID_STATE;
}
if (s_state->sensor.id.PID == 0) {
return ESP_ERR_CAMERA_NOT_SUPPORTED;
}
// memcpy(&s_state->config, config, sizeof(*config));
s_state->config = *config;
esp_err_t err = ESP_OK;
framesize_t frame_size = (framesize_t) config->frame_size;
pixformat_t pix_format = (pixformat_t) config->pixel_format;
ESP_LOGD(TAG, "Setting frame size to %dx%d", s_state->width, s_state->height);
if (set_frame_size(config->frame_size) != 0) {
ESP_LOGE(TAG, "Failed to set frame size");
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
goto fail;
}
// s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
#if ENABLE_TEST_PATTERN
/* Test pattern may get handy
if you are unable to get the live image right.
Once test pattern is enable, sensor will output
vertical shaded bars instead of live image.
*/
s_state->sensor.set_colorbar(&s_state->sensor, 1);
ESP_LOGD(TAG, "Test pattern enabled");
#endif
err = set_pixel_format(config->pixel_format, 0);
if(err) {
ESP_LOGE(TAG, "Requested format is not supported");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
ESP_LOGD(TAG,
"in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d",
s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel,
s_state->fb_size, s_state->sampling_mode, s_state->width,
s_state->height);
ESP_LOGW(TAG, "Initializing I2S and DMA");
i2s_init();
err = dma_desc_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize I2S and DMA");
goto fail;
}
s_state->data_ready = xQueueCreate(16, sizeof(size_t));
s_state->frame_ready = xSemaphoreCreateBinary();
if (s_state->data_ready == NULL || s_state->frame_ready == NULL) {
ESP_LOGE(TAG, "Failed to create semaphores");
err = ESP_ERR_NO_MEM;
goto fail;
}
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10,
&s_state->dma_filter_task, 1)) {
ESP_LOGE(TAG, "Failed to create DMA filter task");
err = ESP_ERR_NO_MEM;
goto fail;
}
ESP_LOGD(TAG, "Initializing GPIO interrupts");
gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE); // GPIO ISR
gpio_intr_enable(s_state->config.pin_vsync);
err = gpio_isr_register(&gpio_isr, (void*) TAG,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM,
&s_state->vsync_intr_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "gpio_isr_register failed (%x)", err);
goto fail;
}
// skip at least one frame after changing camera settings
while (gpio_get_level(s_state->config.pin_vsync) == 0) {
;
}
while (gpio_get_level(s_state->config.pin_vsync) != 0) {
;
}
while (gpio_get_level(s_state->config.pin_vsync) == 0) {
;
}
s_state->frame_count = 0;
ESP_LOGD(TAG, "Init done");
return ESP_OK;
fail: camera_deinit();
return err;
}