娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

讓你的ESP32試試串流吧

發布者:和諧相伴最新更新時間:2025-06-06 來源: jianshu關鍵字:ESP32  串流 手機看文章 掃描二維碼
隨時隨地手機看文章

header-preview


既然有了屏幕,又有了網絡,那豈不是可以串流了!

上篇文章已經展示了如何使用ESP32點亮一塊屏幕,那么這次我們來整個活。利用ESP32來顯示電腦的畫面!如果你用的是其他屏幕也沒關系,只要是ESP32配合TFT_eSPI就可以實現,只是在幀率上會有所區別。
這個鏈接為你展示了在M5StickC上的運行效果
現在是除了游戲性以外,一無所有的原神@bilibili


實現方式

電腦作為發送端,負責發送圖像數據->EPS32作為接收端,負責接收并繪制圖像數據
發送端使用python編寫,使用mss模塊捕獲屏幕畫面,再使用python opencv 編碼為JPG

接收端使用C++編寫,TJpg_Decoder解碼,TFT_eSPI繪制

發送的數據以幀為單位,為了減小幀的體積,將會對每一幀原始位圖數據使用JPG編碼,ESP32接收到數據以后,先對JPG進行解碼,再進行繪制。

一次完整通信流程為:


workflow


精簡的代碼

#ifndef COMMON_MACRO_H_#define COMMON_MACRO_H_// Debug情況下,暫時不去測試串流,也無需連接wifi#define DEBUG/*

======================串流相關======================

*/// 幀數據接收完畢#define FRAMEOK  0x01// 頭部接收完畢,接收幀#define HEADEROK  0x02// 準備完畢,接收頭部 注:0x03無法正常發送// https://www.cnblogs.com/young525/p/5873795.html#define PREPAREOK  0x41/*

======================屏幕相關======================

*/#define SCREEN_WIDTH 240#define SCREEN_HEIGHT 135/*

======================WiFi相關======================

*/#define ssid 'CloseWrt_2.5G'// WiFi 密碼#define password 'have5seeds'#endif 

#ifndef STREAMINGCOMPONENT_H_#define STREAMINGCOMPONENT_H_#define IDLES 0#define RUNNING 1#define EXITING 2#include #include #include #include 'utils.h'#include 'common_macro.h'class StreamingComponent {public:

    StreamingComponent(WiFiClient &clt, TFT_eSPI &tft);



    uint8_t status = IDLES;


    void enter();

    void exit();


    void loop();



    bool drawCallBack(int16_t x, int16_t y, uint16_t w, uint16_t h,

                      uint16_t *bitmap);//    ~StreamingComponent() {//        Serial.printf('~StreamingComponentn');//        free(wifiBuffer);//        free(headerBuffer);//        free(frameSizeBuffer);//    };private:

    // WiFiClient指針

    WiFiClient *client;

    // TFT_eSPI指針

    TFT_eSPI *Tft;

    // 幀率相關

    double fps_avg = 0.0;

    uint32_t sec{}, psec{};

    uint16_t fps = 0, frame_count = 0;

    // 幀率相關


    // 執行時間相關

    // 函數執行時間

    uint32_t cost{};

    // 一次loop執行時間

    uint32_t loopCost{};


    // 緩沖部分


    // 幀數據大小

    uint16_t size{};

    // 已經下載幀數據大小

    uint16_t bSize{};

    // DMA緩沖相關

    // 2020.12.04若出現發送端發送超過wifiFrameSize大小(32kb),

    // 則會導致出錯,而此處無法分配更大內存。

    // 暫時未找到正確開啟SPIRAM方法

    // 2020.12.04將圖片壓縮方式從LZO改為jpg

    const int wifiFrameSize = 1024 * 32;

    // 頭數據大小

    const int headerFrameSize = 10;

    // 待下載的jpg圖片緩沖

    uint8_t *wifiBuffer =

            (uint8_t *) heap_caps_malloc(wifiFrameSize, MALLOC_CAP_8BIT);

    // 頭數據緩沖

    uint8_t *headerBuffer =

            (uint8_t *) heap_caps_malloc(headerFrameSize, MALLOC_CAP_8BIT);

    // 幀數據大小緩沖,用于解析字符串為int

    uint8_t *frameSizeBuffer =

            (uint8_t *) heap_caps_malloc(headerFrameSize - 1, MALLOC_CAP_8BIT);

    // DMA 雙緩沖模式

    uint16_t dmaBuffer1[16 * 16]{};  // Toggle buffer for 16*16 MCU block, 512bytes

    uint16_t dmaBuffer2[16 * 16]{};  // Toggle buffer for 16*16 MCU block, 512bytes

    uint16_t *dmaBufferPtr = dmaBuffer1;

    // 當前使用的DMA緩沖

    bool dmaBufferSel = 0;

    /**

     * 顯示回調,用于Tjpeg

     * 2020-12-06

     */


    /**

     * 接收數據

     * 2020-12-01

     * size: 5222 bytes

     * cost: 16 ms

     */

    void onReceiveData();};#endif

