yuniversi
yuniversi

Reputation: 77

use ffmpeg command to push rtsp stream, it doesn't contain SPS and PPS frame

I use python and opencv-python to capture frames from video, then use ffmpeg command to push rtsp stream with pipe. I can play the rtsp stream via gstreamer and vlc. However, a display device cannot decode and play the rtsp-stream because it cannot receive SPS and PPS frames. Use wireshark to capture stream, found that it doesn't send sps and pps frames, only send IDR frames.

The key codes are as follows.

# ffmpeg command
command = ['ffmpeg',
           '-re',
           '-y',
           '-f', 'rawvideo',
           '-vcodec', 'rawvideo',
           '-pix_fmt', 'bgr24',
           '-s', "{}x{}".format(width, height),
           '-r', str(fps),
           '-i', '-',
           '-c:v', 'libx264',
           '-preset', 'ultrafast',
           '-f', 'rtsp',
           '-flags', 'local_headers', 
           '-rtsp_transport', 'tcp',
           '-muxdelay', '0.1', 
           rtsp_url]
 
p = sp.Popen(command, stdin=sp.PIPE)
 
 
while (cap.isOpened()):
    ret, frame = cap.read()
    if not ret:
        cap = cv2.VideoCapture(video_path)
        continue
    p.stdin.write(frame.tobytes()

May be I miss some options of ffmpeg command?

Upvotes: 5

Views: 4376

Answers (1)

Rotem
Rotem

Reputation: 32094

Try adding the arguments '-bsf:v', 'dump_extra'.

According to FFmpeg Bitstream Filters Documentation:

dump_extra
Add extradata to the beginning of the filtered packets except when said packets already exactly begin with the extradata that is intended to be added.

The filter supposed to add SPS and PPS NAL units with every key frame.


Here is a complete code sample:

import subprocess as sp
import cv2

rtsp_url = 'rtsp://localhost:31415/live.stream'

video_path = 'input.mp4'


# We have to start the server up first, before the sending client (when using TCP). See: https://trac.ffmpeg.org/wiki/StreamingGuide#Pointtopointstreaming
ffplay_process = sp.Popen(['ffplay', '-rtsp_flags', 'listen', rtsp_url])  # Use FFplay sub-process for receiving the RTSP video.


cap = cv2.VideoCapture(video_path)

width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # Get video frames width
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # Get video frames height
fps = int(cap.get(cv2.CAP_PROP_FPS))  # Get video framerate

    
# FFmpeg command
command = ['ffmpeg',
           '-re',
           '-y',
           '-f', 'rawvideo',
           '-vcodec', 'rawvideo',
           '-pix_fmt', 'bgr24',
           '-s', "{}x{}".format(width, height),
           '-r', str(fps),
           '-i', '-',
           '-c:v', 'libx264',
           '-preset', 'ultrafast',
           '-f', 'rtsp',
           #'-flags', 'local_headers', 
           '-rtsp_transport', 'tcp',
           '-muxdelay', '0.1',
           '-bsf:v', 'dump_extra',
           rtsp_url]

p = sp.Popen(command, stdin=sp.PIPE)

while (cap.isOpened()):
    ret, frame = cap.read()

    if not ret:
        break

    p.stdin.write(frame.tobytes())


p.stdin.close()  # Close stdin pipe
p.wait()  # Wait for FFmpeg sub-process to finish
ffplay_process.kill()  # Forcefully close FFplay sub-process

Notes:

  • '-flags', 'local_headers' are not valid arguments in my version of FFmpeg.
  • I don't know how to verify my solution, so I could be wrong...

Upvotes: 5

Related Questions