沉梦听雨的编程指南 沉梦听雨的编程指南
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)

沉梦听雨

时间是最好的浸渍剂,而沉淀是最好的提纯器🚀
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)
  • API网关

  • RPC

  • 消息队列

  • Spring Cloud

  • 分布式事务

  • 云存储

  • 搜索引擎

  • 多媒体框架

    • ffmpeg入门
    • ffmpeg参数篇
    • ffmpeg实现视频转码
    • ffmpeg实现视频流抽帧
      • 抽取实时视频帧
      • ffmpeg 命令
        • 示例 1
        • 示例 2
        • 示例 3
      • 代码示例
        • 引入依赖
        • 1、javacv 写法
        • 2、ffmpeg 命令写法
      • 学习参考
  • 虚拟机

  • 微服务
  • 多媒体框架
沉梦听雨
2025-02-08
目录

ffmpeg实现视频流抽帧

# ffmpeg 实现视频流抽帧

# 抽取实时视频帧

如果你的实时视频是通过 RTSP、UDP 或其他协议获取的,可以直接调用 FFmpeg 命令来抽取帧。

# ffmpeg 命令

# 示例 1

ffmpeg -i rtsp://your_rtsp_stream_url -vf fps=1 -update 1 output.jpg
1

说明:

  • -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"
1

说明:

  • -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"
1

说明:

  • 从指定的 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
2
3
4
5
6
7
8
9
10
11
12

# 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.jpg
                    String 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);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 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("不支持的操作系统类型");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 学习参考

  • https://blog.csdn.net/asialee_bird/article/details/129014872?utm_source=chatgpt.com (opens new window)
  • 公共有效rtmp、rtsp、m3u8、音频视频测试地址(2025.1.23更新)_公开的rtsp流媒体地址-CSDN博客 (opens new window)
上次更新: 2025/2/8 18:02:22
ffmpeg实现视频转码
Linux基础小结

← ffmpeg实现视频转码 Linux基础小结→

Theme by Vdoing | Copyright © 2023-2025 沉梦听雨 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式