JavaCV 拉流存儲到本地示例解析
拉流整體代碼
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
public class LivePuller {
public void pull(String rtmp, String outputVideoPath) {
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorder recorder = null;
try {
FFmpegLogCallback.set();
boolean hasStream = false;
grabber = new FFmpegFrameGrabber(rtmp);
grabber.setOption("nobuffer", "1");
// 采集流超時時間,停止推流后,grab() 大致會在 20 秒左右超時停止;值也不宜太小,否則可能有些音視頻幀采集不到影響推流
grabber.setOption("rw_timeout", "1000000");
grabber.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);
// setPixelFormat,可能會導(dǎo)致圖片幀保存的是黑白照
// grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
grabber.setFormat("flv");
grabber.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
// 先執(zhí)行代碼后有直播流的情況下,grabber.start() 會拋出異常;因此循環(huán)start(),直到有流數(shù)據(jù)時成功
while (!hasStream) {
try {
grabber.start();
hasStream = true;
} catch (Exception ignored) {
} finally {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 重置分辨率
int imageWidth = grabber.getImageWidth();
int imageHeight = grabber.getImageHeight();
if (imageWidth > 1920 || imageHeight > 1080) {
double wScale = imageWidth * 1.0 / 1920;
double hScale = imageHeight * 1.0 / 1080;
double scale = Math.max(wScale, hScale);
grabber.setImageWidth((int) (imageWidth / scale));
grabber.setImageHeight((int) (imageHeight / scale));
}
recorder = new FFmpegFrameRecorder(outputVideoPath, grabber.getImageWidth(), grabber.getImageHeight());
recorder.setOption("nobuffer", "1");
recorder.setAudioChannels(grabber.getAudioChannels());
recorder.setFrameRate(grabber.getFrameRate()); // 保持和采集一樣的幀率,避免音畫不同步;
recorder.setVideoQuality(0); // 視頻質(zhì)量(清晰度)
recorder.setAudioQuality(0); // 音頻質(zhì)量
// --------------------------- 非必須 ------------------------------
// 當(dāng)設(shè)置了下方的錄制器的視頻編碼,需要再加上 setVideoBitrate 保證視頻質(zhì)量
recorder.setVideoBitrate(grabber.getVideoBitrate());
recorder.setVideoCodec(grabber.getVideoCodec()); // 指定為 mp4 錄制時需要 avcodec.AV_CODEC_ID_MPEG4
// // 當(dāng)上面 grabber 沒有設(shè)置 setPixelFormat,recorder.setPixelFormat 最好不要使用 grabber.getPixelFormat();可能異常
// recorder.setPixelFormat(grabber.getPixelFormat());
// recorder.setFormat(grabber.getFormat());
// recorder.setAudioCodec(grabber.getAudioCodec());
// recorder.setSampleRate(grabber.getSampleRate());
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
// 此處不能判斷 frame.streamIndex 為 avutil.AVMEDIA_TYPE_VIDEO 或 avutil.AVMEDIA_TYPE_DATA;遇到過采集流時兩個類型是反著的
if (frame.image != null || frame.samples != null) {
// frame.image 是圖像數(shù)據(jù),frame.samples 是聲音數(shù)據(jù)
recorder.record(frame);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (grabber != null) {
try {
grabber.close();
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
}
if (recorder != null) {
try {
recorder.close();
} catch (FrameRecorder.Exception e) {
e.printStackTrace();
}
}
}
}
}拉流整體代碼如上,基本該有的地方都有注釋;同 JavaCV 推流 也是改變分辨率等參數(shù)保證拉流速率,以及設(shè)置幀率保證音畫同步;
示例解析
另外上面提到 grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); 不要設(shè)置,避免后續(xù)的 JavaCV 保存視頻幀圖片 時保存的圖片是黑白的;另外上面的代碼經(jīng)過測試是可以將拉取的音視頻流保存成 mp4 格式,通過瀏覽器直接播放的

注意測試 mp4 文件保存的時候,盡量每次寫入一個新的文件;避免在調(diào)試過程中,出現(xiàn)因瀏覽器緩存等問題導(dǎo)致之前有問題的 mp4 的文件,在參數(shù)調(diào)整好后依然不能正常播放等情況
以上就是JavaCV 拉流存儲到本地示例解析的詳細(xì)內(nèi)容,更多關(guān)于JavaCV 拉流存儲到本地的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項目的漏洞修復(fù)經(jīng)驗分享
在局域網(wǎng)環(huán)境下,由于無法連接外網(wǎng)下載Maven包,常見解決方案是在外網(wǎng)環(huán)境搭建相同的開發(fā)環(huán)境以便更新Maven包,本次漏洞掃描包括Tomcat、jackson-databind、fastjson、logback等組件,通常解決方法是升級到更高版本2024-10-10
設(shè)計模式之模版方法模式_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了設(shè)計模式之模版方法模式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08

