一、依赖下载
Maven依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.4</version>
</dependency>
Gradle依赖
dependencies {
compile group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.4'
}
一、拉流流程(解码和解封装/解复用流程)
拉流主要用到FrameGrabber及其子类,会涉及到解协议,解复用/解封装,解码三个流程。
FrameGrabber的子类很多,但是用来作为音视频拉流也一样只有FFmpegFrameGrabber和OpenCVFrameGrabber,同样为了采集摄像机头图像和图像处理操作使用OpenCVFrameGrabber及其他FrameGrabber的子类以外,一般情况下音视频拉流还是使用FFmpegFrameGrabber。
拉流时可能会遇到两种情况,一种是拉流时解码,得到的解码后的完整图像像素和pcm音频采样,方便进行图像处理、图像分析、美颜,音频去噪等等操作;另一种是拉流时解复用/解封装,方便直接走复用流程录制成文件或者复用推流。
拉流并解码
//解码流程
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url); //创建一个拉流器,url可以是音视频文件后者流媒体地址
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
for(;;){
//该操作完成了解协议,解封装/解复用和解码操作,默认得到的图像是yuv像素,音频则是pcm采样。
Frame frame=grabber.grab();
}
拉流并解复用/解封装(不解码)
//解复用/解封装流程
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);//创建一个拉流器,url可以是音视频文件后者流媒体地址
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
for(;;){
//该操作完成了解协议,解封装/解复用操作,如果视频源是h264/pcma默认得到的未解码的是一帧h264编码数据(i帧/P帧/B帧),音频则是未解码的pcma编码数据。
AVPacket pkt=grabber.grabPacket();
}
二、推流和转流
推流本身涉及到编码和封装流程,如果只讲推流,那应该只涉及FrameRecorder及其子类,除了少数对接设备的码流外,我们大部分情况下还是需要视频源才能推流,所以推流往往伴随着拉流操作,所以我们把推流和转流放在一起讲。
FrameRecorder的子类FFmpegFrameRecorder和OpenCVFrameRecorder都可以满足要求,但是OpenCVFrameRecorder推流并不包含音频,所以除了少数为了图像处理和大量采集图像样本库的行为以外,一般情况下都使用FFmpegFrameRecorder来推流。
FFmpegFrameRecorder推流同样分为两种情况:转码推流和复用/封装推流(不需要编码,对应解复用/解封装方式拉流)
转码并推流
//推流转码流程
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);//创建一个拉流器,url可以是音视频文件
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(url);//创建一个推流器,url可以是音视频文件后者流媒体地址
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
recorder.start();//初始化,会读取一些推流地址信息
for(;;){
//该操作完成了解协议,解封装/解复用和解码操作,默认得到的图像是yuv像素,音频则是pcm采样。
Frame frame=grabber.grab();
recorder.record(frame);
}
复用/封装并推流
注意FFmpegFrameRecorder的初始化方式是完全不同于上面的转码方式的。
//推流复用/封装流程
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);//创建一个拉流器,url可以是音视频文件
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(url);//创建一个推流器,url可以是音视频文件后者流媒体地址
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
recorder.start(grabber.getFormatContext());//初始化,由于是复用/封装流程,推流器需要拉流器的格式上下文信息才能初始化
for(;;){
//该操作完成了解协议,解封装/解复用操作,如果视频源是h264/pcma默认得到的未解码的是一帧h264编码数据(i帧/P帧/B帧),音频则是未解码的pcma编码数据。
AVPacket pkt=grabber.grabPacket();
//该操作完成了复用/封装推流,并不涉及编解码
recorder.recordPacket(pkt);
}
三、转流流程
在上面的"推流"中已经讲了,直接见上面两种”推流“方式即可
转码方式转流
与上面的"转码并推流"相同
转封装/复用方式转流
与上面的"复用/封装并推流"相同
四、视频流录制文件
与上面的"转码并推流"流程相同,只需要将url改成文件地址即可。
五、视频文件转码
与上面的"转码并推流"流程相同,只需要将url改成文件地址,并设置格式和编码
//推流转码流程
FmpegFrameGrabber grabber = new FFmpegFrameGrabber("xx.mp4");//创建一个拉流器,url是视频文件,假设xx.mp4是h265/aac编码的视频文件
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("xx.flv");//创建一个推流器,url是视频文件,假设我们需要转成xx.flv的格式是h264/aac编码的视频文件
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
recorder.start();//初始化,会读取一些推流地址信息
recorder.setFormat("flv");//设置视频封装格式是flv
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置h264编码
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);//设置aac编码
for(;;){
//该操作完成了解协议,解封装/解复用和解码操作,默认得到的图像是yuv像素,音频则是pcm采样。
Frame frame=grabber.grab();
//该操作完成了编码、封装/复用、推流/录制的操作,录制的音视频已经是h264/aac编码的flv文件了
recorder.record(frame);
}
六、屏幕/窗口录制(截屏、录屏)
屏幕采集稍微复杂,但是javacv依然可以支持windows/linux/mac/ios等平台的屏幕采集。
windows下的屏幕截图依靠的是gdi库,javacv1.5.1以上版本的windows server需要手动安装Media Foundation库才可以使用录屏功能。
//读取windows屏幕
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("desktop");
//FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("title=窗口标题名称");//获取windows某个窗口画面,不支持中文,必须是窗口标题名称,而不是程序进程名称
grabber.setFormat("gdigrab");// 基于windows的gdigrab的输入格式
grabber.start();
for(;;){
//该操作完成了采集屏幕图像的操作,得到的是像素图像
Frame frame=grabber.grab();
}
macos/ios的录屏基于avfoundation。
//mac录屏-eguid原创
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("0");//获取摄像头画面
//FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("1");//获取屏幕画面
//FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("1:0");//同时获取屏幕画面和音频采样
grabber.setFormat("avfoundation");// 基于macos/ios的avfoundation的方式录屏
grabber.start();//初始化,网络不好可能会阻塞,会读取一些音视频分析得到音视频格式编码信息等
for(;;){
//该操作完成了采集屏幕图像的操作,得到的是像素图像
Frame frame=grabber.grab();
}
七、采集本机或者usb外接摄像头
由于采集摄像头都是图像像素数据,所以可以用FFmpegFrameGrabber、OpenCVFrameGrabber,我们主要使用OpenCVFrameGrabber来演示使用本机摄像头或者usb外接摄像头
//采集摄像头图像
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);//opencv采集摄像头,一般的电脑和移动端设备中摄像头默认序号是0,不排除其他情况,
grabber.start();//初始化
for(;;){
grabber.grab(); //获取摄像头图像并在窗口中显示,这里Frame frame=grabber.grab()得到是解码后的视频图像
}
八、录制动态图片
动态图片也像视频一样,由多张图片组成,下面以gif为例简单写一下:
//读取视频源或者摄像头、屏幕等
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);
grabber.start();
//gif录制器
FFmpegFrameRecorder recorder =new FFmpegFrameRecorder(output,width,height,0);
recorder.setPixelFormat(avutil.AV_PIX_FMT_RGB4_BYTE);//设置像素格式
recorder.setVideoCodec(avcodec.AV_CODEC_ID_GIF);//设置录制的视频/图片编码
if(frameRate!=null) {
recorder.setFrameRate(frameRate);//设置帧率
}
recorder.start();
for(;;){
//该操作完成了采集屏幕图像的操作,得到的是像素图像
Frame frame=grabber.grab();
recorder.record(frame);
}
九、制作icon图标
icon图标的制作建立在截图的基础上,由于icon只支持bmp和png编码的图片,bmp和png所需要的像素格式和我们FrameGrabber及其子类默认得到的yuv像素格式不同,所以我们需要进行像素格式转换,将yuv转成rgb/rgba等被bmp和png支持的像素格式才行。
//制作icon流程
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url);
grabber.start();
//录制icon
FFmpegFrameRecorder recorder=new FFmpegFrameRecorder(outputFile,0);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_PNG);
recorder.setPixelFormat(avutil.AV_PIX_FMT_RGBA);//设置像素格式
recorder.setImageWidth(255);
recorder.setImageHeight(255);
recorder.start();
Frame frame = null;
// 只采集图像
if((frame = grabber.grabImage())!=null) {
recorder.record(frame);
}
recorder.stop();
grabber.stop();