原作者: 北京理工大学 陈罡
xvid静态库用起来虽然速度很快,但是很不方便,有必要用c++把它好好封装一下,方便开发人员使用。下面的代码已经在p2p视频会议中采用,很好用,速度也很快。
xvid编码器头文件:
#ifndef _XVID_ENCODE_H #define _XVID_ENCODE_H #include <xvid.h>
class CXvidEncHandler { public: virtual void PostEncHandler(unsigned char * xvid, int key, int xvid_len) = 0 ; };
class CXvidEnc { protected: // original encode routine in xvid lib int enc_core(unsigned char *image, unsigned char *bitstream, int *key);
public: CXvidEnc() ; ~CXvidEnc() ; bool Open(); bool Close(); static void XVID_GLOBAL_INIT(); void Encode(unsigned char * image); void AttachCaller(int width, int height, CXvidEncHandler * enc_caller) ; protected: CXvidEncHandler* m_enc_caller ; void * m_enc_handle; unsigned char* m_bitstream; bool m_closed; int m_key ; int m_width ; int m_height ; }; #endif
编码器源文件:
#include "StdAfx.h" #include "./xvidenc.h" #include "xvid.h"
static const int motion_presets[] = { /* quality 0 */ 0, /* quality 1 */ XVID_ME_ADVANCEDDIAMOND16, /* quality 2 */ XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16, /* quality 3 */ XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8, /* quality 4 */ XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 | XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP, /* quality 5 */ XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 | XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP, /* quality 6 */ XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | XVID_ME_EXTSEARCH16 | XVID_ME_ADVANCEDDIAMOND8 | XVID_ME_HALFPELREFINE8 | XVID_ME_EXTSEARCH8 | XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP, };
#define ME_ELEMENTS (sizeof(motion_presets)/sizeof(motion_presets[0]))
static const int vop_presets[] = { /* quality 0 */ 0, /* quality 1 */ 0, /* quality 2 */ XVID_VOP_HALFPEL, /* quality 3 */ XVID_VOP_HALFPEL | XVID_VOP_INTER4V, /* quality 4 */ XVID_VOP_HALFPEL | XVID_VOP_INTER4V, /* quality 5 */ XVID_VOP_HALFPEL | XVID_VOP_INTER4V | XVID_VOP_TRELLISQUANT, /* quality 6 */ XVID_VOP_HALFPEL | XVID_VOP_INTER4V | XVID_VOP_TRELLISQUANT | XVID_VOP_HQACPRED, };
#define VOP_ELEMENTS (sizeof(vop_presets)/sizeof(vop_presets[0])) ////////////////////////////////////////////////////////////////////////// #define MAX_ZONES 64 /* Maximum number of frames to encode */ #define ABS_MAXFRAMENR 9999 static int ARG_STATS = 0; static int ARG_DUMP = 0; static int ARG_LUMIMASKING = 0; static int ARG_BITRATE = 0; static int ARG_SINGLE = 0; static char *ARG_PASS1 = 0; static char *ARG_PASS2 = 0; static int ARG_QUALITY = ME_ELEMENTS - 1; static float ARG_FRAMERATE = 25.00f; static int ARG_MAXFRAMENR = ABS_MAXFRAMENR; static int ARG_MAXKEYINTERVAL = 0; static char *ARG_INPUTFILE = NULL; static int ARG_INPUTTYPE = 0; static int ARG_SAVEMPEGSTREAM = 0; static int ARG_SAVEINDIVIDUAL = 0; static char *ARG_OUTPUTFILE = NULL; static int ARG_BQRATIO = 150; static int ARG_BQOFFSET = 100; static int ARG_MAXBFRAMES = 0; static int ARG_PACKED = 0; static int ARG_VOPDEBUG = 0; static int ARG_GMC = 0; static int ARG_INTERLACING = 0; static int ARG_QPEL = 0; static int ARG_CLOSED_GOP = 0;
#ifndef READ_PNM #define IMAGE_SIZE(x,y) ((x)*(y)*3/2) #else #define IMAGE_SIZE(x,y) ((x)*(y)*3) #endif
#define MAX(A,B) ( ((A)>(B)) ? (A) : (B) ) #define SMALL_EPS (1e-10) #define SWAP(a) ( (((a)&0x000000ff)<<24) | (((a)&0x0000ff00)<<8) | (((a)&0x00ff0000)>>8) | (((a)&0xff000000)>>24) )
////////////////////////////////////////////////////////////////////////// CXvidEnc::CXvidEnc() { m_closed = true ; m_enc_caller = NULL ; m_enc_handle = NULL ; m_key = 0 ; m_width = 0 ; m_height = 0 ; m_bitstream = NULL ; }
CXvidEnc::~CXvidEnc() { if(m_bitstream) free(m_bitstream) ; m_bitstream = NULL ; }
bool CXvidEnc::Close() { int xerr = 0 ; m_closed = true; /* Destroy the encoder instance */ xerr = xvid_encore(m_enc_handle, XVID_ENC_DESTROY, NULL, NULL); return (xerr) ? false : true ; }
void CXvidEnc::AttachCaller(int width, int height, CXvidEncHandler * enc_caller) { m_width = width ; m_height = height ; m_enc_caller = enc_caller ; if(m_width > 0 && m_height > 0) { // max size int max = (m_width > m_height) ? m_width : m_height ; int xvid_len = (int)(max * max) ; m_bitstream = (unsigned char *)malloc(xvid_len) ; memset(m_bitstream, 0, xvid_len) ; CXvidEnc::XVID_GLOBAL_INIT() ; } }
void CXvidEnc::XVID_GLOBAL_INIT(){ /*------------------------------------------------------------------------ * XviD core initialization *----------------------------------------------------------------------*/ xvid_gbl_init_t xvid_gbl_init; memset(&xvid_gbl_init, 0, sizeof(xvid_gbl_init)); xvid_gbl_init.version = XVID_VERSION; xvid_gbl_init.cpu_flags = XVID_CPU_FORCE | XVID_CPU_ASM ; // here we use asm optimized code /* Initialize XviD core -- Should be done once per __process__ */ xvid_global(NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL); }
bool CXvidEnc::Open() { if(!m_enc_caller) return false ; static xvid_enc_create_t xvid_enc_create; int xerr = 0; m_closed = false; /*------------------------------------------------------------------------ * XviD encoder initialization *----------------------------------------------------------------------*/ memset(&xvid_enc_create, 0, sizeof(xvid_enc_create)); xvid_enc_create.version = XVID_VERSION;
/* Width and Height of input frames */ xvid_enc_create.width = m_width ; xvid_enc_create.height = m_height ; xvid_enc_create.profile = XVID_PROFILE_AS_L4;
/* init plugins */ /* xvid_enc_create.zones = ZONES; xvid_enc_create.num_zones = NUM_ZONES; xvid_enc_create.plugins = plugins; xvid_enc_create.num_plugins = 0; */ /* No fancy thread tests */ xvid_enc_create.num_threads = 0;
/* Frame rate - Do some quick float fps = fincr/fbase hack */ xvid_enc_create.fincr = 1; xvid_enc_create.fbase = (int)10;
/* Maximum key frame interval */ xvid_enc_create.max_key_interval = (int)-1; //--default 10s /* Bframes settings */ xvid_enc_create.max_bframes = ARG_MAXBFRAMES; xvid_enc_create.bquant_ratio = ARG_BQRATIO; xvid_enc_create.bquant_offset = ARG_BQOFFSET; /* Dropping ratio frame -- we don't need that */ xvid_enc_create.frame_drop_ratio = 0; /* Global encoder options */ xvid_enc_create.global = 0; if (ARG_PACKED) xvid_enc_create.global |= XVID_GLOBAL_PACKED;
if (ARG_CLOSED_GOP) xvid_enc_create.global |= XVID_GLOBAL_CLOSED_GOP;
if (ARG_STATS) xvid_enc_create.global |= XVID_GLOBAL_EXTRASTATS_ENABLE;
/* I use a small value here, since will not encode whole movies, but short clips */ xerr = xvid_encore(NULL, XVID_ENC_CREATE, &xvid_enc_create, NULL); m_enc_handle = xvid_enc_create.handle; return true; }
void CXvidEnc::Encode(unsigned char * image) { int ret = 0 ; if(m_closed) return;
ret = enc_core(image, m_bitstream, &m_key) ;
// really encode some images into xvid data if (ret > 0) m_enc_caller->PostEncHandler(m_bitstream, m_key, ret) ; }
/* raw CXvidEnc procedure */ int CXvidEnc::enc_core(unsigned char *image,unsigned char *bitstream, int * key) { int ret; xvid_enc_frame_t xvid_enc_frame; xvid_enc_stats_t xvid_enc_stats; /* Version for the frame and the stats */ memset(&xvid_enc_frame, 0, sizeof(xvid_enc_frame)); xvid_enc_frame.version = XVID_VERSION;
memset(&xvid_enc_stats, 0, sizeof(xvid_enc_stats)); xvid_enc_stats.version = XVID_VERSION;
/* Bind output buffer */ xvid_enc_frame.bitstream = bitstream; xvid_enc_frame.length = -1; /* Initialize input image fields */ xvid_enc_frame.input.plane[0] = image; xvid_enc_frame.input.csp = XVID_CSP_BGR; // suppose we get data from usb web cam xvid_enc_frame.input.stride[0] = m_width*3; /* Set up core's general features */ xvid_enc_frame.vol_flags = 0; /* Set up core's general features */ xvid_enc_frame.vop_flags = vop_presets[ARG_QUALITY-2]; /* Frame type -- let core decide for us */ xvid_enc_frame.type = XVID_TYPE_AUTO; /* Force the right quantizer -- It is internally managed by RC plugins */ xvid_enc_frame.quant = 0;
/* Set up motion estimation flags */ xvid_enc_frame.motion = motion_presets[ARG_QUALITY-2]; /* We don't use special matrices */ xvid_enc_frame.quant_intra_matrix = NULL; xvid_enc_frame.quant_inter_matrix = NULL;
/* Encode the frame */ ret = xvid_encore(m_enc_handle, XVID_ENC_ENCODE, &xvid_enc_frame,NULL); // &xvid_enc_stats); //--判别是否是关键帧 *key = (xvid_enc_frame.out_flags & XVID_KEYFRAME); //*stats_type = xvid_enc_stats.type; //*stats_quant = xvid_enc_stats.quant; //*stats_length = xvid_enc_stats.length; //sse[0] = xvid_enc_stats.sse_y; //sse[1] = xvid_enc_stats.sse_u; //sse[2] = xvid_enc_stats.sse_v; return (ret); }
解码器头文件:
#ifndef _XVID_DECODE_H #define _XVID_DECODE_H #include <xvid.h> class CXvidDecHandler { public: virtual void PostDecHandler(unsigned char * image, int used_bytes) = 0 ; };
class CXvidDec {
public: CXvidDec() ; ~CXvidDec() ; bool Open(); bool Close(); void Decode(unsigned char* xvid, int xvid_len); static void XVID_GLOBAL_INIT(); void AttachCaller(int width, int height, CXvidDecHandler * dec_caller) ;
protected: int dec_core(unsigned char *bitstream, unsigned char *image, int bs_size) ;
protected: CXvidDecHandler* m_dec_caller ; void* m_dec_handle ; unsigned char * m_image ; int m_width ; int m_height ; }; #endif
解码器源文件:
#include "StdAfx.h" #include "./xviddec.h" #include "xvid.h"
CXvidDec::CXvidDec() { m_width = 0 ; m_height = 0 ; m_image = NULL ; m_dec_handle = NULL ; m_dec_caller = NULL ; }
CXvidDec::~CXvidDec() { if(m_image) free(m_image) ; m_image = NULL ; }
void CXvidDec::AttachCaller(int width, int height, CXvidDecHandler * dec_caller) { m_width = width ; m_height = height ; m_dec_caller = dec_caller ; if((m_width > 0) && (m_height > 0)) { int image_len = m_width * m_height * 3 ; m_image = (unsigned char *)malloc(image_len) ; memset(m_image, 0, image_len) ; CXvidDec::XVID_GLOBAL_INIT() ; } }
bool CXvidDec::Close(){ int xerr = 0 ;
/* Destroy the encoder instance */ xerr = xvid_decore(m_dec_handle, XVID_ENC_DESTROY, NULL, NULL);
return (xerr) ? false : true ; }
void CXvidDec::XVID_GLOBAL_INIT() { /*------------------------------------------------------------------------ * XviD core initialization *----------------------------------------------------------------------*/ xvid_gbl_init_t xvid_gbl_init; memset(&xvid_gbl_init, 0, sizeof(xvid_gbl_init)); xvid_gbl_init.version = XVID_VERSION; xvid_gbl_init.cpu_flags = XVID_CPU_FORCE | XVID_CPU_ASM ; // force to use asm optimized routine /* Initialize XviD core -- Should be done once per __process__ */ xvid_global(NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL); }
bool CXvidDec::Open() { if(!m_dec_caller) return false ; static xvid_dec_create_t xvid_dec_create ; int ret = 0; /*------------------------------------------------------------------------ * XviD encoder initialization *----------------------------------------------------------------------*/ memset(&xvid_dec_create, 0, sizeof(xvid_dec_create_t)); xvid_dec_create.version = XVID_VERSION; /* Width and Height of input frames */ xvid_dec_create.width = m_width ; xvid_dec_create.height = m_height ; ret = xvid_decore(NULL, XVID_DEC_CREATE, &xvid_dec_create, NULL) ; m_dec_handle = xvid_dec_create.handle; return true; }
void CXvidDec::Decode(unsigned char * xvid, int xvid_len) { int ret = 0; ret = dec_core(xvid, m_image, xvid_len); if (ret > 0) m_dec_caller->PostDecHandler(m_image, ret) ; }
/* raw xvid_encode procedure */ int CXvidDec::dec_core(unsigned char *bitstream,unsigned char *image, int bs_size) { int ret; xvid_dec_frame_t xvid_dec_frame; /* Reset all structures */ memset(&xvid_dec_frame, 0, sizeof(xvid_dec_frame_t)); /* Set version */ xvid_dec_frame.version = XVID_VERSION; //xvid_dec_stats->version = XVID_VERSION; /* No general flags to set */ xvid_dec_frame.general = 0; /* Input stream */ xvid_dec_frame.bitstream = bitstream; xvid_dec_frame.length = bs_size; /* Output frame structure */ xvid_dec_frame.output.plane[0] = image; xvid_dec_frame.output.stride[0] = m_width*3; xvid_dec_frame.output.csp = XVID_CSP_BGR;
ret = xvid_decore(m_dec_handle, XVID_DEC_DECODE, &xvid_dec_frame, NULL); return(ret); }
大家关注到这里编解码器有两个虚基类:CXvidDecHandler和CXvidEncHandler,使用的时候从这两个类派生,然后重载它即可。注意,这里是为了配合摄像头使用所以是直接对BGR24格式的数据进行编解码,如果对yuv420原始数据进行编解码,需要略微调整一下代码。
在这里我列一下使用流程:
(1)重载虚基类:
class CCapSvrDlg : public CDialog, public CXvidEncHandler, // xvid encode handler public CXvidDecHandler // xvid decode handler { ...
(2)重载纯虚函数:
public: // override the CXvidEncHandler void PostEncHandler(unsigned char * xvid, int key, int xvid_len) ; public: void PostDecHandler(unsigned char * image, int used_bytes) ;
这里的PostEncHandler和PostDecHandler分别代表编码或者解码完毕后的数据处理。
比如:调用编码,xvid编码完毕后,会自动调用这里重载的PostEncHandler,这里的xvid就是编码后的视频数据,xvid_len就是视频数据的长度,这个key就是标明当前编码是否为关键帧。
解码也是一样,不在赘述。
(3)定义编解码器指针:
CXvidEnc * m_vdo_enc ; CXvidDec * m_vdo_dec ;
(4)在OnInitDialog中初始化:
m_vdo_enc = new CXvidEnc() ; m_vdo_enc->AttachCaller(320, 240, this) ; m_vdo_enc->Open() ;
m_vdo_dec = new CXvidDec() ; m_vdo_dec->AttachCaller(320, 240, this) ; m_vdo_dec->Open() ;
(5)编解码操作:
编码:
m_vdo_enc->Encode(pBuffer) ; // 这里pBuffer是BGR24的320x240的数据
编码如果成功,就会自动调用PostEncHandler函数,就可以得到编码后的结果
解码:
m_vdo_dec->Decode(xvid, xvid_len) ; // 传入的存有xvid数据的buffer和长度
解码成功后,会自动调用PostDecHandler,注意对于流媒体数据而言,在这个函数中还有一个形参是标明本次解码用掉了多少个字节的xvid缓冲区的数据,便于下一帧解码的时候调整缓冲区指针
(6)OnDestroy函数中,关闭编解码器
m_vdo_enc->Close() ; m_vdo_dec->Close() ; delete m_vdo_dec ; delete m_vdo_enc ;
这些就是全部的xvid静态库的c++封装了,是不是特简单?只要编译一下xvid-core1.1.2即可。
链接的时候,需要libxvidcore.lib。
注意,debug版的需要debug版的libxvidcore.lib,release版的需要release版的xvidcore库。
|