简述一下流程:
- 使用opencv从摄像头中读取数据。
- 将cv::Mat转换为AVFrame。
- 打开编码器(这里用的是H264)。
- 设置视频的详细参数,以及编码参数。
- 编码并进行写入输出文件。

从cv::Mat到AVFrame的转化如下:
AVFrame *PushOpencv::CVMatToAVFrame(cv::Mat &inMat, int YUV_TYPE) {
//得到Mat信息
AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
int width = inMat.cols;
int height = inMat.rows;
//创建AVFrame填充参数 注:调用者释放该frame
AVFrame *frame = av_frame_alloc();
frame->width = width;
frame->height = height;
frame->format = dstFormat;
//初始化AVFrame内部空间
int ret = av_frame_get_buffer(frame, 64);
if (ret < 0)
{
return nullptr;
}
ret = av_frame_make_writable(frame);
if (ret < 0)
{
return nullptr;
}
//转换颜色空间为YUV420
cv::cvtColor(inMat, inMat, cv::COLOR_BGR2YUV_I420);
//按YUV420格式,设置数据地址
int frame_size = width * height;
unsigned char *data = inMat.data;
memcpy(frame->data[0], data, frame_size);
memcpy(frame->data[1], data + frame_size, frame_size/4);
memcpy(frame->data[2], data + frame_size * 5/4, frame_size/4);
return frame;
}
这里默认用的是yuv420,你当然可以转换到yuv422或YUV444。注意422的数据分布是(frame_size, frame_size/2, frame_size/2),yuv444的数据分布是(frame_size, frame_size, frame_size)。
编码器参数设置如下:
int PushOpencv::open_codec(int width, int height, int den) {
int ret = 0;
avformat_network_init();
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec)
{
throw std::logic_error("Can`t find h264 encoder!"); // 找不到264编码器
}
// b 创建编码器上下文
outputVc = avcodec_alloc_context3(codec);
if (!outputVc)
{
throw std::logic_error("avcodec_alloc_context3 failed!"); // 创建编码器失败
}
// c 配置编码器参数
outputVc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 全局参数
outputVc->codec_id = codec->id;
outputVc->codec_type = AVMEDIA_TYPE_VIDEO;
outputVc->thread_count = 8;
outputVc->bit_rate = 50 * 1024 * 8; // 压缩后每秒视频的bit位大小为50kb
outputVc->width = width;
outputVc->height = height;
outputVc->time_base = {1, den};
outputVc->framerate = {den, 1};
outputVc->gop_size = 30;
outputVc->max_b_frames = 1;
outputVc->qmax = 51;
outputVc->qmin = 10;
outputVc->pix_fmt = AV_PIX_FMT_YUV420P;
// d 打开编码器上下文
ret = avcodec_open2(outputVc, codec, 0);
std::cout << "avcodec_open2 success!" << std::endl;
ret = avformat_alloc_output_context2(&output, nullptr, "rtsp", url.c_str());
vs = avformat_new_stream(output, outputVc->codec);
vs->codecpar->codec_tag = 0;
// 从编码器复制参数
avcodec_parameters_from_context(vs->codecpar, outputVc);
av_dump_format(output, 0, url.c_str(), 1);
// ret = avio_open(&output->pb, url.c_str(), AVIO_FLAG_WRITE);
return ret;
}
解释一下控制画面清晰地参数:
outputVc->gop_size = 30;
h264的为了控制错误传递,以一个gop_size为一个编码组。在close gop模式下,同一组的只能参考同一组的帧进行帧间预测。gop总是以一个idr帧开头(注意不是I帧)且只有一个idr帧。由于I帧是全帧压缩,gop_size越小,被压缩数据越少,视频质量越高,数据量越大。 outputVc->max_b_frames = 1;
顾名思义,最大连续B帧数量。B帧是双向预测帧,一般认为,相较于I帧和P帧,B帧的压缩量大。因此B帧数量越多,压缩率越大,清晰地越低,数据量越小。
outputVc->qmax = 51;
outputVc->qmin = 10;
量化参数的范围:h264编码过程中,经过DCT变换后的数据需要进一步进行量化。可以简单认为,量化系数越大,数据之间的细节越少,进而导致压缩率变大。
outputVc->pix_fmt = AV_PIX_FMT_YUV420P;
像素格式,不在赘言。