1. 程式人生 > >最簡單的視音訊播放示例5:OpenGL播放RGB/YUV

最簡單的視音訊播放示例5:OpenGL播放RGB/YUV

=====================================================

最簡單的視音訊播放示例系列文章列表:

=====================================================


本文記錄OpenGL播放視訊的技術。OpenGL是一個和Direct3D同一層面的技術。相比於Direct3D,OpenGL具有跨平臺的優勢。儘管在遊戲領域,DirectX的影響力已漸漸超越OpenGL並被大多數PC遊戲開發商所採用,但在專業高階繪圖領域,OpenGL因為色彩準確,仍然是不能被取代的主角。


OpenGL簡介



從網上搜集了一些有關OpenGL簡介方面的知識,在這裡列出來。
開放圖形庫(英語:Open Graphics Library,縮寫為OpenGL)是個定義了一個跨程式語言、跨平臺的應用程式介面(API)的規範,它用於生成二維、三維影象。
OpenGL規範由1992年成立的OpenGL架構評審委員會(ARB)維護。ARB由一些對建立一個統一的、普遍可用的API特別感興趣的公司組成。根據OpenGL官方網站,2002年6月的ARB投票成員包括3Dlabs、Apple Computer、ATI Technologies、Dell Computer、Evans & Sutherland、Hewlett-Packard、IBM、Intel、Matrox、NVIDIA、SGI和Sun Microsystems,Microsoft曾是創立成員之一,但已於2003年3月退出。
OpenGL仍然是唯一能夠取代微軟對3D圖形技術的完全控制的API。它仍然具有一定的生命力,但是Silicon Graphics已經不再以任何讓微軟不悅的方式推廣OpenGL,因而它存在較高的風險。在高階的圖形裝置和專業應用方面OpenGL佔據著統治地位(Direct3D目前還不支援)。開放原始碼社群(尤其是Mesa專案)一直致力於提供OpenGL支援。
 

OpenGL渲染管線

下文也是網上看的,搞懂了一部分,但是由於3D方面基礎不牢固有些方面還沒有完全弄懂。

OpenGL渲染管線(OpenGL Pipeline)按照特定的順序對圖形資訊進行處理,這些圖形資訊可以分為兩個部分:頂點資訊(座標、法向量等)和畫素資訊(影象、紋理等)。圖形資訊最終被寫入幀快取中,儲存在幀快取中的資料(影象),可以被應用程式獲得(用於儲存結果,或作為應用程式的輸入等,見下圖中灰色虛線)。



Display List(顯示列表)
顯示列表是一組OpenGL命令,被儲存(編譯)起來用於後續的執行。所有資料,幾何(頂點)資料和畫素資料都可以存入顯示列表。資料和命令快取到顯示列表中可以提高效能。
Vertex Operation(頂點處理)

頂點座標和法線座標經過模式檢視矩陣從物體座標系(object coordinates)轉換為觀察座標系(eye coordinates)。若啟用了光照,對轉換後的定點和法線座標執行光照計算。光照計算更新了頂點的顏色值。

Primitive Assembly(圖元裝配)

頂點處理之後,基本圖元(點、線、多邊形)經過投影矩陣變換,再被視見體裁剪平面裁剪,從觀察座標系轉換為裁剪座標系。之後,進行透視除法(除以w)和視口變換(viewport transform),將3d場景投影到視窗座標系。

Pixel Transfer Operation(畫素操作)

畫素從客戶記憶體中解包出來之後,要經過縮放、偏移、對映、箝拉(clamping)。這些處理即為畫素轉換操作。轉換的資料存在紋理記憶體或直接經過光柵化轉為片段(fragment)。

Texture Memory(紋理記憶體)

紋理影象載入到紋理記憶體中,然後應用到幾何物件上。 

Raterization(光柵化)

光柵化就是把幾何(頂點座標等)和畫素資料轉換為片段(fragment)的過程,每個片段對應於幀緩衝區中的一個畫素,該畫素對應螢幕上一點的顏色和不透明度資訊。片段是一個矩形陣列,包含了顏色、深度、線寬、點的大小等資訊(反鋸齒計算等)。如果渲染模式被設定為GL_FILL,多邊形內部的畫素資訊在這個階段會被填充。



如上圖中的三角形,輸入三角形的三個頂點座標以及其顏色,頂點操作會對三角形的頂點座標以及法向量進行變換,顏色資訊不需要經過變換,但光照計算會影響頂點的顏色資訊。經過光柵化後,三角形被離散為一個個點,不在是三個座標表示,而是由一系列的點組成,每個點儲存了相應的顏色、深度和不透明度等資訊。
 
Fragment Operation(片段操作)
這是將片段轉為幀緩衝區中的畫素要進行的最後處理。首先是紋理單元(texel)生成。一個紋理單元由紋理記憶體中的資料生成,然後應用到每個片段上。之後進行霧計算。 霧計算完成後,還要按序進行若干片段測試,依次為蒙板(scissor)測試,alpha測試,模版(stencil)測試,深度測試。最後,執行混合,抖動,邏輯操作和遮蔽操作,最終的畫素存入framebuffer。

 

OpenGL與Direct3D的對比


有關視訊顯示的技術在《Direct3D》文章中已經有過敘述,在這裡不再重複。在網上看了一下有關於他們不同點的文章,寫得簡單明瞭,在這裡引用一下:
OpenGL與Direct3D的一點點對比
OGL比D3D好的地方:
OGL是業界標準,許多非Windows作業系統下還找不到D3D
OGL的色彩比D3D的要好,表面更光滑
OGL的函式很有規律,不像D3D的,都是指標method,函式名太長了!!
OGL是右手座標系,這是數學裡用慣了的.D3D雖然也可以改變成右手座標系,但是需要d3dx9_36.dll的支援
OGL的常用Matrix,如WorldMatrix都封裝好了,D3D要自己寫。
OGL的繪圖方式很靈活,而D3D的則要事先定義好FVF,要等所有資訊寫進Stream中才繪製。這就使它產生了VertexBuffer和IndexBuffer.好象微軟嫌D3D的Buffer不夠多?搞的多不好學??看人家OGL,哪裡要這個東西?
D3D有好多版本,要是顯示卡不支援就廢柴一垛了。而OGL從幾年前就一直沒變過,所以大部分顯示卡都支援。
還有,我發現D3D的半透明功能有很大的問題!!就是兩個半透明的物體前後順序的問題——前面的會被後面的擋住。
 
但是D3D也有比OGL好的地方:
D3D支援許多格式的圖片檔案,而OGL載入jpg都得自己寫程式碼。
因為D3D是指標呼叫模式,所以做D3D的鉤子有難度,從而增加了外掛的製作難度。
D3D是DirectX的成員。程式設計師要實現聲音播放可以用DirectMusic,配套用總是好的,而OGL則只能畫畫
D3D是被微軟大力推廣的連線庫。相反,微軟則大力壓制OGL(都是Microsoft參與研製出來的產品,待遇怎這麼大?)
正因為此,D3D已成為中國大型遊戲界的主流(我覺得他們是盲目跟風。其實國外很多遊戲都是用OGL)

 

OpenGL視訊顯示的流程

使用OpenGL播放視訊最簡單的情況下需要如下步驟:
1.       初始化
1)         初始化
2)         建立視窗
3)         設定繪圖函式
4)         設定定時器
5)         進入訊息迴圈
2.       迴圈顯示畫面
1)       調整顯示位置,影象大小
2)       畫圖
3)       顯示
在這裡有一點需要說明。即OpenGL不需要使用Direct3D那種使用WinMain()作為主函式的程式初始化視窗。在Direct3D中是必須要這樣做的,即使用Win32的視窗程式並且呼叫CreateWindow()建立一個對話方塊,然後才可以在對話方塊上繪圖。OpenGL只需要使用普通的控制檯程式即可(入口函式為main())。當然,OpenGL也可以像Direct3D那樣把影象繪製在Win32程式的視窗中。
 
下面結合OpenGL播放YUV/RGB的示例程式碼,詳細分析一下上文的流程。
在詳述播放流程之前,再說一點自己學習OpenGL時候的一個明顯的感覺:OpenGL的函式好多啊。OpenGL的函式的特點是數量多,但是每個函式的引數少。而Direct3D的特點和它正好反過來,函式少,但是每個函式的引數多。

1.       初始化

1)         初始化
glutInit()用於初始化glut庫。它原型如下:
void glutInit(int *argcp, char **argv);

它包含兩個引數:argcp和argv。一般情況下,直接把main()函式中的argc,argv傳遞給它即可。
在這裡簡單介紹OpenGL中的3個庫:glu,glut,glew
glu是實用庫,包含有43個函式,函式名的字首為glu。Glu 為了減輕繁重的程式設計工作,封裝了OpenGL函式,Glu函式通過呼叫核心庫的函式,為開發者提供相對簡單的用法,實現一些較為複雜的操作。
  glut是實用工具庫,基本上是用於做視窗介面的,並且是跨平臺的。

        glew是一個跨平臺的擴充套件庫。不是必需的。它能自動識別當前平臺所支援的全部OpenGL高階擴充套件函式。還沒有深入研究。

glutInitDisplayMode()用於設定初始顯示模式。它的原型如下。

void glutInitDisplayMode(unsigned int mode)

其中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: 視窗使用亮度顏色模型
需要注意的是,如果使用雙緩衝(GLUT_DOUBLE),則需要用glutSwapBuffers ()繪圖。如果使用單緩衝(GLUT_SINGLE),則需要用glFlush()繪圖。
在使用OpenGL播放視訊的時候,我們可以使用下述程式碼:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );
 
2)         建立視窗
glutInitWindowPosition()用於設定視窗的位置。可以指定x,y座標。
glutInitWindowSize()用於設定視窗的大小。可以設定視窗的寬,高。
glutCreateWindow()建立一個視窗。可以指定視窗的標題。
上述幾個函式十分基礎,不再詳細敘述。直接貼出一段示例程式碼:
glutInitWindowPosition(100, 100);
glutInitWindowSize(500, 500);
glutCreateWindow("Simplest Video Play OpenGL");      
 
3)         設定繪圖函式
glutDisplayFunc()用於設定繪圖函式。作業系統在必要時刻就會呼叫該函式對窗體進行重新繪製操作。類似於windows程式設計中處理WM_PAINT訊息。例如,當把視窗移動到螢幕邊上,然後又移動回來的時候,就會呼叫該函式對視窗進行重繪。它的原型如下。
void glutDisplayFunc(void (*func)(void));

其中(*func)用於指定重繪函式。

例如在視訊播放的時候,指定display()函式用於重繪:
glutDisplayFunc(&display);

4)         設定定時器
播放視訊的時候,每秒需要播放一定的畫面(一般是25幀),因此使用定時器每間隔一段時間呼叫一下繪圖函式繪製圖形。定時器函式glutTimerFunc()的原型如下。
void glutTimerFunc(unsigned int millis, void (*func)(int value), int value);
它的引數含義如下:
millis:定時的時間,單位是毫秒。1秒=1000毫秒。
(*func)(int value):用於指定定時器呼叫的函式。
value:給回撥函式傳參。比較高階,沒有接觸過。
如果只在主函式中寫一個glutTimerFunc()函式的話,會發現只會呼叫該函式一次。因此需要在回撥函式中再寫一個glutTimerFunc()函式,並呼叫回撥函式自己。只有這樣才能實現反反覆覆迴圈呼叫回撥函式。
例如在視訊播放的時候,指定每40毫秒呼叫一次timeFunc ()函式:
主函式中:
glutTimerFunc(40, timeFunc, 0);