#include 'StreamingComponent.h'StreamingComponent::StreamingComponent(WiFiClient &clt, TFT_eSPI &tft) {

  this->client = &clt;

  this->Tft = &tft;

  Serial.println('StreamingComponent Constuctor');};void StreamingComponent::enter() { status = RUNNING; };void StreamingComponent::exit() { status = EXITING; };void StreamingComponent::loop() {

  if (status == RUNNING) {

    Serial.println('StreamingComponent loop');

    loopCost = millis();

    onReceiveData();

    Serial.printf('fps_avg:%f,loop cost:%d msn', fps_avg, millis() - loopCost);

    Tft->drawString(String(fps_avg), 0, 0, 2);

  } else if (status == EXITING) {

    // 啥也不做

  }};bool StreamingComponent::drawCallBack(int16_t x, int16_t y, uint16_t w,

                                      uint16_t h, uint16_t *bitmap) {

  if (status == RUNNING) {

    if (y >= SCREEN_HEIGHT) return 0;

    if (dmaBufferSel) {

      dmaBufferPtr = dmaBuffer2;

    } else {

      dmaBufferPtr = dmaBuffer1;

    }

    dmaBufferSel = !dmaBufferSel;

    Tft->pushImageDMA(x, y, w, h, bitmap, dmaBufferPtr);

  }

  return true;}//    ~StreamingComponent() {//        Serial.printf('~StreamingComponentn');//        free(wifiBuffer);//        free(headerBuffer);//        free(frameSizeBuffer);//    };void StreamingComponent::onReceiveData() {

  Serial.println('StreamingComponent onReceiveData');

  StreamingComponent::client->write(PREPAREOK);

  Serial.println('StreamingComponent client.write(PREPAREOK);');

  cost = millis();

  if (headerBuffer == nullptr) {

    Serial.printf('headerBuffer is null.n');

  } else {

    client->readBytes(headerBuffer, headerFrameSize);

    Serial.printf('receive header cost:%d msn', millis() - cost);

  }


  int sum = checkSum((const char *)headerBuffer, 8);

  // Serial.printf('headerBuffer checkSum: %dn', sum);

  if ((sum & 0xf) == c2i(headerBuffer[9]) &&

      (sum >> 4) == c2i(headerBuffer[8])) {

    // 有效頭數據,準備接收幀數據

    strncpy((char *)frameSizeBuffer, (char *)headerBuffer, 8);

    frameSizeBuffer[9] = '

主站蜘蛛池模板: 密山市| 营口市| 宾阳县| 沅陵县| 屏东市| 杨浦区| 嫩江县| 饶河县| 阳原县| 霍林郭勒市| 慈溪市| 梨树县| 柯坪县| 日喀则市| 巴马| 砚山县| 淮安市| 广宗县| 登封市| 桃江县| 贵阳市| 黎城县| 日土县| 南昌县| 拉孜县| 芜湖市| 三台县| 柳河县| 蓬莱市| 望谟县| 丰原市| 邳州市| 积石山| 滦南县| 九龙城区| 简阳市| 竹山县| 东辽县| 都江堰市| 安吉县| 昭觉县|