环境准备
Maven 依赖
xml
<!-- JavaCV - 包含 OpenCV 和 FFmpeg -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.10</version>
</dependency>
为什么选择 JavaCV?
- ✅ 跨平台:自动包含 Windows/Linux/macOS 的 FFmpeg 和 OpenCV 二进制文件
- ✅ 无需手动配置:无需单独安装 FFmpeg 或 OpenCV
- ✅ 功能完整:支持所有主流视频格式和协议(RTSP、RTMP、HTTP)
核心依赖库介绍
JavaCV vs OpenCV 选择策略
| 场景 | 推荐库 | 原因 |
|---|---|---|
| 网络流 (RTSP/RTMP/HTTP) | JavaCV FFmpegFrameGrabber | FFmpeg 对网络流支持更好,参数可配置性强 |
| 视频文件 | JavaCV FFmpegFrameGrabber | 支持更多编解码器和格式 |
| 摄像头 | ByteDeco OpenCV VideoCapture | 更稳定,延迟更低 |
| 单张图片 | ByteDeco OpenCV imread | 简单直接 |
ByteDeco OpenCV
- 包名 :
org.bytedeco.opencv - 核心类 :
VideoCapture,Mat,imread,imwrite - 适用场景: 摄像头、单张图片
JavaCV FFmpeg
- 包名 :
org.bytedeco.javacv - 核心类 :
FFmpegFrameGrabber,Frame,OpenCVFrameConverter - 适用场景: 网络流、视频文件
读取单张图片
基本示例
java
import org.bytedeco.opencv.opencv_core.Mat;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
public class ReadImageExample {
public static void main(String[] args) {
// 读取图片
Mat image = imread("input.jpg");
// 检查是否成功读取
if (image.empty()) {
System.err.println("无法读取图片!");
return;
}
// 打印图片信息
System.out.println("图片尺寸: " + image.cols() + " x " + image.rows());
System.out.println("通道数: " + image.channels());
// ==================== 在这里处理图片 ====================
// 可以进行:
// - 目标检测(YOLOv8/v11)
// - 图像分割
// - 特征提取
// - 图像增强
// - 滤波处理
// etc.
// 保存处理后的图片
imwrite("output.jpg", image);
System.out.println("图片已保存到 output.jpg");
// 释放资源
image.release();
}
}
批量读取图片
java
import org.bytedeco.opencv.opencv_core.Mat;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
import java.io.File;
public class BatchReadImageExample {
public static void main(String[] args) {
// 图片目录
String imageFolder = "images/";
// 获取所有图片文件
File folder = new File(imageFolder);
File[] files = folder.listFiles((dir, name) ->
name.toLowerCase().matches(".*\\.(jpg|jpeg|png|bmp)$")
);
if (files == null || files.length == 0) {
System.err.println("目录为空或不存在");
return;
}
System.out.println("找到 " + files.length + " 张图片");
// 循环处理每张图片
int processedCount = 0;
for (File file : files) {
Mat image = imread(file.getAbsolutePath());
if (image.empty()) {
System.err.println("无法读取: " + file.getName());
continue;
}
System.out.println("处理: " + file.getName() +
" (" + image.cols() + "x" + image.rows() + ")");
// ==================== 在这里处理每张图片 ====================
// 保存处理结果(可选)
String outputPath = "output/" + file.getName();
imwrite(outputPath, image);
// 释放资源
image.release();
processedCount++;
}
System.out.println("批量处理完成,共处理 " + processedCount + " 张图片");
}
}
读取视频文件
方式一:使用 OpenCV VideoCapture
java
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_videoio.VideoCapture;
import static org.bytedeco.opencv.global.opencv_videoio.*;
public class ReadVideoFileExample {
public static void main(String[] args) {
String videoPath = "video.mp4";
// 创建 VideoCapture 对象
VideoCapture capture = new VideoCapture();
// 打开视频文件
if (!capture.open(videoPath)) {
System.err.println("无法打开视频文件: " + videoPath);
return;
}
// 获取视频信息
int frameWidth = (int) capture.get(CAP_PROP_FRAME_WIDTH);
int frameHeight = (int) capture.get(CAP_PROP_FRAME_HEIGHT);
double fps = capture.get(CAP_PROP_FPS);
long totalFrames = (long) capture.get(CAP_PROP_FRAME_COUNT);
System.out.println("========== 视频信息 ==========");
System.out.println("分辨率: " + frameWidth + " x " + frameHeight);
System.out.println("帧率: " + fps + " FPS");
System.out.println("总帧数: " + totalFrames);
System.out.println("时长: " + (totalFrames / fps) + " 秒");
System.out.println("==============================\n");
// 创建 Mat 对象存储帧
Mat frame = new Mat();
int frameCount = 0;
// 循环读取每一帧
while (capture.read(frame) && !frame.empty()) {
frameCount++;
// 打印进度
if (frameCount % 100 == 0) {
double progress = (frameCount * 100.0) / totalFrames;
System.out.printf("处理进度: %.1f%% (第 %d 帧)\n", progress, frameCount);
}
// ==================== 在这里处理每一帧 ====================
// 示例:每 30 帧保存一张图片
if (frameCount % 30 == 0) {
String outputPath = "frames/frame_" + frameCount + ".jpg";
imwrite(outputPath, frame);
}
}
System.out.println("\n视频处理完成,共处理 " + frameCount + " 帧");
// 释放资源
frame.release();
capture.release();
}
}
方式二:使用 JavaCV FFmpegFrameGrabber(推荐)
优势:支持更多格式、更稳定、参数可配置
java
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.Mat;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
public class ReadVideoFileJavaCVExample {
public static void main(String[] args) {
String videoPath = "video.mp4";
// 初始化 FFmpegFrameGrabber
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoPath);
try {
// 配置参数
grabber.setPixelFormat(org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_BGR24);
// 启动
System.out.println("正在打开视频文件: " + videoPath);
grabber.start();
System.out.println("打开成功!\n");
// 获取视频信息
int width = grabber.getImageWidth();
int height = grabber.getImageHeight();
double fps = grabber.getFrameRate();
long totalFrames = grabber.getLengthInFrames();
System.out.println("========== 视频信息 ==========");
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + fps + " FPS");
System.out.println("总帧数: " + totalFrames);
System.out.println("时长: " + (totalFrames / fps) + " 秒");
System.out.println("==============================\n");
// 创建 Frame 转 Mat 的转换器
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
// 循环读取每一帧
Frame frame;
int frameCount = 0;
while ((frame = grabber.grabImage()) != null) {
frameCount++;
// 转换为 OpenCV Mat
Mat mat = converter.convert(frame);
if (mat != null && !mat.empty()) {
// 打印进度
if (frameCount % 100 == 0) {
double progress = (frameCount * 100.0) / totalFrames;
System.out.printf("处理进度: %.1f%% (第 %d 帧)\n", progress, frameCount);
}
// ==================== 在这里处理每一帧 ====================
// 示例:保存关键帧
if (frameCount % 50 == 0) {
String outputPath = "keyframes/frame_" + frameCount + ".jpg";
imwrite(outputPath, mat);
}
// 释放 Mat
mat.release();
}
}
System.out.println("\n视频处理完成,共处理 " + frameCount + " 帧");
// 释放资源
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("处理视频时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
读取摄像头
基本示例
java
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_videoio.VideoCapture;
import static org.bytedeco.opencv.global.opencv_videoio.*;
public class ReadCameraExample {
public static void main(String[] args) {
int cameraId = 0; // 0 表示默认摄像头,1 表示第二个摄像头
// 创建 VideoCapture 对象
VideoCapture capture = new VideoCapture();
// 打开摄像头
if (!capture.open(cameraId)) {
System.err.println("无法打开摄像头: " + cameraId);
return;
}
// 获取摄像头信息
int width = (int) capture.get(CAP_PROP_FRAME_WIDTH);
int height = (int) capture.get(CAP_PROP_FRAME_HEIGHT);
double fps = capture.get(CAP_PROP_FPS);
System.out.println("========== 摄像头信息 ==========");
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + fps + " FPS");
System.out.println("================================\n");
System.out.println("开始从摄像头读取帧,按 Ctrl+C 停止...\n");
// 创建 Mat 对象存储帧
Mat frame = new Mat();
int frameCount = 0;
long startTime = System.currentTimeMillis();
// 循环读取帧
while (capture.read(frame) && !frame.empty()) {
frameCount++;
// ==================== 在这里处理每一帧 ====================
}
// 释放资源
frame.release();
capture.release();
}
}
自定义摄像头分辨率
java
public class CustomCameraResolutionExample {
public static void main(String[] args) {
VideoCapture capture = new VideoCapture();
capture.open(0);
// 设置分辨率(在读取帧之前设置)
capture.set(CAP_PROP_FRAME_WIDTH, 1920);
capture.set(CAP_PROP_FRAME_HEIGHT, 1080);
// 设置帧率
capture.set(CAP_PROP_FPS, 30);
// 验证设置是否生效
int actualWidth = (int) capture.get(CAP_PROP_FRAME_WIDTH);
int actualHeight = (int) capture.get(CAP_PROP_FRAME_HEIGHT);
double actualFps = capture.get(CAP_PROP_FPS);
System.out.println("实际分辨率: " + actualWidth + " x " + actualHeight);
System.out.println("实际帧率: " + actualFps + " FPS");
// 读取帧
Mat frame = new Mat();
while (capture.read(frame) && !frame.empty()) {
// ==================== 处理帧 ====================
}
frame.release();
capture.release();
}
}
读取网络流(RTSP/RTMP/HTTP)
RTSP 流示例(监控摄像头)
RTSP(Real Time Streaming Protocol)常用于 IP 摄像头、监控系统。
java
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.Mat;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
public class ReadRTSPStreamExample {
public static void main(String[] args) {
// RTSP 流地址(示例格式)
String rtspUrl = "rtsp://username:password@192.168.1.100:554/stream1";
// 或者无需认证的:rtsp://192.168.1.100:554/stream1
// 创建 FFmpegFrameGrabber
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
try {
// ===== 配置 RTSP 参数(重要!)=====
grabber.setOption("rtsp_transport", "tcp"); // 使用 TCP 传输(更稳定)
grabber.setOption("buffer_size", "1024000"); // 增加缓冲区 1MB
grabber.setOption("max_delay", "500000"); // 最大延迟 500ms
grabber.setOption("stimeout", "10000000"); // 超时 10 秒
// 设置像素格式为 BGR24
grabber.setPixelFormat(org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_BGR24);
// 抑制 FFmpeg 警告日志
org.bytedeco.ffmpeg.global.avutil.av_log_set_level(
org.bytedeco.ffmpeg.global.avutil.AV_LOG_ERROR
);
// 启动
grabber.start();
// 获取流信息
int width = grabber.getImageWidth();
int height = grabber.getImageHeight();
double fps = grabber.getFrameRate();
System.out.println("========== RTSP 流信息 ==========");
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + fps + " FPS");
System.out.println("==================================\n");
// 创建转换器
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
// 循环读取帧
Frame frame;
int frameCount = 0;
long startTime = System.currentTimeMillis();
while ((frame = grabber.grabImage()) != null) {
frameCount++;
// 转换为 Mat
Mat mat = converter.convert(frame);
if (mat != null && !mat.empty()) {
// ==================== 在这里处理每一帧 ====================
// 释放 Mat
mat.release();
}
}
// 释放资源
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("读取 RTSP 流时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
RTMP 流示例(直播流)
RTMP(Real-Time Messaging Protocol)常用于直播推流、流媒体服务。
java
public class ReadRTMPStreamExample {
public static void main(String[] args) {
String rtmpUrl = "rtmp://live.example.com/live/stream";
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtmpUrl);
try {
// RTMP 特定配置
grabber.setOption("flv_metadata", "1");
grabber.setOption("buffer_size", "1024000");
grabber.setPixelFormat(org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_BGR24);
// 抑制日志
org.bytedeco.ffmpeg.global.avutil.av_log_set_level(
org.bytedeco.ffmpeg.global.avutil.AV_LOG_ERROR
);
grabber.start();
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
Frame frame;
int frameCount = 0;
while ((frame = grabber.grabImage()) != null) {
frameCount++;
Mat mat = converter.convert(frame);
if (mat != null && !mat.empty()) {
// ==================== 在这里处理每一帧 ====================
mat.release();
}
}
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("读取 RTMP 流时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
HTTP 视频流示例
java
public class ReadHTTPStreamExample {
public static void main(String[] args) {
String httpUrl = "http://example.com/stream.mjpg";
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(httpUrl);
try {
grabber.setPixelFormat(org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_BGR24);
System.out.println("正在连接 HTTP 流: " + httpUrl);
grabber.start();
System.out.println("连接成功!\n");
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
Frame frame;
int frameCount = 0;
while ((frame = grabber.grabImage()) != null) {
frameCount++;
Mat mat = converter.convert(frame);
if (mat != null && !mat.empty()) {
// ==================== 在这里处理每一帧 ====================
mat.release();
}
}
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("读取 HTTP 流时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
视频推流(RTSP/RTMP/文件录制)
本节介绍如何将处理后的视频帧推送到网络流服务器(RTSP/RTMP)或录制为视频文件。这在以下场景非常有用:
- 📹 将处理后的视频(带检测框、标注)推流到监控平台
- 💾 录制告警视频片段保存到本地或云存储
- 🔄 视频转码和格式转换
- 📡 实时视频分析结果的二次分发
核心工具:FFmpegFrameRecorder
JavaCV 提供了 FFmpegFrameRecorder 类,用于将帧写入视频文件或推送到流媒体服务器。
录制视频到文件
基本示例
java
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
public class RecordVideoToFileExample {
public static void main(String[] args) {
String inputVideo = "input.mp4";
String outputFile = "output.mp4";
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputVideo);
FFmpegFrameRecorder recorder = null;
try {
// 1. 启动输入
grabber.start();
System.out.println("成功打开输入视频");
// 2. 获取视频参数
int width = grabber.getImageWidth();
int height = grabber.getImageHeight();
double frameRate = grabber.getFrameRate();
int audioChannels = grabber.getAudioChannels();
System.out.println("========== 视频信息 ==========");
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + frameRate + " FPS");
System.out.println("==============================\n");
// 3. 创建录制器
recorder = new FFmpegFrameRecorder(outputFile, width, height, audioChannels);
recorder.setVideoCodec(org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264);
recorder.setFormat("mp4");
recorder.setFrameRate(frameRate);
recorder.setVideoBitrate(2000000); // 2 Mbps
// 音频设置(如果有音频)
if (audioChannels > 0) {
recorder.setAudioCodec(org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_AAC);
recorder.setSampleRate(grabber.getSampleRate());
}
// 启动录制器
recorder.start();
System.out.println("开始录制到文件: " + outputFile + "\n");
// 4. 读取并写入帧
Frame frame;
int frameCount = 0;
while ((frame = grabber.grab()) != null) {
frameCount++;
// ==================== 在这里可以处理帧 ====================
// 录制帧
recorder.record(frame);
if (frameCount % 100 == 0) {
System.out.println("已录制 " + frameCount + " 帧");
}
}
System.out.println("\n录制完成!共录制 " + frameCount + " 帧");
System.out.println("输出文件: " + outputFile);
// 5. 释放资源
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("录制视频时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
推流到 RTSP 服务器
RTSP 推流适用于监控系统、视频管理平台(如海康、大华等)。
java
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
public class PushToRTSPExample {
public static void main(String[] args) {
String inputSource = "rtsp://192.168.1.100:554/stream1"; // 输入流
String rtspOutput = "rtsp://localhost:8554/live"; // 输出 RTSP 地址
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputSource);
FFmpegFrameRecorder recorder = null;
try {
// 配置并启动输入
grabber.setOption("rtsp_transport", "tcp");
grabber.start();
System.out.println("成功连接输入流");
// 获取视频参数
int width = grabber.getImageWidth();
int height = grabber.getImageHeight();
double frameRate = grabber.getFrameRate();
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + frameRate + " FPS\n");
// 创建 RTSP 推流器
recorder = new FFmpegFrameRecorder(rtspOutput, width, height);
recorder.setVideoCodec(org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264);
recorder.setFormat("rtsp");
recorder.setFrameRate(frameRate);
recorder.setVideoBitrate(2000000);
recorder.setGopSize((int) frameRate * 2); // 2 秒一个关键帧
// RTSP 推流特定参数
recorder.setOption("rtsp_transport", "tcp");
recorder.setOption("muxdelay", "0.1");
// 启动推流
recorder.start();
System.out.println("开始推流到: " + rtspOutput + "\n");
// 读取并推流
Frame frame;
int frameCount = 0;
long startTime = System.currentTimeMillis();
while ((frame = grabber.grabImage()) != null) {
frameCount++;
// ==================== 在这里处理帧 ====================
// 可以添加检测结果、标注、告警信息等
// 推送帧
recorder.record(frame);
if (frameCount % 100 == 0) {
long elapsed = System.currentTimeMillis() - startTime;
double actualFps = frameCount * 1000.0 / elapsed;
}
// 可选:设置停止条件
if (frameCount >= 3000) {
System.out.println("\n达到帧数限制,停止推流");
break;
}
}
// 释放资源
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
} catch (Exception e) {
System.err.println("RTSP 推流出错: " + e.getMessage());
e.printStackTrace();
}
}
}
常见推流参数配置
编码器参数对照表
| 参数 | 说明 | 推荐值 | 备注 |
|---|---|---|---|
setVideoCodec |
视频编码器 | AV_CODEC_ID_H264 |
H.264 兼容性最好 |
setFormat |
封装格式 | "mp4" / "flv" / "rtsp" |
RTMP 用 flv,RTSP 用 rtsp |
setFrameRate |
帧率 | 25.0 / 30.0 |
根据输入流设置 |
setVideoBitrate |
视频码率 | 2000000 (2 Mbps) |
高清推荐 3-5 Mbps |
setGopSize |
GOP 大小 | frameRate * 2 |
2 秒一个关键帧 |
setPixelFormat |
像素格式 | AV_PIX_FMT_YUV420P |
兼容性最好 |
H.264 编码优化选项
java
// 编码速度优先(低延迟)
recorder.setVideoOption("preset", "ultrafast"); // 最快编码
recorder.setVideoOption("tune", "zerolatency"); // 零延迟调优
// 质量优先(录制存档)
recorder.setVideoOption("preset", "slow"); // 慢速编码
recorder.setVideoOption("crf", "18"); // 高质量(18-23)
// 平衡模式(推荐)
recorder.setVideoOption("preset", "veryfast"); // 很快编码
recorder.setVideoOption("tune", "zerolatency"); // 零延迟
recorder.setVideoOption("crf", "23"); // 中等质量
音频编码参数
java
if (audioChannels > 0) {
recorder.setAudioCodec(org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_AAC);
recorder.setSampleRate(44100); // 采样率 44.1kHz
recorder.setAudioBitrate(128000); // 音频码率 128kbps
recorder.setAudioChannels(2); // 立体声
}
推流应用场景
| 场景 | 输入源 | 输出目标 | 典型应用 |
|---|---|---|---|
| 监控告警录制 | RTSP 摄像头 | 本地 MP4 文件 | 检测到异常时录制视频片段 |
| 实时检测展示 | 摄像头 | RTSP 服务器 | 将检测结果推送到监控平台 |
| 直播审核 | RTMP 直播流 | RTMP 服务器 | 实时审核后转推到 CDN |
| 视频转码 | 视频文件 | 不同格式文件 | 格式转换和压缩 |
| 多路分发 | 单个输入 | 多个 RTSP/RTMP | 一个源推送到多个目标 |
总结
本节介绍了使用 JavaCV 进行视频推流的完整方案:
- ✅ 录制视频文件:将处理后的帧保存为 MP4 等格式
- ✅ RTSP 推流:推送到监控平台和视频管理系统
- ✅ RTMP 推流:推送到直播平台
- ✅ 实时处理推流:边处理边推流的完整流程
- ✅ 参数优化:编码器、码率、帧率等配置指南
- ✅ 故障排查:常见问题和解决方案
关键要点:
- 使用
FFmpegFrameRecorder进行推流和录制 - 根据场景选择合适的编码参数(速度 vs 质量)
- 使用
ultrafast+zerolatency实现低延迟推流 - 合理设置 GOP 大小和码率
实用工具函数
自动重连函数
java
public static VideoCapture openVideoWithRetry(String path, int maxRetries) {
VideoCapture capture = new VideoCapture();
int retryCount = 0;
while (retryCount < maxRetries) {
if (capture.open(path)) {
System.out.println("成功打开视频源: " + path);
return capture;
}
retryCount++;
System.err.println("打开失败,重试 " + retryCount + "/" + maxRetries);
try {
Thread.sleep(2000); // 等待 2 秒
} catch (InterruptedException e) {
break;
}
}
System.err.println("多次重试失败");
return null;
}
// 使用
VideoCapture capture = openVideoWithRetry("rtsp://192.168.1.100:554/stream1", 3);
if (capture != null) {
// 处理视频
}
获取视频信息
java
public static void printVideoInfo(VideoCapture capture) {
if (capture == null || !capture.isOpened()) {
System.err.println("视频源未打开");
return;
}
int width = (int) capture.get(CAP_PROP_FRAME_WIDTH);
int height = (int) capture.get(CAP_PROP_FRAME_HEIGHT);
double fps = capture.get(CAP_PROP_FPS);
long totalFrames = (long) capture.get(CAP_PROP_FRAME_COUNT);
System.out.println("========== 视频信息 ==========");
System.out.println("分辨率: " + width + " x " + height);
System.out.println("帧率: " + fps + " FPS");
if (totalFrames > 0) {
System.out.println("总帧数: " + totalFrames);
System.out.println("时长: " + (totalFrames / fps) + " 秒");
} else {
System.out.println("总帧数: 未知(实时流)");
}
System.out.println("==============================");
}
总结
本文介绍了使用 Java 读取各种视频源的完整方案,采用面向过程编程风格:
| 数据源类型 | 推荐工具 | 代码复杂度 | 适用场景 |
|---|---|---|---|
| 单张图片 | OpenCV imread |
⭐ | 批量图像处理、离线分析 |
| 视频文件 | JavaCV FFmpegFrameGrabber |
⭐⭐ | 视频分析、离线检测 |
| 摄像头 | OpenCV VideoCapture |
⭐⭐ | 实时监控、人机交互 |
| 网络流 | JavaCV FFmpegFrameGrabber |
⭐⭐⭐ | 网络监控、直播分析 |
因为是javacv-platform 此依赖包含了ffmpeg的相关封装,不需要本地安装ffmpeg,和引入opencv的相关的opencv_videoio_ffmpeg470_64.dll等额外依赖,既能实现跨平台又无需安装ffmpeg,以及依赖版本的问题。
关键要点:
- ✅ 代码简洁直观,易于理解和复制
- ✅ 根据场景选择 OpenCV 或 JavaCV
- ✅ 及时释放
Mat对象避免内存泄漏 - ✅ 配置合理的网络参数提高稳定性
- ✅ 使用多线程和跳帧策略优化性能
在循环处理每一帧时,可以进行:
- 🔍 目标检测(YOLOv8/v11)
- 🎨 图像分割
- 👤 人脸识别
- 🚗 车牌识别
- 📊 视频分析
- 🖼️ 图像增强
- 📹 视频录制
- 🚨 异常检测与告警
参考资料
- JavaCV 官方文档: https://github.com/bytedeco/javacv
- ByteDeco OpenCV: https://github.com/bytedeco/javacpp-presets/tree/master/opencv
- FFmpeg 参数文档: https://ffmpeg.org/ffmpeg-protocols.html