H.264视频编码在VC++.Net中的实现
From:?http://blog.csdn.net/xwchen/article/details/5052981
?引言:H.264編碼技術(shù)是俱樂部在過去一段時(shí)間內(nèi)研究的一個(gè)方向,對(duì)該編碼技術(shù)進(jìn)行過實(shí)際的開發(fā)和應(yīng)用,并取得了很大的收獲。下面將重點(diǎn)介紹H.264視頻編碼在VC++.Net中的實(shí)現(xiàn)。
1.???H.264編碼的介紹
???????? H.264是一種視頻高壓縮技術(shù),全稱是MPEG-4 AVC,用中文說是“活動(dòng)圖像專家組-4的高等視頻編碼”,或稱為MPEG-4 Part10。它是由國際電信標(biāo)準(zhǔn)化部門ITU-T和規(guī)定MPEG的國際標(biāo)準(zhǔn)化組織ISO/國際電工協(xié)會(huì)IEC共同制訂的一種活動(dòng)圖像編碼方式的國際標(biāo)準(zhǔn)格式。由于H.264在制定時(shí)就充分考慮了多媒體通信對(duì)視頻編解碼的各種要求,并借鑒了H系列和MPEG系列視頻標(biāo)準(zhǔn)的研究成果,因而具有明顯的優(yōu)勢(shì)。H.264作為最新的國際建議標(biāo)準(zhǔn),在IP視頻監(jiān)控系統(tǒng)中有著重要的意義。它與目前的Mpeg4和H.263編碼相比較,優(yōu)勢(shì)表現(xiàn)在以下幾個(gè)方面:
1)??????壓縮率和圖像質(zhì)量方面
???????? H.264通過對(duì)傳統(tǒng)的幀內(nèi)預(yù)測(cè)、幀間預(yù)測(cè)、變換編碼和熵編碼等算法的改進(jìn)來進(jìn)一步提高編碼效率和圖像質(zhì)量。在相同的重建圖像質(zhì)量下,H.264比H.263節(jié)約50%左右的碼率,比Mpeg4節(jié)約35%左右。
2)??????網(wǎng)絡(luò)適應(yīng)性方面
????????? H.264支持不同網(wǎng)絡(luò)資源下的分級(jí)編碼傳輸,從而獲得平穩(wěn)的圖像質(zhì)量。H.264能適應(yīng)于不同網(wǎng)絡(luò)中的視頻傳輸,網(wǎng)絡(luò)親和性好。H.264的基本系統(tǒng)無需使用版權(quán),具有開放的性質(zhì),能很好地適應(yīng)IP和無線網(wǎng)絡(luò)的使用,這對(duì)目前的因特網(wǎng)傳輸多媒體信息、移動(dòng)網(wǎng)中傳輸寬帶信息等都具有重要的意義。
3)??????抗丟包和抗誤碼方面
???????? H.264具有較強(qiáng)的抗誤碼特性,可適應(yīng)丟包率高、干擾嚴(yán)重的信道中的視頻傳輸。
???????? 實(shí)際應(yīng)用中,實(shí)時(shí)性和較好的圖像質(zhì)量,較低的網(wǎng)絡(luò)帶寬占用以及帶寬適應(yīng)能力是監(jiān)控系統(tǒng)的主要考慮因素。H.264 相比較以前的視頻編碼標(biāo)準(zhǔn),主要在網(wǎng)絡(luò)接口友好性和高的壓縮性能上有了很大的提高。綜合以上因素在本系統(tǒng)中采用H.264作為視頻數(shù)據(jù)的編碼方式。
2.???H.264編碼的實(shí)現(xiàn)原理
?????????
?
1)??????量化
???????? H. 264為了提高碼率控制的能力, 量化步長(zhǎng)的變化的幅度控制在12.5 %左右, 而不是以不變的增幅變化。變換系數(shù)幅度的歸一化被放在反量化過程中處理以減少計(jì)算的復(fù)雜性。為了強(qiáng)調(diào)彩色的逼真性,對(duì)色度系數(shù)采用了較小量化步長(zhǎng)。
2)??????運(yùn)動(dòng)補(bǔ)償
???????? 宏塊劃分方式: H. 264 支持形狀不等的宏塊劃分, 其劃分方法有: 16×16, 16×8, 8×16, 8×8, 8×4, 4×8, 4×4. 這種更小的、更多形狀的的宏塊劃分,更切合圖形中實(shí)際運(yùn)動(dòng)物體的形狀, 改善運(yùn)動(dòng)補(bǔ)償?shù)木?#xff0c; 更好的實(shí)現(xiàn)運(yùn)動(dòng)隔離, 提高圖像質(zhì)量和編碼效率. 搜索精度: H. 264 支持1/4 和1/8 精度的運(yùn)動(dòng)補(bǔ)償, 使用一個(gè)6 抽頭濾波器從整像素樣本的到1/2 像素樣本, 用線性插值獲得1/4 像素樣本, 用8 抽頭濾波器實(shí)現(xiàn)1/8 像素精度。 多參考幀模式: H. 264 在對(duì)周期性的運(yùn)動(dòng)或背景切換進(jìn)行預(yù)測(cè)時(shí), 多參考幀可以提供更好的預(yù)測(cè)效果。
3)??????幀內(nèi)預(yù)測(cè)
4)??????變換編碼
5)??????熵編碼
3???H.264編碼算法的實(shí)現(xiàn)
???????? 在H.264編碼具體實(shí)現(xiàn)過程中,采用了目前國際上應(yīng)用最廣泛的開源編碼器X.264作為實(shí)現(xiàn)的基礎(chǔ)。X.264和JM系列編碼器、T.264編碼器相比有著優(yōu)秀的性能和出色效果。由于X.264沒有提供直接的開發(fā)API,所以在本系統(tǒng)中的編碼部分重新封裝了X.264的編碼API,便于軟件系統(tǒng)的設(shè)計(jì)和使用。以下是本系統(tǒng)中H.264編碼的具體實(shí)現(xiàn)過程:
1)??????RGB和YUV顏色空間的轉(zhuǎn)換
???????? 在系統(tǒng)中通過Logitech攝像頭獲得的視頻數(shù)據(jù)為RGB24格式,但是X.264的輸入流為標(biāo)準(zhǔn)的YUV(4:2:0)的圖像子采樣格式。因此,在編碼前需要將RGB顏色空間轉(zhuǎn)換為YUV的顏色空間。實(shí)現(xiàn)的函數(shù)調(diào)用有InitLookupTable()用于初始化色彩空間轉(zhuǎn)換;
RGB2YUV420(int x_dim, int y_dim, unsigned char *bmp, unsigned char *yuv, int flip);用于實(shí)際的轉(zhuǎn)換。由于人眼的生理特性,經(jīng)過圖像子采樣后,實(shí)際的圖像大小已經(jīng)減小為采樣前的1.5個(gè)樣本點(diǎn),即減小了一半的數(shù)據(jù)量。
2)??????設(shè)置H.264編碼參數(shù)
???????? 使用x264_param_default(x264_param_t *param)對(duì)當(dāng)前需要編碼的圖像參數(shù)進(jìn)行設(shè)置。包括數(shù)據(jù)幀數(shù)量(param .i_frame_total)、采樣圖像的長(zhǎng)寬度和高度(param .i_width,param .i_height)、視頻數(shù)據(jù)比特率(param .rc.i_bitrate) 、視頻數(shù)據(jù)幀率(param .i_fps_num)等參數(shù)進(jìn)行設(shè)置,以完成編碼前預(yù)設(shè)置。
3)??????初始化編碼器
???????? 將上步中的設(shè)置作為編碼器初始化的參數(shù),x264_t *x264_encoder_open?? ( x264_param_t *param )。如果初始化失敗將返回NULL,在這里需要對(duì)編碼器初始化結(jié)果進(jìn)行處理。
4)??????分配編碼空間
???????? 如果編碼器初始化成功,則需要為本次處理分配內(nèi)存空間
Void x264_picture_alloc(x264_picture_t *pic, int i_csp, int i_width, int i_height)。
5)??????圖像編碼
???????? 將以上步驟初始化后的數(shù)據(jù)作為編碼輸入,使用下面的方法進(jìn)行編碼:
int x264_encoder_encode( x264_t *h,x264_nal_t **pp_nal, int *pi_nal,x264_picture_t *pic_in,x264_picture_t *pic_out );
6)??????資源回收
???????? 編碼完成后,需要回收系統(tǒng)資源和關(guān)閉編碼器,使用以下函數(shù)調(diào)用實(shí)現(xiàn)回收。
void x264_picture_clean( x264_picture_t *pic );
void x264_encoder_close( x264_t *h );
至此,完成了H.264編碼,編碼后的數(shù)據(jù)量將大大減小。我們可以對(duì)編碼后的數(shù)據(jù)做相關(guān)的進(jìn)一步處理。
4?H.264編碼算法的完整源代碼
文件:VideoEncoderX264.h
class CVideoEncoderX264 :?
{
public:
?CVideoEncoderX264(void);
?~CVideoEncoderX264(void);
?virtual bool Connect(CVideoEnDecodeNotify* pNotify, const CVideoEnDecodeItem& Item);
?virtual void Release(void);
?virtual void Encode(BYTE* pInData, int nLen, BYTE* pOutBuf, int& nOutLen, int& nKeyFrame);
private:
? x264_picture_t m_Pic;
? x264_t *h;
? x264_param_t param;
? void Flush(void);
};
文件:VideoEncoderX264.cpp
bool CVideoEncoderX264::Connect(CVideoEnDecodeNotify* pNotify, const CVideoEnDecodeItem& Item)
{
?CBase::Connect(pNotify, Item);
?ParseSize(Item.m_stSize);
?x264_param_default( ¶m );
?param.i_threads = 1;
?param.i_frame_total = 0;
?param.i_width? = m_nWidth;
?param.i_height = m_nHeight;
?param.i_keyint_min = Item.m_nKeyInterval;
?param.i_keyint_max = Item.m_nKeyInterval * 10;
?param.i_fps_num = Item.m_nFps;*/
?param.i_log_level = X264_LOG_NONE;
if( ( h = x264_encoder_open( ¶m ) ) == NULL )
??? {
??????? return false;
??? }
??? /* Create a new pic */
??? x264_picture_alloc( &m_Pic, X264_CSP_I420, param.i_width, param.i_height );
?return true;
}
void CVideoEncoderX264::Release(void)
{
?Flush();
?x264_picture_clean( &m_Pic );
??? x264_encoder_close( h );
?CBase::Release();
}
void CVideoEncoderX264::Encode(BYTE* pInData, int nLen, BYTE* pOutBuf, int& nOutLen, int& nKeyFrame)
{
?if(nLen != param.i_width * param.i_height * 3)
??return;
?param.i_frame_total ++;
?memcpy(m_Pic.img.plane[0], pInData, param.i_width * param.i_height);
?memcpy(m_Pic.img.plane[1], pInData + param.i_width * param.i_height, param.i_width * param.i_height / 4);
?memcpy(m_Pic.img.plane[2], pInData + param.i_width * param.i_height * 5 / 4, param.i_width * param.i_height / 4);??
?m_Pic.i_pts = (int64_t)param.i_frame_total * param.i_fps_den;
?static x264_picture_t pic_out;
??? x264_nal_t *nal = NULL;
??? int i_nal, i;
??? if( &m_Pic )
??? {
??????? m_Pic.i_type = X264_TYPE_AUTO;
??????? m_Pic.i_qpplus1 = 0;
??? }
?//TraceTime("x264_encoder_encode begin");
??? if( x264_encoder_encode( h, &nal, &i_nal, &m_Pic, &pic_out ) < 0 )?? {
??return;
??? }
?//TraceTime("x264_encoder_encode?? end");
?int nOutCanUse = nOutLen;
?nOutLen = 0;
??? for( i = 0; i < i_nal; i++ )
??? {
??int i_size = 0;
??if( ( i_size = x264_nal_encode( pOutBuf + nOutLen, &nOutCanUse, 1, &nal[i] ) ) > 0 )
??{
???nOutLen += i_size;
???nOutCanUse -= i_size;
??}?
?}
?nKeyFrame = pic_out.i_type==X264_TYPE_IDR;// || (pic_out.i_type==X264_TYPE_I && coCfg->x264_max_ref_frames==1);
}
void CVideoEncoderX264::Flush(void)
{
?x264_picture_t pic_out;
??? x264_nal_t *nal;
??? int i_nal, i;
??? int i_file = 0;
??? if( x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out ) < 0 ){
??? }
}
總結(jié)
以上是生活随笔為你收集整理的H.264视频编码在VC++.Net中的实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 退出控屏软件(极域等)的通用最好方法
- 下一篇: c++ 栈 STL的基本操作