歡迎訪問我的GitHub
這裡分類和彙總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos
本篇概覽
- 本文是《JavaCV的攝像頭實戰》的第五篇,一起來考慮個問題:本地攝像頭的內容,如何讓網路上的其他人看見?
- 這就涉及到了推流,如下圖,基於JavaCV的應用將攝像頭的視訊幀推送到媒體伺服器,觀看者用播放器軟體遠端連線媒體伺服器,就能觀看攝像頭的內容了:
- 今天的主要工作就是開發上圖的JavaCV應用,然後驗證功能是否正常;
編碼
- 《JavaCV的攝像頭實戰之一:基礎》一文建立的<font color="red">simple-grab-push</font>工程中已寫好父類<font color="blue">AbstractCameraApplication</font>,本篇繼續使用該工程,建立子類實現那些抽象方法即可
- 編碼前先回顧父類的基礎結構,如下圖,粗體是父類定義的各個方法,紅色塊都是需要子類來實現抽象方法,所以接下來,我們們以本地視窗預覽為目標實現這三個紅色方法即可:
- 新建檔案<font color="blue">RecordCamera.java</font>,這是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方法,注意引數<font color="blue">600</font>表示抓取和錄製的操作執行600秒:
public static void main(String[] args) {
new RecordCamera().action(600);
}
- 執行main方法,等到控制檯輸出下圖紅框的內容時,表示已經開始推流:
- 用本機或區域網內另一臺電腦,用VLC軟體開啟剛才推流的地址<font color="blue">rtmp://192.168.50.43:21935/hls/camera</font>,稍等幾秒鐘後開始正常播放:
- 至此,我們們已完成了推流功能,驗證遠端播放也正常,得益於JavaCV的強大,整個過程是如此的輕鬆愉快,接下來請繼續關注欣宸原創,《JavaCV的攝像頭實戰》系列還會呈現更多豐富的應用;
- 此刻聰明的您一定發現了問題:只推視訊嗎?連聲音都沒有,就這?沒錯,接下來的實戰,我們們該挑戰音訊處理了
原始碼下載
- 這個git專案中有多個資料夾,本篇的原始碼在<font color="blue">javacv-tutorials</font>資料夾下,如下圖紅框所示:
- <font color="blue">javacv-tutorials</font>裡面有多個子工程,《JavaCV的攝像頭實戰》系列的程式碼在<font color="red">simple-grab-push</font>工程下:
你不孤單,欣宸原創一路相伴
https://github.com/zq2599/blog_demos