Skip to content

Commit 7c87f2a

Browse files
jpeg encoding on the fly for mjpeg stream
1 parent 5e5bab4 commit 7c87f2a

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Alter camera pixels before sending them via MJPEG stream
3+
* (requires enough RAM to run)
4+
* (expect 0.5 - 2 FPS)
5+
*
6+
* BE SURE TO SET "TOOLS > CORE DEBUG LEVEL = INFO"
7+
* to turn on debug messages
8+
*/
9+
#define WIFI_SSID "SSID"
10+
#define WIFI_PASS "PASSWORD"
11+
#define HOSTNAME "esp32cam"
12+
13+
#include <eloquent_esp32cam.h>
14+
#include <eloquent_esp32cam/viz/mjpeg.h>
15+
16+
using namespace eloq;
17+
using namespace eloq::viz;
18+
19+
uint16_t jpeg_length = 0;
20+
size_t tick = 0;
21+
22+
23+
// prototype of the function that will
24+
// re-encode the frame on-the-fly
25+
void reencode_frame(WiFiClient *client, camera_fb_t* frame);
26+
27+
// prototype of the functon that will
28+
// put JPEG-encoded data back into the frame
29+
size_t buffer_jpeg(void * arg, size_t index, const void* data, size_t len);
30+
31+
32+
/**
33+
*
34+
*/
35+
void setup() {
36+
delay(3000);
37+
Serial.begin(115200);
38+
Serial.println("__RE-ENCODE MJPEG STREAM__");
39+
40+
// camera settings
41+
// replace with your own model!
42+
camera.pinout.aithinker();
43+
camera.brownout.disable();
44+
// higher resolution cannot be handled
45+
camera.resolution.qvga();
46+
camera.quality.best();
47+
48+
// since we want to access the raw pixels
49+
// capture in RGB565 format
50+
// keep in mind that you need a lot of RAM to store
51+
// all this data at high resolutions
52+
// (e.g. QVGA = 320 x 240 x 2 = 1536 kB)
53+
camera.pixformat.rgb565();
54+
55+
// MJPEG settings
56+
mjpeg.onFrame(&reencode_frame);
57+
58+
// init camera
59+
while (!camera.begin().isOk())
60+
Serial.println(camera.exception.toString());
61+
62+
// connect to WiFi
63+
while (!wifi.connect().isOk())
64+
Serial.println(wifi.exception.toString());
65+
66+
// start mjpeg http server
67+
while (!mjpeg.begin().isOk())
68+
Serial.println(mjpeg.exception.toString());
69+
70+
// assert camera can capture frames
71+
while (!camera.capture().isOk())
72+
Serial.println(camera.exception.toString());
73+
74+
Serial.println("Camera OK");
75+
Serial.println("ToF OK");
76+
Serial.println("WiFi OK");
77+
Serial.println("MjpegStream OK");
78+
Serial.println(mjpeg.address());
79+
}
80+
81+
/**
82+
*
83+
*/
84+
void loop() {
85+
// nothing to do here, MJPEG server runs in background
86+
}
87+
88+
89+
/**
90+
* Apply your custom processing to pixels
91+
* then encode to JPEG.
92+
* You will need to modify this
93+
*/
94+
void reencode_frame(WiFiClient *client, camera_fb_t* frame) {
95+
// log how much time elapsed from last frame
96+
const size_t now = millis();
97+
const uint16_t height = camera.resolution.getHeight();
98+
const uint16_t width = camera.resolution.getWidth();
99+
100+
ESP_LOGI("benchmark", "%d ms elapsed from last frame", now - tick);
101+
tick = now;
102+
103+
// frame->buf contains RGB565 data
104+
// that is, 2 bytes per pixel
105+
//
106+
// in this test, we're going to drop the B component
107+
// feel free to replace this with your own code
108+
for (uint16_t y = 0; y < height; y++) {
109+
uint16_t *row = (uint16_t*) (frame->buf + width * 2 * y);
110+
111+
for (uint16_t x = 0; x < width; x++) {
112+
// read pixel and parse to R, G, B components
113+
const uint16_t pixel = row[x];
114+
uint16_t r = (pixel >> 11);
115+
uint16_t g = (pixel >> 5) & 0b111111;
116+
uint16_t b = pixel & 0b11111;
117+
118+
// actual work: drop B component
119+
b = 0;
120+
121+
// re-pack to RGB565
122+
row[x] = (r << 11) | (g << 5) | b;
123+
}
124+
}
125+
126+
// encode to jpeg
127+
uint8_t quality = 90;
128+
frame2jpg_cb(frame, quality, &buffer_jpeg, NULL);
129+
ESP_LOGI("var_dump", "JPEG size=%d", jpeg_length);
130+
}
131+
132+
133+
/**
134+
* Put JPEG-encoded data back into the original frame
135+
* (you don't have to modify this)
136+
*/
137+
size_t buffer_jpeg(void *arg, size_t index, const void* data, size_t len) {
138+
if (index == 0) {
139+
// first MCU block => reset jpeg length
140+
jpeg_length = 0;
141+
}
142+
143+
if (len == 0) {
144+
// encoding is done
145+
camera.frame->len = jpeg_length;
146+
return 0;
147+
}
148+
149+
jpeg_length += len;
150+
151+
// override input data
152+
memcpy(camera.frame->buf + index, (uint8_t*) data, len);
153+
154+
return len;
155+
}

0 commit comments

Comments
 (0)