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博客