diff --git a/src/esp32cam.cpp b/src/esp32cam.cpp index 0b3ca67..f0c24d9 100644 --- a/src/esp32cam.cpp +++ b/src/esp32cam.cpp @@ -1,8 +1,11 @@ -#include "esp32cam.h" +#include "esp32cam.h" #include #include +#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" +// #undef BOUNDARY + namespace esp32cam { CameraClass Camera; @@ -10,13 +13,15 @@ CameraClass Camera; bool CameraClass::begin(const Config& config) { - return esp_camera_init(reinterpret_cast(config.m_cfg)) == ESP_OK; + lastEspErr = esp_camera_init(reinterpret_cast(config.m_cfg)); + return (lastEspErr == ESP_OK ? true : false); } bool CameraClass::end() { - return esp_camera_deinit() == ESP_OK; + lastEspErr = esp_camera_deinit(); + return (lastEspErr == ESP_OK ? true : false); } bool @@ -39,6 +44,505 @@ CameraClass::changeResolution(const Resolution& resolution, int sleepFor) return true; } +bool +CameraClass::changeFrequence(int iMhz) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + int iLedTimer = 0; + int ret = 0; + sensor->xclk_freq_hz = iMhz * 1000000U; +// ret = xclk_timer_conf(iLedTimer, sensor->xclk_freq_hz); + // if (sensor->set_xclk(sensor, iLedTimer, iMhz) != 0) { + if (ret != 0) { + return false; + } else + return true; +} + +bool +CameraClass::changeContrast(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_contrast(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeBrightness(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_brightness(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeSaturation(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_saturation(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeSpecialEffect(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_special_effect(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeWbMode(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_wb_mode(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeAeLevels(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_ae_level(sensor, ilevel)) { + return true; + } else + return false; +} + +bool +CameraClass::changAgcGain(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_agc_gain(sensor, ilevel) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changGainceilingSensor(int iGainCeiling) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if ((gainceiling_t)iGainCeiling < GAINCEILING_2X || + (gainceiling_t)iGainCeiling > GAINCEILING_128X) { + return false; + } + + if (sensor->set_gainceiling(sensor, (gainceiling_t)iGainCeiling) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeAwbGainControl(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_awb_gain(sensor, iEnable) == 0) { + return true; + } else + return false; +} + + +bool +CameraClass::changeGaincontrol(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_gain_ctrl(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeColorbar(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_colorbar(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeWhitebalance(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_whitebal(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeQuality(int iQuality) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (iQuality < 0 || + iQuality > 63) { + return false; + } + + if (sensor->set_quality(sensor, iQuality) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changAec2(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_aec2(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeAecValue(int iValue) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (iValue < 0 || + iValue > 1200) { + return false; + } + + if (sensor->set_aec_value(sensor, iValue) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changHMirror(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_hmirror(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changVFlip(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_vflip(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changBPC(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_bpc(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changWPC(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_wpc(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changDenoise(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_denoise(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changLenc(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_lenc(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changRawGMA(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_raw_gma(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changAutoExposurecontrol(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_exposure_ctrl(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changDcw(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_dcw(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +bool +CameraClass::changeZoom(int iEnable) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + // sensor->status.aec2 = enable; + if (sensor->set_reg(sensor, 0x12 /*COM7*/, 0xFF, iEnable?0x04/*COM7_ZOOM_EN*/:0) == 0) { +// if (sensor->set_reg(sensor, 0x01 /*BANK_SENSOR*/, 0x12 /*COM7*/, iEnable?0x04/*COM7_ZOOM_EN*/:0) == 0) { +// if (sensor->set_zoom(sensor, iEnable) == 0) { + return true; + } else + return false; +} + +int +CameraClass::getBandMode() +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + return sensor->get_reg(sensor, 0x3C0C, 0x01); +} + +bool +CameraClass::changeBandMode(int ilevel) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + // OV3660 registers + if ( ilevel == 0 ) { + // Auto Mode: Enable 0x3004[2] + if (sensor->set_reg(sensor, 0x3a00, 0x32, 0x32) == 0 && + sensor->set_reg(sensor, 0x3004, 0x04, 0x04) == 0 && + sensor->set_reg(sensor, 0x3c00, 0x04, 0x00) == 0 /* 60Hz? */ ) + return true; + else + return false; + } else { + // Manual Mode: Disable registers + if (sensor->set_reg(sensor, 0x3a00, 0x32, 0x00) == 0 && + sensor->set_reg(sensor, 0x3004, 0x04, 0x00) == 0 ) { + if ( ilevel == 1 ) { +// TESTING sensor->set_reg(sensor, 0x5000, 0x20, 0x20); + // 50 HZ + if (sensor->set_reg(sensor, 0x3c00, 0x04, 0x04) == 0) { + sensor->set_reg(sensor, 0x3a08, 0x00, 0x00);//50HZ BAND WIDTH + sensor->set_reg(sensor, 0x3a09, 0xeb, 0xeb);//50HZ BAND WIDTH + sensor->set_reg(sensor, 0x3a0e, 0x06, 0x06);//50HZ MAX BAND + + sensor->set_reg(sensor, 0x3C0C, 0x01, 0x01); + return true; + } else + return false; + } else { +// TESTING sensor->set_reg(sensor, 0x5000, 0x20, 0x00); + // 60 HZ + if (sensor->set_reg(sensor, 0x3c00, 0x04, 0x00) == 0) { + sensor->set_reg(sensor, 0x3a0a, 0x00, 0x00);//60HZ BAND WIDTH + sensor->set_reg(sensor, 0x3a0b, 0xc4, 0xc4);//60HZ BAND WIDTH + sensor->set_reg(sensor, 0x3a0d, 0x08, 0x08);//60HZ MAX BAND + + sensor->set_reg(sensor, 0x3C0C, 0x01, 0x00); + return true; + } else + return false; + } + } else { + return false; + } + } + + /* OV3660 + {0x3a00, 0x38}, //BIT[5]:1,banding function enable;bit[2] night mode disable + // /******if use 50HZ banding remove, refer to below setting******** + {0x3c00, 0x04}, //BIT[2]:1,ENABLE 50HZ + {0x3a14, 0x06}, //NIGHT MODE CEILING, 50HZ + {0x3a15, 0x6d}, //NIGHT MODE CEILING, 50HZ + {0x3a08, 0x00}, //50HZ BAND WIDTH + {0x3a09, 0xeb}, //50HZ BAND WIDTH + {0x3a0e, 0x06}, //50HZ MAX BAND + + {0x380e, 0x06}, + {0x380f, 0x6d}, //inset 81 dummy lines for banding filter//1c + // /******if use 60HZ banding remove, refer to below setting******** + + //{0x3c00, 0x00}, //BIT[2]:0,ENABLE 50HZ + {0x3a02, 0x06}, //NIGHT MODE CEILING, 60HZ + {0x3a03, 0x6d}, //NIGHT MODE CEILING, 60HZ + {0x3a0a, 0x00}, //60HZ BAND WIDTH + {0x3a0b, 0xc4}, //60HZ BAND WIDTH + {0x3a0d, 0x08}, //60HZ MAX BAND + + 0x3004[2] = 1 = AUTO + 0x3004[2] = 0 = MANUAL + 0X3C0C[0] = 0 = 60hz + 0X3C0C[0] = 1 = 50hz + */ +} + +bool +CameraClass::changePll(int iMul, int iPre, int iPclk) +{ + sensor_t* sensor = esp_camera_sensor_get(); + if (sensor == nullptr) { + return false; + } + + if (sensor->set_pll( sensor, + false, + iMul, + 1, + iPre, + false, + 0, + true, + iPclk) == 0) { + return true; + } else + return false; +} + + std::unique_ptr CameraClass::capture() { @@ -49,10 +553,8 @@ CameraClass::capture() return std::unique_ptr(new Frame(fb)); } -int -CameraClass::streamMjpeg(Client& client, const StreamMjpegConfig& cfg) -{ -#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" +int +CameraClass::streamMjpeg(Client& client, const StreamMjpegConfig& cfg) { client.print("HTTP/1.1 200 OK\r\n" "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" "\r\n"); @@ -81,7 +583,57 @@ CameraClass::streamMjpeg(Client& client, const StreamMjpegConfig& cfg) client.print("\r\n--" BOUNDARY "\r\n"); } return nFrames; -#undef BOUNDARY +} + +int +CameraClass::streamMjpeg(AsyncClient& client, const StreamMjpegConfig* cfg) { + client.write("HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" + "\r\n"); + auto lastCapture = millis(); + int nFrames; + int nNullFrames = 0; + for (nFrames = 0; cfg->maxFrames < 0 || nFrames < cfg->maxFrames; ++nFrames) { + auto now = millis(); + auto sinceLastCapture = now - lastCapture; + if (static_cast(sinceLastCapture) < cfg->minInterval) { + delay(cfg->minInterval - sinceLastCapture); + } + lastCapture = millis(); + + auto frame = capture(); + if (frame == nullptr) { + nNullFrames++; + if (nNullFrames > 10) + break; + else { + continue; + } + } else { + nNullFrames = 0; + } + + char szTmp[256]; + size_t stLen = snprintf_P(szTmp, 256, PSTR( + "Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n" + "\r\n"), static_cast(frame->size()) + ); + if (!client.disconnected()) { + client.write((const char*) szTmp, stLen); + if (!frame->writeTo(client, cfg->frameTimeout)) { + break; + } + } else { + break; + } + client.write("\r\n--" BOUNDARY "\r\n"); + taskYIELD(); + + if (cfg->bAbort) + break; + } + return nFrames; } } // namespace esp32cam diff --git a/src/esp32cam.h b/src/esp32cam.h index b84a5ae..88a586b 100644 --- a/src/esp32cam.h +++ b/src/esp32cam.h @@ -8,6 +8,19 @@ namespace esp32cam { +struct StreamMjpegConfig +{ +/// minimum interval between frame captures. +int minInterval = 0; +/// maximum number of frames before disconnecting. +int maxFrames = -1; +/// time limit of writing one frame in millis. +int frameTimeout = 10000; +/// Abort flags +bool bAbort = false; + +}; + class CameraClass { public: @@ -29,33 +42,231 @@ class CameraClass bool changeResolution(const Resolution& resolution, int sleepFor = 500); + /** \brief Change camera resolution. + * \pre Change Camera Frequence Clock + * \param iMhz new Frequence, must be in MHZ + */ + bool + changeFrequence(int iMhz); + + /** \brief Change Sensor Contrast level + * must be -2 to +2, default 0 + */ + bool + changeContrast(int ilevel); + + /** \brief Change Brightness level + * \param ilevel must be -2 to +2, default 0 + */ + bool + changeBrightness(int ilevel); + + /** \brief Change Saturation level + * \param ilevel must be -2 to +2, default 0 + */ + bool + changeSaturation(int ilevel); + + /** \brief Change SpecialEffect + * \param ilevel must be > 0 && <= NUM_SPECIAL_EFFECTS, default 0 + * \ 1 = no effect + * \ 2 = negative + * \ 3 = black and white + * \ 4 = reddish + * \ 5 = greenish + * \ 6 = blue + * \ 7 = retro + */ + bool + changeSpecialEffect(int ilevel); + + /** \brief Change WbMode + * \param ilevel must be > 0 && <= NUM_WB_MODES, default 0 + * \ 0 = default + * \ 1 = sunny + * \ 2 = cloudy + * \ 3 = office + * \ 4 = home + */ + bool + changeWbMode(int ilevel); + + /** \brief Change AE Levels + * \param ilevel must be -2 to +2, default 0 + */ + bool + changeAeLevels(int ilevel); + + /** \brief Enable/Disable AEC (Auto Exposure Control) + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changAutoExposurecontrol(int iEnable); + + /** \brief Change AGC Gain + * \param ilevel must be >= 0 and <= 30, default 30 + */ + bool + changAgcGain(int ilevel); + + /** \brief Change Gainceiling + * \param iGainCeiling must be between GAINCEILING_2X and GAINCEILING_128X + */ + bool + changGainceilingSensor(int iGainCeiling); + + /** \brief Enable/Disable Awb GainControl + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changeAwbGainControl(int iEnable); + + /** \brief Enable/Disable Gaincontrol + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changeGaincontrol(int iEnable); + + /** \brief Enable/Disable Testing Colorbar + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changeColorbar(int iEnable); + + /** \brief Enable/Disable Whitebalance + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changeWhitebalance(int iEnable); + + /** \brief Change JPEG Quality + * \param iEnable iQuality must be within 0 and 63 + */ + bool changeQuality(int iQuality); + + /** \brief Change AEC Value + * \param iEnable iValue must be 0 to 1200, default 0 + */ + bool + changeAecValue(int iValue); + + /** \brief Enable/Disable Auto/Manual Exposure control + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changAec2(int iEnable); + + /** \brief Enable/Disable horizontal mirror + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changHMirror(int iEnable); + + /** \brief Enable/Disable vertical mirror + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changVFlip(int iEnable); + + /** \brief Enable/Disable black pixel correction + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changBPC(int iEnable); + + /** \brief Enable/Disable white pixel correction + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changWPC(int iEnable); + + /** \brief Enable/Disable Denoise Control + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changDenoise(int iEnable); + + /** \brief Enable/Disable Lenc Correction + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changLenc(int iEnable); + + /** \brief Enable/Disable RAW Gamma + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changRawGMA(int iEnable); + + /** \brief Enable/Disable DCW + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changDcw(int iEnable); + + /** \brief Enable/Disable COM7_ZOOM_EN + * \param iEnable must be 0(disable) or 1 (enable) + */ + bool + changeZoom(int iEnable); + + /** \brief Get detectedBandMode Light Frequence Detection (Antiflicker) + * return + * \ 0 = 60hz + * \ 1 = 50hz + */ + int + getBandMode(void); + + /** \brief Change BandMode Light Frequewnce Detection (Antiflicker) + * \param ilevel must be > 0 && <= NUM_SPECIAL_EFFECTS, default 0 + * \ 0 = AUTO + * \ 1 = MANUAL 50hz + * \ 2 = MANUAL 60hz + */ + bool + changeBandMode(int ilevel); + + /** \brief TODO + * \param iMul + * \param iPclk + * \param iPre + * \ is set to 50hz or 60hz. + */ + bool + changePll(int iMul, int iPre, int iPclk); + + /** \brief Set Double Clock Mode for ov2640 + */ + bool + setDoubleClk(); + /** \brief Capture a frame of picture. */ std::unique_ptr capture(); - struct StreamMjpegConfig - { - /// minimum interval between frame captures. - int minInterval = 0; - /// maximum number of frames before disconnecting. - int maxFrames = -1; - /// time limit of writing one frame in millis. - int frameTimeout = 10000; - }; - /** \brief Stream Motion JPEG. * \pre The camera has been initialized to JPEG mode. * \return number of frames streamed. */ - int - streamMjpeg(Client& client, const StreamMjpegConfig& cfg); - - int - streamMjpeg(Client& client) - { + int streamMjpeg(Client& client, const StreamMjpegConfig& cfg); + int streamMjpeg(Client& client) { return streamMjpeg(client, StreamMjpegConfig()); } + + /** \brief Stream Motion JPEG. + * \Function cloned for AsyncClient + * \pre The camera has been initialized to JPEG mode. + * \return number of frames streamed. + */ + int streamMjpeg(AsyncClient& client, const StreamMjpegConfig* cfg); + + /** \brief Return value for esp API calls + * \Store last api return value here useful for error handling! + */ + esp_err_t lastEspErr; + }; extern CameraClass Camera; diff --git a/src/internal/config.cpp b/src/internal/config.cpp index 9ae4c26..fed90a1 100644 --- a/src/internal/config.cpp +++ b/src/internal/config.cpp @@ -78,6 +78,14 @@ Config::setRgb() return *this; } +Config& +Config::setRgb888() +{ + m_cfg->pixel_format = PIXFORMAT_RGB888; + return *this; +} + + Config& Config::setYuv() { @@ -100,4 +108,11 @@ Config::setJpeg(int quality) return *this; } +Config& +Config::setFreqHz(int freq) +{ + m_cfg->xclk_freq_hz = freq; + return *this; +} + } // namespace esp32cam diff --git a/src/internal/config.hpp b/src/internal/config.hpp index ed9a82b..22e8785 100644 --- a/src/internal/config.hpp +++ b/src/internal/config.hpp @@ -36,6 +36,11 @@ class Config */ Config& setRgb(); + /** \brief Change pixel format to RGB888. + */ + Config& + setRgb888(); + /** \brief Change pixel format to YUV422. */ Config& setYuv(); @@ -49,6 +54,12 @@ class Config */ Config& setJpeg(int quality); + /** \brief Change xclk freq hz + * \param Change xclk freq hz default 20000000 + */ + Config& + setFreqHz(int freq); + private: class CameraConfigT; ///< camera_config_t CameraConfigT* m_cfg; diff --git a/src/internal/frame.cpp b/src/internal/frame.cpp index 8957d7c..ea5be07 100644 --- a/src/internal/frame.cpp +++ b/src/internal/frame.cpp @@ -38,38 +38,63 @@ Frame::releaseFb() } } -bool -Frame::writeTo(Print& os, int timeout) -{ +bool +Frame::writeTo(Print& os, int timeout) { return writeToImpl(os, timeout, nullptr); } -bool -Frame::writeTo(Client& os, int timeout) -{ +bool +Frame::writeTo(Client& os, int timeout) { return writeToImpl(os, timeout, &os); } -bool -Frame::writeToImpl(Print& os, int timeout, Client* client) -{ +bool +Frame::writeTo(AsyncClient& os, int timeout) { + return writeToImpl(timeout, &os); +} + +bool +Frame::writeToImpl(Print& os, int timeout, Client* client) { auto startTime = millis(); for (size_t i = 0; i < m_size; i += os.write(&m_data[i], m_size - i)) { if (millis() - startTime > static_cast(timeout) || (client != nullptr && !client->connected())) { return false; } - yield(); } return true; } +bool +Frame::writeToImpl(int timeout, AsyncClient* client) { + auto startTime = millis(); + for (size_t i = 0; i < m_size; i += client->write((const char*) &m_data[i], m_size - i)) { + if (millis() - startTime > static_cast(timeout) || + (client != nullptr && !client->connected())) { + return false; + } + // Force send now + client->send(); + taskYIELD(); + } + return true; +} + + bool Frame::isJpeg() const { return m_pixFormat == PIXFORMAT_JPEG; } +void +Frame::setData(uint8_t* newdata, size_t newsize, bool bIsBmp) { + releaseFb(); + m_data = newdata; + m_size = newsize; + m_pixFormat = (int)(bIsBmp ? PIXFORMAT_BMP : PIXFORMAT_JPEG); +} + bool Frame::toJpeg(int quality) { diff --git a/src/internal/frame.hpp b/src/internal/frame.hpp index badb3eb..2226b54 100644 --- a/src/internal/frame.hpp +++ b/src/internal/frame.hpp @@ -7,6 +7,15 @@ class Client; class Print; +#if defined(ESP8266) +# include +# include +#endif +#if defined(ESP32) +# include "freertos/FreeRTOS.h" +# include +#endif + namespace esp32cam { /** \brief A frame of picture. @@ -42,7 +51,8 @@ class Frame * \retval true writing completed. * \retval false writing disrupted by timeout. */ - bool writeTo(Print& os, int timeout = 10000); + bool + writeTo(Print& os, int timeout = 10000); /** \brief Write frame buffer to \p os . * \param os output socket. @@ -52,6 +62,15 @@ class Frame */ bool writeTo(Client& os, int timeout = 10000); + /** \brief Write frame buffer to \p os . + * \param os output socket. + * \param timeout total time limit in millis. + * \retval true writing completed. + * \retval false writing disrupted by timeout or socket error. + */ + bool + writeTo(AsyncClient& os, int timeout = 10000); + public: // conversion bool isJpeg() const; @@ -65,9 +84,21 @@ class Frame return m_pixFormat == PIXFORMAT_BMP; } + int + getPixFormat() + { + return m_pixFormat; + } + /** \brief Convert frame to BMP. */ bool toBmp(); + + /** \brief Assign buffer from outside + * \We converted the buffer ourself outside the lib and need to set it! + */ + void + setData(uint8_t* newdata, size_t newsize, bool bIsBmp); private: Frame(); @@ -76,6 +107,8 @@ class Frame bool writeToImpl(Print& os, int timeout, Client* client); + bool writeToImpl(int timeout, AsyncClient* client); + void releaseFb(); private: diff --git a/src/internal/resolution.cpp b/src/internal/resolution.cpp index 0e9eb38..330eebe 100644 --- a/src/internal/resolution.cpp +++ b/src/internal/resolution.cpp @@ -16,7 +16,7 @@ Resolution::getWidth() const if (!isValid()) { return -1; } - return ::resolution[m_frameSize][0]; + return ::resolution[m_frameSize].width; } int @@ -25,7 +25,7 @@ Resolution::getHeight() const if (!isValid()) { return -1; } - return ::resolution[m_frameSize][1]; + return ::resolution[m_frameSize].height; } Resolution @@ -40,4 +40,4 @@ Resolution::find(int minWidth, int minHeight) return res; } -} // namespace esp32cam +} // namespace esp32cam \ No newline at end of file diff --git a/src/internal/resolution.hpp b/src/internal/resolution.hpp index 8a609e7..653562c 100644 --- a/src/internal/resolution.hpp +++ b/src/internal/resolution.hpp @@ -35,4 +35,4 @@ class Resolution } // namespace esp32cam -#endif // ESP32CAM_RESOLUTION_HPP +#endif // ESP32CAM_RESOLUTION_HPP \ No newline at end of file