歡迎訪問我的GitHub
這裡分類和彙總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos
本篇概覽
-
本文是《JavaCV的攝像頭實戰》的第五篇,一起來考慮個問題:本地攝像頭的內容,如何讓網路上的其他人看見?
-
這就涉及到了推流,如下圖,基於JavaCV的應用將攝像頭的視訊幀推送到媒體伺服器,觀看者用播放器軟體遠端連線媒體伺服器,就能觀看攝像頭的內容了:
- 今天的主要工作就是開發上圖的JavaCV應用,然後驗證功能是否正常;
編碼
-
《JavaCV的攝像頭實戰之一:基礎》一文建立的simple-grab-push工程中已寫好父類AbstractCameraApplication,本篇繼續使用該工程,建立子類實現那些抽象方法即可
-
編碼前先回顧父類的基礎結構,如下圖,粗體是父類定義的各個方法,紅色塊都是需要子類來實現抽象方法,所以接下來,我們們以本地視窗預覽為目標實現這三個紅色方法即可:
-
新建檔案RecordCamera.java,這是AbstractCameraApplication的子類,其程式碼很簡單,接下來按上圖順序依次說明
-
《JavaCV的攝像頭實戰之一:基礎》中已部署好了媒體伺服器,這裡定義一個成員變數儲存媒體伺服器的推流地址,請您按自己的情況調整:
private static final String RECORD_ADDRESS = "rtmp://192.168.50.43:21935/hls/camera";
- 還要準備一個成員變數,推流的時候在幀上新增時間戳:
protected long startRecordTime = 0L;
- 將視訊幀推送到媒體伺服器的功能來自FrameRecorder,這是個抽象類,本篇用到的是其子類FFmpegFrameRecorder,所以定義FrameRecorder型別的成員變數:
// 幀錄製器
protected FrameRecorder recorder;
- 然後是初始化操作,請注意各項引數設定(1280*720解析度攝像頭的情況):
@Override
protected void initOutput() throws Exception {
// 例項化FFmpegFrameRecorder,將SRS的推送地址傳入
recorder = FrameRecorder.createDefault(RECORD_ADDRESS, getCameraImageWidth(), getCameraImageHeight());
// 降低啟動時的延時,參考
// https://trac.ffmpeg.org/wiki/StreamingGuide)
recorder.setVideoOption("tune", "zerolatency");
// 在視訊質量和編碼速度之間選擇適合自己的方案,包括這些選項:
// ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow
// ultrafast offers us the least amount of compression (lower encoder
// CPU) at the cost of a larger stream size
// at the other end, veryslow provides the best compression (high
// encoder CPU) while lowering the stream size
// (see: https://trac.ffmpeg.org/wiki/Encode/H.264)
// ultrafast對CPU消耗最低
recorder.setVideoOption("preset", "ultrafast");
// Constant Rate Factor (see: https://trac.ffmpeg.org/wiki/Encode/H.264)
recorder.setVideoOption("crf", "28");
// 2000 kb/s, reasonable "sane" area for 720
recorder.setVideoBitrate(2000000);
// 設定編碼格式
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
// 設定封裝格式
recorder.setFormat("flv");
// FPS (frames per second)
// 一秒內的幀數
recorder.setFrameRate(getFrameRate());
// Key frame interval, in our case every 2 seconds -> 30 (fps) * 2 = 60
// 關鍵幀間隔
recorder.setGopSize((int)getFrameRate()*2);
// 幀錄製器開始初始化
recorder.start();
}
- 接下來是output方法,關鍵是recorder.record,另外要注意時間戳的計算和設定:
@Override
protected void output(Frame frame) throws Exception {
if (0L==startRecordTime) {
startRecordTime = System.currentTimeMillis();
}
// 時間戳
recorder.setTimestamp(1000 * (System.currentTimeMillis()-startRecordTime));
// 存檔
recorder.record(frame);
}
- 最後是處理視訊的迴圈結束後,程式退出前要做的事情,即關閉幀抓取器:
@Override
protected void releaseOutputResource() throws Exception {
recorder.close();
}
- 另外還要注意兩幀之間的延時,由於推流涉及到網路,因此不能像本地預覽那樣根據幀率嚴格計算,實際間隔要更小一些:
@Override
protected int getInterval() {
// 相比本地預覽,推流時兩幀間隔時間更短
return super.getInterval()/4;
}
- 至此,推流功能已開發完成,再寫上main方法,注意引數600表示抓取和錄製的操作執行600秒:
public static void main(String[] args) {
new RecordCamera().action(600);
}
- 執行main方法,等到控制檯輸出下圖紅框的內容時,表示已經開始推流:
- 用本機或區域網內另一臺電腦,用VLC軟體開啟剛才推流的地址rtmp://192.168.50.43:21935/hls/camera,稍等幾秒鐘後開始正常播放:
- 還可用VLC的工具檢視編碼資訊:
-
至此,我們們已完成了推流功能,驗證遠端播放也正常,得益於JavaCV的強大,整個過程是如此的輕鬆愉快,接下來請繼續關注欣宸原創,《JavaCV的攝像頭實戰》系列還會呈現更多豐富的應用;
-
此刻聰明的您一定發現了問題:只推視訊嗎?連聲音都沒有,就這?沒錯,接下來的實戰,我們們該挑戰音訊處理了
原始碼下載
- 《JavaCV的攝像頭實戰》的完整原始碼可在GitHub下載到,地址和連結資訊如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 連結 | 備註 |
---|---|---|
專案主頁 | https://github.com/zq2599/blog_demos | 該專案在GitHub上的主頁 |
git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該專案原始碼的倉庫地址,https協議 |
git倉庫地址(ssh) | git@github.com:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協議 |
- 這個git專案中有多個資料夾,本篇的原始碼在javacv-tutorials資料夾下,如下圖紅框所示:
- javacv-tutorials裡面有多個子工程,《JavaCV的攝像頭實戰》系列的程式碼在simple-grab-push工程下: