最简单的视音频播放演示样例5:OpenGL播放RGB/YUV
=====================================================
最簡(jiǎn)單的視音頻播放演示樣例系列文章列表:
最簡(jiǎn)單的視音頻播放演示樣例1:總述
最簡(jiǎn)單的視音頻播放演示樣例2:GDI播放YUV, RGB
最簡(jiǎn)單的視音頻播放演示樣例3:Direct3D播放YUV,RGB(通過(guò)Surface)
最簡(jiǎn)單的視音頻播放演示樣例4:Direct3D播放RGB(通過(guò)Texture)
最簡(jiǎn)單的視音頻播放演示樣例5:OpenGL播放RGB/YUV
最簡(jiǎn)單的視音頻播放演示樣例6:OpenGL播放YUV420P(通過(guò)Texture。使用Shader)
最簡(jiǎn)單的視音頻播放演示樣例7:SDL2播放RGB/YUV
最簡(jiǎn)單的視音頻播放演示樣例8:DirectSound播放PCM
最簡(jiǎn)單的視音頻播放演示樣例9:SDL2播放PCM
=====================================================
本文記錄OpenGL播放視頻的技術(shù)。
OpenGL是一個(gè)和Direct3D同一層面的技術(shù)。
相比于Direct3D。OpenGL具有跨平臺(tái)的優(yōu)勢(shì)。雖然在游戲領(lǐng)域。DirectX的影響力已漸漸超越OpenGL并被大多數(shù)PC游戲開(kāi)發(fā)商所採(cǎi)用,但在專(zhuān)業(yè)高端畫(huà)圖領(lǐng)域,OpenGL由于色彩準(zhǔn)確。仍然是不能被代替的主角。
OpenGL簡(jiǎn)單介紹
從網(wǎng)上搜集了一些有關(guān)OpenGL簡(jiǎn)單介紹方面的知識(shí),在這里列出來(lái)。
開(kāi)放圖形庫(kù)(英語(yǔ):Open Graphics Library。縮寫(xiě)為OpenGL)是個(gè)定義了一個(gè)跨編程語(yǔ)言、跨平臺(tái)的應(yīng)用程序接口(API)的規(guī)范。它用于生成二維、三維圖像。
OpenGL規(guī)范由1992年成立的OpenGL架構(gòu)評(píng)審委員會(huì)(ARB)維護(hù)。ARB由一些對(duì)創(chuàng)建一個(gè)統(tǒng)一的、普遍可用的API特別感興趣的公司組成。依據(jù)OpenGL官方站點(diǎn),2002年6月的ARB投票成員包括3Dlabs、Apple Computer、ATI Technologies、Dell Computer、Evans & Sutherland、Hewlett-Packard、IBM、Intel、Matrox、NVIDIA、SGI和Sun Microsystems,Microsoft曾是創(chuàng)立成員之中的一個(gè),但已于2003年3月退出。
OpenGL仍然是唯一能夠代替微軟對(duì)3D圖形技術(shù)的全然控制的API。
它仍然具有一定的生命力。可是Silicon Graphics已經(jīng)不再以不論什么讓微軟不悅的方式推廣OpenGL,因而它存在較高的風(fēng)險(xiǎn)。
在高端的圖形設(shè)備和專(zhuān)業(yè)應(yīng)用方面OpenGL占領(lǐng)著統(tǒng)治地位(Direct3D眼下還不支持)。開(kāi)放源代碼社區(qū)(尤其是Mesa項(xiàng)目)一直致力于提供OpenGL支持。
?
OpenGL渲染管線
下文也是網(wǎng)上看的。搞懂了一部分。可是由于3D方面基礎(chǔ)不堅(jiān)固有些方面還沒(méi)有全然弄懂。
OpenGL渲染管線(OpenGL Pipeline)依照特定的順序?qū)D形信息進(jìn)行處理,這些圖形信息能夠分為兩個(gè)部分:頂點(diǎn)信息(坐標(biāo)、法向量等)和像素信息(圖像、紋理等)。
圖形信息終于被寫(xiě)入幀緩存中,存儲(chǔ)在幀緩存中的數(shù)據(jù)(圖像),能夠被應(yīng)用程序獲得(用于保存結(jié)果,或作為應(yīng)用程序的輸入等,見(jiàn)下圖中灰色虛線)。
Display List(顯示列表)
顯示列表是一組OpenGL命令。被存儲(chǔ)(編譯)起來(lái)用于興許的運(yùn)行。
全部數(shù)據(jù),幾何(頂點(diǎn))數(shù)據(jù)和像素?cái)?shù)據(jù)都能夠存入顯示列表。數(shù)據(jù)和命令緩存到顯示列表中能夠提高性能。
Vertex Operation(頂點(diǎn)處理)
頂點(diǎn)坐標(biāo)和法線坐標(biāo)經(jīng)過(guò)模式視圖矩陣從物體坐標(biāo)系(object coordinates)轉(zhuǎn)換為觀察坐標(biāo)系(eye coordinates)。若啟用了光照。對(duì)轉(zhuǎn)換后的定點(diǎn)和法線坐標(biāo)運(yùn)行光照計(jì)算。光照計(jì)算更新了頂點(diǎn)的顏色值。
Primitive Assembly(圖元裝配)
頂點(diǎn)處理之后。基本圖元(點(diǎn)、線、多邊形)經(jīng)過(guò)投影矩陣變換。再被視見(jiàn)體裁剪平面裁剪,從觀察坐標(biāo)系轉(zhuǎn)換為裁剪坐標(biāo)系。之后,進(jìn)行透視除法(除以w)和視口變換(viewport transform),將3d場(chǎng)景投影到窗體坐標(biāo)系。
Pixel Transfer Operation(像素操作)
像素從客戶(hù)內(nèi)存中解包出來(lái)之后,要經(jīng)過(guò)縮放、偏移、映射、箝拉(clamping)。
這些處理即為像素轉(zhuǎn)換操作。
轉(zhuǎn)換的數(shù)據(jù)存在紋理內(nèi)存或直接經(jīng)過(guò)光柵化轉(zhuǎn)為片段(fragment)。
Texture Memory(紋理內(nèi)存)
紋理圖像加載到紋理內(nèi)存中,然后應(yīng)用到幾何對(duì)象上。?
Raterization(光柵化)
光柵化就是把幾何(頂點(diǎn)坐標(biāo)等)和像素?cái)?shù)據(jù)轉(zhuǎn)換為片段(fragment)的過(guò)程。每一個(gè)片段相應(yīng)于幀緩沖區(qū)中的一個(gè)像素,該像素相應(yīng)屏幕上一點(diǎn)的顏色和不透明度信息。片段是一個(gè)矩形數(shù)組,包括了顏色、深度、線寬、點(diǎn)的大小等信息(反鋸齒計(jì)算等)。假設(shè)渲染模式被設(shè)置為GL_FILL。多邊形內(nèi)部的像素信息在這個(gè)階段會(huì)被填充。
如上圖中的三角形,輸入三角形的三個(gè)頂點(diǎn)坐標(biāo)以及其顏色,頂點(diǎn)操作會(huì)對(duì)三角形的頂點(diǎn)坐標(biāo)以及法向量進(jìn)行變換,顏色信息不須要經(jīng)過(guò)變換,但光照計(jì)算會(huì)影響頂點(diǎn)的顏色信息。
經(jīng)過(guò)光柵化后,三角形被離散為一個(gè)個(gè)點(diǎn)。不在是三個(gè)坐標(biāo)表示,而是由一系列的點(diǎn)組成,每一個(gè)點(diǎn)存儲(chǔ)了相應(yīng)的顏色、深度和不透明度等信息。
?
Fragment Operation(片段操作)
這是將片段轉(zhuǎn)為幀緩沖區(qū)中的像素要進(jìn)行的最后處理。首先是紋理單元(texel)生成。
一個(gè)紋理單元由紋理內(nèi)存中的數(shù)據(jù)生成,然后應(yīng)用到每一個(gè)片段上。
之后進(jìn)行霧計(jì)算。
霧計(jì)算完畢后,還要按序進(jìn)行若干片段測(cè)試,依次為蒙板(scissor)測(cè)試,alpha測(cè)試。模版(stencil)測(cè)試,深度測(cè)試。最后,運(yùn)行混合,抖動(dòng)。邏輯操作和遮蔽操作,終于的像素存入framebuffer。
?
OpenGL與Direct3D的對(duì)照
有關(guān)視頻顯示的技術(shù)在《Direct3D》文章中已經(jīng)有過(guò)敘述,在這里不再重復(fù)。在網(wǎng)上看了一下有關(guān)于他們不同點(diǎn)的文章,寫(xiě)得簡(jiǎn)單明了。在這里引用一下:
OpenGL與Direct3D的一點(diǎn)點(diǎn)對(duì)照
OGL比D3D好的地方:
OGL是業(yè)界標(biāo)準(zhǔn),很多非Windows操作系統(tǒng)下還找不到D3D
OGL的色彩比D3D的要好,表面更光滑
OGL的函數(shù)非常有規(guī)律。不像D3D的。都是指針method,函數(shù)名太長(zhǎng)了!
!
OGL是右手坐標(biāo)系,這是數(shù)學(xué)里用慣了的.D3D雖然也能夠改變成右手坐標(biāo)系,可是須要d3dx9_36.dll的支持
OGL的經(jīng)常使用Matrix。如WorldMatrix都封裝好了,D3D要自己寫(xiě)。
OGL的畫(huà)圖方式非常靈活,而D3D的則要事先定義好FVF,要等全部信息寫(xiě)進(jìn)Stream中才繪制。這就使它產(chǎn)生了VertexBuffer和IndexBuffer.好象微軟嫌D3D的Buffer不夠多?搞的多不好學(xué)??看人家OGL,哪里要這個(gè)東西?
D3D有好多版本號(hào),要是顯卡不支持就廢柴一垛了。而OGL從幾年前就一直沒(méi)變過(guò),所以大部分顯卡都支持。
還有,我發(fā)現(xiàn)D3D的半透明功能有非常大的問(wèn)題!!就是兩個(gè)半透明的物體前后順序的問(wèn)題——前面的會(huì)被后面的擋住。
?
可是D3D也有比OGL好的地方:
D3D支持很多格式的圖片文件,而OGL加載jpg都得自己寫(xiě)代碼。
由于D3D是指針調(diào)用模式。所以做D3D的鉤子有難度,從而添加了外掛的制作難度。
D3D是DirectX的成員。
程序猿要實(shí)現(xiàn)聲音播放能夠用DirectMusic,配套用總是好的,而OGL則僅僅能畫(huà)畫(huà)
D3D是被微軟大力推廣的連接庫(kù)。
相反。微軟則大力壓制OGL(都是Microsoft參與研制出來(lái)的產(chǎn)品,待遇怎這么大?)
正由于此。D3D已成為中國(guó)大型游戲界的主流(我認(rèn)為他們是盲目跟風(fēng)。事實(shí)上國(guó)外非常多游戲都是用OGL)
?
OpenGL視頻顯示的流程
使用OpenGL播放視頻最簡(jiǎn)單的情況下須要例如以下步驟:1. ? ? ? 初始化
1) ? ? ? ? 初始化
2) ? ? ? ? 創(chuàng)建窗體
3) ? ? ? ? 設(shè)置畫(huà)圖函數(shù)
4) ? ? ? ? 設(shè)置定時(shí)器
5) ? ? ? ? 進(jìn)入消息循環(huán)2. ? ? ? 循環(huán)顯示畫(huà)面
1) ? ? ? 調(diào)整顯示位置,圖像大小
2) ? ? ? 畫(huà)圖
3) ? ? ? 顯示在這里有一點(diǎn)須要說(shuō)明。
即OpenGL不須要使用Direct3D那種使用WinMain()作為主函數(shù)的程序初始化窗體。
在Direct3D中是必須要這樣做的,即使用Win32的窗體程序而且調(diào)用CreateWindow()創(chuàng)建一個(gè)對(duì)話框。然后才干夠在對(duì)話框上畫(huà)圖。
OpenGL僅僅須要使用普通的控制臺(tái)程序就可以(入口函數(shù)為main())。當(dāng)然。OpenGL也能夠像Direct3D那樣把圖像繪制在Win32程序的窗體中。
?
以下結(jié)合OpenGL播放YUV/RGB的演示樣例代碼,具體分析一下上文的流程。
在詳述播放流程之前。再說(shuō)一點(diǎn)自己學(xué)習(xí)OpenGL時(shí)候的一個(gè)明顯的感覺(jué):OpenGL的函數(shù)好多啊。
OpenGL的函數(shù)的特點(diǎn)是數(shù)量多,可是每一個(gè)函數(shù)的參數(shù)少。
而Direct3D的特點(diǎn)和它正好反過(guò)來(lái)。函數(shù)少,可是每一個(gè)函數(shù)的參數(shù)多。
1. ? ? ? 初始化
1) ? ? ? ? 初始化glutInit()用于初始化glut庫(kù)。它原型例如以下:
void glutInit(int *argcp, char **argv);
它包括兩個(gè)參數(shù):argcp和argv。
普通情況下,直接把main()函數(shù)中的argc,argv傳遞給它就可以。
在這里簡(jiǎn)單介紹OpenGL中的3個(gè)庫(kù):glu。glut,glew
glu是有用庫(kù),包括有43個(gè)函數(shù),函數(shù)名的前綴為glu。Glu 為了減輕繁重的編程工作,封裝了OpenGL函數(shù),Glu函數(shù)通過(guò)調(diào)用核心庫(kù)的函數(shù)。為開(kāi)發(fā)人員提供相對(duì)簡(jiǎn)單的使用方法。實(shí)現(xiàn)一些較為復(fù)雜的操作。
glut是有用工具庫(kù)。基本上是用于做窗體界面的,而且是跨平臺(tái)的。
? ? ? ? glew是一個(gè)跨平臺(tái)的擴(kuò)展庫(kù)。沒(méi)必要的。
它能自己主動(dòng)識(shí)別當(dāng)前平臺(tái)所支持的全部OpenGL高級(jí)擴(kuò)展函數(shù)。還沒(méi)有深入研究。
glutInitDisplayMode()用于設(shè)置初始顯示模式。它的原型例如以下。
void glutInitDisplayMode(unsigned int mode)當(dāng)中mode能夠選擇以下值或組合:
GLUT_RGB: 指定 RGB 顏色模式的窗體
GLUT_RGBA: 指定 RGBA 顏色模式的窗體
GLUT_INDEX: 指定顏色索引模式的窗體
GLUT_SINGLE: 指定單緩存窗體
GLUT_DOUBLE: 指定雙緩存窗體
GLUT_ACCUM: 窗體使用累加緩存
GLUT_ALPHA: 窗體的顏色分量包括 alpha 值
GLUT_DEPTH: 窗體使用深度緩存
GLUT_STENCIL: 窗體使用模板緩存
GLUT_MULTISAMPLE: 指定支持多樣本功能的窗體
GLUT_STEREO: 指定立體窗體
GLUT_LUMINANCE: 窗體使用亮度顏色模型須要注意的是,假設(shè)使用雙緩沖(GLUT_DOUBLE),則須要用glutSwapBuffers ()畫(huà)圖。假設(shè)使用單緩沖(GLUT_SINGLE),則須要用glFlush()畫(huà)圖。
在使用OpenGL播放視頻的時(shí)候。我們能夠使用下述代碼:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );?
2) ? ? ? ? 創(chuàng)建窗體
glutInitWindowPosition()用于設(shè)置窗體的位置。
能夠指定x。y坐標(biāo)。
glutInitWindowSize()用于設(shè)置窗體的大小。
能夠設(shè)置窗體的寬,高。
glutCreateWindow()創(chuàng)建一個(gè)窗體。能夠指定窗體的標(biāo)題。
上述幾個(gè)函數(shù)十分基礎(chǔ),不再具體敘述。直接貼出一段演示樣例代碼:
3) ? ? ? ? 設(shè)置畫(huà)圖函數(shù)
glutDisplayFunc()用于設(shè)置畫(huà)圖函數(shù)。
操作系統(tǒng)在必要時(shí)刻就會(huì)調(diào)用該函數(shù)對(duì)窗體進(jìn)行又一次繪制操作。
相似于windows程序設(shè)計(jì)中處理WM_PAINT消息。比如。當(dāng)把窗體移動(dòng)到屏幕邊上,然后又移動(dòng)回來(lái)的時(shí)候,就會(huì)調(diào)用該函數(shù)對(duì)窗體進(jìn)行重繪。
它的原型例如以下。
當(dāng)中(*func)用于指定重繪函數(shù)。
比如在視頻播放的時(shí)候,指定display()函數(shù)用于重繪:glutDisplayFunc(&display);
4) ? ? ? ? 設(shè)置定時(shí)器
播放視頻的時(shí)候,每秒須要播放一定的畫(huà)面(通常是25幀)。因此使用定時(shí)器每間隔一段時(shí)間調(diào)用一下畫(huà)圖函數(shù)繪制圖形。定時(shí)器函數(shù)glutTimerFunc()的原型例如以下。
millis:定時(shí)的時(shí)間。單位是毫秒。1秒=1000毫秒。
(*func)(int value):用于指定定時(shí)器調(diào)用的函數(shù)。
value:給回調(diào)函數(shù)傳參。比較高端,沒(méi)有接觸過(guò)。
假設(shè)僅僅在主函數(shù)中寫(xiě)一個(gè)glutTimerFunc()函數(shù)的話。會(huì)發(fā)現(xiàn)僅僅會(huì)調(diào)用該函數(shù)一次。
因此須要在回調(diào)函數(shù)中再寫(xiě)一個(gè)glutTimerFunc()函數(shù),并調(diào)用回調(diào)函數(shù)自己。僅僅有這樣才干實(shí)現(xiàn)反重復(fù)復(fù)循環(huán)調(diào)用回調(diào)函數(shù)。
比如在視頻播放的時(shí)候。指定每40毫秒調(diào)用一次timeFunc ()函數(shù):
主函數(shù)中:
而后在timeFunc()函數(shù)中例如以下設(shè)置。
void timeFunc(int value){display();// Present frame every 40 msglutTimerFunc(40, timeFunc, 0); }
這樣就實(shí)現(xiàn)了每40ms調(diào)用一次display()。
?
5) ? ? ? ? 進(jìn)入消息循環(huán)
glutMainLoop()將會(huì)進(jìn)入GLUT事件處理循環(huán)。一旦被調(diào)用,這個(gè)程序?qū)⒂肋h(yuǎn)不會(huì)返回。視頻播放的時(shí)候,調(diào)用該函數(shù)之后即開(kāi)始播放視頻。
?
2. ? ? ? 循環(huán)顯示畫(huà)面
1) ? ? ? 調(diào)整顯示位置,圖像大小這一步主要是調(diào)整一下圖像的大小和位置。
假設(shè)不做這一步而直接使用glDrawPixels()進(jìn)行畫(huà)圖的話,會(huì)發(fā)現(xiàn)圖像位于窗體的左下角。而且是上下顛倒的(當(dāng)然,假設(shè)窗體和圖像一樣大的話,就不存在圖像位于角落的問(wèn)題)。效果例如以下圖所看到的。
為了解決上述問(wèn)題,須要調(diào)用有關(guān)的函數(shù)對(duì)圖像進(jìn)行變換。
變換用到了兩個(gè)函數(shù):glRasterPos3f()和glPixelZoom()。
glRasterPos3f()能夠平移圖像。它的原型例如以下。
當(dāng)中x用于指定x坐標(biāo);y用于指定y坐標(biāo)。Z這里還沒(méi)有用到。
在這里介紹一下OpenGL的坐標(biāo)。
原點(diǎn)位于屏幕的中心。屏幕的邊上相應(yīng)的值是1.0。和數(shù)學(xué)中的坐標(biāo)系基本上是一樣的。屏幕的左下角是(-1,-1),左上角是(-1。1)。
比如我們使用glRasterPos3f(-1.0f,0.0f,0)。圖像就會(huì)移動(dòng)至(-1。0),例如以下圖所看到的。
glPixelZoom()能夠放大、縮小和翻轉(zhuǎn)圖像。它的原型例如以下。
void glPixelZoom (GLfloat xfactor, GLfloat yfactor);
當(dāng)中xfactor、yfactor用于指定在x軸,y軸上放大的倍數(shù)(假設(shè)數(shù)值小于1則是縮小)。
假設(shè)指定負(fù)值,則能夠?qū)崿F(xiàn)翻轉(zhuǎn)。上文已經(jīng)說(shuō)過(guò)。使用OpenGL直接顯示像素?cái)?shù)據(jù)的話,會(huì)發(fā)現(xiàn)圖像是倒著的。因此須要在Y軸方向?qū)D像進(jìn)行翻轉(zhuǎn)。
比如:像素?cái)?shù)據(jù)的寬高分別為pixel_w 。pixel_h ;窗體大小為screen_w,screen_h的話。使用下述代碼能夠?qū)D像拉伸至窗體大小,而且翻轉(zhuǎn):
結(jié)合上述兩個(gè)函數(shù)。即“平移+翻轉(zhuǎn)+拉伸之后”,就能夠得到全屏的圖像了,例如以下圖所看到的。
PS:這種方法屬于比較笨的方法。應(yīng)該還有更好的方法吧。只是再?zèng)]有進(jìn)行深入研究了。
使用glDrawPixels()能夠繪制指定內(nèi)存中的像素?cái)?shù)據(jù)。該函數(shù)的原型例如以下。
該函數(shù)的參數(shù)的含義例如以下所看到的:
Width:像素?cái)?shù)據(jù)的寬。
Height:像素?cái)?shù)據(jù)的高。
Format:像素?cái)?shù)據(jù)的格式,比如GL_RGB,GL_BGR,GL_BGRA等。
Type:像素?cái)?shù)據(jù)在內(nèi)存中的格式。
Pixels:指針,指向存儲(chǔ)像素?cái)?shù)據(jù)的內(nèi)存。
比如繪制RGB24格式的數(shù)據(jù)。寬為pixel_w,高為pixel_h,像素?cái)?shù)據(jù)存儲(chǔ)在buffer中。能夠使用例如以下代碼。
glDrawPixels(pixel_w, pixel_h,GL_RGB, GL_UNSIGNED_BYTE, buffer);?
3) ? ? ? 顯示
使用雙緩沖的時(shí)候。調(diào)用函數(shù)glutSwapBuffers()進(jìn)行顯示。
使用單緩沖的時(shí)候。調(diào)用函數(shù)glFlush()進(jìn)行顯示。
?
?
視頻顯示的流程總結(jié)
視頻顯示的函數(shù)調(diào)用結(jié)構(gòu)能夠總結(jié)為下圖
代碼
貼上源代碼。
?
/*** 最簡(jiǎn)單的OpenGL播放視頻的樣例(OpenGL播放RGB/YUV)* Simplest Video Play OpenGL (OpenGL play RGB/YUV) ** 雷霄驊 Lei Xiaohua* leixiaohua1020@126.com* 中國(guó)傳媒大學(xué)/數(shù)字電視技術(shù)* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序使用OpenGL播放RGB/YUV視頻像素?cái)?shù)據(jù)。本程序?qū)嶋H上僅僅能 * 播放RGB(RGB24,BGR24,BGRA)數(shù)據(jù)。假設(shè)輸入數(shù)據(jù)為YUV420P * 數(shù)據(jù)的話。須要先轉(zhuǎn)換為RGB數(shù)據(jù)之后再進(jìn)行播放。
* 本程序是最簡(jiǎn)單的使用OpenGL播放像素?cái)?shù)據(jù)的樣例,適合OpenGL新手學(xué)習(xí)。 * * 函數(shù)調(diào)用過(guò)程例如以下: * * [初始化] * glutInit(): 初始化glut庫(kù)。
* glutInitDisplayMode(): 設(shè)置顯示模式。
* glutCreateWindow(): 創(chuàng)建一個(gè)窗體。 * glutDisplayFunc(): 設(shè)置畫(huà)圖函數(shù)(重繪的時(shí)候調(diào)用)。 * glutTimerFunc(): 設(shè)置定時(shí)器。 * glutMainLoop(): 進(jìn)入消息循環(huán)。 * * [循環(huán)渲染數(shù)據(jù)] * glRasterPos3f(),glPixelZoom(): 調(diào)整顯示位置,圖像大小。 * glDrawPixels(): 繪制。 * glutSwapBuffers(): 顯示。
* * This software plays RGB/YUV raw video data using OpenGL. This * software support show RGB (RGB24, BGR24, BGRA) data on the screen. * If the input data is YUV420P, it need to be convert to RGB first. * This program is the simplest example about play raw video data * using OpenGL, Suitable for the beginner of OpenGL. * * The process is shown as follows: * * [Init] * glutInit(): Init glut library. * glutInitDisplayMode(): Set display mode. * glutCreateWindow(): Create a window. * glutDisplayFunc(): Set the display callback. * glutTimerFunc(): Set timer. * glutMainLoop(): Start message loop. * * [Loop to Render data] * glRasterPos3f(),glPixelZoom(): Change picture's size and position. * glDrawPixels(): draw. * glutSwapBuffers(): show. */ #include <stdio.h> #include "glew.h" #include "glut.h" #include <stdlib.h> #include <malloc.h> #include <string.h> //set '1' to choose a type of file to play #define LOAD_RGB24 1 #define LOAD_BGR24 0 #define LOAD_BGRA 0 #define LOAD_YUV420P 0 int screen_w=500,screen_h=500; const int pixel_w = 320, pixel_h = 180; //Bit per Pixel #if LOAD_BGRA const int bpp=32; #elif LOAD_RGB24|LOAD_BGR24 const int bpp=24; #elif LOAD_YUV420P const int bpp=12; #endif //YUV file FILE *fp = NULL; unsigned char buffer[pixel_w*pixel_h*bpp/8]; unsigned char buffer_convert[pixel_w*pixel_h*3]; inline unsigned char CONVERT_ADJUST(double tmp) { return (unsigned char)((tmp >= 0 && tmp <= 255)?
tmp:(tmp < 0 ?
0 : 255)); } //YUV420P to RGB24 void CONVERT_YUV420PtoRGB24(unsigned char* yuv_src,unsigned char* rgb_dst,int nWidth,int nHeight) { unsigned char *tmpbuf=(unsigned char *)malloc(nWidth*nHeight*3); unsigned char Y,U,V,R,G,B; unsigned char* y_planar,*u_planar,*v_planar; int rgb_width , u_width; rgb_width = nWidth * 3; u_width = (nWidth >> 1); int ypSize = nWidth * nHeight; int upSize = (ypSize>>2); int offSet = 0; y_planar = yuv_src; u_planar = yuv_src + ypSize; v_planar = u_planar + upSize; for(int i = 0; i < nHeight; i++) { for(int j = 0; j < nWidth; j ++) { // Get the Y value from the y planar Y = *(y_planar + nWidth * i + j); // Get the V value from the u planar offSet = (i>>1) * (u_width) + (j>>1); V = *(u_planar + offSet); // Get the U value from the v planar U = *(v_planar + offSet); // Cacular the R,G,B values // Method 1 R = CONVERT_ADJUST((Y + (1.4075 * (V - 128)))); G = CONVERT_ADJUST((Y - (0.3455 * (U - 128) - 0.7169 * (V - 128)))); B = CONVERT_ADJUST((Y + (1.7790 * (U - 128)))); /* // The following formulas are from MicroSoft' MSDN int C,D,E; // Method 2 C = Y - 16; D = U - 128; E = V - 128; R = CONVERT_ADJUST(( 298 * C + 409 * E + 128) >> 8); G = CONVERT_ADJUST(( 298 * C - 100 * D - 208 * E + 128) >> 8); B = CONVERT_ADJUST(( 298 * C + 516 * D + 128) >> 8); R = ((R - 128) * .6 + 128 )>255?
255:(R - 128) * .6 + 128; G = ((G - 128) * .6 + 128 )>255?255:(G - 128) * .6 + 128; B = ((B - 128) * .6 + 128 )>255?255:(B - 128) * .6 + 128; */ offSet = rgb_width * i + j * 3; rgb_dst[offSet] = B; rgb_dst[offSet + 1] = G; rgb_dst[offSet + 2] = R; } } free(tmpbuf); } void display(void){ if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){ // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp); } //Make picture full of window //Move to(-1.0,1.0) glRasterPos3f(-1.0f,1.0f,0); //Zoom, Flip glPixelZoom((float)screen_w/(float)pixel_w, -(float)screen_h/(float)pixel_h); #if LOAD_BGRA glDrawPixels(pixel_w, pixel_h,GL_BGRA, GL_UNSIGNED_BYTE, buffer); #elif LOAD_RGB24 glDrawPixels(pixel_w, pixel_h,GL_RGB, GL_UNSIGNED_BYTE, buffer); #elif LOAD_BGR24 glDrawPixels(pixel_w, pixel_h,GL_BGR_EXT, GL_UNSIGNED_BYTE, buffer); #elif LOAD_YUV420P CONVERT_YUV420PtoRGB24(buffer,buffer_convert,pixel_w,pixel_h); glDrawPixels(pixel_w, pixel_h,GL_RGB, GL_UNSIGNED_BYTE, buffer_convert); #endif //GLUT_DOUBLE glutSwapBuffers(); //GLUT_SINGLE //glFlush(); } void timeFunc(int value){ display(); // Present frame every 40 ms glutTimerFunc(40, timeFunc, 0); } int main(int argc, char* argv[]) { #if LOAD_BGRA fp=fopen("../test_bgra_320x180.rgb","rb+"); #elif LOAD_RGB24 fp=fopen("../test_rgb24_320x180.rgb","rb+"); #elif LOAD_BGR24 fp=fopen("../test_bgr24_320x180.rgb","rb+"); #elif LOAD_YUV420P fp=fopen("../test_yuv420p_320x180.yuv","rb+"); #endif if(fp==NULL){ printf("Cannot open this file.\n"); return -1; } // GLUT init glutInit(&argc, argv); //Double, Use glutSwapBuffers() to show glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB ); //Single, Use glFlush() to show //glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB ); glutInitWindowPosition(100, 100); glutInitWindowSize(screen_w, screen_h); glutCreateWindow("Simplest Video Play OpenGL"); printf("Simplest Video Play OpenGL\n"); printf("Lei Xiaohua\n"); printf("http://blog.csdn.net/leixiaohua1020\n"); printf("OpenGL Version: %s\n", glGetString(GL_VERSION)); glutDisplayFunc(&display); glutTimerFunc(40, timeFunc, 0); // Start! glutMainLoop(); return 0; }
代碼注意事項(xiàng)
1. ? ? ? 能夠通過(guò)設(shè)置定義在文件開(kāi)始出的宏。決定讀取哪個(gè)格式的像素?cái)?shù)據(jù)(bgra,rgb24,bgr24,yuv420p)。?
//set '1' to choose a type of file to play #define LOAD_RGB24 1 #define LOAD_BGR24 0 #define LOAD_BGRA 0 #define LOAD_YUV420P 0?
2. ? ? ? 窗體的寬高為screen_w,screen_h。像素?cái)?shù)據(jù)的寬高為pixel_w,pixel_h。它們的定義例如以下。
?//Width, Height const int screen_w=500,screen_h=500; const int pixel_w=320,pixel_h=180;
3. ? ? ? 注意顯示方式的不同
BGRA,BGR24,RGB24這3種格式能夠直接在glDrawPixels()中設(shè)置像素格式顯示出來(lái)。而YUV420P是不能直接顯示出來(lái)的。
本文演示樣例採(cǎi)用的方式是先將YUV420P轉(zhuǎn)換成RGB24,然后進(jìn)行顯示。
運(yùn)行結(jié)果
不管選擇加載哪個(gè)文件,運(yùn)行結(jié)果都是一樣的,例如以下圖所看到的。
下載
代碼位于“Simplest Media Play”中?
?
SourceForge項(xiàng)目地址:https://sourceforge.net/projects/simplestmediaplay/
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8054395
??
注:
該項(xiàng)目會(huì)不定時(shí)的更新并修復(fù)一些小問(wèn)題。最新的版本號(hào)請(qǐng)參考該系列文章的總述頁(yè)面:
?《最簡(jiǎn)單的視音頻播放演示樣例1:總述》
上述project包括了使用各種API(Direct3D,OpenGL。GDI,DirectSound,SDL2)播放多媒體樣例。當(dāng)中音頻輸入為PCM採(cǎi)樣數(shù)據(jù)。輸出至系統(tǒng)的聲卡播放出來(lái)。
視頻輸入為YUV/RGB像素?cái)?shù)據(jù)。
輸出至顯示器上的一個(gè)窗體播放出來(lái)。
通過(guò)本project的代碼剛開(kāi)始學(xué)習(xí)的人能夠高速學(xué)習(xí)使用這幾個(gè)API播放視頻和音頻的技術(shù)。
一共包括了例如以下幾個(gè)子project:
simplest_audio_play_directsound: ? ? ? ? 使用DirectSound播放PCM音頻採(cǎi)樣數(shù)據(jù)。
simplest_audio_play_sdl2: ? ? ? ? ? ? ? ? ? ? ? 使用SDL2播放PCM音頻採(cǎi)樣數(shù)據(jù)。
simplest_video_play_direct3d: ? ? ? ? ? ? ? ?使用Direct3D的Surface播放RGB/YUV視頻像素?cái)?shù)據(jù)。
simplest_video_play_direct3d_texture:使用Direct3D的Texture播放RGB視頻像素?cái)?shù)據(jù)。
simplest_video_play_gdi: ? ? ? ? ? ? ? ? ? ? ? ? ?使用GDI播放RGB/YUV視頻像素?cái)?shù)據(jù)。
simplest_video_play_opengl: ? ? ? ? ? ? ? ? ? 使用OpenGL播放RGB/YUV視頻像素?cái)?shù)據(jù)。
simplest_video_play_opengl_texture: ? ?使用OpenGL的Texture播放YUV視頻像素?cái)?shù)據(jù)。
simplest_video_play_sdl2: ? ? ? ? ? ? ? ? ? ? ? ?使用SDL2播放RGB/YUV視頻像素?cái)?shù)據(jù)。
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/zfyouxi/p/5084643.html
總結(jié)
以上是生活随笔為你收集整理的最简单的视音频播放演示样例5:OpenGL播放RGB/YUV的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一些dos下简单命令
- 下一篇: Clipboard.js实现点击自动复制