【视频技术解读】编解码的理论和实践
一、視頻編解碼基礎(chǔ)知識(shí)
1、視頻介紹
視頻的本質(zhì)是圖像序列,根據(jù)視覺(jué)暫留的原理,每秒播放20~25張圖像,就會(huì)形成連續(xù)平滑的視覺(jué)效果,人眼將無(wú)法區(qū)分其中單幅的圖像,就這樣連續(xù)的畫面叫做視頻。每秒播放的圖像數(shù)量叫作幀率。圖像是由像素構(gòu)成的,在彩色圖像中,每個(gè)像素由R、G、B三個(gè)分量構(gòu)成,每個(gè)分量用一個(gè)字節(jié)存儲(chǔ)。分辨率用于描述圖像的尺寸,例如分辨率1280x720就表示圖像寬度是1280個(gè)像素、高度是720個(gè)像素。
2、壓縮視頻的原因
為什么要對(duì)視頻進(jìn)行壓縮?假如有一段時(shí)長(zhǎng)為60秒視頻,它的分辨率是1280x720,幀率是25,那么這段視頻的大小等于:60 x 25 x 1280 x 720 x 3 = 4147200000字節(jié),大約是3955MB,如此龐大的數(shù)據(jù),如果不進(jìn)行壓縮,那么磁盤空間將會(huì)很快被占滿。
多媒體數(shù)據(jù)占了互聯(lián)網(wǎng)數(shù)據(jù)量的80%以上,其中大部分都是圖像視頻數(shù)據(jù),未壓縮之前的視頻非常龐大,不利于存儲(chǔ)和傳輸,因此很有必要對(duì)視頻進(jìn)行壓縮,視頻壓縮也叫作視頻編碼,它利用視頻中存在的空間冗余和時(shí)間冗余,剔除人眼不敏感的信息,達(dá)到數(shù)據(jù)壓縮的目的。
3、視頻壓縮的依據(jù)
視頻能夠進(jìn)行壓縮的根本原因是信息冗余,視頻中存在兩種冗余信息:
(1)空間冗余。對(duì)于視頻的每一幀圖像,在一定尺度范圍內(nèi),像素的變化是非常平緩的,像素之間非常相似,這就是空間冗余。
(2)時(shí)間冗余。視頻是由連續(xù)變化的圖像構(gòu)成的,在一個(gè)很短的時(shí)間內(nèi),相鄰圖像之間的變化很小,因此相鄰圖像很相似,這就是時(shí)間冗余。
?4、視頻編碼的原理和細(xì)節(jié)
由于視頻中存在大量的信息冗余,想要對(duì)視頻進(jìn)行壓縮,就必須找到去除冗余信息的方法,標(biāo)準(zhǔn)的視頻編碼過(guò)程包含下面幾個(gè)步驟:
(1)預(yù)測(cè)編碼。所謂預(yù)測(cè)就是利用前面像素值來(lái)推算當(dāng)前的像素值。根據(jù)前面的知識(shí)我們知道,在空間或者時(shí)間上相鄰的像素是很相似,因此只要預(yù)測(cè)方法合適,預(yù)測(cè)值和實(shí)際值會(huì)很接近,假設(shè)當(dāng)前像素的實(shí)際值是X,預(yù)測(cè)值是P,那么X和P之間的殘差A(yù)=X-P。如果我們只對(duì)殘差數(shù)據(jù)進(jìn)行編碼,那么將會(huì)大大壓縮數(shù)據(jù)量。這就是預(yù)測(cè)編碼的原理。預(yù)測(cè)編碼可以分為空間預(yù)測(cè)(幀內(nèi)預(yù)測(cè))和時(shí)間預(yù)測(cè)(幀間預(yù)測(cè))兩種。預(yù)測(cè)編碼可以分為下面幾個(gè)步驟:
?i. 利用前面已經(jīng)編碼并重構(gòu)像素塊預(yù)測(cè)當(dāng)前像素塊的值
?ii.?求當(dāng)前像素和預(yù)測(cè)像素的差值
iii. 對(duì)像素差值進(jìn)行編碼
?iv. 把編碼后的數(shù)據(jù)傳輸?shù)浇獯a端,在解碼端按照同樣的方法對(duì)像素值進(jìn)行預(yù)測(cè),再加上殘差,就可以重構(gòu)得到原始圖像了。
為了得到最佳的預(yù)測(cè)值,編碼器通常需要使用不同預(yù)測(cè)方法進(jìn)行預(yù)測(cè),然后根據(jù)某個(gè)評(píng)判標(biāo)準(zhǔn)(一般是SAD、STAD、RDO等)得到最優(yōu)的預(yù)測(cè)值(注意不是預(yù)測(cè)值越接近實(shí)際值就越優(yōu),所謂最優(yōu)是計(jì)算復(fù)雜度、失真和碼率之間的綜合考量),特別對(duì)于幀間預(yù)測(cè)來(lái)說(shuō),在預(yù)測(cè)的時(shí)候要在參考幀中找到當(dāng)前塊的匹配塊,需要大量的計(jì)算,因此預(yù)測(cè)模塊一般是編碼器中計(jì)算量最大的模塊,而預(yù)測(cè)方法的好壞也直接決定了視頻的壓縮率。
(2)變換編碼。變化編碼本身不會(huì)對(duì)數(shù)據(jù)進(jìn)行壓縮,變換的目的是把空域信息轉(zhuǎn)換為頻域信息,去除了信息之間的相關(guān)性,在接下來(lái)的編碼操作中可以得到更高的壓縮比,變化編碼是為下一步的量化編碼做準(zhǔn)備的。在視頻編碼中常見(jiàn)的變換方法是DCT變換和哈達(dá)瑪變換。
(3)量化編碼。量化是視頻產(chǎn)生失真的根本原因。經(jīng)過(guò)預(yù)測(cè)編碼、變換編碼之后得到的數(shù)據(jù)被稱為變換系數(shù),雖然變換系數(shù)相對(duì)于原始數(shù)據(jù)數(shù)來(lái)說(shuō)已經(jīng)很小了,但是系數(shù)值的變化范圍仍然很大,利用量化把變換系數(shù)的連續(xù)取值轉(zhuǎn)換成有限的離散值,這樣可大大提高壓縮率。假設(shè)有這樣一組數(shù)據(jù)[16,96,100,600,50],量化參數(shù)是25,那么量化之后數(shù)據(jù)是round([16 / 25, 96 / 25, 100 / 24, 600 / 25, 50 / 25]) = [0,4,4,24,2],可以看到,量化之前的數(shù)據(jù)范圍是16~600,需要大量的比特?cái)?shù)才能表示,量化之后的數(shù)據(jù)范圍是0~24,只需要少量比特?cái)?shù)就可以表示,實(shí)現(xiàn)了壓縮數(shù)據(jù)的目的。編碼是一個(gè)不可逆的操作,它是失真產(chǎn)生的根本原因。
(4)環(huán)路濾波。環(huán)路濾波和壓縮沒(méi)有很大關(guān)系,卻和視頻的畫面質(zhì)量有很大關(guān)系。視頻編碼是以塊為單位進(jìn)行的,在經(jīng)過(guò)量化模塊時(shí),可能每個(gè)塊選擇的量化系數(shù)不同,導(dǎo)致每個(gè)塊產(chǎn)生的失真不一樣,造成像素塊的邊界不連續(xù),因此人眼觀看視頻的時(shí)會(huì)有方塊效應(yīng)(也就是有很多馬賽克現(xiàn)象)。為了提高視頻質(zhì)量,有必要對(duì)方塊效應(yīng)進(jìn)行消除,這就是環(huán)路濾波模塊的功能。經(jīng)過(guò)量化操作之后的數(shù)據(jù)塊,再經(jīng)過(guò)反量化、反變換,然后進(jìn)行重構(gòu),接著進(jìn)入去方塊濾波進(jìn)行去方塊操作,得到的像素塊就可以作為參考像素塊給預(yù)測(cè)模塊使用。環(huán)路濾波是一個(gè)可選模塊。
(5)熵編碼。熵編碼的目的是去除統(tǒng)計(jì)冗余,實(shí)現(xiàn)數(shù)據(jù)的進(jìn)一步壓縮。這么說(shuō)可能有點(diǎn)抽象,舉個(gè)例子,假設(shè)有這樣一組數(shù)據(jù)[0,4,4,24,2],如果使用定長(zhǎng)編碼來(lái)進(jìn)行編碼,這段數(shù)據(jù)可以表示為[00000,00100,00100,11000,00010],每個(gè)數(shù)據(jù)需要5 bit來(lái)表示,這段數(shù)據(jù)的總長(zhǎng)度是5 bit x 5=25 bit;如果我們使用哈夫曼來(lái)編碼,這個(gè)數(shù)組中每個(gè)元素概率值分別如下:數(shù)值0的概率是20%,數(shù)值4的概率是40%,數(shù)值24的概率是20%,數(shù)值2的概率是20%,即[20%,40%,20%,20%],根據(jù)每個(gè)元素的概率構(gòu)造哈夫曼樹(shù)(節(jié)點(diǎn)中括號(hào)內(nèi)的是數(shù)組元素值),如下圖所示,因此這段數(shù)據(jù)可以表示為[0,10,110,111],數(shù)據(jù)的總長(zhǎng)度是11 bit。
由此可見(jiàn)使用哈夫曼編碼可以去除統(tǒng)計(jì)冗余,實(shí)現(xiàn)數(shù)據(jù)壓縮的目的,哈夫曼編碼就是一種熵編碼方法,但是由于哈夫曼編碼對(duì)錯(cuò)誤非常敏感,不適合實(shí)際應(yīng)用,因此實(shí)際應(yīng)用中通常使用變長(zhǎng)編碼(CAVLC)和算術(shù)編碼(CABAC)作為熵編碼算法。算術(shù)編碼利用一個(gè)0到1之間的浮點(diǎn)數(shù)來(lái)描述一個(gè)信號(hào)序列,然后用這個(gè)浮點(diǎn)數(shù)來(lái)表示這個(gè)信號(hào)序列,極大的壓縮了數(shù)據(jù)。算術(shù)編碼可以使用一個(gè)簡(jiǎn)單的例子說(shuō)明,假設(shè)有一個(gè)二進(jìn)制串“10101110”,符號(hào)0和1的概率值都是50%,算術(shù)編碼的過(guò)程如下:
(1)設(shè)置定一個(gè)區(qū)間,通常這個(gè)區(qū)間我們?cè)O(shè)定為[0,1),然后使用low指向區(qū)間的下界,用high指向區(qū)間的上界,現(xiàn)在low指向0,rhigh指向1.
(2)輸入每一個(gè)符號(hào),按照符號(hào)的不同對(duì)low和high進(jìn)行調(diào)整,調(diào)整方法如下面公式所示,最后把low指向的浮點(diǎn)數(shù)作為這個(gè)符號(hào)串的碼字,表示這個(gè)二進(jìn)制串,可以看到算術(shù)編碼的壓縮效率比哈夫曼編碼高很多,在x264中使用CABAC(上下文自適應(yīng)的二進(jìn)制算數(shù)編碼)作為熵編碼算法。
二、視頻編解碼實(shí)踐
1、Ffmpeg介紹
上面介紹了視頻編碼的基礎(chǔ)知識(shí),讓我們看看實(shí)際應(yīng)用中的視頻編碼是如何操作的。
在實(shí)際工程項(xiàng)目中,ffmpeg是應(yīng)用最多的多媒體處理框架,它提供了音視頻采集、編解碼、圖像處理,格式轉(zhuǎn)換等功能,并且擁有很強(qiáng)的擴(kuò)展能力,通過(guò)ffmpeg可以很容易集成第三方庫(kù)(例如:x264、openh264等),通過(guò)這種能力,它可以實(shí)現(xiàn)更強(qiáng)大的功能。Ffmpeg由下面幾個(gè)部分構(gòu)成:
Libavformat:音視頻格式處理
Libavcodec:音視頻編解碼
Libavfilter:音視頻濾鏡
Libavdevice:音視頻設(shè)備采集
Libswscale:圖像縮放、轉(zhuǎn)換
Libswresample:音頻重采樣
Ffmpeg:一個(gè)命令行的轉(zhuǎn)碼工具
Ffplay:一個(gè)命令行播放器
Ffprobe:簡(jiǎn)單的媒體格式分析工具
?2、Ffmpeg視頻編碼
視頻編碼是ffmpeg提供的基本功能之一,通過(guò)ffmpeg可以很容易實(shí)現(xiàn)視頻編碼操作。使用ffmpeg進(jìn)行視頻編碼之前需要把x264、openh264等第三方編解碼庫(kù)集成到ffmpeg中才能使用。編碼步驟如下:
(1)注冊(cè)編碼器
(2)根據(jù)名字或者ID查找你想使用的編碼器(例如x264、x265、openh264等)
(3)創(chuàng)建一個(gè)編碼器上下文對(duì)象
(4)在編碼器上下文對(duì)象中設(shè)置編碼器參數(shù)
(5)打開(kāi)編碼器
(6)讀取一幀圖像進(jìn)行編碼,一直重復(fù)該過(guò)程,直到處理結(jié)束
(7)關(guān)閉編碼器
示例代碼如下:
avcodec_register_all();// 注冊(cè)所有可用的編碼器codec =avcodec_find_encoder_by_name(“l(fā)ibx264”); // 查找編碼器ctx =avcodec_alloc_context3(codec); // 創(chuàng)建編碼器上下文ctx->width = 1280; //設(shè)置編碼器參數(shù)ctx->height = 720;// ….其他的參數(shù)設(shè)置avcodec_open2(ctx,codec, NULL); // 打開(kāi)編碼器while(read_frame(frame)){AVPacket pkt; // 存放編碼之后的數(shù)據(jù)int got_output = 0; // 是否成功編碼得到一個(gè)圖像avcodec_encode_video2(ctx, &pkt, frame, &got_output);// 編碼if(got_output){// 得到編碼后的數(shù)據(jù),進(jìn)行后續(xù)操作}}avcodec_free_context(&ctx);// 關(guān)閉編碼器可以看到,ffmpeg隱藏了大部分的編碼細(xì)節(jié),調(diào)用者不需要了解預(yù)測(cè)、變換、量化、熵編碼等細(xì)節(jié),這些細(xì)節(jié)都已經(jīng)被ffmpeg封裝好了,開(kāi)發(fā)者只要把編碼參數(shù)設(shè)置好,然后調(diào)用相關(guān)的接口函數(shù),即可實(shí)現(xiàn)視頻編碼功能。當(dāng)然,這只是最基本的編碼功能,要想在畫面質(zhì)量和壓縮率之間取得平衡,必須了解視頻編碼的細(xì)節(jié),然后設(shè)置相應(yīng)的參數(shù)
3、Ffmpeg視頻解碼
Ffmpeg自帶了H264的視頻解碼器,開(kāi)發(fā)者可選擇直接使用ffmpeg自帶的H264解碼器或者第三方的解碼庫(kù)進(jìn)行視頻解碼。和視頻編碼一樣,解碼操作的大部分細(xì)節(jié)都已經(jīng)被ffmpeg隱藏起來(lái)了,開(kāi)發(fā)者只需要設(shè)置好相關(guān)的解碼參數(shù),然后調(diào)用接口函數(shù)就可以實(shí)現(xiàn)解碼功了。解碼流程如下:
(1)??注冊(cè)解碼器
(2)??查找解碼器
(3)??創(chuàng)建解碼器上下文對(duì)象
(4)??設(shè)置解碼參數(shù)
(5)??打開(kāi)解碼器?
(6)??讀取數(shù)據(jù)進(jìn)行解碼,直到結(jié)束
(7)??關(guān)閉解碼器
代碼示例如下:
avcodec_register_all();//注冊(cè)解碼器codec =avcodec_find_decoder_by_name(“h264”); // 查找解碼器ctx =avcodec_alloc_context3(codec); // 創(chuàng)建解碼器上下文對(duì)象//…設(shè)置解碼參數(shù)avcodec_open2(ctx,codec, NULL); // 打開(kāi)解碼器while(read_packet(pkt)){AVFrame frame; // 存放解碼之后的圖像數(shù)據(jù)int got_frame = 0;avcodec_decode_video2(ctx, frame, &got_frame,pkt); // 解碼if(got_frame){// 解碼得到一幀圖像,進(jìn)行后續(xù)操作…}}avcodec_free_context(&ctx);// 關(guān)閉解碼器三、結(jié)束
可以看到ffmpeg是個(gè)非常強(qiáng)大的多媒體處理框架,通過(guò)ffmpeg我們可以很容易進(jìn)行音視頻方面的處理。上面介紹的只是ffmpeg的冰山一角,要想熟練使用ffmpeg,必須花很多時(shí)間去熟練使用它。
點(diǎn)擊下方“閱讀原文”,技術(shù)干貨
↓↓↓
總結(jié)
以上是生活随笔為你收集整理的【视频技术解读】编解码的理论和实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 互联网1分钟 | 0920
- 下一篇: 记一次Socket.IO长链服务的性能压