Skip to content

Commit 3fffba8

Browse files
committed
update to use QVGA (320x240) for transmission to camera-display app on ESP32-S3-BOX. Updated camera-streamer code and python test code to multicast to the group so that the camera-streamer app can automatically find the server.
1 parent 03c3d32 commit 3fffba8

File tree

2 files changed

+101
-28
lines changed

2 files changed

+101
-28
lines changed

display_frames.py

+67-24
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,110 @@
11
import argparse
22
import cv2
3+
import math
34
import socket
45
from turbojpeg import TurboJPEG
56
import time
7+
import threading
8+
import re
9+
10+
quitting = False
11+
got_connection = False
12+
13+
def publish_mc_group():
14+
global quitting
15+
global got_connection
16+
MCAST_GRP = '239.1.1.1'
17+
MCAST_PORT = 5000
18+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
19+
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
20+
while not quitting and not got_connection:
21+
try:
22+
sock.sendto(b'Hello World!', (MCAST_GRP, MCAST_PORT))
23+
time.sleep(1)
24+
except:
25+
break
26+
print("Multicast thread exiting")
627

728
def main(host="0.0.0.0", port=8888):
29+
global quitting
30+
global got_connection
831
header = b'\xaa\xbb\xcc\xdd'
932
jpeg = TurboJPEG()
33+
t = threading.Thread(target=publish_mc_group, args=[])
34+
t.start()
1035
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
1136
s.bind((host, port))
1237
print("server lisening on {}:{}".format(host,port))
1338
s.listen()
1439
conn, addr = s.accept()
1540
with conn:
1641
print(f"Connected by {addr}")
42+
got_connection = True
1743
total_bytes = 0
1844
total_frames = 0
1945
start = None
2046
while True:
2147
# try to get all chunks of the message
48+
# print("Trying to get new image")
2249
img_data = bytearray()
23-
max_recv_size = 40*1024
50+
max_recv_size = 1*1024
2451
remaining = max_recv_size
2552
got_header = False
2653
while True:
2754
recv_len = remaining if remaining < max_recv_size else max_recv_size
2855
# print("receiving", recv_len, "bytes")
29-
data = conn.recv(recv_len)
30-
if not data:
31-
print("breaking loop because (not data) == true")
32-
break
56+
try:
57+
data = conn.recv(recv_len)
58+
except KeyboardInterrupt:
59+
print("quitting...")
60+
quitting = True
61+
t.join()
62+
return
3363
if not start:
3464
start = time.time()
35-
if data[0:4] == header:
36-
length = (int(data[4]) << 24) + (int(data[5]) << 16) + (int(data[6]) << 8) + int(data[7])
37-
remaining = length - len(data) + 8
38-
img_data.extend(data[8:])
39-
got_header = True
40-
# print("Got header, image length:", length)
41-
# print("remaining:", remaining)
65+
# find the header in the bytearray
66+
if not got_header:
67+
try:
68+
index = data.index(header)
69+
if index >= 0:
70+
got_header = True
71+
length = \
72+
(int(data[index+4]) << 24) + \
73+
(int(data[index+5]) << 16) + \
74+
(int(data[index+6]) << 8) + \
75+
int(data[index+7])
76+
data_start = index + 8
77+
data_bytes = len(data) - data_start
78+
num_img_bytes = min(length, data_bytes)
79+
remaining = length - num_img_bytes
80+
data_end = data_start + num_img_bytes + 1
81+
img_data.extend(data[data_start:data_end])
82+
# print("Got index:", index)
83+
# print("Got data_start:", data_start)
84+
# print("Got data_end:", data_end)
85+
# print("Got header, image length:", length)
86+
# print("remaining:", remaining)
87+
except:
88+
pass
4289
elif got_header and remaining > 0:
43-
if remaining >= len(data):
44-
remaining = remaining - len(data)
45-
img_data.extend(data)
46-
else:
47-
print("got more data than we needed:", len(data), remaining)
48-
img_data.extend(data[:remaining])
49-
remaining = 0
50-
if got_header and remaining <= 0:
90+
num_img_bytes = min(remaining, len(data))
91+
img_data.extend(data[:num_img_bytes])
92+
remaining = remaining - num_img_bytes
93+
94+
if got_header and remaining == 0:
5195
break;
96+
5297
end = time.time()
5398
elapsed = end - start
5499
total_bytes += len(img_data)
55100
total_frames += 1
56-
print("got image data, len:", len(img_data))
101+
# print("got image data, len:", len(img_data))
57102
print("metrics: {:.0f} KB/s, {:.1f} FPS".format((total_bytes / 1024) / elapsed, total_frames / elapsed))
58103
try:
59104
img = jpeg.decode(img_data)
60105
cv2.imshow('Remote Camera', img)
61106
cv2.waitKey(1)
62-
except Exception as e:
63-
# print("Could not open jpeg image:")
64-
# print(e)
107+
except:
65108
pass
66109

