Video Streaming from ESP32-CAM

Hi everyone,

I have ESP32-CAM and want to streaming video from it to Blynk Iot, but every tutorials telling about OLD ver. of Blynk…

I trying to create DDNS server, but it not working too.

May u knows, how to fix it.

Tell us what you’ve done with port forwarding, DDNS service, DDNS Update client and what tests you’ve done using a browser outside your network and Blynk.
Are you using the Blynk app (in which case what OS and version) or the web dashboard?

What port are you using for your ESP32 Cam?


Hello. The simplest implementation I saw was:

  1. Make images with a camera (not video)
  2. When image is done - upload it to some server (AWS S3 for example)
  3. When upload is done, set the image widget with setProperty("url", "uploaded image path");

When it comes to video it’s much more complicated, because you need to forward the video stream somewhere: either to some 3-d party service like wowza or to your own server with ffmpeg or some 3-d party open-source server (like motion-project) that does what you need, after that you can generate a stream url and consume it from the blynk widget.


I use no-ip and setting my router
After it, I use this code:

/*Tech Trends Shameer
//Live Streaming*/

  This is a simple MJPEG streaming webserver implemented for AI-Thinker ESP32-CAM and
  ESP32-EYE modules.
  This is tested to work with VLC and Blynk video widget.

  Inspired by and based on this Instructable: $9 RTSP Video Streamer Using the ESP32-CAM Board

  Board: AI-Thinker ESP32-CAM

#include "src/OV2640.h"
#include <WiFi.h>
#include <WebServer.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

char auth[] = "UvjQcb_fAobpiFUQ26XC_3L7eAGFhhYX";
// Select camera model

#include "camera_pins.h"

const char* ssid = "***";
const char* password = "***";

OV2640 cam;

WebServer server(80);

const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
                      "Access-Control-Allow-Origin: *\r\n" \
                      "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n";
const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
const int hdrLen = strlen(HEADER);
const int bdrLen = strlen(BOUNDARY);
const int cntLen = strlen(CTNTTYPE);

void handle_jpg_stream(void)
  char buf[32];
  int s;

  WiFiClient client = server.client();

  client.write(HEADER, hdrLen);
  client.write(BOUNDARY, bdrLen);

  while (true)
    if (!client.connected()) break;;
    s = cam.getSize();
    client.write(CTNTTYPE, cntLen);
    sprintf( buf, "%d\r\n\r\n", s );
    client.write(buf, strlen(buf));
    client.write((char *)cam.getfb(), s);
    client.write(BOUNDARY, bdrLen);

const char JHEADER[] = "HTTP/1.1 200 OK\r\n" \
                       "Content-disposition: inline; filename=capture.jpg\r\n" \
                       "Content-type: image/jpeg\r\n\r\n";
const int jhdLen = strlen(JHEADER);

void handle_jpg(void)
  WiFiClient client = server.client();;
  if (!client.connected()) return;

  client.write(JHEADER, jhdLen);
  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);

void setup()

  //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;

  // Frame parameters
  //  config.frame_size = FRAMESIZE_UXGA;
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 12;
  config.fb_count = 2;

  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);


  IPAddress ip;

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  ip = WiFi.localIP();
  Serial.println(F("WiFi connected"));
  Serial.print("Stream Link: http://");
  server.on("/mjpeg/1", HTTP_GET, handle_jpg_stream);
  server.on("/jpg", HTTP_GET, handle_jpg);

void loop()

Of course, I replace “***” on my SSID and password :slight_smile:

THX for answer! Can u help me with Wowza? How to use Wowza and integrate it in Blynk?

That doesn’t answer my questions does it?
If you want my assistance you need to provide more info…


I am having project connect esp32-cam and display video on blynk app but when i run this code and get ip:http://192.168.x.x/mjpeg/1 and i enter url of Blynk IoT app but it inactive
Help me please !

The IP address of 192.168.x.x is internal to your home network.
It can’t be resolved by the Blynk app or server.

Your WiFi router has two IP addresses - its public address, known as the WAN and its internal address, known as the LAN address. It’s the LAN (Local Area Network) address that you’ve quoted above, but that is local to your network.

Blynk can only see your WAN address (Wide Area Network). This is a single public IP address that is assigned by your ISP, and is normally dynamic (it changes from time to time, usually when you reboot your router).
Data sent to this WAN IP address is routed to the correct internal device by your router (hence its name).
For the router to correctly route data to your ESP32 CAM module you need to tell your router that any traffic on port XXXX needs to be routed to internal IP address
This is done by setting-up port forwarding rules in your router. Exactly how you do that will depend on your router, and the access to these settings that your ISP allows.

You may think that the traffic is flowing outwards from your ESP32 CAM module to Blynk, so why do inbound requests on the video streaming port need to be routed to the ESP32 CAM. This is because the Blynk widget needs to initialise the stream, so it sends an inbound message saying “give me your video”. Without this the video stream won’t initialise.

Most people have a dynamic public IP address, and to ensure that the Blynk app knows what that is whenever it changes, you need to use a Dynamic Domain Name System (DDNS) service which gives you a static URL, but forwards inbound data on that URL to your current public (WAN) IP address. For the DDNS service to know your current public IP address you need to be running an updater service on a device within your private network. This could be your router, if it has a DDNS facility, or it could be a device running an updater client.
Common free DDNS services are and

So, (unless you have a static public IP addresss) you need a DDNS service and an updater.
You then need to have your ESP32 CAM set-up with a static internal IP address (so that your router’s DHCP system doesn’t allocate it a different IP when it reboots) and forward inbound and outbound data packets on whichever port you’re using for video streaming to/from your ESP32 CAM’s internal IP address.
You then use your DDNS URL and the video streaming port in the Blynk video streaming app.



We did a “streaming” project for one of our white label client, so now we have some expertise here. Here is my observations:

  1. You can directly stream from your camera, but this involves DDNS (or static ip), port forwarding, network setup, etc. So your streaming url could be accessible from the outside. This solution works for “makers”, home projects. But not suitable in business use cases. As end users will never be able to setup that
  2. So we come to point 2. Constant streaming is actually expensive. There are 2 main components: 1) traffic; 2) server (CPU/RAM) for streaming. Traffic is more expensive than server. In average 1 hour of streaming + 1 viewer costs ~1-3 $ / per hour on different services. Both traffic and server costs could be optimized with different approaches, but in both cases it requires some understanding of what is going on (some minimal expertise in video streaming). The typical optimization is - send the streaming only when something happens.
  3. The simplest solution is to use existing services (wowza, aws streaming). However, there is no “free” streaming services. At least I didn’t find any.