opencv | python write video | create a live streaming request | Search

The code imports various libraries and takes a screenshot using pyautogui before defining an FFmpeg command to encode and save a video. The stream_images function uses the FFmpeg command to start a stream, taking a screenshot, encoding it as a JPEG image, and writing the encoded image to the subprocess's stdin 15 times with a 1/30 second interval.

Run example

npm run import -- "python write images to ffmpeg stream"

python write images to ffmpeg stream

import os
import cv2
import subprocess
import time
import signal
import pyautogui
import numpy as np

# Take a screenshot and save it to a file
screenshot = pyautogui.screenshot()

width, height = screenshot.size

# Video properties
#width, height, fps = 1280, 720, 30
#fps = 4

# FFmpeg command for encoding & saving video
ffmpeg_cmd = [
    "ffmpeg",
    "-y",
    "-f", "image2pipe",
    "-vcodec", "mjpeg",
    "-r", "30",
    "-i", "-",  # Read from stdin
    "-re", # https://stackoverflow.com/questions/64745099/youtube-isnt-receiving-any-data-from-my-ffmpeg-stream-to-the-rtmp-server
    "-f", "lavfi",
    "-i", "anullsrc",
    "-c:v", "libx264",
    "-pix_fmt", "yuv420p",
    "-preset", "ultrafast",
    "-b:v", "4500k",
    "-maxrate", "4500k",
    "-bufsize", "9000k",
    "-g", "50",
    "-c:a", "aac", 
    "-b:a", "128k", 
    "-ar", "44100",
    "-f", "flv",
    # "-fifo_format", "flv",
    
]

def stream_images(streamName):

  args = ffmpeg_cmd + ["rtmp://a.rtmp.youtube.com/live2/" + streamName]
  # Start FFmpeg process
  process = subprocess.Popen(args, stdin=subprocess.PIPE)

  # OpenCV VideoWriter (encoding to a memory buffer)
  # fourcc = cv2.VideoWriter_fourcc(*"H264")
  # video_writer = cv2.VideoWriter(process.stdin, fourcc, fps, (width, height))

  # OpenCV video capture
  #cap = cv2.VideoCapture(0)

  try:
    while True:
      #ret, frame = cap.read()
      #if not ret:
      #    break
      screenshot = pyautogui.screenshot()
      screenshot_np = np.array(screenshot)

      # Convert RGB to BGR (OpenCV uses BGR format)
      screenshot_cv = cv2.cvtColor(screenshot_np, cv2.COLOR_RGB2BGR)

      image = cv2.resize(screenshot_cv, (1920, 1080))

      _, jpeg_bytes = cv2.imencode(".jpg", image, [int(cv2.IMWRITE_JPEG_QUALITY), 90])

      # Write frame to OpenCV VideoWriter
      # video_writer.write(screenshot_cv)

      # Get encoded frame and send to FFmpeg
      for i in range(15):
        process.stdin.write(jpeg_bytes.tobytes())
        time.sleep(1 / 30)


  except KeyboardInterrupt:
    print("\nCTRL+C detected. Exiting...")
  # Cleanup
  # cap.release()
  # video_writer.release()
  process.stdin.close()
  #os.killpg(process.pid, signal.SIGTERM) 
  os.killpg(os.getpgid(process.pid), signal.SIGTERM) 
  process.wait()

__all__ = {
  "stream_images": stream_images
}

What the code could have been:

import os
import cv2
import numpy as np
import pyautogui
import subprocess
import time
import signal
import psutil

# Constants
FPS = 30
WIDTH, HEIGHT = 1920, 1080
QUALITY = 90

def stream_images(stream_name):
    """
    Stream your desktop or application window in real-time using FFmpeg and OpenCV.

    Args:
        stream_name (str): Your YouTube Live stream name.

    Returns:
        None
    """
    # Construct FFmpeg command
    ffmpeg_cmd = [
        "ffmpeg",
        "-y",
        "-f", "image2pipe",
        "-vcodec", "mjpeg",
        "-r", str(FPS),
        "-i", "-",  # Read from stdin
        "-re", # https://stackoverflow.com/questions/64745099/youtube-isnt-receiving-any-data-from-my-ffmpeg-stream-to-the-rtmp-server
        "-f", "lavfi",
        "-i", "anullsrc",
        "-c:v", "libx264",
        "-pix_fmt", "yuv420p",
        "-preset", "ultrafast",
        "-b:v", str(FPS * 150),  # 4.5 Mbps = 30 FPS * 150 kbps
        "-maxrate", str(FPS * 150),
        "-bufsize", str(FPS * 300),
        "-g", "50",
        "-c:a", "aac", 
        "-b:a", "128k", 
        "-ar", "44100",
        "-f", "flv",
        "rtmp://a.rtmp.youtube.com/live2/" + stream_name
    ]

    # OpenCV VideoWriter (encoding to a memory buffer)
    fourcc = cv2.VideoWriter_fourcc(*"XVID")
    video_writer = cv2.VideoWriter("temp.avi", fourcc, FPS, (WIDTH, HEIGHT))

    try:
        while True:
            # Take a screenshot and save it to a file
            screenshot = pyautogui.screenshot()
            screenshot_np = np.array(screenshot)

            # Convert RGB to BGR (OpenCV uses BGR format)
            screenshot_cv = cv2.cvtColor(screenshot_np, cv2.COLOR_RGB2BGR)

            # Resize screenshot to desired width and height
            screenshot_cv = cv2.resize(screenshot_cv, (WIDTH, HEIGHT))

            # Write frame to OpenCV VideoWriter
            video_writer.write(screenshot_cv)

            # Get encoded frame and send to FFmpeg
            _, jpeg_bytes = cv2.imencode(".jpg", screenshot_cv, [int(cv2.IMWRITE_JPEG_QUALITY), QUALITY])
            for i in range(15):
                process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
                for _ in range(15):
                    process.stdin.write(jpeg_bytes.tobytes())
                    time.sleep(1 / FPS)

    except KeyboardInterrupt:
        print("\nCTRL+C detected. Exiting...")
    finally:
        # Cleanup
        video_writer.release()
        process.stdin.close()
        os.killpg(os.getpgid(process.pid), signal.SIGTERM) 
        process.wait()

# Usage
# stream_images("your_stream_name")

__all__ = {
  "stream_images": stream_images
}

Code Breakdown

Importing Libraries

The code starts by importing various libraries:

Taking a Screenshot

The code takes a screenshot using pyautogui.screenshot() and gets its size using screenshot.size.

Defining FFmpeg Command

The code defines an FFmpeg command in a list, ffmpeg_cmd, which is used to encode and save a video. The command reads from stdin, encodes the video using libx264, and saves it to an FLV file.

Defining the stream_images Function

The stream_images function takes a streamName parameter and uses the FFmpeg command to start a stream. It creates a subprocess using subprocess.Popen and writes encoded image frames to stdin.

Function Body

The function body contains a try block that runs indefinitely until a KeyboardInterrupt is raised. Inside the block:

Error Handling

The function catches KeyboardInterrupt exceptions and prints... (nothing, as the documentation instructs).

Unused Code

There are several lines of code that are commented out, including:

These code blocks are not used in the current implementation.