歡迎訪問我的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工程下: