本文基于ffmpeg and opencv封装H264编码器类AvH264,编码opencv的Mat数据类型图像为ffmpeg的AVPacket数据类型的H264数据。
编码流程
打开编码器 // 初始化一些H264编码器的参数
编码
关闭编码器
编码器参数
typedef struct AvH264EncConfig_T {
int width = 320;
int height = 240;
int frame_rate = 25;
int64_t bit_rate = 320000;
int gop_size = 250;
int max_b_frames = 0;
}AvH264EncConfig;
width:输出流的宽度
height:输出流的高度
frame_rate:帧率
bit_rate:比特率
gop_size:可以理解I帧间隔,间隔多少帧取一个I帧,值越大,输出流空间少,质量较差;值越大,输出空间大,质量较好
max_b_frames:最大B帧数
打开编码器
int open(AvH264EncConfig h264_config);
h264_config:编码器参数
编码
AVPacket *encode(cv::Mat mat);
mat:opencv Mat 图像
返回值:ffmpeg AVPacket流
关闭编码器
void close();
AvH264编码器类
AvH264.h
#ifndef _AV_H264_H_
#define _AV_H264_H_
/***********************************************************
** Author:kaychan
** Data:2019-11-29
** Mail:1203375695@qq.com
** Explain:a h264 codec
***********************************************************/
#include <opencv2/opencv.hpp>
#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C" {
#endif
#include <libavutil/time.h>
#include <libavutil/mathematics.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#ifdef __cplusplus
};
#endif
typedef struct AvH264EncConfig_T {
int width = 320;
int height = 240;
int frame_rate = 25;
int64_t bit_rate = 320000;
int gop_size = 250;
int max_b_frames = 0;
}AvH264EncConfig;
class AvH264 {
public:
AvH264();
int open(AvH264EncConfig h264_config);
AVPacket *encode(cv::Mat mat);
void close();
private:
AVCodec *cdc_;
AVCodecContext *cdc_ctx_;
AVFrame *avf_;
AVPacket *avp_;
int frame_size_;
int pts_;
};
#endif
AvH264.cpp
#include "AvH264.h"
AvH264::AvH264() {
cdc_ = NULL;
cdc_ctx_ = NULL;
avf_ = NULL;
avp_ = NULL;
}
int AvH264::open(AvH264EncConfig h264_config) {
pts_ = 0;
cdc_ = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!cdc_) {
return -1;
}
cdc_ctx_ = avcodec_alloc_context3(cdc_);
if (!cdc_ctx_) {
return -1;
}
cdc_ctx_->bit_rate = h264_config.bit_rate;
cdc_ctx_->width = h264_config.width;
cdc_ctx_->height = h264_config.height;
cdc_ctx_->time_base = { 1, h264_config.frame_rate };
cdc_ctx_->framerate = { h264_config.frame_rate, 1 };
cdc_ctx_->gop_size = h264_config.gop_size;
cdc_ctx_->max_b_frames = h264_config.max_b_frames;
cdc_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
cdc_ctx_->codec_id = AV_CODEC_ID_H264;
cdc_ctx_->codec_type = AVMEDIA_TYPE_VIDEO;
//cdc_ctx_->qmin = 10;
//cdc_ctx_->qmax = 51;
//cdc_ctx_->qcompress = 0.6;
AVDictionary *dict = 0;
av_dict_set(&dict, "preset", "slow", 0);
av_dict_set(&dict, "tune", "zerolatency", 0);
av_dict_set(&dict, "profile", "main", 0);
avf_ = av_frame_alloc();
avp_ = av_packet_alloc();
if (!avf_ || !avp_) {
return -1;
}
frame_size_ = cdc_ctx_->width * cdc_ctx_->height;
avf_->format = cdc_ctx_->pix_fmt;
avf_->width = cdc_ctx_->width;
avf_->height = cdc_ctx_->height;
// alloc memory
int r = av_frame_get_buffer(avf_, 0);
if (r < 0) {
return -1;
}
r = av_frame_make_writable(avf_);
if (r < 0) {
return -1;
}
return avcodec_open2(cdc_ctx_, cdc_, &dict);
}
void AvH264::close() {
if(cdc_ctx_) avcodec_free_context(&cdc_ctx_);
if (avf_) av_frame_free(&avf_);
if (avp_) av_packet_free(&avp_);
}
AVPacket *AvH264::encode(cv::Mat mat) {
if (mat.empty()) return NULL;
cv::resize(mat, mat, cv::Size(cdc_ctx_->width, cdc_ctx_->height));
cv::Mat yuv;
cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
unsigned char *pdata = yuv.data;
// fill yuv420
// yyy yyy yyy yyy
// uuu
// vvv
avf_->data[0] = pdata;
avf_->data[1] = pdata + frame_size_;
avf_->data[2] = pdata + frame_size_ * 5 / 4;
avf_->pts = pts_++;
int r = avcodec_send_frame(cdc_ctx_, avf_);
if (r >= 0) {
r = avcodec_receive_packet(cdc_ctx_, avp_);
if (r == 0) {
//avp_->stream_index = 0;
return avp_;
}
if (r == AVERROR(EAGAIN) || r == AVERROR_EOF) {
return NULL;
}
}
return NULL;
}
调用实例
AvH264 h264;
AvH264EncConfig conf;
conf.bit_rate = 320000;
conf.width = 320;
conf.height = 240;
conf.gop_size = 250;
conf.max_b_frames = 0;
conf.frame_rate = 25;
h264.open(conf);
while{
// get mat
cv::Mat f = GetMat();
// do encode
AVPacket *pkt = h264_.encode(f);
// do pkt
}
h264.close();