2017-07-20 76 views
2

我看到大量有關將raspivid流直接傳遞給FFMPEG以進行編碼,複用和重新定向的信息,但這些用例大部分來自bash;類似於:如何使用子流程(Python)將Picamera視頻轉換爲FFMPEG

raspivid -n -w 480 -h 320 -b 300000 -fps 15 -t 0 -o - | ffmpeg -i - -f mpegts udp://192.168.1.2:8090ffmpeg

我希望能夠利用Picamera庫的功能,所以我可以使用OpenCV和類似的方法進行併發處理,同時仍然使用FFMPEG進行流式處理。但我不知道如何正確打開FFMPEG作爲子流程和管道視頻數據。我看到很多嘗試,unanswered posts和人claiming to have done it,但它似乎沒有在我的Pi上工作。

我應該用Picamera創建一個視頻緩衝區,並將原始視頻傳輸到FFMPEG?我可以使用camera.capture_continuous()並傳遞FFMPEG我用於OpenCV計算的bgr24圖像嗎?

我試過各種各樣的變化,我不知道如果我只是誤解如何使用子進程模塊,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()可以給出一個對象像這樣,當你初始化picam):

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') 
(...) 

任何這幫助將是驚人的!

回答

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) 

或者是不是你想要的?

+0

這可能是一個更容易:)雖然把AVC視頻的MPEG-1 PS容器是怪異叫 - 不,即使是工作?在任何情況下,OP都作爲TS流傳輸到UDP,以便部分受到控制。 – hobbs

+0

似乎爲我工作!輸出可以在omplayer中播放。但我願意提出改進建議! –

+0

這正是我需要的!我清楚地誤用了子流程的管道,但它幫助我意識到我在FFMPEG中嘗試使用的一些設置也給我帶來了問題。謝謝! – VeniVidiReliqui

0

兩個問題,我第一眼看到:

  1. 在你的第一個例子,你寫你的數據放入子的stdout,而不是它的stdin。這絕對不行,可能會導致掛起。

  2. 在這兩個示例中,您將開始使用stdin=sp.PIPE, stderr=sp.PIPE的過程,然後從不讀取這些管道。這意味着只要ffmpeg寫入足夠的輸出來填充管道緩衝區,就會阻塞,並且會產生死鎖。使用默認的stdout=None, stderr=None可以讓ffmpeg的輸出轉到您的進程的stdout和stderr,或者將它們連接到打開到/dev/null的文件句柄以丟棄輸出。或者每次寫入輸入時使用communicate方法獲取輸出,並對其執行一些有用的操作(如監視流的狀態)。

0

嗨,你可以使用OpenCV的&的ffmpeg &它restream到wowza否則後果不堪設想。

這裏是在write_jpeg moethod的fwrite和fflush呼叫的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; 
} 

的樣品。

這將

testOutput.exe ORIGINAL_SOURCE RTMP_OUTPUT