前提:
1.準備好rtsp、rtmp服務
2.準備好視頻流接收程序文章來源:http://www.zghlxwxcb.cn/news/detail-505166.html
POM依賴配置
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.4.4</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.1-1.4.4</version>
</dependency>
<!-- ${javacpp.platform.dependencies} 根據(jù)運行環(huán)境取值
windows:windows-x86_64;linux:linux-x86_64 -->
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.1-1.4.4</version>
<classifier>${javacpp.platform.dependencies}</classifier>
</dependency>
關鍵代碼
/**
* 推流到rtsp、rtmp服務器
*/
@Slf4j
public class Pusher extends Thread {
private final PipedInputStream pis = new PipedInputStream();
private final PipedOutputStream pos = new PipedOutputStream();
private final String deviceId;
private final String channel;
private volatile boolean running = true;
private boolean flag = false;
private String rtspBaseAddress = "rtsp://127.0.0.1:554/";
private String rtmpBaseAddress = "rtmp://127.0.0.1:1935/hls/";
public RtmpPusher(String deviceId, String channel) {
this.deviceId = deviceId;
this.channel = channel;
try {
// 使用管道流實現(xiàn)線程間通信
pos.connect(pis);
} catch (IOException e) {
log.info("pos connect pis error.{}", e.getMessage());
}
}
/**
* 將視頻流寫入管道
*/
public void onMediaStream(byte[] data) {
try {
if (!flag) {
log.info("receive data...");
flag = true;
}
pos.write(data);
} catch (IOException e) {
log.error("write video data error.{}", e.getMessage());
try {
pos.close();
} catch (IOException ex) {
log.error("pos close error.{}", ex.getMessage());
}
}
}
/**
* 轉(zhuǎn)流器, 指定format
*/
public void recordPushWithFormat() {
long startTime = 0;
long videoTS;
try {
log.info("grabber start ... {} > {}", deviceId, channel);
// 從管道流中讀取視頻流
// maximumSize 設置為0,不設置會阻塞
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(pis, 0);
//此配置以減少啟動時間!若不設置,啟動最起碼半分鐘;
//類似一個緩沖區(qū),用來檢測流的正確性,值越小,啟動越快
grabber.setOption("probesize", "1024*20");
grabber.setFrameRate(30);
grabber.setVideoBitrate(2000000);
//阻塞式,直到通道有數(shù)據(jù)
grabber.start();
log.info("grabber start suc. and start recorder ... {} > {}", deviceId, channel);
// 獲取推流地址對應的recorder,用于錄制視頻
// rtmp對應format是flv
// rtsp對應format是rtsp
FrameRecorder rtspRecorder = getRecorder(rtspBaseAddress + deviceId + Constant.SEPARATOR + channel, "rtsp");
FrameRecorder rtmpRecorder = getRecorder(rtmpBaseAddress + deviceId + Constant.SEPARATOR + channel, "flv");
rtspRecorder.start();
rtmpRecorder.start();
Frame grabframe;
// 從視頻流中捕獲幀以錄制視頻
while (running && (grabframe = grabber.grab()) != null) {
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
videoTS = 1000 * (System.currentTimeMillis() - startTime);
// 推流到rtsp server
if (videoTS > rtspRecorder.getTimestamp()) {
rtspRecorder.setTimestamp(videoTS);
}
rtspRecorder.record(grabframe);
// 推流到rtmp server
if (videoTS > rtmpRecorder.getTimestamp()) {
rtmpRecorder.setTimestamp(videoTS);
}
rtmpRecorder.record(grabframe);
}
} catch (Exception e) {
log.error("record push error.", e);
} finally {
try {
pis.close();
} catch (IOException e) {
log.error("pis close error.{}", e.getMessage());
}
}
}
private FrameRecorder getRecorder(String address, String format) {
FrameRecorder recorder = new FFmpegFrameRecorder(address, 720, 480);
recorder.setInterleaved(true);
recorder.setVideoOption("tune", "zerolatency");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoOption("crf", "28");
recorder.setVideoBitrate(2000000);//碼率 越大越清晰
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat(format);//flv rtsp
recorder.setFrameRate(30);//幀率 30fps 每秒包含的幀數(shù) 24-30越大越流暢
recorder.setGopSize(60);//30*2 每60幀存在一個關鍵幀
return recorder;
}
public void stopPush() {
this.running = false;
this.interrupt();
}
public boolean isRunning() {
return this.running;
}
@Override
public void run() {
recordPushWithFormat();
}
}
原理說明
基本思路是:啟動兩個線程,線程1接收視頻流,線程2使用JavaCV將視頻流推送到RTSP、RTMP服務,兩者之間使用管道流進行通信。線程2接收到視頻流后的具體操作:啟動grabber接收視頻流并捕獲視頻幀,然后啟動recoder將捕獲的視頻幀推送到RTSP、RTMP服務。文章來源地址http://www.zghlxwxcb.cn/news/detail-505166.html
到了這里,關于JAVA實現(xiàn)H264視頻流推送到RTSP、RTMP服務----JavaCV的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!