SpringBoot使用JavaCV處理rtsp流的示例代碼
為什么寫這篇文章?
起因是項(xiàng)目中會用到rtsp流,用于分析視頻,和語音口令,還有就是控制攝像頭和做監(jiān)控時(shí)候做云端存儲功能。在網(wǎng)上找了一大圈發(fā)現(xiàn)這方面資料很少,資料中大部分人還是FFmpeg的安裝程序加代碼的方式實(shí)現(xiàn)。
其實(shí)JAVACV和OPENCV是非常強(qiáng)大的。當(dāng)然我了解的也不多(因?yàn)樵创a沒有什么注釋??!理解起來很困難)。這篇文章最主要是針對于目前GPT和通義前問中提供的代碼進(jìn)行糾錯(cuò)和改正(特別是從rtsp中只取出音頻)。
部分代碼是他人提供,所以看起來會有差異,不過我會進(jìn)行說明。
pom
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.9</version> </dependency>
取出視頻保存為mp4代碼
/**
* 拉取指定時(shí)長的視頻-保存為mp4
*
* @param rtspUrl 流地址
* @param duration 時(shí)長(秒)(TimeUnit Seconds)
* @param outputFile 輸出位置
*/
public static void videoPuller(String rtspUrl, int duration, String outputFile) {
// 創(chuàng)建抓取器
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
try {
grabber.start();
// 創(chuàng)建錄制器-設(shè)置的文件保存位置以及視頻畫面的寬度和高度
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 設(shè)置視頻編解碼器
recorder.setFormat("mp4"); // 設(shè)置視頻輸出格式
// 設(shè)置音頻相關(guān)參數(shù)
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
//這里直接寫2以及后續(xù)寫固定值也可以,當(dāng)然我們可以從grabber對象中g(shù)et出來
//grabber.getAudioChannels();
// recorder.setAudioChannels(grabber.getAudioChannels());
recorder.setAudioChannels(2);
//grabber.getSampleRate();
recorder.setSampleRate(44100);
//grabber.getAudioBitrate();
recorder.setAudioBitrate(192000);
recorder.start();
Frame frame;
long startTime = System.currentTimeMillis();
long endTime = startTime + (duration * 1000);
while ((frame = grabber.grabFrame()) != null && System.currentTimeMillis() <= endTime) {
recorder.record(frame);
}
recorder.stop();
grabber.stop();
recorder.close();
grabber.close();
} catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
videoPuller("rtsp流地址",60, "D:\\va\\a.mp4");
}
只保存音頻
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
/**
* @author by Guoshun
* @version 1.0.0
* @description 提取音頻測試
* @date 2024/2/26 16:57
*/
public class AudioExtractor {
public static void extractAudioFromRtsp(String rtspUrl, String outputPath, int maxDurationSeconds) throws Exception {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
grabber.start();
//1.從視頻流中取出讀取到的參數(shù)
int audioChannels = grabber.getAudioChannels();//音頻通道
int sampleRate = grabber.getSampleRate();
int audioBitrate = grabber.getAudioBitrate();
//創(chuàng)建recorder對象,設(shè)置編碼器和解碼器
//這里也是所有AI都回答錯(cuò)誤的地方
//通義和GPT會回答為這樣
//FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, audioChannels, sampleRate);
//上面這種錯(cuò)誤傳參方式導(dǎo)致,audioChannels變?yōu)闉閷?,sampleRate為高
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, audioChannels);
recorder.setSampleRate(sampleRate);
recorder.setAudioBitrate(audioBitrate);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE); // 使用PCM 16位小端模式編碼
recorder.start();
long startTime = System.currentTimeMillis();
Frame frame;
while ((frame = grabber.grabSamples()) != null && (System.currentTimeMillis() - startTime) / 1000 < maxDurationSeconds) {
recorder.record(frame, 0);
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
}
public static void main(String[] args) {
try {
extractAudioFromRtsp("rtsp://admin:12345qaz@192.168.20.164:554", "D:\\va\\audio.wav", 60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
完整代碼,并行執(zhí)行
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.opencv_core.Mat;
import javax.validation.constraints.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite;
/**
* @author by Guoshun
* @version 1.0.0
* @description RTSP流處理
* 流地址格式 rtsp://賬戶:密碼@ip地址
* @date 2024/2/27 10:21
*/
public class RtspUtils {
/**
* 判斷流地址是否在線
* @param rtspUrl 流地址
* @return
*/
public static Boolean isOnline(String rtspUrl) {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
try {
grabber.setTimeout(3000);
grabber.start();
return true;
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
} finally {
if (grabber != null) {
close(grabber);
}
}
return false;
}
/**
* 關(guān)閉grabber
*
* @param grabber
* @return
*/
private static boolean close(@NotNull FFmpegFrameGrabber grabber) {
try {
grabber.close();
return true;
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 拉取指定時(shí)長的視頻-保存為mp4
* @param rtspUrl 流地址
* @param duration 時(shí)長(秒)(TimeUnit Seconds)
* @param outputFile 輸出位置
*/
public static void videoPuller(String rtspUrl, int duration, String outputFile) {
// 創(chuàng)建抓取器
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
try {
grabber.start();
// 創(chuàng)建錄制器
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 設(shè)置視頻編解碼器
recorder.setFormat("mp4"); // 設(shè)置視頻輸出格式
// 設(shè)置音頻相關(guān)參數(shù)
recorder.setAudioChannels(2);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.setSampleRate(44100);
recorder.setAudioBitrate(192000);
recorder.start();
Frame frame;
long startTime = System.currentTimeMillis();
long endTime = startTime + (duration * 1000);
while ((frame = grabber.grabFrame()) != null && System.currentTimeMillis() <= endTime) {
recorder.record(frame);
}
recorder.stop();
grabber.stop();
recorder.close();
grabber.close();
} catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
e.printStackTrace();
}
}
/**
* 提取音頻
*
* @param rtspUrl
* @param outputPath
* @param maxDurationSeconds
*/
public static void extractAudioFromRtsp(String rtspUrl, String outputPath, int maxDurationSeconds) {
try(
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, 0,0)
){
grabber.setOption("map", "0:a:0");
grabber.start();
//1.從視頻流中取出讀取到的參數(shù)
int audioChannels = grabber.getAudioChannels();//音頻通道
int sampleRate = grabber.getSampleRate();
int audioBitrate = grabber.getAudioBitrate();
//創(chuàng)建recorder對象,設(shè)置編碼器和解碼器
recorder.setAudioChannels(audioChannels);
recorder.setSampleRate(sampleRate);
recorder.setAudioBitrate(audioBitrate);
recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE); // 使用PCM 16位小端模式編碼
recorder.start();
long startTime = System.currentTimeMillis();
Frame frame;
while ((frame = grabber.grabSamples()) != null && (System.currentTimeMillis() - startTime) / 1000 < maxDurationSeconds) {
recorder.record(frame, 0);
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
}catch (FrameGrabber.Exception | FrameRecorder.Exception e){
e.printStackTrace();
}
}
/**
* 保存圖片
* @param rtspUrl
* @param outputDir
* @param maxDurationSeconds 時(shí)長范圍
*/
public static void savePicture(String rtspUrl, String outputDir, int maxDurationSeconds) {
try(
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat()
){
grabber.start();
long startTime = System.currentTimeMillis();
Frame frame;
int i = 0;
while ((frame = grabber.grab()) != null && (System.currentTimeMillis() - startTime) / 1000 < maxDurationSeconds) {
// 將Frame轉(zhuǎn)換為OpenCV Mat
Mat mat = converter.convert(frame);
if (mat != null && i % 100 == 0) {
// 保存幀到磁盤
//String fileName = "frame_" + i + ".jpg";
//imwrite(outputDir + fileName, mat);
imwrite(outputDir + "frame_" + i + ".jpg", mat);
}
i++;
}
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
}
private static String DEFAULT_RTSP_URL = "rtsp://賬戶:密碼@ip地址";
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(4); // 創(chuàng)建線程池
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> videoPuller(DEFAULT_RTSP_URL, 60, "D:\\va\\a.mp4"), executor);
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> savePicture(DEFAULT_RTSP_URL, "D:\\va\\", 60), executor);
CompletableFuture<Void> future3 = CompletableFuture.runAsync(() ->
extractAudioFromRtsp(DEFAULT_RTSP_URL, "D:\\va\\audio.wav", 60), executor);
CompletableFuture.allOf(future1, future2, future3).thenRun(() -> System.out.println("所有任務(wù)都已完成"));
// 在應(yīng)用結(jié)束前關(guān)閉線程池
CompletableFuture.supplyAsync(() -> {
return true;
}, executor)
.thenAccept((v) -> executor.shutdown());
}
}
到此這篇關(guān)于SpringBoot使用JavaCV處理rtsp流的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot JavaCV處理rtsp內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java線程池隊(duì)列LinkedBlockingDeque
這篇文章主要為大家介紹了Java線程池隊(duì)列LinkedBlockingDeque示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Java static方法用法實(shí)戰(zhàn)案例總結(jié)
這篇文章主要介紹了Java static方法用法,結(jié)合具體案例形式總結(jié)分析了java static方法功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-09-09
Java模擬死鎖發(fā)生之演繹哲學(xué)家進(jìn)餐問題案例詳解
這篇文章主要介紹了Java模擬死鎖發(fā)生之演繹哲學(xué)家進(jìn)餐問題,結(jié)合具體演繹哲學(xué)家進(jìn)餐問題的案例形式詳細(xì)分析了死鎖機(jī)制與原理,需要的朋友可以參考下2019-10-10
SpringBoot+Tess4j實(shí)現(xiàn)牛的OCR識別工具的示例代碼
這篇文章主要介紹了SpringBoot+Tess4j實(shí)現(xiàn)牛的OCR識別工具的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
深入解析Java的Struts框架中的控制器DispatchAction
這篇文章主要介紹了深入解析Java的Struts框架中的控制器DispatchAction,Struts是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12
MybatisPlus使用@TableId主鍵id自增長無效的解決
本文主要介紹了MybatisPlus使用@TableId主鍵id自增長無效的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Spring注入Map集合實(shí)現(xiàn)策略模式詳解
這篇文章主要介紹了Spring注入Map集合實(shí)現(xiàn)策略模式詳解,Spring提供通過@Resource注解將相同類型的對象注入到Map集合,并將對象的名字作為key,對象作為value封裝進(jìn)入Map,需要的朋友可以參考下2023-11-11

