【问题标题】:How to pipe Picamera video to FFMPEG with subprocess (Python)如何使用子进程(Python)将 Picamera 视频传输到 FFMPEG
【发布时间】:2017-12-26 18:37:33
【问题描述】:

我看到了大量关于将 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 和人们claiming to have done it,但似乎没有一个在我的 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 子进程并写入它的标准输入(初始化 picam 时,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')
(...)

在这方面的任何帮助都会很棒!

【问题讨论】:

    标签: python ffmpeg subprocess raspberry-pi3


    【解决方案1】:

    我已经能够使用以下内容将 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)
    

    或者这不是你想要的?

    【讨论】:

    • 这可能要容易得多 :) 尽管将 AVC 视频放入 MPEG-1 PS 容器中很奇怪 — 这是否可行?在任何情况下,OP 都作为 TS 流式传输到 UDP,因此该部分处于控制之下。
    • 似乎对我有用!输出可在 omxplayer 中播放。但我愿意接受改进建议!
    • 这正是我所需要的!我显然在滥用子进程的管道,但它帮助我意识到我试图在 FFMPEG 中使用的一些设置也给我带来了问题。谢谢!
    • @KevinVillela .mp4 文件(ISO BMFF)将是常见的。
    【解决方案2】:

    我第一眼看到的两个问题:

    1. 在您的第一个示例中,您将数据写入子进程的stdout,而不是其stdin。这绝对行不通,而且可能会导致挂起。

    2. 在这两个示例中,您都使用stdin=sp.PIPE, stderr=sp.PIPE 启动进程,然后从不读取这些管道。这意味着一旦 ffmpeg 写入足够的输出来填充管道缓冲区,它就会阻塞并且您将遇到死锁。使用默认的stdout=None, stderr=None 让ffmpeg 的输出进入您进程的stdout 和stderr,或者将它们连接到打开到/dev/null 的文件句柄以丢弃输出。或者每次写一些输入时使用communicate 方法获取输出,并用它做一些有用的事情(比如监控流的状态)。

    【讨论】:

      【解决方案3】:

      您好,您可以使用 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 方法 fwrite 和 fflush 调用中。

      这将调用

      testOutput.exe ORIGINAL_SOURCE RTMP_OUTPUT

      【讨论】:

        猜你喜欢
        • 2020-04-11
        • 2020-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-14
        • 2018-09-22
        • 2018-06-09
        • 1970-01-01
        相关资源
        最近更新 更多