虽然在opencv中提供了videoWriter类可以将opencv图像编码成视频,但是由于自带的videoWriter支持的格式都是未经压缩的,具体参考如下:
CV_FOURCC('P', 'I', 'M', '1') = MPEG-1 codec
CV_FOURCC('M', 'J', 'P', 'G') = motion-jpeg codec
CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec
CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec
CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec
CV_FOURCC('U', '2', '6', '3') = H263 codec
CV_FOURCC('I', '2', '6', '3') = H263I codec
CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec
对于视觉开发末期经常需要将处理过后的视频压缩存储,此时就需要借助一些特定编码器完成,opencv中视频支持相关的都是采用ffmpeg完成的,而原本的opencv中并不支持所有的ffmpeg功能。所以一种方案是自己编译opencv + ffmpeg,另一种就是自己使用对应编码器完成编码。
本文采用第二种方法,通过参考雷霄骅大神的代码,自己封装了一个可供opencv调用的视频编码类(videoEncode),类的接口和opencv的videoWriter比较类似,可以调用各种编码器直接将Mat数据压缩成avi封装格式的视频(理论上是可以各种封装格式的,无奈没有解决pts和dts的问题,其他格式播放过快)。
贴出部分代码:
#ifndef VIDEOENCODER_H
#define VIDEOENCODER_H
#include
#include
#include
#include
#include "ffCodecs.hpp"
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
}
using namespace std;
using namespace cv;
typedef unsigned long long uInt64;
class videoEncoder
{
public:
videoEncoder();
videoEncoder(std::string filename, int codecID, double frameRate, int imgWidth, int imgHeight); //编码器和输出文件名
~videoEncoder();
bool isOpened(); //编码器是否已经打开
bool open(std::string filename, int codecID, double frameRate, int imgWidth, int imgHeight); //打开编码器
void setParam(int codecID, double frameRate, int imgWidth, int imgHeight); //设置参数
void setBitrateControl(std::string controlMode, int bpsValue); //码流控制方式及大小
void close(); //关闭编码器
bool write(const Mat &frame); //写视频
protected:
void init();
bool allocParamMem(); //为编码器参数分配空间
std::string int2String(int interger);
int flushEncoder(AVFormatContext *fmt_ctx,unsigned int stream_index); //清空编码器的缓冲区
//设置编码器
int setCodec(char codec[4]);
private:
AVFormatContext* pFormatCtx; //
AVOutputFormat* fmt;
AVStream* video_st;
AVCodecContext* pCodecCtx;
AVCodec* pCodec; //编码器格式,可自主指定,默认由封装格式后缀名决定
AVPacket pkt; //视频帧packet
//uint8_t* picture_buf; //图像buffer
unsigned char* picture_buf; //图像buffer
AVFrame* picture;
int size;
int y_size; //帧大小
int bpsValue; //码流控制方式的对应值
int bpsMode; //码流控制方式,0表示平均码率(abr),1表示固定码率(crf),2表示固定质量(cqp)
cv::Mat image; //opencv中传来待编码的帧
int img_width; //帧宽
int img_height; //帧高
std::string outputFileName; //输出文件名,由后缀决定封装格式
int fourcc; //编码器的四字节码
double fps; //帧率
uInt64 frameIndex; //当前帧序号
bool isSetEncoderFlag; //是否自设定编码器
bool isOpenedFlag; //编码器是否已经打开
};
#endif // VIDEOENCODER_H
//写视频帧
bool videoEncoder::write(const cv::Mat& frame)
{
cv::Mat temp;
cvtColor(frame,temp,COLOR_BGR2YUV_I420);
picture_buf = temp.data;
picture->data[0] = picture_buf; //Y
picture->data[1] = picture_buf+y_size; //U
picture->data[2] = picture_buf+(y_size*5/4);//V
picture->pts = frameIndex++;
int got_picture = 0;
int ret = avcodec_encode_video2(pCodecCtx,&pkt,picture,&got_picture);
if(ret<0)
{
printf("encode error!\n");
return false;
}
if(got_picture==1)
{
pkt.stream_index = video_st->index;
printf("pkt_pts:%d dts:%d\n",pkt.pts,pkt.dts);
//ret = av_write_frame(pFormatCtx, &pkt);
ret = av_interleaved_write_frame(pFormatCtx,&pkt);
av_free_packet(&pkt);
}
return true;
}
完整工程(Qt工程,vs可以自己选取c文件和h,hpp文件建立):
videoEncoder
对于封装成其他格式如MP4,flv,mkv时出现播放过快的问题没有解决,希望知道的大神给与指点!同时该类并不是和opencv自带的videoWriter一样是线程安全的,如果希望线程安全需要自己改写!