簡介
本文將嘗試指出在PHP Web腳本中使用FFmpeg時需要了解的所有重要事項。它還將顯示一些使用示例,以使事情更清楚。這個想法也可以應用到其他web腳本語言。
從PHP腳本調用命令行工具
選擇一個模型
網頁設計為快速執行,以便瀏覽您的網站的人不必等待太多的響應。因為如果他們無聊的等待,他們只會導航到另一個,更靈敏(通常是您的競爭對手的)網站。話雖如此,你可以做的最糟糕的事情是從你的web腳本運行一個命令行工具(如ffmpeg),並等待它完成處理,以便能夠將結果返回到等待在線用戶(異常這是小/快的工具,其執行時間太小而不能注意到)。
您想要做的是將命令行工具的長處理與Web腳本執行分開,以使腳本更具響應性。所以,你有至少兩個選項:
- 運行命令行工具(使用了shell_exec()在后台為例),並繼續與web腳本執行,定期刷新狀態頁面,顯示當前進度
- 創建一個新的“作業”(通常是數據庫表中的一個新行),並有一些背景“工作”進程(cron作業,shell腳本,批處理腳本),它將監視“作業列表”需要處理的新工作
這兩種方法看起來是一樣的,但是他們不是。最重要的區別是,第二種方法可以更好地擴展更高的網站流量,因為它允許“工作”進程在完全獨立的機器上運行。此外,可以有幾個“工作”機器,一起工作,分裂工作負載,涉及小的同步。
第一種方法通常是大多數想要快速完成工作的人的首選,但是在他們的網站變得流行的時間,他們的web服務器變得不那么敏感,由於不斷的cpu飢餓,由多個命令行工具(ffmpeg實例)在后台運行。在那一刻,他們通常開始考慮第二種方法。
在后台運行的ffmpeg
以下示例適用於基於Linux的操作系統。對於基於Windows的操作系統,請繼續閱讀本文,而是你讀了之后,也看到了如何使用START和CMD后台運行的過程。這將給你一個想法如何應用相同的邏輯到基於Windows的操作系統。
讓我們考慮從PHP腳本運行ffmpeg的自然方式:
<?php echo "Starting ffmpeg...\n\n"; echo shell_exec("ffmpeg -i input.avi output.avi &"); echo "Done.\n"; ?>
這里有幾個問題需要指出。第一個是,雖然我們指定我們希望在后台執行ffmpeg(使用和號運算符“&”),PHP腳本將不會繼續執行,直到ffmpeg完成其執行。這是由於這樣的事實,在該票據中提及了PHP的exec()函數,上面寫着:
如果程序使用此函數啟動,為了使其在后台繼續運行,程序的輸出必須重定向到文件或另一個輸出流。如果不這樣做,將導致PHP掛起,直到程序的執行結束。
不要混淆的例子顯示了shell_exec()調用,而不是EXEC() 。所有的PHP的程序執行功能共享相同的代碼庫和限制。
所以,要解決這個問題,我們需要做這樣的事情:
<?php echo "Starting ffmpeg...\n\n"; echo shell_exec("ffmpeg -i input.avi output.avi >/dev/null 2>/dev/null &"); echo "Done.\n"; ?>
該說的部分“ >的/ dev / null的 ”將FFmpeg的實例的標准輸出(stdout)重定向到/ dev / null的(有效地忽略輸出)和“ 2>的/ dev / null的 ”將重定向標准錯誤(錯誤)到/ dev / null(有效地忽略任何錯誤日志消息)。這兩個可以組合成一個較短的表示:“ >的/ dev / null的2>&1 ”。如果你喜歡,你可以閱讀更多關於I / O重定向。
這里應該提到一個重要的注意事項。ffmpeg命令行工具使用stderr輸出錯誤日志消息,並且保留stdout用於可能使用的管道(將從ffmpeg生成的輸出媒體流重定向到其他命令行工具)。也就是說,如果你在后台運行你的ffmpeg,你很可能想把stderr重定向到一個日志文件,以便能夠檢查它。
需要注意的另一件事是標准INPUT(stdin)。命令行ffmpeg工具被設計為一個交互式實用程序,它接受用戶的輸入(通常來自鍵盤),並在用戶的當前屏幕/終端上報告錯誤日志。當我們在后台運行ffmpeg時,我們想要告訴ffmpeg沒有從stdin接受任何輸入(也不等待)。我們可以告訴這ffmpeg的,再次使用I / O重定向“ <的/ dev / null的 ”,這樣最后一個例子,安全運行ffmpeg的命令行工具在后台將與此類似:
<?php echo "Starting ffmpeg...\n\n"; echo shell_exec("ffmpeg -y -i input.avi output.avi </dev/null >/dev/null 2>/var/log/ffmpeg.log &"); echo "Done.\n"; ?>
在“-y”選項用於自動覆蓋輸出文件(output.avi)不要求是/否確認。如果需要相反的情況,如果輸出文件已經存在,要自動取消整個過程,請改用“-n”選項。
包裝庫
一些PHP庫允許將ffmpeg調用包裝到PHP對象中,並且如果您不喜歡使用命令行,可以為您提供一個不錯的語法。其中之一是在積極維護PHP-FFmpeg的。它只需要你下載一個最新的ffmpeg和ffprobe從安裝PHP組件構建分開。然后你可以這樣運行PHP代碼:
$ffmpeg = FFMpeg\FFMpeg::create(); $video = $ffmpeg->open('video.mpg'); $video->filters() ->resize(new FFMpeg\Coordinate\Dimension(320, 240)) ->synchronize(); $video->save(new FFMpeg\Format\Video\X264(), 'export-x264.mp4')
當然,你需要在后台運行這樣的任務。圖書館如GearmanClient促成。
注: ffmpeg的- PHP的是,2007年以來未開發的擴展名(和要求“的ffmpeg-0.4.9_pre1或更高版本”),這意味着你僅限於使用一種非常古老的版本的ffmpeg,沒有可能把它更新到最新版本。由於進行了大量的更改/改進,在ffmpeg的代碼中,每天,它使得ffmpeg-php與最新的ffmpeg不兼容。
案例成功測試結果:
<?php echo "正在啟動ffmpeg...\n\n"; echo shell_exec("ffmpeg -y -i rtmp://tinywan.123.com/live/4001483413136 -c copy -f flv rtmp://131.180/live/4001483492781?vhost=13.168 </dev/null >/dev/null 2>/var/log/ffmpeg.log &"); echo "完成.\n";
正在啟動ffmpeg...
完成.
日志文件:
ffmpeg version 2.3.git Copyright (c) 2000-2014 the FFmpeg developers built on Sep 1 2014 19:01:47 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1) configuration: --prefix=/usr/local/ffmpeg/build --extra-cflags=-I/usr/local/ffmpeg/build/include --extra-ldflags=-L/usr/local/ffmpeg/build/lib --bindir=/usr/local/ffmpeg/bin --enable-gpl --enable-libass --enable-libfaac --enable-libfdk-aac --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-nonfree --enable-openssl --enable-libspeex --enable-libxvid libavutil 54. 7.100 / 54. 7.100 libavcodec 56. 1.100 / 56. 1.100 libavformat 56. 3.100 / 56. 3.100 libavdevice 56. 0.100 / 56. 0.100 libavfilter 5. 0.103 / 5. 0.103 libswscale 3. 0.100 / 3. 0.100 libswresample 1. 1.100 / 1. 1.100 libpostproc 53. 0.100 / 53. 0.100 [flv @ 0x2862000] Stream discovered after head already parsed Last message repeated 1 times Input #0, flv, from 'rtmp://tinywan.3213.com/live/4001483413136': Metadata: Server : Tengine displayWidth : 1920 displayHeight : 1080 fps : 25 profile : level : Duration: 00:00:00.00, start: 765.097000, bitrate: N/A Stream #0:0: Video: h264 (High), yuv420p(tv, bt709), 1920x1080, 1048 kb/s, 25 fps, 25 tbr, 1k tbn, 2k tbc Stream #0:1: Data: none Stream #0:2: Audio: aac, 44100 Hz, stereo, fltp Output #0, flv, to 'rtmp://1232130/live/4001483492781?vhost=321312': Metadata: Server : Tengine displayWidth : 1920 displayHeight : 1080 fps : 25 profile : level : encoder : Lavf56.3.100 Stream #0:0: Video: h264 ([7][0][0][0] / 0x0007), yuv420p, 1920x1080, q=2-31, 1048 kb/s, 25 fps, 1k tbn, 1k tbc Stream #0:1: Audio: aac ([10][0][0][0] / 0x000A), 44100 Hz, stereo Stream mapping: Stream #0:0 -> #0:0 (copy) Stream #0:2 -> #0:1 (copy) Press [q] to stop, [?] for help
測試成功!