ESP32CAM:实现远程视频传输的服务端方案

esp32cam 服务端远程视频方案

说明

  • 本方案为esp32cam 服务端 浏览器 三端联合使用。将服务端部署在公网即可远程使用,没有远程需求,可以直接在局域网使用。代码无需修改。

  • 本文取缔了esp32cam自身运行http服务的相关逻辑,使得esp32cam只负责不停拍照片发给服务端,从而减少esp32cam的压力,提升了其视频流畅度。

  • 本文代码开源地址:https://gitcode.net/qq_26700087/simpleVideoServer,该项目的比当前文章更新,更加流畅,并且支持合宙ESP32S3。如果使用本文的代码,则使用本文提供链接中的发行版文件。使用该项目最新代码,请使用该项目的页面提供的下载服务端链接下载服务端发行版

  • #mermaid-svg-p6MVOztAqoknqrsn {font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-p6MVOztAqoknqrsn .error-icon{fill:#552222;}#mermaid-svg-p6MVOztAqoknqrsn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p6MVOztAqoknqrsn .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-p6MVOztAqoknqrsn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p6MVOztAqoknqrsn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p6MVOztAqoknqrsn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p6MVOztAqoknqrsn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p6MVOztAqoknqrsn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p6MVOztAqoknqrsn .marker.cross{stroke:#333333;}#mermaid-svg-p6MVOztAqoknqrsn svg{font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p6MVOztAqoknqrsn .label{font-family:”trebuchet ms”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-p6MVOztAqoknqrsn .cluster-label text{fill:#333;}#mermaid-svg-p6MVOztAqoknqrsn .cluster-label span{color:#333;}#mermaid-svg-p6MVOztAqoknqrsn .label text,#mermaid-svg-p6MVOztAqoknqrsn span{fill:#333;color:#333;}#mermaid-svg-p6MVOztAqoknqrsn .node rect,#mermaid-svg-p6MVOztAqoknqrsn .node circle,#mermaid-svg-p6MVOztAqoknqrsn .node ellipse,#mermaid-svg-p6MVOztAqoknqrsn .node polygon,#mermaid-svg-p6MVOztAqoknqrsn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p6MVOztAqoknqrsn .node .label{text-align:center;}#mermaid-svg-p6MVOztAqoknqrsn .node.clickable{cursor:pointer;}#mermaid-svg-p6MVOztAqoknqrsn .arrowheadPath{fill:#333333;}#mermaid-svg-p6MVOztAqoknqrsn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p6MVOztAqoknqrsn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p6MVOztAqoknqrsn .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-p6MVOztAqoknqrsn .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-p6MVOztAqoknqrsn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-p6MVOztAqoknqrsn .cluster text{fill:#333;}#mermaid-svg-p6MVOztAqoknqrsn .cluster span{color:#333;}#mermaid-svg-p6MVOztAqoknqrsn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-p6MVOztAqoknqrsn :root{–mermaid-font-family:”trebuchet ms”,verdana,arial,sans-serif;}

    mjpeg视频流

    esp32cam

    SimpleVideoServer

    浏览器

    一种较为流畅的esp32cam远程视频方案

    您需要准备

    物料 说明
    esp32cam开发板 esp32cam,本人是使用其自带摄像头,代码仅仅在其自带摄像头下测试
    一台装有windows或者linux操作系统的计算机 用于运行服务端
    USB转TTL模块/或底座 用于烧录/串口监控
    杜邦线若干 用于io0接地/使用底座烧录不需要

    鄙人无MacOs系统PC,未对MacOS进行测试。但服务端由java编写,自行下载macOS版本的jdk17,运行服务端,理论上不会有问题

    也可以将java源代码重新编译,甚至移植到安卓App上。

    本文以windows系统为例,使用安信可家esp32cam模块 和安信可家USB转TTL CP2012模块

    烧录esp32cam

    本文以ardunio 框架开发,你可以选择 ardunio ide 编译和烧录

    或者platformio(基于vs code/clion)新建项目选择 以ardunio framework新建。

    ardunio ide 准备esp32cam环境

    1.追加附加板地址

    打开菜单 -> 文件 -> 首选项

    ardunio IDE 首选项

    附加开发板管理器网址中追加一行

    https://dl.espressif.com/dl/package_esp32_index.json
    

    最近出现了下面这个地址,这个是esp32 的2.0环境,一些新特性需要,可能不需要上方的那个json。

    https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
    

    2.安装esp32环境

    在 工具 -> 开发板 -> 开发板管理器里 搜索 esp32 点击进行安装。

    此处可能需要把dns修改为腾讯或者阿里公共dns,才容易成功。

    ardunio ide安装esp32

    3.新建项目

    选择esp32cam

    ardunio ide选择esp32cam

    platformio 准备esp32cam环境

    新建项目找到 AI Thinker ESP32-CAM,则选择了开发板。框架选择 ardunio。若没有初始化esp32环境,会自动下载,同样建议修改dns。

    新建后生成的ini文件如下(串口相关设置需要手动添加)

    [env:esp32cam]
    platform = espressif32
    board = esp32cam
    framework = arduino
    upload_speed= 115200
    upload_port = COM3
    monitor_speed= 115200
    monitor_port = COM3
    

    代码

    存在多个文件和服务端相关代码,点此跳转

    引导方面本文更加详细。可先参考本文。

    esp32cam代码共两个文件。也可以合成一个。

    文件 ai_thinker_esp32_cam_meta.h

    表示esp32cam相关GPIO引脚定义

    #ifndef AI_THINKER_32_CAM_META
    #define AI_THINKER_32_CAM_META
    #define PWDN_GPIO_NUM 32
    #define RESET_GPIO_NUM -1
    #define XCLK_GPIO_NUM 0
    #define SIOD_GPIO_NUM 26
    #define SIOC_GPIO_NUM 27
    
    #define Y9_GPIO_NUM 35
    #define Y8_GPIO_NUM 34
    #define Y7_GPIO_NUM 39
    #define Y6_GPIO_NUM 36
    #define Y5_GPIO_NUM 21
    #define Y4_GPIO_NUM 19
    #define Y3_GPIO_NUM 18
    #define Y2_GPIO_NUM 5
    #define VSYNC_GPIO_NUM 25
    #define HREF_GPIO_NUM 23
    #define PCLK_GPIO_NUM 22
    #endif
    
    文件main.cpp

    ardunio ide里无需考虑该文件名,而只是把该文件内容复制到你的.ino 文件中。

    请按照具体情况修改wifi的ssid和密码,一般可以使用你pc运行服务端,可以将PC的ip填写到以下源文件host变量的值中。

    #include <Arduino.h>
    #include <WiFi.h>
    #include "esp_camera.h"
    #define CAMERA_MODEL_AI_THINKER
    #include "ai_thinker_esp32_cam_meta.h"
    
    char* ssid = "test0";
    const char* passwd = "12345687";
    const char* host = "192.168.137.1";
    
    WiFiClient streamSender;
    
    void connectWifi(const char* ssid, const char* passphrase) {
        WiFi.mode(WIFI_STA);
        WiFi.begin(ssid, passphrase);
    
        Serial.println("connecting to router... ");
        //等待wifi连接成功
        while (WiFi.status() != WL_CONNECTED) {
            Serial.print(".");
            delay(500);
        }
        Serial.print("\nWiFi connected, local IP address:");
        Serial.println(WiFi.localIP());
    }
    
    void setup() {
        Serial.begin(115200);
        Serial.setDebugOutput(true);
        while (!Serial) {
            /* code */
        }
        camera_config_t config;
        config.ledc_channel = LEDC_CHANNEL_0;
        config.ledc_timer = LEDC_TIMER_0;
        config.pin_d0 = Y2_GPIO_NUM;
        config.pin_d1 = Y3_GPIO_NUM;
        config.pin_d2 = Y4_GPIO_NUM;
        config.pin_d3 = Y5_GPIO_NUM;
        config.pin_d4 = Y6_GPIO_NUM;
        config.pin_d5 = Y7_GPIO_NUM;
        config.pin_d6 = Y8_GPIO_NUM;
        config.pin_d7 = Y9_GPIO_NUM;
        config.pin_xclk = XCLK_GPIO_NUM;
        config.pin_pclk = PCLK_GPIO_NUM;
        config.pin_vsync = VSYNC_GPIO_NUM;
        config.pin_href = HREF_GPIO_NUM;
        config.pin_sscb_sda = SIOD_GPIO_NUM;
        config.pin_sscb_scl = SIOC_GPIO_NUM;
        config.pin_pwdn = PWDN_GPIO_NUM;
        config.pin_reset = RESET_GPIO_NUM;
        config.xclk_freq_hz = 20000000;
        config.pixel_format = PIXFORMAT_JPEG;
    
        // if PSRAM IC present, init with UXGA resolution and higher JPEG quality
        //                      for larger pre-allocated frame buffer.
        if (psramFound()) {
            config.frame_size = FRAMESIZE_UXGA;
            config.jpeg_quality = 10;
            config.fb_count = 2;
        } else {
            config.frame_size = FRAMESIZE_SVGA;
            config.jpeg_quality = 12;
            config.fb_count = 1;
        }
    
        // camera init
        esp_err_t err = esp_camera_init(&config);
        if (err != ESP_OK) {
            Serial.printf("Camera init failed with error 0x%x", err);
            return;
        }
        Serial.println("get sensor ");
        sensor_t* s = esp_camera_sensor_get();
        // drop down frame size for higher initial frame rate
        s->set_framesize(s, FRAMESIZE_VGA);
    
        connectWifi(ssid, passwd);
        Serial.println("connect stream channel");
        if (!streamSender.connect(host, 8004)) {
            Serial.println("connect stream channel failed");
        }
        streamSender.setNoDelay(true);
        // 发送mac地址作为设备序列号,用于摄像头频道号
        uint8_t mac[6];
        WiFi.macAddress(mac);
        char macStr[12] = {0};
        sprintf(macStr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3],
                mac[4], mac[5]);
        Serial.println("sprint mac ");
        streamSender.print(String(macStr));
        streamSender.flush();
    }
    
    void loop() {
        camera_fb_t* fb = NULL;
        size_t len = 0;
        Serial.println("do loop");
        uint8_t end[5] = {'j', 'p', 'e', 'g', '\n'};
        while (true) {
            fb = esp_camera_fb_get();
            if (!fb) {
                Serial.println("Camera capture failed");
                return;
            }
            len = fb->len;
            streamSender.write(fb->buf, len);
            streamSender.write(end, 5);
            streamSender.flush();
            esp_camera_fb_return(fb);
        }
    }
    

    查看PC的ip的办法

    win + R 输入cmdenter打开控制台

    输入 ipconfig 回车,找到有ipv4地址的那一行。如果有多个适配器,大概率需要自己确定和无线路由器所在同一网络的ip。

    一般wifi连接则是网卡名带 无线适配器,而路由器网线直连则自己抉择。

    烧录

    使用USB转TTL模块时接线

    USB转TTL模块引脚 esp32cam引脚
    5V 5V
    GND GND
    TXD U0R
    RXD U0T

    当上载控制台出现连接某某com口,请尽快连接IO0和GND,再按RST。

    使用烧录底座将esp32cam安上底座,则无需考虑针脚连接具体如何。

    串口监控

    鄙人不知烧录底座是否可以串口监控,使用USB转TTL模块时,在烧录完成后,断开IO0与GND的连接,再按RST重启esp32cam则可以查看串口打印,但此时没有运行服务端SimpleVideoServer。

    下载和运行服务端

    根据自己需要选择以下任意方式运行服务端

  • 下载 windows服务端发行版

    解压之后,进入对应目录点击run.bat文件启动服务器。

  • 下载linux服务端发行版

    需要unzip或p7zip等可以解压zip的应用

    运行

    unzip SimpleVideoSever_linux.zip
    cd linux_release/
    sh run.sh
    
  • 发行版内部仅仅是一些java17 版本的class文件和jre以及启动脚本,你可以使用任意其它的jre17运行这些class文件。并非需要发行版。

  • 访问视频服务

    新增了频道功能,也就是说每个摄像头处在不同频道,访问不同的摄像头需要不同的地址。

    如鄙人执行的服务端日志打印含有esp32Cam接入的后的相关打印如下:

    D:\Users\immor\idea\SimpleVideoServer\out\win_release>.\jre\bin\java -classpath SimpleVideoServer org.btik.server.video.Main
    bio video server started
    bio Device Channel started
    new channel:
    http://127.0.0.1:8003/video/441793EE3C08
    http://192.168.0.116:8003/video/441793EE3C08
    http://192.168.137.1:8003/video/441793EE3C08
    start /192.168.137.234:53051
    
    
    

    每接入一个esp32Cam会新建一个频道,在new channel:的打印后会出现,相关可以访问视频流的地址。

    你可以在本机,或者局域网的其他设备访问。

    部署在云服务器的同学把端口打开后,把内网ip替换成公网ip,或者域名即可。

  • 关于如何把视频节目嵌入其它网页

    如果你擅长web开发,或者不喜欢在多个窗口查看多个摄像头可以参考以下方法增加自己的内容。

    本视频http请求是允许跨域的,若希望在自己的网页里面加入本服务端提供esp32Cam视频窗口,

    其实不用html的 iframe标签,img标签即可。

    比如以下html代码,新建一个文件比如a.html 复制以下内容,根据实际情况,替换img标签src属性的内容。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>直播间</title>
        <style>
            .videoContainer{
                display: inline-block;
            }
        </style>
    </head>
    <body>
    <div style="padding: 0;margin:30px auto; width: 1300px">
        <div class="videoContainer">
            <img src="http://127.0.0.1:8003/video/441793EE3C08">
        </div>
        <div class="videoContainer">
            <img src="http://127.0.0.1:8003/video/58BF2581F024">
        </div>
    </div>
    
    </body>
    </html>
    

    以上是我的两个esp32Cam的视野,效果如下:

    esp32Cam双摄像头

    也就是说,你可以把该项目植入任何其它可以用到web前端的项目。通过查询在线设备,可以动态打开每个摄像头的视频。

  • 配置项

    一般无需修改,但提供改法。后续此处可能会有变化,可以跟随该开源项目SimpleVideoSever具体描述。

    light-video.properties
    

    里面含有三个配置

    http.port=8003
    http.clients.limit=10
    stream.port=8004
    

    http.port 为http的端口。

    http.clients.limit 摄像头在线接入限制数。本意是想限制客户端参与的数量故名为http.clients.limit,实际的实现是限制了摄像头的数量

    stream.port esp32cam像服务端发送照片的端口,在本项目中使用该默认端口,如果需要修改,一并修改esp32cam代码中连接的端口。一般无需修改。

    使用远程服务器如云服务器同学则无需额外指导。注意本服务端毫不安全,没有任何安全机制,在公网使用不宜长久暴露服务端口。

    物联沃分享整理
    物联沃-IOTWORD物联网 » ESP32CAM:实现远程视频传输的服务端方案

    发表评论