而後在timeFunc()函式中如下設定。
void timeFunc(int value){
    display();
    // Present frame every 40 ms
    glutTimerFunc(40, timeFunc, 0);
}

這樣就實現了每40ms呼叫一次display()。
 
5)         進入訊息迴圈
glutMainLoop()將會進入GLUT事件處理迴圈。一旦被呼叫,這個程式將永遠不會返回。視訊播放的時候,呼叫該函式之後即開始播放視訊。
 

2.       迴圈顯示畫面

1)       調整顯示位置,影象大小
這一步主要是調整一下影象的大小和位置。如果不做這一步而直接使用glDrawPixels()進行繪圖的話,會發現影象位於視窗的左下角,而且是上下顛倒的(當然,如果視窗和影象一樣大的話,就不存在影象位於角落的問題)。效果如下圖所示。

為了解決上述問題,需要呼叫有關的函式對影象進行變換。變換用到了兩個函式:glRasterPos3f()和glPixelZoom()。
glRasterPos3f()可以平移影象。它的原型如下。
void glRasterPos3f (GLfloat x, GLfloat y, GLfloat z);

其中x用於指定x座標;y用於指定y座標。Z這裡還沒有用到。
在這裡介紹一下OpenGL的座標。原點位於螢幕的中心。螢幕的邊上對應的值是1.0。和數學中的座標系基本上是一樣的。螢幕的左下角是(-1,-1),左上角是(-1,1)。

例如我們使用glRasterPos3f(-1.0f,0.0f,0),影象就會移動至(-1,0),如下圖所示。


glPixelZoom()可以放大、縮小和翻轉影象。它的原型如下。
void glPixelZoom (GLfloat xfactor, GLfloat yfactor);

其中xfactor、yfactor用於指定在x軸,y軸上放大的倍數(如果數值小於1則是縮小)。如果指定負值,則可以實現翻轉。上文已經說過,使用OpenGL直接顯示畫素資料的話,會發現影象是倒著的。因此需要在Y軸方向對影象進行翻轉。

例如:畫素資料的寬高分別為pixel_w ,pixel_h ;視窗大小為screen_w,screen_h的話,使用下述程式碼可以將影象拉伸至視窗大小,並且翻轉:
glPixelZoom((float)screen_w/(float)pixel_w, -(float)screen_h/pixel_h);

結合上述兩個函式,即“平移+翻轉+拉伸之後”,就可以得到全屏的影象了,如下圖所示。

PS:這個方法屬於比較笨的方法,應該還有更好的方法吧。不過再沒有進行深入研究了。

2)       畫圖
使用glDrawPixels()可以繪製指定記憶體中的畫素資料。該函式的原型如下。
void glDrawPixels (
GLsizei width, GLsizei height,
GLenum format,
GLenum type,
const GLvoid *pixels);

該函式的引數的含義如下所示:
Width:畫素資料的寬。
Height:畫素資料的高。
Format:畫素資料的格式,例如GL_RGB,GL_BGR,GL_BGRA等。
Type:畫素資料在記憶體中的格式。
Pixels:指標,指向儲存畫素資料的記憶體。
例如繪製RGB24格式的資料,寬為pixel_w,高為pixel_h,畫素資料儲存在buffer中。可以使用如下程式碼。
glDrawPixels(pixel_w, pixel_h,GL_RGB, GL_UNSIGNED_BYTE, buffer);
 
3)       顯示
使用雙緩衝的時候,呼叫函式glutSwapBuffers()進行顯示。
使用單緩衝的時候,呼叫函式glFlush()進行顯示。
 
 

視訊顯示的流程總結

視訊顯示的函式呼叫結構可以總結為下圖


 

程式碼


