最近項目上要求增加視頻直播功能,用戶又不想多花錢購買專業的視頻直播系統組件,客戶是上帝沒辦法只能自己用Java實現一套推拉流中間件使用了。技術不算困難,開發思路也比較清晰,主要是通Nginx實現流媒體服務支撐,JavaCV實現客戶端視頻數據流通過RTMP協議向服務端推流操作。
主要技術
RTMP
RTMP是Real Time Messaging Protocol(實時消息傳輸協議)的首字母縮寫。該協議基於TCP,是一個協議族,包括RTMP基本協議及RTMPT/RTMPS/RTMPE等多種變種。RTMP是一種設計用來進行實時數據通信的網絡協議,主要用來在Flash/AIR平台和支持RTMP協議的流媒體/交互服務器之間進行音視頻和數據通信。
Nginx
Nginx是一款自由的、開源的、高性能的HTTP服務器和反向代理服務器;同時也是一個IMAP、POP3、SMTP代理服務器;Nginx可以作為一個HTTP服務器進行網站的發布處理,另外Nginx可以作為反向代理進行負載均衡的實現。
JavaCV
JavaCV 是一款開源的視覺處理庫,基於Apache License Version 2.0協議和GPLv2兩種協議 ,對各種常用計算機視覺庫封裝后的一組jar包,封裝了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等計算機視覺編程人員常用庫的接口。
HLS
HTTP Live Streaming,也就是我們常說的HLS。HLS是蘋果公司提出的基於HTTP的流媒體網絡傳輸協議。類似於MPEG-DASH,但是HLS更加簡潔,它的基本原理也是服務端把文件或媒體流按照不同的碼率切分成一個個小片段進行傳輸,客戶端在播放碼流時,可以根據自身的帶寬及性能限制,在同一視頻內容的不同碼率的備用源中,選擇合適碼率的碼流進行下載播放。在傳輸會話開始時,客戶端首先需要下載描述不同碼流元數據的M3U8索引文件(類似於DASH中的MPD文件)。
服務端環境搭建
1. 安裝nginx服務(下載地址https://github.com/arut/nginx-rtmp-module)
2. 配置nginx服務
worker_processes 1;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 6064;
location / {
root html;
}
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root html;
}
#HLS配置開始,這個配置為了`客戶端`能夠以http協議獲取HLS的拉流
location /hls {
#server hls fragments
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias temp/hls;
expires -1;
}
}
}
rtmp {
server {
listen 1935;
application live {
live on;
record off;
publish_notify on;
#on_publish http://localhost:8080/newsweb/api/v1/rtmp/on_publish;
#on_publish_done http://localhost:8080/newsweb/api/v1/rtmp/on_publish_done;
#on_play http://localhost:8080/newsweb/api/v1/rtmp/on_play;
#on_play_done http://localhost:8080/newsweb/api/v1/rtmp/on_play_done;
}
application hls {
live on;
hls on; #是否開啟hls
hls_path temp/hls; #本地切片路徑
hls_fragment 8s; #本地切片長度
publish_notify on;
#on_publish http://localhost:8080/newsweb/api/v1/rtmp/on_publish;
#on_publish_done http://localhost:8080/newsweb/api/v1/rtmp/on_publish_done;
#on_play http://localhost:8080/newsweb/api/v1/rtmp/on_play;
#on_play_done http://localhost:8080/newsweb/api/v1/rtmp/on_play_done;
}
}
}
3. 啟動nginx服務程序
Nginx常用命令
1、啟動nginx
nginx.exe 或者 start nginx
2、停止nginx
nginx -s stop或者nginx -s quit
3、重載nginx命令
nginx -s reload
用瀏覽器訪問地址:http://xxx.xxx.xxx.xxx:6064/ 可以看到如下頁面說明ngxin啟動成功。
客戶端代碼實現
創建BStreamer基礎視頻流類主要作為通用視頻流的基類使用,在他的基礎上可以擴展實現RTMP和RTSP的推流操作類。
/**
* 基礎視頻流
*/
public class BStreamer {
private int width = 640;
private int height = 480;
private String url;
public BStreamer(String url) {
this.url = url;
}
public BStreamer(String url, int w, int h) {
this.url = url;
if (w > 0 && h > 0) {
this.width = w;
this.height = h;
}
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Pusher 類通過繼承上面的BStreamer 類實現基於RTMP協議的視頻流推送操作。
/**
* RTMP視頻推流器
*/
public class Pusher extends BStreamer {
private Thread thread;
/**
* 視頻采集器(攝像頭)
*/
OpenCVFrameGrabber grabberCV;
///推流
FrameRecorder recorder;
private boolean exit = true;
public Pusher(String url) {
super(url);
}
public Pusher(String url, int w, int h) {
super(url, w, h);
}
public void close() throws Exception {
exit = false;
if(grabberCV!=null){
grabberCV.close();
}
if(recorder.isInterleaved()){
recorder.close();
recorder.setInterleaved(false);
}
this.thread.interrupt();
}
public void start() throws Exception {
exit = true;
if(grabberCV!=null){
grabberCV.start();
}
if(recorder.isInterleaved()){
recorder.start();
recorder.setInterleaved(true);
}
this.thread.start();
}
public void push(Consumer<Frame> consumer) throws Exception {
///采集攝像頭
grabberCV = new OpenCVFrameGrabber(0);
grabberCV.setImageWidth(getWidth());
grabberCV.setImageHeight(getHeight());
///推流
// recorder = FrameRecorder.createDefault(getUrl(), getWidth(), getHeight());
recorder=new FFmpegFrameRecorder(getUrl(), getWidth(), getHeight());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 28
recorder.setFormat("flv"); // rtmp的類型
recorder.setFrameRate(30);
// recorder.setVideoBitrate(10 * 1024 * 1024);
this.thread = new Thread(() -> {
try {
while (exit) {
Frame f = grabberCV.grab();
if (f != null) {
if (recorder.isInterleaved()) {
ConsoleOut.println("push stream...");
recorder.record(f);
consumer.accept(f);
}
}
}
} catch (Exception e) {
e.printStackTrace();
recorder.setInterleaved(false);
}
});
}
}
TestRTMPPusher單元測試類,通過單元測試類完成代碼效果驗證。
public class TestRTMPPusher {
public static void main(String[] args) throws Exception {
//String url="rtsp://192.168.56.1:554/Sword";
String url="rtmp://127.0.0.1/live/stream";
Pusher pusher = new Pusher(url);
CanvasFrame cf = Windows.build("測試RTMP推流", w -> {
ConsoleOut.println("窗口已關閉!");
try {
pusher.close();
} catch (Exception e) {
e.printStackTrace();
}
});
pusher.push(f -> {
cf.showImage(f);
});
pusher.start();
}
}
運行測試程序結果如下。
打開上面提到的nginx服務端程序界面,即可看到推流成功。
至此基於JavaCV實現的IPC推流功能就實現了,相關代碼我已經上傳到csdn資源,如有需要請自行下載學習。
————————————————
版權聲明:本文為CSDN博主「黒木涯」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/xxxlllbbb/article/details/104819683