ffmpeg 實現視頻流抽幀
抽取實時視頻幀
如果你的實時視頻是通過 RTSP、UDP 或其他協議獲取的,可以直接調用 FFmpeg 命令來抽取幀。
ffmpeg 命令
示例 1
ffmpeg -i rtsp://your_rtsp_stream_url -vf fps=1 -update 1 output.jpg
說明:
- -i rtsp://your_rtsp_stream_url:指定輸入的實時視頻流 URL。
- -vf fps=1:使用視頻濾鏡,每秒抽取 1 幀(可根據需要調整幀率,例如
fps=1/5
表示每 5 秒抽取一幀)。 - -update 1 output.jpg:參數
-update 1
表示不斷用最新的幀更新同一個輸出文件(output.jpg),適合用于監控場景;如果需要保存多張圖片,則可以使用類似output_%04d.jpg
的命名格式保存為連續文件。
示例 2
ffmpeg -i "rtmp://ns8.indexforce.com/home/mystream" -vf fps=1 "frame_%03d.jpg"
說明:
-i "rtsp://your_rtsp_stream_url"
:指定 RTSP 視頻流的 URL。-vf fps=1
:設置幀率,每秒抽取一幀。您可以根據需要調整此值,例如fps=1/5
表示每 5 秒抽取一幀。"frame_%03d.jpg"
:指定輸出的圖像文件名,%03d
表示編號,生成的文件名將依次為frame_001.jpg
、frame_002.jpg
等。
示例 3
ffmpeg -i "rtmp://ns8.indexforce.com/home/mystream" -ss 1 -frames:v 1 "C:\Users\26913\Videos\ffmpeg-img\frame.jpg"
說明:
- 從指定的 RTSP 流中讀取數據,并只輸出一幀圖像,保存到
C:\Users\26913\Videos\ffmpeg-img
目錄下,文件名為frame.jpg
。 -ss 1
:表示設置時間偏移量為 1 秒。也就是說,從視頻的第 1 秒處開始處理。對于抽幀來說,FFmpeg 會在視頻的 1 秒處截取當前幀。- 不加這個參數的話,會獲取視頻開頭的第一幀。
- 若視頻流開頭是黑屏或加載幀,可能會影響抓取效果。
- 效率較低,因為 FFmpeg 需要解析部分流的關鍵幀來決定輸出。
代碼示例
引入依賴
<!-- 集成javacv --><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.11</version></dependency><!-- 集成ffmpeg:https://mvnrepository.com/artifact/org.bytedeco/ffmpeg --><dependency><groupId>org.bytedeco</groupId><artifactId>ffmpeg</artifactId><version>7.1-1.5.11</version></dependency>
1、javacv 寫法
public class LiveStreamFrameExtractor {private static final Logger log = LoggerFactory.getLogger(LiveStreamFrameExtractor.class);/*** javacv實時抓取視頻幀*/public static void main(String[] args) {String streamUrl = VideoConstant.RTSP_URL_2;String outputDirPath = VideoConstant.FRAME_FILE_PREFIX;File outputDir = new File(outputDirPath);if (!outputDir.exists()) {outputDir.mkdirs();}// 使用 try-with-resources 自動管理資源try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(streamUrl);Java2DFrameConverter converter = new Java2DFrameConverter()) {// 啟動抓取器,開始實時視頻讀取grabber.start();int frameCount = 0;Frame frame;// 無限循環抓取實時視頻幀while ((frame = grabber.grabImage()) != null) {// 可根據需要添加實時顯示、處理等操作BufferedImage image = converter.convert(frame);if (image != null) {// 構造圖片輸出路徑,例如 live_frame_0001.jpgString outputFileName = outputDirPath + File.separator + String.format("live_frame_%04d.jpg", frameCount);File outputFile = new File(outputFileName);ImageIO.write(image, "jpg", outputFile);System.out.println("保存幀:" + frameCount + " 到文件:" + outputFileName);}frameCount++;// 根據抓取幀率或其他需求設置適當延時,避免過快抽幀TimeUnit.MILLISECONDS.sleep(50);if (frameCount > 5) {break;}}System.out.println("抓取結束,共抓取 " + frameCount + " 幀");// 停止抓取器grabber.stop();} catch (Exception e) {log.error("抓取視頻幀出錯:", e);}}
}
2、ffmpeg 命令寫法
@Slf4j
public class FfmpegProcess {static String rtspUrl = VideoConstant.RTSP_URL_1;static String streamOutputFile = VideoConstant.FRAME_FILE_PREFIX + File.separator + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ".jpg";public static void main(String[] args) throws Exception {String command = getStreamFrameExtractionCommand(rtspUrl, streamOutputFile);System.out.println("執行命令: " + command);// 創建操作系統進程ProcessBuilder builder = new ProcessBuilder();// 執行命令executeCommand(builder, command, "windows");// 合并標準輸出和標準錯誤輸出流builder.redirectErrorStream(true);Process process = builder.start();// 異步讀取輸出流,避免阻塞CompletableFuture<Void> future = readOutputAsync(process.getInputStream());int exitCode = process.waitFor();if (exitCode == 0) {System.out.println("執行成功");} else {System.err.println("執行過程中出現錯誤,退出代碼:" + exitCode);}}/*** 獲取實時視頻流抽幀的命令(只抓取一張圖片)** @param url 視頻流 url,例如 rtsp 流地址* @param outputFile 輸出文件路徑* @return 實時視頻流抽幀的命令*/private static String getStreamFrameExtractionCommand(String url, String outputFile) {return String.format("%s -y -i %s -ss 1 -frames:v 1 %s", VideoConstant.FFMPEG_PATH, url, outputFile);}/*** 異步讀取 ffmpeg 輸出流*/private static CompletableFuture<Void> readOutputAsync(InputStream inputStream) {return CompletableFuture.runAsync(() -> {try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}} catch (IOException e) {log.error("讀取 ffmpeg 輸出流異常", e);}});}/*** 執行命令** @param builder 進程構建器* @param command 命令* @param osType 操作系統類型*/private static void executeCommand(ProcessBuilder builder, String command, String osType) {switch (osType) {case "windows":builder.command("cmd", "/c", command);break;case "Linux":case "macOS":builder.command("bash", "-c", command);break;default:throw new RuntimeException("不支持的操作系統類型");}}
}
學習參考
- https://blog.csdn.net/asialee_bird/article/details/129014872?utm_source=chatgpt.com
- 公共有效rtmp、rtsp、m3u8、音頻視頻測試地址(2025.1.23更新)_公開的rtsp流媒體地址-CSDN博客