ESP-32 Cam, FFMPEG, and Node Red

ESP-32 Cam, FFMPEG, and Node Red

This post will cover how to use FFMpeg to add video feeds from a ESP32-Cam module into Node Red. The process can utilize most RTSP and HTTP sources. As a comparison, these stream sources will be setup on MotionEye.

The first task is to list or itemize each of the sources and the hyperlink to their streams. If unsure what stream is available from the source, a search for “motioneye rtsp stream url” should turn up some tips on how to. Here is an example list.

rtsp://192.168.0.11:554/live/ch1
rtsp://192.168.0.12:554/live/ch1
rtsp://192.168.0.13:554/live/ch1
rtsp://192.168.0.14:554/live/ch1
http://192.168.0.21
http://192.168.0.22
http://192.168.0.23
http://192.168.0.24

The list above has two camera sources, the first group being RTSP streams, while the second being HTTP streams. The RTSP group are consumer security cameras that are built for this purpose. The second HTTP group are the ESP32-Cam modules with the streaming firmware loaded on them.

More about MotionEye can be found here, https://peyanski.com/motioneye-is-it-the-ultimate-nvr/. The setup of MotionEye can be found here, https://bkjaya.wordpress.com/2021/02/02/how-to-install-motioneye-cctv-software-on-ubuntu-20-10-groovy/

If you follow the setup link, it is helpful to know the exact formatting of your timezone. This can be done with this command.

cat /etc/timezone

It might also be worth mentioning that the installation is available for either ARM or i386 platforms. Choose the source that applies.

docker pull ccrisan/motioneye:master-amd64 = x86 Processors
docker pull ccrisan/motioneye:master-armhf = Raspberry Pi

Adding the above streams to MotionEye should be a simple process. When they have been entered in correctly, the video stream for each of the sources should appear in MotionEye. This is a good way to check that everything is working before continuing on with Node Red.

The setup and install of Node Red is not covered here, that has been demonstrated elsewhere. Here is a useful intro and demo if new to this topic. Just be sure to install FFMpeg on the Node Red host so it can support the video streams.

It should be disclaimed that Node Red is not intended for use in critical applications. I have had numerous occasions where it failed without any indicators. Node Red is a multipurpose tool that should be used with this in mind, do not rely solely on it for tending to critical systems.

With the limitations of Node Red out of the way, here is a demo of how to setup the RTSP flow. These are node types needed for the flow.

Inejct
Change
Exec
Template
Base64
UI_Text
UI_Template
UI_Group
UI_Tab

With all of these nodes installed, the process for setup should work fine using the method demonstrated in this video.

The purpose built security cameras should be ready to go, once the stream URL is known. The ESP32-Cam modules will need to be programmed with a firmware suited for this purpose.

This video demonstrates and provides links to code that can be used, https://github.com/bnbe-club/rtsp-video-streamer-diy-14

https://youtu.be/1xZ-0UGiUsY

During initial testing, it became clear that the HTTP stream performed much better than RTSP. For that reason only the HTTP stream is recommended as of this writing. Other firmware sources might offer more reliability for RTSP, but those will not be detailed here. Ultimately, this was the code base used.

/****************************************************************************************************************************************************
 *
 *  Sources
 *
 *  Original Author: Frenoy Osburn
 *  YouTube Video: https://youtu.be/1xZ-0UGiUsY
 *  BnBe Post: https://www.bitsnblobs.com/rtsp-video-streamer---esp32
 ****************************************************************************************************************************************************/
/
 
#include "src/OV2640.h"
#include <WiFi.h>
#include <WebServer.h>
#include <WiFiClient.h>

#include "src/SimStreamer.h"
#include "src/OV2640Streamer.h"
#include "src/CRtspSession.h"

#define ENABLE_WEBSERVER

#define CAMERA_MODEL_AI_THINKER

#include "camera_pins.h"
#include "wifikeys.h"

OV2640 cam;

#ifdef ENABLE_WEBSERVER
WebServer server(80);
#endif


#ifdef ENABLE_WEBSERVER
void handle_jpg_stream(void)
{
    WiFiClient client = server.client();
    String response = "HTTP/1.1 200 OK\r\n";
    response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
    server.sendContent(response);

    while (1)
    {
        cam.run();
        if (!client.connected())
            break;
        response = "--frame\r\n";
        response += "Content-Type: image/jpeg\r\n\r\n";
        server.sendContent(response);

        client.write((char *)cam.getfb(), cam.getSize());
        server.sendContent("\r\n");
        if (!client.connected())
            break;
    }
}

void handle_jpg(void)
{
    WiFiClient client = server.client();

    cam.run();
    if (!client.connected())
    {
        return;
    }
    String response = "HTTP/1.1 200 OK\r\n";
    response += "Content-disposition: inline; filename=capture.jpg\r\n";
    response += "Content-type: image/jpeg\r\n\r\n";
    server.sendContent(response);
    client.write((char *)cam.getfb(), cam.getSize());
}

void handleNotFound()
{
    String message = "Server is running!\n\n";
    message += "URI: ";
    message += server.uri();
    message += "\nMethod: ";
    message += (server.method() == HTTP_GET) ? "GET" : "POST";
    message += "\nArguments: ";
    message += server.args();
    message += "\n";
    server.send(200, "text/plain", message);
}
#endif



void setup()
{

    Serial.begin(115200);
    //while (!Serial);            //wait for serial connection. 

    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;
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 12; 
    config.fb_count = 2;       
  
    cam.init(config);
    
    IPAddress ip;

    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(F("."));
    }
    ip = WiFi.localIP();
    Serial.println(F("WiFi connected"));
    Serial.println("");
    Serial.println(ip);
    Serial.print("Stream Link: http://");
    Serial.println(ip);

#ifdef ENABLE_WEBSERVER
    server.on("/", HTTP_GET, handle_jpg_stream);
    server.on("/jpg", HTTP_GET, handle_jpg);
    server.onNotFound(handleNotFound);
    server.begin();
#endif

}

CStreamer *streamer;
CRtspSession *session;
WiFiClient client; // FIXME, support multiple clients

void loop()
{
#ifdef ENABLE_WEBSERVER
    server.handleClient();
#endif

}

It’s worth noting that there are more settings that can be customized for the ESP32-Cam module, as pointed out in this link, https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/. Many of the features of the ESP32-Cam module are covered in more detail here, https://github.com/espressif/esp32-camera

If for some reason the stream doesn’t work as expected, it is good to test using FFPlay on a different host. This will help isolate trouble spots. Here is an example command on how to view a stream directly using FFPlay.

ffplay -i "rtsp://192.168.0.11:8554/mjpeg/1"
ffplay -i "http://192.168.0.21"

Node Red is a useful tool, much like having an extra set of eyes. It does have limitations, but working within those ranges, it can be a rewarding tool.

Comments are closed.