乐鑫Esp32学习之旅29 安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,实现本地视频流监控。(附带源码)


  • 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。

1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。
2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。
3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。
4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。
5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。
6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。
7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。
8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。
9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。
10、乐鑫esp32 SDK编程利用rmt驱动ws2812七彩灯,实现彩虹渐变效果。
11、入门 乐鑫esp-adf 音频框架开发,esp32造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。
12、开源一个微信公众号airkiss配网esp32以及局域网发现功能的工程,分享一个airkiss配网小工具。
13、esp32 内置 dns 服务器,无需外网访问域名返回指定网页。
14、esp32 sdk编程实现门户强制认证,连接esp32热点之后,自动强制弹出指定的登录界面。
15、认识本地离线语音唤醒识别框架 esp-skainet ,实现较低成本的硬件语音本地识别控制。
16、学习本地语音唤醒离线识别框架 esp-skainet ,如何修改唤醒词? 如何自定义命令词?如何做意图动作?
17、全网首发,乐鑫esp32 sdk直连京东微联·小京鱼 · IoT开放平台,实现叮咚音响语音智能控制。
18、入门京东微联·小京鱼的控制面板H5开发,读懂vue语法,做自己的控制页面。
19、重磅开源,如何在微信小程序上ble蓝牙配网esp32,blufi的那些事!
20、一篇好文,开发过程中编译esp32固件太大,无法正常启动?教你如何自定义分区表partitions.csv。
21、 esp32蓝牙配网blufi的高度封装,集成简单、使用简单、容易上手,提高开发效率!
22、讨论下程序员 “青春饭” 那些事,分享在esp32实现多种加密算法md5 |AES CBC-ECB| Sha1 | Sha256 等!
23、安信可 esp32-a1s 音频开发板移植最新 esp-adf 音频框架,小试牛刀如何实现在线文字转语音播放。
25、分享在 esp32 SDK实现冷暖光色温平滑调节的封装,轻松集成到您的项目去。
26、分享下如何在window下使用CMake编译,编译速度提高传统 make 编译一个档次。
27、windows10平台下自带的Linux安装 ESP8266/ESP32 环境,再无需额外安装虚拟机了。
28、安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,实现MQTT远程拍照传输到私有服务器。
29、安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,实现本地视频流监控。

一、前言

    上篇给大家介绍带来了如何实现拍照到私有服务器,这篇给大家带来本地视频流监控实现原理和代码;

    本文章的源码免费获取:关注本人微信公众号 “徐宏blog” ,发送 210104 获取!

二、原理

在这里插入图片描述

  1. 中间路由器作为一个AP热点,开发板作为一个 station 接入路由器,并且开启一个 web服务器;
  2. 手机作为client访问其web服务器,返回一个图片视频流给client;

    客户端请求HTTP格式:

GET /stream HTTP/1.1
Host: 10.10.10.114

    服务器返回HTTP格式,这里是分块编码返回给客户端:

Content-Type: multipart/x-mixed-replace;boundary=123456789000000000000987654321
Transfer-Encoding: chunked

<pic date>

三、步骤

    1、设置本地路由器SSID和Password,模块连接路由器;

void app_wifi_initialise(void)
{
    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = "aiot@xuhongv",
            .password = "12345678",
        },
    };
    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}

    2、开启一个web服务端,资源访问路径是 /stream


  httpd_uri_t stream = {
    .uri = "/stream",
    .method = HTTP_GET,
    .handler = http_get_handler,
    .user_ctx = "stream!"
    };
    
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &stream);
    }
    else
    {
        ESP_LOGI(TAG, "Error starting server!");
    }
    ESP_LOGI(TAG, "Http End");

    3、数据流处理返回给客户端;

#define PART_BOUNDARY "123456789000000000000987654321"
static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

esp_err_t http_get_handler(httpd_req_t *req)
{

    //获取芯片可用内存
    printf("esp_get_free_heap_size : %d  \n", esp_get_free_heap_size());

    camera_fb_t *fb = NULL;
    esp_err_t res = ESP_OK;
    size_t _jpg_buf_len;
    uint8_t *_jpg_buf;
    char *part_buf[64];
    static int64_t last_frame = 0;
    if (!last_frame)
    {
        last_frame = esp_timer_get_time();
    }

    res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
    if (res != ESP_OK)
    {
        return res;
    }

    while (true)
    {
        fb = esp_camera_fb_get();
        if (!fb)
        {
            ESP_LOGE(TAG, "Camera capture failed");
            res = ESP_FAIL;
            break;
        }
        if (fb->format != PIXFORMAT_JPEG)
        {
            bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
            if (!jpeg_converted)
            {
                ESP_LOGE(TAG, "JPEG compression failed");
                esp_camera_fb_return(fb);
                res = ESP_FAIL;
            }
        }
        else
        {
            _jpg_buf_len = fb->len;
            _jpg_buf = fb->buf;
        }

        if (res == ESP_OK)
        {
            res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
        }
        if (res == ESP_OK)
        {
            size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);

            res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
        }
        if (res == ESP_OK)
        {
            res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
        }
        if (fb->format != PIXFORMAT_JPEG)
        {
            free(_jpg_buf);
        }
        esp_camera_fb_return(fb);
        if (res != ESP_OK)
        {
            break;
        }
        int64_t fr_end = esp_timer_get_time();
        int64_t frame_time = fr_end - last_frame;
        last_frame = fr_end;
        frame_time /= 1000;
        ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)",
                 (uint32_t)(_jpg_buf_len / 1024),
                 (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
    }

    last_frame = 0;
    return res;
}

    4、LOG日志打印,可看到在本地局域网的IP地址是:10.10.10.114,要访问地址为:10.10.10.114/stream:

I (479) gpio: GPIO[32]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (629) camera: Detected OV2640 camera
I (639) camera: Allocating 1 frame buffers (60 KB total)
I (639) camera: Allocating 60 KB frame buffer in OnBoard RAM
I (1479) wifi:new:<9,0>, old:<1,0>, ap:<255,255>, sta:<9,0>, prof:1
I (2469) wifi:state: init -> auth (b0)
I (2479) wifi:state: auth -> assoc (0)
I (2479) wifi:state: assoc -> run (10)
I (2769) wifi:connected with aiot@xuhongv, aid = 1, channel 9, BW20, bssid = d8:c8:e9:05:c4:d8
I (2769) wifi:security type: 4, phy: bgn, rssi: -37
I (2769) wifi:pm start, type: 1

I (2839) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (3699) event: sta ip: 10.10.10.114, mask: 255.255.255.0, gw: 10.10.10.1
I (3699) example:take_picture: Http Start
I (3699) example:take_picture: Starting server on port: '80'
I (3699) example:take_picture: Registering URI handlers
I (3709) example:take_picture: Http End

    5、访问效果:

在这里插入图片描述


    本文章的源码免费获取:关注本人微信公众号 “徐宏blog” ,发送 210104 获取!

另外,不要把我的博客作为学习标准,我的只是笔记,难有疏忽之处,如果有,请指出来,也欢迎留言哈!

  • 玩转esp8266带你飞、加群QQ群,不喜的朋友勿喷勿加:434878850
  • 个人邮箱:xuhongv@yeah.net 24小时在线,有发必回复!
  • esp8266源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp8266
  • esp32源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp32
  • 关注下面微信公众号二维码,干货多多,第一时间推送!

在这里插入图片描述

半颗心脏 CSDN认证博客专家 硬件架构 ESP专家
智能家居领域Android攻城狮,会DXP电路图制作,单片机,8266通讯,https://github.com/xuhongv
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页