雖然在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 <iostream>
#include <string>
#include <strstream>
#include <opencv.hpp>
#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
<pre name="code" class="cpp">//寫視頻幀
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文件建立):
對於封裝成其他格式如MP4,flv,mkv時出現播放過快的問題沒有解決,希望知道的大神給與指點!同時該類並不是和opencv自帶的videoWriter一樣是線程安全的,如果希望線程安全需要自己改寫!