如何使用子进程将 Picamera 视频传输到 FFMPEG (Python)
How to pipe Picamera video to FFMPEG with subprocess (Python)
我看到大量关于将 raspivid 流直接传输到 FFMPEG 以进行编码、混合和重新流式传输的信息,但这些用例主要来自 bash;类似于:
raspivid -n -w 480 -h 320 -b 300000 -fps 15 -t 0 -o - | ffmpeg -i - -f mpegts udp://192.168.1.2:8090ffmpeg
我希望利用 Picamera 库的功能,这样我就可以在使用 FFMPEG 进行流式处理的同时使用 OpenCV 和类似的方法进行并发处理。但我无法弄清楚如何正确打开 FFMPEG 作为子进程并将视频数据传输给它。我已经看到了很多尝试,unanswered posts, and people claiming to have done it,但其中 none 似乎在我的 Pi 上有效。
我是否应该使用 Picamera 创建视频缓冲区并将原始视频通过管道传输到 FFMPEG?我可以使用 camera.capture_continuous() 并将我用于 OpenCV 计算的 bgr24 图像传递给 FFMPEG 吗?
我已经尝试了各种变体,我不确定我是否只是误解了如何使用子进程模块 FFMPEG,或者我只是缺少一些设置。我知道原始流不会有任何元数据,但我不完全确定我需要为 FFMPEG 提供哪些设置才能理解我提供的内容。
我有一个 Wowza 服务器,我最终会流式传输到它,但我目前正在通过流式传输到笔记本电脑上的 VLC 服务器进行测试。我目前试过这个:
import subprocess as sp
import picamera
import picamera.array
import numpy as np
npimage = np.empty(
(480, 640, 3),
dtype=np.uint8)
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 24
camera.start_recording('/dev/null', format='h264')
command = [
'ffmpeg',
'-y',
'-f', 'rawvideo',
'-video_size', '640x480',
'-pix_fmt', 'bgr24',
'-framerate', '24',
'-an',
'-i', '-',
'-f', 'mpegts', 'udp://192.168.1.54:1234']
pipe = sp.Popen(command, stdin=sp.PIPE,
stdout=sp.PIPE, stderr=sp.PIPE, bufsize=10**8)
if pipe.returncode != 0:
output, error = pipe.communicate()
print('Pipe failed: %d %s %s' % (pipe.returncode, output, error))
raise sp.CalledProcessError(pipe.returncode, command)
while True:
camera.wait_recording(0)
for i, image in enumerate(
camera.capture_continuous(
npimage,
format='bgr24',
use_video_port=True)):
pipe.stdout.write(npimage.tostring())
camera.stop_recording()
我也试过将流写入一个类似文件的对象,它只创建 FFMPEG 子进程并写入它的标准输入(camera.start_recording() 可以在初始化时被赋予这样的对象皮卡姆):
class PipeClass():
"""Start pipes and load ffmpeg."""
def __init__(self):
"""Create FFMPEG subprocess."""
self.size = 0
command = [
'ffmpeg',
'-f', 'rawvideo',
'-s', '640x480',
'-r', '24',
'-i', '-',
'-an',
'-f', 'mpegts', 'udp://192.168.1.54:1234']
self.pipe = sp.Popen(command, stdin=sp.PIPE,
stdout=sp.PIPE, stderr=sp.PIPE)
if self.pipe.returncode != 0:
raise sp.CalledProcessError(self.pipe.returncode, command)
def write(self, s):
"""Write to the pipe."""
self.pipe.stdin.write(s)
def flush(self):
"""Flush pipe."""
print("Flushed")
usage:
(...)
with picamera.PiCamera() as camera:
p = PipeClass()
camera.start_recording(p, format='h264')
(...)
任何对此的帮助都会很棒!
我已经能够使用类似以下内容将 PiCamera 输出流式传输到 ffmpeg:
import picamera
import subprocess
# start the ffmpeg process with a pipe for stdin
# I'm just copying to a file, but you could stream to somewhere else
ffmpeg = subprocess.Popen([
'ffmpeg', '-i', '-',
'-vcodec', 'copy',
'-an', '/home/pi/test.mpg',
], stdin=subprocess.PIPE)
# initialize the camera
camera = picamera.PiCamera(resolution=(800, 480), framerate=25)
# start recording to ffmpeg's stdin
camera.start_recording(ffmpeg.stdin, format='h264', bitrate=2000000)
或者这不是您要找的东西?
第一眼看到的两个问题:
在您的第一个示例中,您将数据写入子进程的 stdout
而不是其 stdin
。那绝对行不通,可能会导致挂起。
在这两个示例中,您都是从 stdin=sp.PIPE, stderr=sp.PIPE
开始进程,然后从不从这些管道中读取数据。这意味着一旦 ffmpeg 写入足够的输出来填充管道缓冲区,它就会阻塞并且您将遇到死锁。使用默认值 stdout=None, stderr=None
让 ffmpeg 的输出转到您进程的标准输出和标准错误,或者将它们连接到打开到 /dev/null
的文件句柄以丢弃输出。或者使用 communicate
方法在每次写入一些输入时获取输出,并用它做一些有用的事情(比如监视流的状态)。
您好,您可以使用 opencv 和 ffmpeg 并将其重新流式传输到 wowza 或其他。
这是一个带有 opencv && ffmpeg 的示例
int main(int argc, char* argv[])
{
if (argc < 4){
cout << "eksik parametre" << endl;
return -1;
}
int fps = 1; //fps degeri varsayilan 1
char *input_adress = argv[1]; //goruntunun alinacagi dosya yada adres bilgisi
char *output_adress = argv[3]; //ciktinin gonderilecegi dosya yada adres bilgisi
sscanf(argv[2], "%d", &fps); //fps degeri okundu
VideoCapture video(input_adress); //kamera acildi
if (!video.isOpened()){
cout << "Yayin acilamadi!!!" << endl;
getchar();
return -1;
}
Mat frame;//frame ornegi
FILE *pipe;//pipe icin acilan process in input streami
char *cmd = (char*)calloc(100 + sizeof(output_adress), sizeof(char));//komut icin alan alindi
sprintf(cmd, "ffmpeg -y -f image2pipe -vcodec mjpeg -r %d -i - -r %d -vcodec libx264 -f flv %s", fps, fps, output_adress);//ffmpeg komutu
//ffmpeg komutu aciliyor
if (!(pipe = _popen(cmd, "wb"))){
cout << "Acilamadi!!!" << endl;
return 1;
}
float wait_time = 1000.0f / (float)fps;//kac milisaniye bekletilecek
while (true)
{
try {
//videodan siradaki frame okunuyor
video >> frame;
if (frame.empty()) //eger bos frame ise video bitmis demektir
break;
adjust_brightness(frame); //parlaklik ayarlamasini yapıyoruz
write_jpeg(frame, pipe); //jpeg formatina cevirip pipe a yazıyoruz
if (waitKey(wait_time) >= 0) break;
}
catch (Exception ex){
}
}
video.release();
return 0;
}
在write_jpeg moethod fwrite 和 fflush 调用。
这将调用
testOutput.exe ORIGINAL_SOURCE RTMP_OUTPUT
我看到大量关于将 raspivid 流直接传输到 FFMPEG 以进行编码、混合和重新流式传输的信息,但这些用例主要来自 bash;类似于:
raspivid -n -w 480 -h 320 -b 300000 -fps 15 -t 0 -o - | ffmpeg -i - -f mpegts udp://192.168.1.2:8090ffmpeg
我希望利用 Picamera 库的功能,这样我就可以在使用 FFMPEG 进行流式处理的同时使用 OpenCV 和类似的方法进行并发处理。但我无法弄清楚如何正确打开 FFMPEG 作为子进程并将视频数据传输给它。我已经看到了很多尝试,unanswered posts, and people claiming to have done it,但其中 none 似乎在我的 Pi 上有效。
我是否应该使用 Picamera 创建视频缓冲区并将原始视频通过管道传输到 FFMPEG?我可以使用 camera.capture_continuous() 并将我用于 OpenCV 计算的 bgr24 图像传递给 FFMPEG 吗?
我已经尝试了各种变体,我不确定我是否只是误解了如何使用子进程模块 FFMPEG,或者我只是缺少一些设置。我知道原始流不会有任何元数据,但我不完全确定我需要为 FFMPEG 提供哪些设置才能理解我提供的内容。
我有一个 Wowza 服务器,我最终会流式传输到它,但我目前正在通过流式传输到笔记本电脑上的 VLC 服务器进行测试。我目前试过这个:
import subprocess as sp
import picamera
import picamera.array
import numpy as np
npimage = np.empty(
(480, 640, 3),
dtype=np.uint8)
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
camera.framerate = 24
camera.start_recording('/dev/null', format='h264')
command = [
'ffmpeg',
'-y',
'-f', 'rawvideo',
'-video_size', '640x480',
'-pix_fmt', 'bgr24',
'-framerate', '24',
'-an',
'-i', '-',
'-f', 'mpegts', 'udp://192.168.1.54:1234']
pipe = sp.Popen(command, stdin=sp.PIPE,
stdout=sp.PIPE, stderr=sp.PIPE, bufsize=10**8)
if pipe.returncode != 0:
output, error = pipe.communicate()
print('Pipe failed: %d %s %s' % (pipe.returncode, output, error))
raise sp.CalledProcessError(pipe.returncode, command)
while True:
camera.wait_recording(0)
for i, image in enumerate(
camera.capture_continuous(
npimage,
format='bgr24',
use_video_port=True)):
pipe.stdout.write(npimage.tostring())
camera.stop_recording()
我也试过将流写入一个类似文件的对象,它只创建 FFMPEG 子进程并写入它的标准输入(camera.start_recording() 可以在初始化时被赋予这样的对象皮卡姆):
class PipeClass():
"""Start pipes and load ffmpeg."""
def __init__(self):
"""Create FFMPEG subprocess."""
self.size = 0
command = [
'ffmpeg',
'-f', 'rawvideo',
'-s', '640x480',
'-r', '24',
'-i', '-',
'-an',
'-f', 'mpegts', 'udp://192.168.1.54:1234']
self.pipe = sp.Popen(command, stdin=sp.PIPE,
stdout=sp.PIPE, stderr=sp.PIPE)
if self.pipe.returncode != 0:
raise sp.CalledProcessError(self.pipe.returncode, command)
def write(self, s):
"""Write to the pipe."""
self.pipe.stdin.write(s)
def flush(self):
"""Flush pipe."""
print("Flushed")
usage:
(...)
with picamera.PiCamera() as camera:
p = PipeClass()
camera.start_recording(p, format='h264')
(...)
任何对此的帮助都会很棒!
我已经能够使用类似以下内容将 PiCamera 输出流式传输到 ffmpeg:
import picamera
import subprocess
# start the ffmpeg process with a pipe for stdin
# I'm just copying to a file, but you could stream to somewhere else
ffmpeg = subprocess.Popen([
'ffmpeg', '-i', '-',
'-vcodec', 'copy',
'-an', '/home/pi/test.mpg',
], stdin=subprocess.PIPE)
# initialize the camera
camera = picamera.PiCamera(resolution=(800, 480), framerate=25)
# start recording to ffmpeg's stdin
camera.start_recording(ffmpeg.stdin, format='h264', bitrate=2000000)
或者这不是您要找的东西?
第一眼看到的两个问题:
在您的第一个示例中,您将数据写入子进程的
stdout
而不是其stdin
。那绝对行不通,可能会导致挂起。在这两个示例中,您都是从
stdin=sp.PIPE, stderr=sp.PIPE
开始进程,然后从不从这些管道中读取数据。这意味着一旦 ffmpeg 写入足够的输出来填充管道缓冲区,它就会阻塞并且您将遇到死锁。使用默认值stdout=None, stderr=None
让 ffmpeg 的输出转到您进程的标准输出和标准错误,或者将它们连接到打开到/dev/null
的文件句柄以丢弃输出。或者使用communicate
方法在每次写入一些输入时获取输出,并用它做一些有用的事情(比如监视流的状态)。
您好,您可以使用 opencv 和 ffmpeg 并将其重新流式传输到 wowza 或其他。
这是一个带有 opencv && ffmpeg 的示例
int main(int argc, char* argv[])
{
if (argc < 4){
cout << "eksik parametre" << endl;
return -1;
}
int fps = 1; //fps degeri varsayilan 1
char *input_adress = argv[1]; //goruntunun alinacagi dosya yada adres bilgisi
char *output_adress = argv[3]; //ciktinin gonderilecegi dosya yada adres bilgisi
sscanf(argv[2], "%d", &fps); //fps degeri okundu
VideoCapture video(input_adress); //kamera acildi
if (!video.isOpened()){
cout << "Yayin acilamadi!!!" << endl;
getchar();
return -1;
}
Mat frame;//frame ornegi
FILE *pipe;//pipe icin acilan process in input streami
char *cmd = (char*)calloc(100 + sizeof(output_adress), sizeof(char));//komut icin alan alindi
sprintf(cmd, "ffmpeg -y -f image2pipe -vcodec mjpeg -r %d -i - -r %d -vcodec libx264 -f flv %s", fps, fps, output_adress);//ffmpeg komutu
//ffmpeg komutu aciliyor
if (!(pipe = _popen(cmd, "wb"))){
cout << "Acilamadi!!!" << endl;
return 1;
}
float wait_time = 1000.0f / (float)fps;//kac milisaniye bekletilecek
while (true)
{
try {
//videodan siradaki frame okunuyor
video >> frame;
if (frame.empty()) //eger bos frame ise video bitmis demektir
break;
adjust_brightness(frame); //parlaklik ayarlamasini yapıyoruz
write_jpeg(frame, pipe); //jpeg formatina cevirip pipe a yazıyoruz
if (waitKey(wait_time) >= 0) break;
}
catch (Exception ex){
}
}
video.release();
return 0;
}
在write_jpeg moethod fwrite 和 fflush 调用。
这将调用
testOutput.exe ORIGINAL_SOURCE RTMP_OUTPUT