JavaCV入门指南:帧录制器/推流器(FrameRecorder)的原理与应用

/ Java / 1 条评论 / 6082浏览

JavaCV入门指南:帧录制器/推流器(FrameRecorder)的原理与应用

## 前言 上一章大体讲解了FrameGrabber(抓取器/采集器),本章就FrameRecorder展开探索。

FrameRecorder(录制器/推流器)介绍

用于音视频/图片的封装、编码、推流和录制保存等操作。把从FrameGrabber或者FrameFilter获取的Frame中的数据取出并进行编码、封装、推流发送等操作流程。为了方便理解和阅读,下文开始我们统一把FrameRecorder简称为:录制器

FrameRecorder的结构和分析

FrameRecorder与FrameGrabber类似,也是个抽象类,抽象了所有录制器的通用方法和一些共用属性。FrameRecorder只有两个子类实现:FFmpegFrameRecorderOpenCVFrameRecorder

两个FrameRecorder实现类的介绍

总得来说,音视频这块还是首选ffmpeg的录制器。但是凡事都有例外,由于javacv的包实在太大,开发的时候下载依赖都要半天。为了减少程序的体积大小,如果只需要进行图像处理/图像那个识别的话只使用opencv的情况就能解决问题,那就opencvFrameGrabber和OpenCVFrameRecorder配套使用吧,毕竟可以省很多空间。

同样的,如果只是做音视频流媒体,那么能使用ffmpeg解决就不要用其他库了,能节省一点空间是一点,确实javacv整个完整包太大了,快要1G了。。。这些都是题外话了,还是回归正题吧。

FrameRecorder的结构和流程

FrameRecorder的整个代码结构和运作流程很简单

初始化和设置参数--->start()--->循环record(Frame frame)--->close()

close()包含stop()和release()两个操作,这个要注意。由于只有两个实现类,就直接分开单独分析了

FFmpegFrameRecorder结构和分析

FFmpegFrameRecorder是比较复杂的,我们主要它来实现像素格式转换、视频编码和录制推流。我们把流程分为转封装流程和转码流程

  1. 转封装流程:
FFmpegFrameRecorder初始化-->start()-->循环recordPacket(AVPacket)-->close()

这里的AVPacket是未解码的解复用视频帧。转封装的示例:转封装在rtsp转rtmp流中的应用 2. 转码编码流程:

FFmpegFrameRecorder初始化-->start()-->循环record(Frame)/recordImage()/recordSamples()-->close()

转码的示例:

FFmpegFrameRecorder初始化及参数说明

FFmpegFrameRecorder初始化支持文件地址、流媒体推流地址、OutputStream流的形式和imageWidth(视频宽度)、imageHeight(图像高度)、audioChannels(音频通道,1-单声道,2-立体声)

FFmpegFrameRecorder参数较多,不一一列举,直接看代码中的参数说明:

FFmpegFrameRecorder recorder=new FFmpegFrameRecorder(output, width,height,0);
recorder.setVideoCodecName(videoCodecName);//优先级高于videoCodec
recorder.setVideoCodec(videoCodec);//只有在videoCodecName没有设置或者没有找到videoCodecName的情况下才会使用videoCodec
//recorder.setFormat(format);//只支持flv,mp4,3gp和avi四种格式,flv:AV_CODEC_ID_FLV1;mp4:AV_CODEC_ID_MPEG4;3gp:AV_CODEC_ID_H263;avi:AV_CODEC_ID_HUFFYUV;
recorder.setPixelFormat(pixelFormat);// 只有pixelFormat,width,height三个参数任意一个不为空才会进行像素格式转换
recorder.setImageScalingFlags(imageScalingFlags);//缩放,像素格式转换时使用,但是并不是像素格式转换的判断参数之一
recorder.setGopSize(gopSize);//gop间隔
recorder.setFrameRate(frameRate);//帧率
recorder.setVideoBitrate(videoBitrate);
recorder.setVideoQuality(videoQuality);
//下面这三个参数任意一个会触发音频编码
recorder.setSampleFormat(sampleFormat);//音频采样格式,使用avutil中的像素格式常量,例如:avutil.AV_SAMPLE_FMT_NONE
recorder.setAudioChannels(audioChannels);
recorder.setSampleRate(sampleRate);
recorder.start();

FFmpegFrameRecorder的start

start中其实做了很多事情:一堆初始化操作、打开网络流、查找编码器、把format字符转换成对应的videoCodec和videoFormat等等。

FFmpegFrameRecorder中的各种record

FFmpegFrameRecorder中的stop和close

非常需要的注意的是,当我们在录制文件的时候,一定要保证stop()方法被调用,因为stop里面包含了写出文件头的操作,如果没有调用stop就会导致录制的文件损坏,无法被识别或无法播放。close()方法中包含stop()和release()方法

OpenCVFrameRecorder结构分析

OpenCVFrameRecorder的代码很简单,不到120行的代码,主要是基于opencv的videoWriter封装,流程与FrameRecorder的相同:

初始化和设置参数--->start()--->循环record(Frame frame)--->close()

在start中会初始化opencv的VideoWriter,VideoWriter是opencv中用来保存视频的模块,是比较简单的,可以对照参考opencv官方文档介绍:https://docs.opencv.org/master/dd/d9e/classcv_1_1VideoWriter.html

OpenCVFrameRecorder的初始化和参数设置

OpenCVFrameRecorder初始化参数只有三个:filename(文件名称或者文件保存地址),imageWidth(图像宽度), imageHeight(图像高度)。但是OpenCVFrameRecorder的有作用的参数只有六个,其中pixelFormat和videoCodec这两个比较特殊。

OpenCVFrameRecorder开始录制start

调用start时其实就是初始化了opencv的VideoWriter。这个比较简单,传了上面那六个参数。

OpenCVFrameRecorder录制record

在循环中把Frame放进record中就可以一直录制,内部是通过OpenCVFrameConverter把Frame转换成了Mat调用VideoWriter进行录制。

OpenCVFrameRecorder销毁stop/close

不管是stop还是close,其实都是调用的release()方法, release则是调用了opencv的VideoWriter的release(),结构很简单。