JavaCV與FFmpeg
FFmpeg是一款開源的多媒體處理工具集,它包含了一系列用于處理音頻、視頻、字幕等多媒體數據的庫和工具。
JavaCV集成了FFmpeg庫,使得Java開發者可以使用FFmpeg的功能,比如視頻解碼、編碼、格式轉換等。
除了FFmpeg,Javacv封裝了以下庫:
- OpenCV: JavaCV封裝了OpenCV(Open Source Computer Vision Library),這是一個廣泛用于計算機視覺應用的開源庫。
- FlyCapture: 用于 Point Grey 系列相機的庫。
- ARToolKit: 一個增強現實(Augmented Reality)庫,用于跟蹤相機圖像中的標記。
- JavaCpp: 這是JavaCV的底層庫,用于在Java中調用C++代碼,是整個JavaCV項目的基礎。
- Libdc1394: 用于相機和攝像機的庫。
- JavaCV Presets: 提供了一系列預設,將原生的C/C++函數封裝為Java接口,簡化了在Java中調用這些功能的過程。
準備
1、引入maven包
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.6</version>
</dependency>
2、類與方法說明
FFmpegFrameGrabber
和 FFmpegFrameRecorder
是 JavaCV 中用于處理視頻的兩個關鍵類,分別用于抓取視頻幀和錄制視頻幀,底層使用了 FFmpeg 庫。
我們可以使用 FFmpegFrameGrabber
打開視頻文件,獲取視頻信息,然后不斷地從視頻中獲取幀。
FFmpegFrameRecorder
則從輸入視頻中抓取幀,進行解碼后,將每一幀寫入輸出視頻文件。
FFmpegFrameGrabber
與 FFmpegFrameGrabber
配合使用,可以實現從視頻源中抓取幀并將幀寫入視頻文件的完整流程。
2、推流流程
3、Nginx搭建rtmp服務器
(1)下載帶有rtmp模塊的nginx(Gryphon) http://nginx-win.ecsds.eu/download/
(2)在nginx中的conf文件夾配置rtmp。nginx-win.conf增加如下代碼:
rtmp {server {listen 1935;application flv-live{live on;record off;allow play all;}}
}
添加后,cmd跳轉到nginx目錄,執行命令 nginx.exe -c conf/nginx-win.conf
即可
FLV直播和HLS直播
FLV(Flash Video)直播:
- 傳輸協議: FLV 使用 RTMP(Real-Time Messaging Protocol)作為傳輸協議。RTMP 是一種實時通信協議,通常用于傳輸音頻、視頻和數據。
- 實時性: FLV 提供較低的延遲,通常在數秒到十幾秒之間,適用于需要更快實時性的應用場景。
- 支持性: FLV 需要 Flash 播放器來播放,而在現代瀏覽器和設備中,對 Flash 的支持逐漸減少。這導致了使用 FLV 的限制。
HLS(HTTP Live Streaming)直播:
- 傳輸協議: HLS 使用 HTTP 協議,這使得它更容易穿越防火墻和代理服務器。它通常基于標準的 HTTP 80/443 端口,因此更容易被防火墻允許。
- 實時性: 相對于 FLV,HLS 通常有更高的延遲,通常在 10 到 30 秒之間。這使得它不太適合需要極低延遲的實時應用。
- 支持性: HLS 更廣泛地支持各種設備和瀏覽器,因為它基于標準的 HTTP 和 HTML5 規范,無需專門的插件或播放器。
- 自適應比特率: HLS 提供了自適應比特率功能,可以根據用戶的網絡情況自動調整視頻質量,提供更好的觀看體驗。
選擇 FLV 還是 HLS 取決于你的具體需求。如果需要較低的延遲,并且可以接受使用 Flash 播放器的限制,那么 FLV 可能是一個合適的選擇。如果需要更廣泛的設備和瀏覽器支持,并且可以接受稍高的延遲,那么 HLS 可能更適合。在實際應用中,有時候也會結合兩者,使用不同的協議來滿足不同的需求。
實現Flv推流
拿一個h264格式的mov視頻來演示,先將mov轉為flv并且進行推流。
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;public class RtmpFlv {private static final String outputUrl = "rtmp://localhost:1935/flv-live/test";private static final String inputUrl = "D:\視頻.mov";public static void main(String[] args) throws FrameGrabber.Exception, FrameRecorder.Exception, InterruptedException {//設置FFmpeg日志級別avutil.av_log_set_level(avutil.AV_LOG_INFO);FFmpegLogCallback.set();//以文件路徑的方式傳入視頻,當然也支持以流的方式傳入FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputUrl);//開始捕獲視頻流grabber.start();//用于將捕獲到的視頻流轉換為輸出URL的mp4格式。FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputUrl, grabber.getImageWidth(), grabber.getImageHeight());recorder.setFormat("flv");recorder.setVideoBitrate(grabber.getVideoBitrate()); // 設置視頻比特率recorder.setFrameRate(grabber.getVideoFrameRate()); // 設置幀率recorder.setGopSize((int) grabber.getVideoFrameRate()); // 設置關鍵幀間隔// CRF 是一種用于控制視頻/音頻質量的參數,它允許在保持目標質量的同時動態地調整比特率。較低的CRF值表示更高的質量,但也可能導致較大的文件大小recorder.setAudioOption("crf", "23");Frame frame;//設置音頻編碼為AACif (grabber.getAudioChannels() > 0) {recorder.setAudioChannels(grabber.getAudioChannels());recorder.setAudioBitrate(grabber.getAudioBitrate());recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);}recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);//將解碼后的幀記錄到輸出文件中//recorder.start通常用于處理已經解碼成圖像的視頻數據recorder.start();while ((frame = grabber.grab()) != null) {recorder.record(frame);}recorder.close();grabber.close();}
}
運行后,打開vlc軟件,點擊媒體-》打開串流網絡
,輸入rtmp://localhost:1935/flv-live/test
進行播放
實現M3U8推流
代碼和上面的大同小異,將一個H264的mp4視頻轉為hls,利用http播放m3u8文件。
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;public class RtmpM3U8 {private static final String outputUrl = "D:\nginx_rtmp\html\test.m3u8";private static final String inputUrl = "D:\視頻.mp4";public static void main(String[] args) throws FrameGrabber.Exception, FrameRecorder.Exception {//設置FFmpeg日志級別avutil.av_log_set_level(avutil.AV_LOG_ERROR);FFmpegLogCallback.set();//以文件路徑的方式傳入視頻,當然也支持以流的方式傳入FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputUrl);//開始捕獲視頻流grabber.start();AVFormatContext avFormatContext = grabber.getFormatContext();//獲取視頻時長//long duration = avFormatContext.duration();//檢查文件是否媒體流(視頻流、音頻流)if (avFormatContext.nb_streams() == 0) {//表明沒有媒體流return;}//用于將捕獲到的視頻流轉換為輸出URL的mp4格式。FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputUrl, grabber.getImageWidth(), grabber.getImageHeight(),grabber.getAudioChannels());recorder.setFormat("hls");recorder.setVideoBitrate(grabber.getVideoBitrate()); // 設置視頻比特率recorder.setFrameRate(grabber.getVideoFrameRate()); // 設置幀率recorder.setGopSize((int) grabber.getVideoFrameRate()); // 設置關鍵幀間隔// 設置HLS切片參數//將每個切片時長設置為10秒recorder.setOption("hls_time", "15");//設置切片數大小recorder.setOption("hls_list_size", "20");//設置切片循環次數為50recorder.setOption("hls_wrap", "20");//每次切片完成后,都會刪除之前的切片文件。如果不設置或設置為其他值,則不會刪除之前的切片文件。recorder.setOption("hls_flags", "delete_segments");//在使用 H.264 編碼時,通常要求輸入的像素格式為 YUV420P。如果輸入的像素格式不匹配,就可能導致 avcodec_send_frame() 錯誤recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);//CRF 是一種用于控制視頻/音頻質量的參數,它允許在保持目標質量的同時動態地調整比特率。較低的CRF值表示更高的質量,但也可能導致較大的文件大小recorder.setAudioOption("crf", "23");Frame frame;//設置音頻編碼為AACif (grabber.getAudioChannels() > 0) {recorder.setAudioChannels(grabber.getAudioChannels());recorder.setAudioBitrate(grabber.getAudioBitrate());recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);}recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);//設置音頻編碼為AACif (grabber.getAudioChannels() > 0) {recorder.setAudioChannels(grabber.getAudioChannels());recorder.setAudioBitrate(grabber.getAudioBitrate());recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);}//將解碼后的幀記錄到輸出文件中//recorder.start通常用于處理已經解碼成圖像的視頻數據recorder.start();while ((frame = grabber.grab()) != null) {recorder.record(frame);}recorder.close();grabber.close();}}
開始推流后,會在D:\nginx_rtmp\html\生成m3u8和ts文件,我們只需要播放m3u8文件即可。
打開vlc軟件,點擊媒體-》打開串流網絡
,輸入http://localhost:8080/test.m3u8
進行播放
優化TODO
后繼利用高性能網絡框架netty進行直播多路復用,避免視頻重復解碼推流,詳情關注最新文章!