阅读量:1
很久没写文章了,最近闲来无事,又研究了下关于esp32_cam,之前也写过一篇文章,讲述了如何把视频流推送至公网服务器,但是用到的代码很不统一,esp32_cam端用的是c语言,服务器用的是nodejs,拉流客户端用的是python,看上去很别扭,更重要的是,虽然能推流至服务器,但传输效果也不是很理想,于是把三端的代码统一成了python,话不多说,直接上代码。
这里重点说明一下,使用的esp32_cam固件是国外一个老哥开发的,里面带有camera库,使用其他的固件是没有camera库的,需要的小伙伴可以留言或者私信我留下邮箱,我会发给你们。
首先是esp32_cam端代码(使用thonny开发并烧录):
import socket import network import camera import time # 连接wifi wlan = network.WLAN(network.STA_IF) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') wlan.connect('xxxx', 'xxxx') # 此处替换成自己的wifi和密码 while not wlan.isconnected(): pass print('网络配置:', wlan.ifconfig()) # 摄像头初始化 try: camera.init(0, format=camera.JPEG) time.sleep(0.5) except Exception as e: camera.deinit() # camera.init(0, format=camera.JPEG) # 其他设置: # 上翻下翻 camera.flip(1) #左/右 camera.mirror(1) # 分辨率 camera.framesize(camera.FRAME_CIF) # 选项如下: # FRAME_96X96 FRAME_QQVGA FRAME_QCIF FRAME_HQVGA FRAME_240X240 # FRAME_QVGA FRAME_CIF FRAME_HVGA FRAME_VGA FRAME_SVGA # FRAME_XGA FRAME_HD FRAME_SXGA FRAME_UXGA FRAME_FHD # FRAME_P_HD FRAME_P_3MP FRAME_QXGA FRAME_QHD FRAME_WQXGA # FRAME_P_FHD FRAME_QSXGA # 有关详细信息,请查看此链接:https://bit.ly/2YOzizz # 特效 camera.speffect(camera.EFFECT_NONE) #选项如下: # 效果\无(默认)效果\负效果\ BW效果\红色效果\绿色效果\蓝色效果\复古效果 # EFFECT_NONE (default) EFFECT_NEG \EFFECT_BW\ EFFECT_RED\ EFFECT_GREEN\ EFFECT_BLUE\ EFFECT_RETRO # 白平衡 # camera.whitebalance(camera.WB_HOME) #选项如下: # WB_NONE (default) WB_SUNNY WB_CLOUDY WB_OFFICE WB_HOME # 饱和 camera.saturation(0) #-2,2(默认为0). -2灰度 # -2,2 (default 0). -2 grayscale # 亮度 camera.brightness(0) #-2,2(默认为0). 2亮度 # -2,2 (default 0). 2 brightness # 对比度 camera.contrast(0) #-2,2(默认为0).2高对比度 #-2,2 (default 0). 2 highcontrast # 质量 camera.quality(30) #10-63数字越小质量越高 # socket TCP 的创建 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('xxx.xxx.xxx.xxx', xxxx)) # 此处替换成自己的公网ip和端口号(端口号是自定义) time.sleep(0.5) try: while True: buf = camera.capture() # 获取图像数据 len_buf = len(buf) # 发送数据长度 s.sendall(str.encode('%-10s' % len_buf)) s.sendall(buf) # 向服务器发送图像数据 # time.sleep_ms(50) except Exception as e: print(e) camera.deinit() finally: camera.deinit()
需要替换的有两处,wifi名称和密码以及公网ip和端口号。
然后上服务器端代码:
import socket import time import cv2 import io from PIL import Image import numpy as np import threading cc = [] # 用来保存已经连接到服务器的摄像头socket cp = [] # 用来保存已经连接到服务器的电脑socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("0.0.0.0", xxxx)) # 此处填写自定义的端口号,注意,要跟esp32_cam中定义的端口号一致 s.listen(10) def receive_all(sock, count): buf = b'' while count: recv_data_temp = sock.recv(count) if not recv_data_temp: return None buf += recv_data_temp count -= len(recv_data_temp) return buf def client_camera(clientsocket): global cp global cc print('摄像头已连接') while True: # 注释调两个if语句后,视频流会相对流畅一些 if clientsocket.recv(1024) == b'': print('摄像头已退出') cc = [] break if len(cp) == 0: time.sleep(1) continue try: length = receive_all(clientsocket, 10) data = receive_all(clientsocket, int(length)) # 目前只设计为一个摄像头和一台pc len_buf = len(data) # 发送数据长度 cp[0].sendall(str.encode('%-10s' % len_buf)) cp[0].sendall(data) except Exception as e: pass # print(f'摄像头以退出:{e}') def client_pc(clientsocket): global cp while True: time.sleep(1) if clientsocket.recv(1024) == b'': print('客户端已退出') cp = [] break while True: # 接收所有连接,每受到一个客户端,就启动一个新线程 clientsocket, addr = s.accept() try: if clientsocket.recv(1024).decode('utf-8') == 'client': # 说明该消息是客户端pc发送的拉流请求 cp.append(clientsocket) print(cp) threading.Thread(target=client_pc, args=(clientsocket,)).start() else: # 说明是摄像头推流的请求 cc.append(clientsocket) threading.Thread(target=client_camera, args=(clientsocket,)).start() print(cc) except Exception as e: clientsocket.close()
此处要修改的是端口号,要跟esp32_cam中自定义的端口号一致,其它的看注释即可
最后上拉流的客户端代码:
import socket import cv2 import io from PIL import Image import numpy as np import time def receive_all(sock, count): buf = b'' while count: recv_data_temp = sock.recv(count) if not recv_data_temp: return None buf += recv_data_temp count -= len(recv_data_temp) return buf s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.connect(('xxx.xxx.xxx.xxx', xxx)) # 此处设置公网的ip地址以及自定义的端口号 s.send('client'.encode('utf-8')) time.sleep(0.5) while True: try: length = receive_all(s, 10) data = receive_all(s, int(length)) bytes_stream = io.BytesIO(data) image = Image.open(bytes_stream) img = np.asarray(image) img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # ESP32采集的是RGB格式,要转换为BGR(opencv的格式) cv2.imshow("ESP32 Capture Image", img) if cv2.waitKey(1) == ord("q"): break except Exception as e: print(e)
此处要修改的是公网ip和自定义的端口号
目前代码如果都跑在局网内,效果还是很不错的,我使用的公网带宽是3m,效果嘛你们自己部署后自己看吧,有时间我还会再优化这一套流程,如果有好想法的小伙伴也可以给我留言一起讨论。