67110
if __name__ == "__main__":

main/main.cpp

+34-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "oneshot_adc.hpp"
1515
#include "task.hpp"
1616
#include "tcp_socket.hpp"
17+
#include "udp_socket.hpp"
1718
#include "wifi_sta.hpp"
1819

1920
#include "esp_camera.h"
@@ -76,6 +77,36 @@ extern "C" void app_main(void) {
7677
logger.info("waiting for wifi connection...");
7778
std::this_thread::sleep_for(1s);
7879
}
80+
// use UDP multicast to listen for where the server is
81+
std::string multicast_group = "239.1.1.1";
82+
size_t port = 5000;
83+
espp::UdpSocket server_socket({.log_level=espp::Logger::Verbosity::WARN});
84+
auto server_task_config = espp::Task::Config{
85+
.name = "UdpServer",
86+
.callback = nullptr,
87+
.stack_size_bytes = 6 * 1024,
88+
};
89+
std::atomic<bool> found_receiver = false;
90+
std::string receiver_ip;
91+
auto server_config = espp::UdpSocket::ReceiveConfig{
92+
.port = port,
93+
.buffer_size = 1024,
94+
.is_multicast_endpoint = true,
95+
.multicast_group = multicast_group,
96+
.on_receive_callback = [&found_receiver, &receiver_ip](auto& data, auto& source) -> auto {
97+
fmt::print("Server received: {}\n"
98+
" from source: {}:{}\n",
99+
data, source.address, source.port);
100+
receiver_ip = source.address;
101+
found_receiver = true;
102+
return std::nullopt;
103+
}
104+
};
105+
server_socket.start_receiving(server_task_config, server_config);
106+
while (!found_receiver) {
107+
logger.info("waiting for receiver to multicast their info over 239.1.1.1...");
108+
std::this_thread::sleep_for(1s);
109+
}
79110
// initialize camera
80111
/**
81112
* @note display sizes supported:
@@ -120,7 +151,7 @@ extern "C" void app_main(void) {
120151
.ledc_channel = LEDC_CHANNEL_0,
121152

122153
.pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
123-
.frame_size = FRAMESIZE_UXGA,// QVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
154+
.frame_size = FRAMESIZE_QVGA,// QVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
124155

125156
.jpeg_quality = 15, //0-63, for OV series camera sensors, lower number means higher quality
126157
.fb_count = 2, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
@@ -269,16 +300,15 @@ extern "C" void app_main(void) {
269300
// make the tcp_client to transmit to the network
270301
std::atomic<int> num_frames_transmitted{0};
271302
std::atomic<float> transmission_elapsed{0};
272-
auto transmit_task_fn = [&transmit_queue, &num_frames_transmitted, &transmission_elapsed, &logger](auto& m, auto& cv) {
273-
static std::string ip_address = "192.168.1.23";
303+
auto transmit_task_fn = [&receiver_ip, &transmit_queue, &num_frames_transmitted, &transmission_elapsed, &logger](auto& m, auto& cv) {
274304
static size_t port = 8888;
275305
static Image image;
276306
static auto start = std::chrono::high_resolution_clock::now();
277307
static auto tcp_client = std::make_shared<espp::TcpSocket>(espp::TcpSocket::Config{});
278308
// wait on the queue until we have an image ready to transmit
279309
if (xQueueReceive(transmit_queue, &image, portMAX_DELAY) == pdPASS) {
280310
if (!tcp_client->is_connected()) {
281-
if (!tcp_client->connect({.ip_address = ip_address, .port = port})) {
311+
if (!tcp_client->connect({.ip_address = receiver_ip, .port = port})) {
282312
// destroy the socket and try again on the next go-around?
283313
tcp_client.reset();
284314
tcp_client = std::make_shared<espp::TcpSocket>(espp::TcpSocket::Config{});

0 commit comments

Comments
 (0)