Add mutex to many camera functions. (#18655)

* Add mutex to many camera functions.

* Allow stream to continue after wcinit command (and other commands which reconfigure).

* Adust retries on camera init, specifically log success if it retried.
Shorten messages to save rom.
I have seen fail of 0x103 and 0x20002 succeed on second try.
This commit is contained in:
btsimonh 2023-05-16 11:21:25 +01:00 committed by GitHub
parent 81d7785f66
commit f8b26a90f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 76 additions and 9 deletions

View File

@ -96,9 +96,17 @@
* remarks for AI-THINKER
* GPIO0 zero must be disconnected from any wire after programming because this pin drives the cam clock and does
* not tolerate any capictive load
* the AITHINKER module does not have CAM_RESET - so if you get the camera into a bad state, power off restart is the only way out.
* flash led = gpio 4
* red led = gpio 33
* optional rtsp url: rtsp://xxx.xxx.xxx.xxx:8554/mjpeg/1
*
* SH 2023-05-14 - added mutex for many webcam functions - this is to prevent multi-threaded access to the camera functions, which
* can case error 0x105 upon re-init.
* Errors 0x103 and 0xffffffff could indicate CAM_PWDN incorrect.
*
* I2C use: if USE_I2C is enabled, you can set GPIO26 to I2c_SDA/2 and GPIO27 to I2C_SCL/2, and then use the shared I2C bus 2.
* Then you can use cmd i2cscan2 to check for camera presence.
*/
/*********************************************************************************************/
@ -114,6 +122,12 @@
bool HttpCheckPriviledgedAccess(bool);
extern ESP8266WebServer *Webserver;
SemaphoreHandle_t WebcamMutex = nullptr;;
// use mutex like:
// TasAutoMutex localmutex(&WebcamMutex, "somename");
// in any function. Will wait for mutex to be clear, and auto-release when the function exits.
#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e"
#ifndef MAX_PICSTORE
@ -165,6 +179,7 @@ struct {
/*********************************************************************************************/
void WcInterrupt(uint32_t state) {
TasAutoMutex localmutex(&WebcamMutex, "WcInterrupt");
// Stop camera ISR if active to fix TG1WDT_SYS_RESET
if (!Wc.up) { return; }
@ -189,6 +204,9 @@ bool WcPinUsed(void) {
// }
// }
}
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: i2c_enabled_2: %d"), TasmotaGlobal.i2c_enabled_2);
if (!PinUsed(GPIO_WEBCAM_XCLK) || !PinUsed(GPIO_WEBCAM_PCLK) ||
!PinUsed(GPIO_WEBCAM_VSYNC) || !PinUsed(GPIO_WEBCAM_HREF) ||
((!PinUsed(GPIO_WEBCAM_SIOD) || !PinUsed(GPIO_WEBCAM_SIOC)) && !TasmotaGlobal.i2c_enabled_2) // preferred option is to reuse and share I2Cbus 2
@ -199,6 +217,7 @@ bool WcPinUsed(void) {
}
void WcFeature(int32_t value) {
TasAutoMutex localmutex(&WebcamMutex, "WcFeature");
sensor_t * wc_s = esp_camera_sensor_get();
if (!wc_s) { return; }
@ -232,6 +251,7 @@ void WcFeature(int32_t value) {
}
void WcApplySettings() {
TasAutoMutex localmutex(&WebcamMutex, "WcApplySettings");
sensor_t * wc_s = esp_camera_sensor_get();
if (!wc_s) { return; }
@ -308,13 +328,20 @@ void WcSetDefaults(uint32_t upgrade) {
}
uint32_t WcSetup(int32_t fsiz) {
TasAutoMutex localmutex(&WebcamMutex, "WcSetup");
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: WcSetup"));
if (fsiz >= FRAMESIZE_FHD) { fsiz = FRAMESIZE_FHD - 1; }
int stream_active = Wc.stream_active;
Wc.stream_active = 0;
if (fsiz < 0) {
esp_camera_deinit();
Wc.up = 0;
if (Wc.up){
esp_camera_deinit();
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Deinit fsiz %d"), fsiz);
Wc.up = 0;
}
return 0;
}
@ -329,6 +356,8 @@ uint32_t WcSetup(int32_t fsiz) {
camera_config_t config;
memset(&config, 0, sizeof(config));
if (WcPinUsed()) {
config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM;
config.pin_d1 = Pin(GPIO_WEBCAM_DATA, 1); // Y3_GPIO_NUM;
@ -408,10 +437,24 @@ uint32_t WcSetup(int32_t fsiz) {
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found"));
}
esp_err_t err = esp_camera_init(&config);
esp_err_t err;
// cannot hurt to retry...
for (int i = 0; i < 3; i++){
err = esp_camera_init(&config);
if (err != ESP_OK) {
AddLog(LOG_LEVEL_INFO, PSTR("CAM: InitErr 0x%x try %d"), err, (i+1));
esp_camera_deinit();
} else {
if (i){
AddLog(LOG_LEVEL_INFO, PSTR("CAM: InitOK try %d"), (i+1));
}
break;
}
}
if (err != ESP_OK) {
AddLog(LOG_LEVEL_INFO, PSTR("CAM: Init failed with error 0x%x"), err);
AddLog(LOG_LEVEL_INFO, PSTR("CAM: InitErr 0x%x"), err);
return 0;
}
@ -440,6 +483,9 @@ uint32_t WcSetup(int32_t fsiz) {
Wc.up = 1;
if (psram) { Wc.up = 2; }
// restore stream_active if we setup ok.
Wc.stream_active = stream_active;
return Wc.up;
}
@ -447,6 +493,8 @@ uint32_t WcSetup(int32_t fsiz) {
int32_t WcSetOptions(uint32_t sel, int32_t value) {
int32_t res = 0;
TasAutoMutex localmutex(&WebcamMutex, "WcSetOptions");
sensor_t *s = esp_camera_sensor_get();
if (!s) { return -99; }
@ -556,6 +604,8 @@ int32_t WcSetOptions(uint32_t sel, int32_t value) {
}
uint32_t WcGetWidth(void) {
TasAutoMutex localmutex(&WebcamMutex, "WcGetWidth");
camera_fb_t *wc_fb = esp_camera_fb_get();
if (!wc_fb) { return 0; }
Wc.width = wc_fb->width;
@ -564,6 +614,7 @@ uint32_t WcGetWidth(void) {
}
uint32_t WcGetHeight(void) {
TasAutoMutex localmutex(&WebcamMutex, "WcGetWidth");
camera_fb_t *wc_fb = esp_camera_fb_get();
if (!wc_fb) { return 0; }
Wc.height = wc_fb->height;
@ -595,6 +646,7 @@ uint32_t WcSetMotionDetect(int32_t value) {
void WcDetectMotion(void) {
camera_fb_t *wc_fb;
uint8_t *out_buf = 0;
TasAutoMutex localmutex(&WebcamMutex, "WcDetectMotion");
if ((millis()-wc_motion.motion_ltime) > wc_motion.motion_detect) {
wc_motion.motion_ltime = millis();
@ -651,6 +703,7 @@ uint32_t WcGetFrame(int32_t bnum) {
uint8_t * _jpg_buf = NULL;
camera_fb_t *wc_fb = 0;
bool jpeg_converted = false;
TasAutoMutex localmutex(&WebcamMutex, "WcGetFrame");
if (bnum < 0) {
if (bnum < -MAX_PICSTORE) { bnum=-1; }
@ -753,6 +806,8 @@ void HandleImage(void) {
response += "Content-type: image/jpeg\r\n\r\n";
Webserver->sendContent(response);
TasAutoMutex localmutex(&WebcamMutex, "HandleImage");
if (!bnum) {
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
@ -804,6 +859,7 @@ void HandleImageBasic(void) {
}
}
TasAutoMutex localmutex(&WebcamMutex, "HandleImage");
camera_fb_t *wc_fb;
wc_fb = esp_camera_fb_get(); // Acquire frame
if (!wc_fb) {
@ -874,6 +930,9 @@ void HandleWebcamMjpegTask(void) {
"\r\n");
Wc.stream_active = 2;
}
TasAutoMutex localmutex(&WebcamMutex, "HandleWebcamMjpegTask");
if (2 == Wc.stream_active) {
wc_fb = esp_camera_fb_get();
if (!wc_fb) {
@ -947,12 +1006,14 @@ void HandleWebcamRoot(void) {
/*********************************************************************************************/
uint32_t WcSetStreamserver(uint32_t flag) {
if (TasmotaGlobal.global_state.network_down) { return 0; }
Wc.stream_active = 0;
if (TasmotaGlobal.global_state.network_down) {
Wc.stream_active = 0;
return 0;
}
if (flag) {
if (!Wc.CamServer) {
Wc.stream_active = 0;
Wc.CamServer = new ESP8266WebServer(81);
Wc.CamServer->on("/", HandleWebcamRoot);
Wc.CamServer->on("/cam.mjpeg", HandleWebcamMjpeg);
@ -963,6 +1024,7 @@ uint32_t WcSetStreamserver(uint32_t flag) {
}
} else {
if (Wc.CamServer) {
Wc.stream_active = 0;
Wc.CamServer->stop();
delete Wc.CamServer;
Wc.CamServer = NULL;
@ -973,8 +1035,13 @@ uint32_t WcSetStreamserver(uint32_t flag) {
}
void WcInterruptControl() {
TasAutoMutex localmutex(&WebcamMutex, "WcInterruptControl");
WcSetStreamserver(Settings->webcam_config.stream);
if(Wc.up == 0) {WcSetup(Settings->webcam_config.resolution);}
if(Wc.up == 0) {
WcSetup(Settings->webcam_config.resolution);
}
}
/*********************************************************************************************/
@ -1382,7 +1449,7 @@ void CmndWebcamClock(void){
}
void CmndWebcamInit(void) {
Wc.up = 0;
WcSetup(Settings->webcam_config.resolution);
WcInterruptControl();
ResponseCmndDone();
}