Skip to content

Commit d23dbfe

Browse files
committed
asyncweb: MJPEG continuous mode
refs #34
1 parent e81846a commit d23dbfe

File tree

6 files changed

+35
-20
lines changed

6 files changed

+35
-20
lines changed

examples/AsyncCam/AsyncCam.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ setup() {
3434
Config cfg;
3535
cfg.setPins(pins::AiThinker);
3636
cfg.setResolution(initialResolution);
37+
cfg.setBufferCount(3);
3738
cfg.setJpeg(80);
3839

3940
bool ok = Camera.begin(cfg);

examples/AsyncCam/handlers.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ static const char FRONTPAGE[] = R"EOT(
1818
<input type="submit" value="update">
1919
</p></form>
2020
<p id="controls">
21-
<button data-act="mjpeg">show Motion JPEG stream</button>
22-
<button data-act="jpg">show still JPEG image</button>
23-
<button data-act="">hide</button>
21+
<button data-res="cam.jpg">still JPEG</button>
22+
<button data-res="cam.mjpeg">MJPEG</button>
23+
<button data-res="continuous.mjpeg">MJPEG-continuous</button>
24+
<button data-res="">hide</button>
2425
</p>
2526
<div id="display"></div>
2627
<footer>Powered by <a href="https://esp32cam.yoursunny.dev/">esp32cam</a></footer>
@@ -57,12 +58,8 @@ for (const $ctrl of document.querySelectorAll("#controls button")) {
5758
$img.src = "";
5859
}
5960
60-
const act = evt.target.getAttribute("data-act");
61-
if (act === "") {
62-
$display.innerHTML = "";
63-
} else {
64-
$display.innerHTML = `<img src="/cam.${act}?_=${Math.random()}" alt="camera image">`;
65-
}
61+
const resource = evt.target.getAttribute("data-res");
62+
$display.innerHTML = resource && `<img src="/${resource}?_=${Math.random()}" alt="${resource}">`;
6663
});
6764
}
6865
</script>
@@ -172,4 +169,9 @@ addRequestHandlers() {
172169

173170
server.on("/cam.jpg", esp32cam::asyncweb::handleStill);
174171
server.on("/cam.mjpeg", esp32cam::asyncweb::handleMjpeg);
172+
server.on("/continuous.mjpeg", HTTP_GET, [](AsyncWebServerRequest* req) {
173+
esp32cam::MjpegConfig cfg;
174+
cfg.minInterval = -1;
175+
req->send(new esp32cam::asyncweb::MjpegResponse(cfg));
176+
});
175177
}

src/esp32cam/asyncweb.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace esp32cam {
1212
namespace detail {
1313

14-
CaptureTask::CaptureTask(uint32_t queueLength, uint32_t priority, int core) {
14+
CaptureTask::CaptureTask(uint32_t queueLength, uint32_t priority) {
1515
m_queue = xQueueCreate(queueLength, sizeof(Frame*));
1616
if (m_queue == nullptr) {
1717
return;
@@ -41,10 +41,11 @@ CaptureTask::~CaptureTask() {
4141
}
4242

4343
void
44-
CaptureTask::request() {
45-
if (m_task == nullptr) {
44+
CaptureTask::request(bool continuous) {
45+
if (m_task == nullptr || m_continuous) {
4646
return;
4747
}
48+
m_continuous = continuous;
4849
xTaskNotify(reinterpret_cast<TaskHandle_t>(m_task), 1, eSetValueWithOverwrite);
4950
}
5051

@@ -62,10 +63,12 @@ void
6263
CaptureTask::run(void* ctx) {
6364
auto self = reinterpret_cast<CaptureTask*>(ctx);
6465
while (true) {
65-
uint32_t value = 0;
66-
xTaskNotifyWait(0, UINT32_MAX, &value, pdMS_TO_TICKS(10000));
67-
if (value == 0) {
68-
continue;
66+
if (!self->m_continuous) {
67+
uint32_t value = 0;
68+
xTaskNotifyWait(0, UINT32_MAX, &value, pdMS_TO_TICKS(10000));
69+
if (value == 0) {
70+
continue;
71+
}
6972
}
7073

7174
auto frame = Camera.capture().release();
@@ -173,7 +176,7 @@ MjpegResponse::_fillBuffer(uint8_t* buf, size_t buflen) {
173176
// fallthrough
174177
}
175178
case Ctrl::CAPTURE: {
176-
m_task.request();
179+
m_task.request(m_ctrl.cfg.minInterval < 0);
177180
m_ctrl.notifyCapture();
178181
return RESPONSE_TRY_AGAIN;
179182
}

src/esp32cam/asyncweb.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ namespace detail {
1010

1111
class CaptureTask {
1212
public:
13-
explicit CaptureTask(uint32_t queueLength, uint32_t priority = 1, int core = 1);
13+
explicit CaptureTask(uint32_t queueLength, uint32_t priority = 1);
1414

1515
~CaptureTask();
1616

1717
explicit operator bool() const {
1818
return m_queue != nullptr && m_task != nullptr;
1919
}
2020

21-
void request();
21+
void request(bool continuous = false);
2222

2323
std::unique_ptr<Frame> retrieve();
2424

@@ -28,6 +28,7 @@ class CaptureTask {
2828
private:
2929
void* m_queue = nullptr;
3030
void* m_task = nullptr;
31+
bool m_continuous = false;
3132
};
3233

3334
} // namespace detail
@@ -81,6 +82,12 @@ handleStill(AsyncWebServerRequest* request) {
8182
* different images.
8283
* If task creation fails, respond with HTTP 500 error.
8384
* If image capture fails, the stream is stopped.
85+
*
86+
* Normally, a new frame is captured after the prior frame has been fully sent to the client.
87+
* Setting MjpegConfig::minInternal to -1 enables continuous mode, in which a new frame is
88+
* captured while the prior frame is still being sent to the client. This improves FPS rate
89+
* but also increases video latency. This mode is effective only if there are more than one
90+
* frame buffer created during camera initialization.
8491
*/
8592
class MjpegResponse : public AsyncAbstractResponse {
8693
public:

src/esp32cam/mjpeg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ MjpegController::decideAction() {
2525
void
2626
MjpegController::notifyCapture() {
2727
m_nextAction = RETURN;
28-
m_nextCaptureTime = millis() + static_cast<unsigned long>(cfg.minInterval);
28+
m_nextCaptureTime = millis() + static_cast<unsigned long>(std::max(0, cfg.minInterval));
2929
MC_LOG("notifyCapture next=%lu", m_nextCaptureTime);
3030
}
3131

src/esp32cam/mjpeg.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace esp32cam {
1111
struct MjpegConfig {
1212
/**
1313
* @brief Minimum interval between frame captures in millis.
14+
*
15+
* Negative value causes @c asyncweb::MjpegResponse to enter continuous mode.
1416
*/
1517
int minInterval = 0;
1618

0 commit comments

Comments
 (0)