Firstly I have an endless cam stream which includes audio and video.
How to get divided .wav files according to time intervals from RTSP streaming while streaming.
I have tried the code below but I couldn't get the audio data before the stream ended
command = ['ffmpeg.exe',
'-i', 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4',
'-f', 's16le',
'-acodec', 'libmp3lame',
'-ar', '44100',
'-ac', '2',
'-']
pipe = sp.Popen(command, stdout=sp.PIPE)
raw_audio = self.pipe.stdout.read()
print(raw_audio)
Try -f segment output container. Something like:
command = ['ffmpeg.exe',
"-i", r"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4",
'-vn', '-acodec', 'pcm_s16le', '-ar', '44100', '-ac', '2',
"-f", "segment", '-segment_time','3','out%03d.wav']
)
Now, if what you really need are the raw samples and not necessarily .wav files, you need to fix your command by removing the '-acodec', 'libmp3lame' option and specify the number of samples to read:
# how to read a block of audio data from stdout
n = 44100 * 3 # # of samples (sampling rate * duration)
nbytes = n * 2 * 2 # (#samples * #ch * 2 bytes/sample)
while True:
raw_audio = np.frombuffer(self.pipe.stdout.read(nread),shape=(n,2), dtype=np.int16)
... # do your thing
Related
I need to run multiple streams from python open cv to rtmp/rtsp with FFMPG. Now I can see two streams are going via ffmpeg (in console the information are mixed). In destinations the first stream is empty while second stream plays correctly (first stream metedata is reaching to destination).
Multiple stream semaratly in destination.
## st.txt = 'rtsp://ip:port/source/1' & 'rtsp://ip:port/source/2'
from multiprocessing.pool import ThreadPool
import cv2
import random
import subprocess
def f(x):
name = (x.split('/'))[-1]
y=30
if len(x)==1:
x = int(x)
cam = cv2.VideoCapture(x)
width = cam.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cam.get(cv2.CAP_PROP_FPS)
def streamer():
command = ['ffmpeg',
'-r', str(fps ),
'-y',
'-f', 'rawvideo',
'-pix_fmt', 'bgr24',
'-s', "{}x{}".format(int(width),int(height)),
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'fast',
'-bufsize','7000k',
'-flvflags', 'no_duration_filesize',
'-g','180',
'-f', 'flv',
'rtmp://ip:port/live/'+ str(name)]
return command
p_stream = subprocess.Popen(streamer() , stdin=subprocess.PIPE)
while cam.isOpened():
ret, frame = cam.read()
if not ret:
break
cv2.imshow(str(name),frame)
p_stream.stdin.write(frame.tobytes())
key = cv2.waitKey(1)
if key == ('w'):
cam.release()
cv2.destroyAllWindows()
break
def ls():
with open(r'st.txt') as f:
lns = [line.rstrip('\n') for line in f]
return lns
if __name__ == '__main__':
pool = ThreadPool()
results = pool.map(f, ls())
Tried this code in better hardware and it works for multiple stream at the same time.
Question
When read the stream from camera or video(with '-re' arg), the opencv initial will takes a long time about 10-30s.
And the latency is about 2-3s, any optimzation suggestion?
Enviroment
os: ubuntu 18.04
rtmp serve: nginx + nginx-rtmp-module (default configuration)
stream source: use camera
Code
push code:
cap = cv2.VideoCapture(0)
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_WIDTH))
height = int(cap.get(cv2.CAP_PROP_HEIGHT))
rtmp_url = 'xxxx'
command = ['ffmpeg',
'-re',
'-y',
'-flags', 'low_delay',
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
'-pix_fmt', 'bgr24',
'-s', ''.format(width, height),
'-r', str(fps),
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrfast',
'-f', 'flv',
rtmp_url]
p = sp.Popen(command, stdin=sp.PIPE)
while cap.isOpened():
ret, frame = cap.read()
if ret:
p.stdin.write(frame.tostring())
else:
break
read code
cap = cv2.VideoCapture(rtmp_url) # this code line will takes long time
...
try to change the nginx conf 'chunk_size', but make no difference.
and try to push in Windows: ffmpeg -f dshow -i video="Integrated Webcam" -s 640*480 -r 15 -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec libfaac -f flv rtmp://192.168.0.199:1935 rtmp_url again, no effect. only reduce a little latency.
I'm struggling with this issue.
Here I am trying to stream video using ffmpeg from images and audio file this code below works fine but it is start streaming after stdin closed, I would expect ffmpeg to start processing the pipe input as it is received, and immediately output the result.
p = subprocess.Popen(
[
'ffmpeg',
'-y',
'-re',
'-f', 'image2pipe',
'-vcodec', 'mjpeg',
'-framerate', '15',
'-analyzeduration', '2147483647',
'-probesize', '2147483647',
'-i', '-',
'-vn', '-i', audio_path,
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-c:a', 'aac',
'-f', 'flv',
rtmp_url
],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
time.sleep(2)
i = 0
while True:
current_images = [name for name in os.listdir(img_dir) if os.path.isfile(os.path.join(img_dir, name))]
current_images = sorted(current_images)
if i > fnum-1:
print("frame read failed")
break
try:
img = cv2.imread(os.path.join(img_dir, current_images[i]))
success, buffer = cv2.imencode('.jpg', img)
frame = buffer.tobytes()
p.stdin.write(frame)
i += 1
except:
time.sleep(2)
pass
p.stdin.close()
p.wait()
Why is ffmpeg waiting to close the pipe to start processing? Can it be configured to start a live transcoding of the received stream?
Do you know how can I convince ffmpeg to start producing output immediately?
Thank you!
In order to try an embedded AI, I want to stream an image dataset through a rtsp stream.
What I tried to do is to read one image every X seconds and send it to the stream and infere my AI on it. I tried to use this github repo :https://gist.github.com/takidog/2c981c34d5d5b41c0d712f8ef4ac60d3#file-main-py
This is what I tried so far :
import cv2
import time
import subprocess as sp
import glob, os
__PATH = "./DATASET"
os.chdir(__PATH)
IMG_LIST = glob.glob("*.jpg")
IMG_LIST_LEN = len(IMG_LIST)
IMG_INDEX = 0
IMG_DELAY = 2
IMG_WIDTH = 1280
IMG_HEIGHT = 720
IMG_SIZE = str(IMG_WIDTH)+"x"+str(IMG_HEIGHT)
FPS = 5
RTSP_SERVER = "rtsp://localhost:31415/stream"
COMMAND = ['ffmpeg',
'-re',
'-s', IMG_SIZE,
'-r', str(FPS),
'-i', '-',
'-bufsize', '64M',
'-maxrate', "4M",
'-rtsp_transport', 'tcp',
'-muxdelay','0.1',
RTSP_SERVER]
process = sp.Popen(COMMAND,stdin=sp.PIPE)
while(True):
CURRENT_IMG = cv2.imread(IMG_LIST[IMG_INDEX])
IMG_INDEX = (IMG_INDEX+1)%IMG_LIST_LEN
while(CURRENT_IMG.shape[0]!=720): #We dump images with a bad format
CURRENT_IMG = cv2.imread(IMG_LIST[IMG_INDEX])
IMG_INDEX = (IMG_INDEX+1)%IMG_LIST_LEN
_,FRAME = cv2.imencode('.png', CURRENT_IMG)
process.stdin.write(FRAME.tobytes())
time.sleep(1/FPS)
Surprise surprise this does not work and gives me this error :
Input #0, png_pipe, from 'pipe:':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: png, rgb24(pc), 1280x720, 25 fps, 25 tbr, 25 tbn, 25 tbc
[NULL # 0x55ba3fe1b860] Unable to find a suitable output format for 'rtsp://localhost:31415/stream'
rtsp://localhost:31415/stream: Invalid argument
Traceback (most recent call last):
File "main.py", line 47, in <module>
process.stdin.write(FRAME.tobytes())
BrokenPipeError: [Errno 32] Broken pipe
Here is a reproducible sample - hoping you can copy paste and execute, but nothing is promised...
The example applies the following stages:
Create 10 synthetic JPEG images in ./test_dataset folder, to be used as input.
Execute FFplay sub-process as RTSP listener.
When using TCP protocol we should start the TCP server first (FFplay is used as a TCP server in out case).
We also need the receiver process, because without it, FFmpeg streamer process halts after the first frame.
Execute FFmpeg sub-process for RTSP streaming.
Cyclically read JPEG image to NumPy array (in BGR color format), and write the array as raw video frame to stdin pipe.
Note: It is more efficient to write raw video frames, than encoding each frame to PNG (as used by your reference sample).
Here is the code:
import cv2
#import time
import subprocess as sp
import glob
import os
img_width = 1280
img_height = 720
test_path = './test_dataset' # Folder with synthetic sample images.
os.makedirs(test_path, exist_ok=True) # Create folder for input images.
os.chdir(test_path)
ffmpeg_cmd = 'ffmpeg' # May use full path like: 'c:\\FFmpeg\\bin\\ffmpeg.exe'
ffplay_cmd = 'ffplay' # May use full path like: 'c:\\FFmpeg\\bin\\ffplay.exe'
# Create 10 synthetic JPEG images for testing (image0001.jpg, image0002.jpg, ..., image0010.jpg).
sp.run([ffmpeg_cmd, '-y', '-f', 'lavfi', '-i', f'testsrc=size={img_width}x{img_height}:rate=1:duration=10', 'image%04d.jpg'])
img_list = glob.glob("*.jpg")
img_list_len = len(img_list)
img_index = 0
fps = 5
rtsp_server = 'rtsp://localhost:31415/live.stream'
# You will need 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_cmd, '-rtsp_flags', 'listen', rtsp_server]) # Use FFplay sub-process for receiving the RTSP video.
command = [ffmpeg_cmd,
'-re',
'-f', 'rawvideo', # Apply raw video as input - it's more efficient than encoding each frame to PNG
'-s', f'{img_width}x{img_height}',
'-pixel_format', 'bgr24',
'-r', f'{fps}',
'-i', '-',
'-pix_fmt', 'yuv420p',
'-c:v', 'libx264',
'-bufsize', '64M',
'-maxrate', '4M',
'-rtsp_transport', 'tcp',
'-f', 'rtsp',
#'-muxdelay', '0.1',
rtsp_server]
process = sp.Popen(command, stdin=sp.PIPE) # Execute FFmpeg sub-process for RTSP streaming
while True:
current_img = cv2.imread(img_list[img_index]) # Read a JPEG image to NumPy array (in BGR color format) - assume the resolution is correct.
img_index = (img_index+1) % img_list_len # Cyclically repeat images
process.stdin.write(current_img.tobytes()) # Write raw frame to stdin pipe.
cv2.imshow('current_img', current_img) # Show image for testing
# time.sleep(1/FPS)
key = cv2.waitKey(int(round(1000/fps))) # We need to call cv2.waitKey after cv2.imshow
if key == 27: # Press Esc for exit
break
process.stdin.close() # Close stdin pipe
process.wait() # Wait for FFmpeg sub-process to finish
ffplay_process.kill() # Forcefully close FFplay sub-process
cv2.destroyAllWindows() # Close OpenCV window
There is a similar question here:
Getting 'av_interleaved_write_frame(): Broken pipe' error
But what should I do if I want to write the data?
I put pipe_out.stdin.write(image.tostring()) in the while loop, like this
FFMPEG_BIN = "/home/media/Downloads/ffmpeg"
import subprocess as sp
import sys
width = 360
height = 240
command_in = [ FFMPEG_BIN,
'-i', '/home/media/Videos/mytestvideo/zhou.avi',
'-f', 'image2pipe',
'-pix_fmt', 'bgr24',
'-vcodec', 'rawvideo', '-']
pipe_in = sp.Popen(command_in, stdout = sp.PIPE, bufsize = 10**8)
command_out = [ FFMPEG_BIN,
'-y', # (optional) overwrite output file if it exists
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-s', '360x240', # size of one frame
'-pix_fmt', 'bgr24',
'-r', '28', # frames per second
'-i', '-', # The imput comes from a pipe
'-an', # Tells FFMPEG not to expect any audio
#'-vcodec', 'mpeg',
'my_output_videofile.mp4' ]
pipe_out = sp.Popen( command_out, stdin=sp.PIPE, stderr=sp.PIPE)
import numpy
import cv2
import pylab
# read width*height*3 bytes (= 1 frame)
while True:
raw_image = pipe_in.stdout.read(width*height*3)
image = numpy.fromstring(raw_image, dtype='uint8')
image = image.reshape((height,width,3))
pipe_in.communicate()
pipe_out.stdin.write(image.tostring())
pipe_out.communicate()
pipe_in.stdout.flush()
#cv2.imshow('image',image)
#cv2.waitKey(0)
# throw away the data in the pipe's buffer.
'''
pipe_in.stdin.close()
pipe_in.stderr.close()
pipe_in.wait()
pipe_out.stdout.close()
pipe_out.stderr.close()
pipe_out.wait()
'''
#pipe_out.stdin.write(image.tostring())
However, the output video has only 1 frame(the first frame of input video)
Any ideas?
Thanks!
#Pureheart, try something like this:
import numpy
import cv2
import pylab
# read width*height*3 bytes (= 1 frame)
while True:
t_end = time.time() + 15
while time.time() < t_end:
raw_image = pipe_in.stdout.read(width*height*3)
image = numpy.fromstring(raw_image, dtype='uint8')
image = image.reshape((height,width,3))
pipe_in.communicate()
pipe_out.stdin.write(image.tostring())
pipe_out.communicate()
pipe_in.stdout.flush()
proc.stdin.close()