貼上原始碼。
/**
 * 最簡單的OpenGL播放視訊的例子(OpenGL播放RGB/YUV)
 * Simplest Video Play OpenGL (OpenGL play RGB/YUV) 
 *
 * 雷霄驊 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程式使用OpenGL播放RGB/YUV視訊畫素資料。本程式實際上只能
 * 播放RGB(RGB24,BGR24,BGRA)資料。如果輸入資料為YUV420P
 * 資料的話,需要先轉換為RGB資料之後再進行播放。
 * 本程式是最簡單的使用OpenGL播放畫素資料的例子,適合OpenGL新手學習。
 *
 * 函式呼叫步驟如下: 
 *
 * [初始化]
 * glutInit(): 初始化glut庫。
 * glutInitDisplayMode(): 設定顯示模式。
 * glutCreateWindow(): 建立一個視窗。
 * glutDisplayFunc(): 設定繪圖函式(重繪的時候呼叫)。
 * glutTimerFunc(): 設定定時器。
 * glutMainLoop(): 進入訊息迴圈。
 *
 * [迴圈渲染資料]
 * glRasterPos3f(),glPixelZoom(): 調整顯示位置,影象大小。
 * 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;
}

 

程式碼注意事項

1.       可以通過設定定義在檔案開始出的巨集,決定讀取哪個格式的畫素資料(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。畫素資料的寬高為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()中設定畫素格式顯示出來。而YUV420P是不能直接顯示出來的。本文示例採用的方式是先將YUV420P轉換成RGB24,然後進行顯示。

執行結果

無論選擇載入哪個檔案,執行結果都是一樣的,如下圖所示。


 

下載

程式碼位於“Simplest Media Play”中
 
 
 
 

注:

該專案會不定時的更新並修復一些小問題,最新的版本請參考該系列文章的總述頁面:


上述工程包含了使用各種API(Direct3D,OpenGL,GDI,DirectSound,SDL2)播放多媒體例子。其中音訊輸入為PCM取樣資料。輸出至系統的音效卡播放出來。視訊輸入為YUV/RGB畫素資料。輸出至顯示器上的一個視窗播放出來。

通過本工程的程式碼初學者可以快速學習使用這幾個API播放視訊和音訊的技術。

一共包括瞭如下幾個子工程:

simplest_audio_play_directsound:         使用DirectSound播放PCM音訊取樣資料。
simplest_audio_play_sdl2:                       使用SDL2播放PCM音訊取樣資料。
simplest_video_play_direct3d:                使用Direct3D的Surface播放RGB/YUV視訊畫素資料。
simplest_video_play_direct3d_texture:使用Direct3D的Texture播放RGB視訊畫素資料。
simplest_video_play_gdi:                          使用GDI播放RGB/YUV視訊畫素資料。
simplest_video_play_opengl:                   使用OpenGL播放RGB/YUV視訊畫素資料。
simplest_video_play_opengl_texture:    使用OpenGL的Texture播放YUV視訊畫素資料。
simplest_video_play_sdl2:                        使用SDL2播放RGB/YUV視訊畫素資料。
 
 
 
 

相關推薦

簡單音訊播放示例5OpenGL播放RGB/YUV

=====================================================最簡單的視音訊播放示例系列文章列表:=====================================================本文記錄OpenGL播放視訊

簡單音訊播放示例6OpenGL播放YUV420P(通過Texture,使用Shader)

=====================================================最簡單的視音訊播放示例系列文章列表:=====================================================本文記錄OpenGL播放視訊

簡單音訊播放示例9SDL2播放PCM

最簡單的視音訊播放示例系列文章列表: ===================================================== 本文記錄SDL播放音訊的技術。在這裡使用的版本是SDL2。實際上SDL本身並不提供視音訊播放的功能,它只

簡單音訊播放示例7SDL2播放RGB/YUV

=====================================================最簡單的視音訊播放示例系列文章列表:=====================================================本文記錄SDL播放視訊的技術

簡單的基於libVLC的例子簡單的基於libVLC的視訊播放器(圖形介面版)

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

HTML5用audio標籤做一個簡單音訊播放

在做系統的時候,要求做一個音訊播放器,就在網上查找了一些資料,發現這樣的資料還是很千篇一律的,EasyUI框架並沒有給我們一個音訊播放器的功能,在bootstrap上有,但是也是結合html5來寫的,因此,我們在這裡就用純的html5血一個音訊播放器,如何播放本地的音訊。

簡單的基於libVLC的例子簡單的基於libVLC的視訊播放

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

MongoDB簡單的入門教程之四使用Spring Boot操作MongoDB

Spring Boot 是一個輕量級框架,可以完成基於 Spring 的應用程式的大部分配置工作。Spring Boot的目的是提供一組工具,以便快速構建容易配置的Spring應用程式,省去大量傳統Spring專案的繁瑣配置。 MongoDB是一個基於分散式檔

音訊資料處理入門PCM音訊取樣資料處理

                =====================================================視音訊資料處理入門系列文章:=====================================================上一篇文章記錄了RGB/YUV視訊畫素

史上簡單的 SpringCloud 教程 | 第一篇 服務的註冊與發現(Eureka)

一、spring cloud簡介 spring cloud 為開發人員提供了快速構建分散式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理

簡單C++程式的編寫5模擬ATM機

運用C++語言,模擬24小時運營的ATM機,實現其五大功能: 1、餘額查詢 2、取款 3、存款 4、轉賬 5、密碼修改 程式碼: #include<iostream.h> #include<conio.h> char *password

史上簡單的 SpringCloud 教程 | 第一篇 服務的註冊與發現Eureka(Finchley版本)

一、spring cloud簡介 鑑於《史上最簡單的Spring Cloud教程》很受讀者歡迎,再次我特意升級了一下版本,目前支援的版本為Spring Boot版本2.0.3.RELEASE,Spring Cloud版本為Finchley.RELEASE。

簡單的spring入門示例

應群裡一位朋友的要求,寫一個最簡單的spring示例,使用spring的MVC,並應用了spring的依賴注入 ,實現簡單應用,索性放在這裡供還沒入門的spring愛好者參考,初步感受一下spring應用(spring高手就不必看了,這裡並沒有涉及高階特性,比如與ORM框架的

簡單的基於libVLC的例子簡單的基於libVLC的推流器

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄基於libVLC

shareding-jdbc實現讀寫分離簡單的容易理解示例

資料庫建立create database demo_ds_master; //建立主庫create database demo_ds_slave_0; //建立從庫1create database

windows下簡單音訊採集示例

最近需要在window下進行音訊採集,網上找了很久都沒找到win7下如何採集pcm資料的完整示例,經過一翻折騰後寫了一個很簡單的demo程式以供同行進行參考,如有不正確的地方請指正 本例是採用audio core進行音訊採集 程式碼塊 #include "

簡單的基於FFMPEG+SDL的音視訊播放

一、概述         在《最簡單的基於FFMPEG+SDL的音訊播放器》記錄一中,我們實現了音訊的播放。更早前,我們在《最簡單的基於FFMPEG+SDL的視訊播放器》記錄一和二中,實現了視訊的播放。在實現視訊播放的時候,我們設定了一個延遲40ms,否則視訊就會以解碼的速

音訊資料處理入門UDP-RTP協議解析

=====================================================視音訊資料處理入門系列文章:=====================================================本文介紹網路協議資料的處理程式。網路

音訊資料處理入門AAC音訊碼流解析

=====================================================視音訊資料處理入門系列文章:=====================================================本文繼續上一篇文章的內容,介紹一個音

音訊資料處理入門FLV封裝格式解析

=====================================================視音訊資料處理入門系列文章:=====================================================前兩篇文章介紹了音訊碼流處理程式和視