35.FFmpeg+OpenGLES+OpenSLES播放器實現(九.OpenGLES播放視訊)
完整程式碼,一切盡在註釋中
extern "C"
JNIEXPORT void JNICALL
Java_com_rzm_ffmpegplayer_FFmpegPlayer_initOpenGL(JNIEnv *env, jobject instance, jstring url_,
jobject surface) {
const char *url = env->GetStringUTFChars(url_, 0);
FILE *fp = fopen(url, "rb");
if (!fp) {
LOGE("open file %s failed!", url);
return;
}
/**************************EGL初始化********************************************/
//1.建立渲染視窗
ANativeWindow *aNativeWindow = ANativeWindow_fromSurface(env, surface);
//2.EGL Display建立
//EGL提供了平臺無關型別EGLDisplay表示視窗。定義EGLNativeDisplayType是為了匹配原生視窗系統的顯示型別,
// 對於Windows,EGLNativeDisplayType被定義為HDC,
// 對於Linux系統,被定義為Display*型別,
// 對於Android系統,定義為ANativeWindow *型別,
// 為了方便的將程式碼轉移到不同的作業系統上,應該傳入EGL_DEFAULT_DISPLAY,返回與預設原生視窗的連線。
// 如果連線不可用,則返回EGL_NO_DISPLAY
EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL_NO_DISPLAY) {
LOGE("eglGetDisplay failed%d", eglGetError());
return;
}
//3.初始化Display
//建立與本地原生視窗的連線後需要初始化EGL,使用函式eglInitialize進行初始化操作。如果 EGL 不能初始化,
// 它將返回EGL_FALSE,並將EGL錯誤碼設定為EGL_BAD_DISPLAY表示制定了不合法的EGLDisplay,
// 或者EGL_NOT_INITIALIZED表示EGL不能初始化。使用函式eglGetError用來獲取最近一次呼叫EGL函式出錯的錯誤程式碼
//引數
//EGLDisplay display 建立的EGL連線
//EGLint *majorVersion 返回EGL主機板版本號
//EGLint *minorVersion 返回EGL次版本號
if (EGL_TRUE != eglInitialize(eglDisplay, 0, 0)) {
LOGE("eglInitialize failed");
return;
}
//3. surface視窗配置
//視窗配置有兩種方式:一種方式是使用eglGetConfigs函式獲取底層視窗系統支援的所有EGL表面配置(Config),然後再使用
// eglGetConfigAttrib依次查詢每個EGLConfig相關的資訊,Config有眾多的Attribute,這些Attribute決定FrameBuffer
// 的格式和能力,通過eglGetConfigAttrib ()來讀取,但不能修改。EGLConfig包含了渲染表面的所有資訊,包括可用顏色、
// 緩衝區等其他特性。
// 另一種方式是指定我們需要的渲染表面配置,讓EGL自己選擇一個符合條件的EGLConfig配置。eglChooseChofig
// 呼叫成功返回EGL_TRUE,失敗時返回EGL_FALSE,如果attribList包含了未定義的EGL屬性,或者屬性值不合法,
// EGL程式碼被設定為EGL_BAD_ATTRIBUTR
//Config實際就是FrameBuffer的引數
EGLConfig eglConfig;
//attribList引數在EGL函式中可以為null或者指向一組以EGL_NONE結尾的鍵對值
//通常以id,value依次存放,對於個別標識性的屬性可以只有id,沒有value
EGLint configAttr[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_NONE
};
EGLint eglConfigNum;
//引數
//EGLDisplay dpy 建立的和本地視窗系統的連線
//const EGLint *attrib_list, 指定渲染表面的引數列表,可以為null
//EGLConfig *configs 呼叫成功,返會符合條件的EGLConfig列表
//EGLint config_size, 最多返回的符合條件的EGLConfig個數
//EGLint *num_config 實際返回的符合條件的EGLConfig個數
if (EGL_TRUE != eglChooseConfig(eglDisplay, configAttr, &eglConfig, 1, &eglConfigNum)) {
LOGE("eglChooseConfig failed");
return;
}
//4. 建立surface
// 有了符合條件的EGLConfig後,就可以通過eglCreateWindowSurface函式建立渲染表面。使用這個函式的前提是要使
// 用原生視窗系統提供的API建立一個視窗。eglCreateWindowSurface中attribList一般可以使用null即可。
// 函式呼叫失敗會返回EGL_NO_SURFACE,並設定對應的錯誤碼
//使用eglCreateWindowSurface函式建立在視窗上的渲染表面,此外還可以使用eglCreatePbufferSurface建立
// 螢幕外渲染表面(Pixel Buffer 畫素緩衝區)。使用Pbuffer一般用於生成紋理貼圖,不過該功能已經被
// FrameBuffer替代了,使用幀緩衝物件的好處是所有的操作都由OpenGL ES來控制。使用Pbuffer的方法和前面建立
// 視窗渲染表面一樣,需要改動的地方是在選取EGLConfig時,增加EGL_SURFACE_TYPE引數使其值包含
// EGL_PBUFFER_BIT。而該引數預設值為EGL_WINDOW_BIT。
//EGLSurface eglCreatePbufferSurface( EGLDisplay display, EGLConfig config, EGLint const * attrib_list // 指定畫素緩衝區屬性列表 );
//Surface實際上就是一個FrameBuffer
EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, aNativeWindow, 0);
if (eglSurface == EGL_NO_SURFACE) {
LOGE("eglCreateWindowSurface failed");
return;
}
//4 建立關聯的上下文
//使用eglCreateContext為當前的渲染API建立EGL渲染上下文,返回一個上下文,當前的渲染API是由函式eglBindAPI
// 設定的。OpenGL ES是一個狀態機,用一系列變數描述OpenGL ES當前的狀態如何執行,我們通常使用如下途徑去更改
// OpenGL狀態:設定選項,操作緩衝。最後,我們使用當前OpenGL上下文來渲染。比如我想告訴OpenGL ES接下來要繪製
// 三角形,可以通過一些上下文變數來改變OpenGL ES的狀態,一旦改變了OpenGL ES的狀態為繪製三角形,下一個命令
// 就會畫出三角形。通過這些狀態設定函式就會改變上下文,接下來的操作總會根據當前上下文的狀態來執行,除非再次
// 重新改變狀態。
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, ctxAttr);
if (eglContext == EGL_NO_CONTEXT) {
LOGE("eglCreateContext failed!");
return;
}
//5.指定某個EGLContext為當前上下文。使用eglMakeCurrent函式進行當前上下文的繫結。一個程式可能建立多個EGLContext,
// 所以需要關聯特定的EGLContext和渲染表面,一般情況下兩個EGLSurface引數設定成一樣的。
if (EGL_TRUE != eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
LOGE("eglMakeCurrent failed!");
return;
}
LOGI("EGL Init Success!");
/**************************EGL初始化********************************************/
/**************************shader初始化********************************************/
//定點shader初始化
GLint vshader = initShader(vertexShader, GL_VERTEX_SHADER);
//片元yuv420 shader初始化
GLint fshader = initShader(fragYUV420P, GL_FRAGMENT_SHADER);
/**************************shader初始化********************************************/
/**************************渲染程式初始化********************************************/
//7.使用OpenGL相關的API進行繪製操作。.....
//建立渲染程式
GLint program = glCreateProgram();
if (program == 0) {
LOGE("glCreateProgram failed!");
return;
}
//渲染程式中加入著色器程式碼
glAttachShader(program, vshader);
glAttachShader(program, fshader);
//連結程式
glLinkProgram(program);
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status != GL_TRUE) {
LOGE("glLinkProgram failed!");
return;
}
glUseProgram(program);
LOGI("glLinkProgram success!");
/////////////////////////////////////////////////////////////
//加入三維頂點資料 兩個三角形組成正方形
//頂點座標系描述了GopenGL的繪製範圍,他以繪製中心為原點,在2D圖形下,左邊界為到x -1,右邊界到x 1,上邊界到y 1
//下邊界到y -1,3D下同樣道理。定點座標系就是OpenGL的繪製區間
static float vers[] = {
1.0f, -1.0f, 0.0f, //右下
-1.0f, -1.0f, 0.0f, //左下
1.0f, 1.0f, 0.0f, //右上
-1.0f, 1.0f, 0.0f, //左上
};
GLuint apos = (GLuint) glGetAttribLocation(program, "aPosition");
glEnableVertexAttribArray(apos);
//傳遞頂點 取3個資料,跳轉12個位元組位(3個資料)再取另外3個資料,這是實現塊狀資料儲存的關鍵,很多函式裡都有這個引數,通常寫作int stride
glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 12, vers);
//加入紋理座標資料
//紋理座標的座標系以紋理左下角為座標原點,向右為x正軸方向,向上為y軸正軸方向。他的總長度是1。即紋理圖片的
// 四個角的座標分別是:(0,0)、(1,0)、(0,1)、(1,1),分別對應左下、右下、左上、右上四個頂點。
static float txts[] = {
1.0f, 0.0f, //右下
0.0f, 0.0f, //左下
1.0f, 1.0f, //右上
0.0, 1.0 //左上
};
GLuint atex = (GLuint) glGetAttribLocation(program, "aTexCoord");
glEnableVertexAttribArray(atex);
glVertexAttribPointer(atex, 2, GL_FLOAT, GL_FALSE, 8, txts);
LOGI("glVertexAttribPointer success");
/**************************渲染程式傳遞資料********************************************/
/**************************紋理設定********************************************/
int width = 176;
int height = 144;
width = 352;
height = 288;
//材質紋理初始化
//設定紋理層
//對於紋理第1層
glUniform1i(glGetUniformLocation(program, "yTexture"), 0);
//對於紋理第2層
glUniform1i(glGetUniformLocation(program, "uTexture"), 1);
//對於紋理第3層
glUniform1i(glGetUniformLocation(program, "vTexture"), 2);
//建立opengl紋理
GLuint texts[3] = {0};
//建立三個紋理物件
//在紋理資源使用完畢後(一般是程式退出或場景轉換時),一定要刪除紋理物件,釋放資源。
//glDeleteTextures(Count:Integer;TexObj:Pointer);
glGenTextures(3, texts);
//使用glBindTexture將建立的紋理繫結到當前紋理。這樣所有的紋理函式都將針對當前紋理。
glBindTexture(GL_TEXTURE_2D, texts[0]);
//設定縮小濾鏡
/**
* 第一個參數列明是針對何種紋理進行設定
* 第二個參數列示要設定放大濾鏡還是縮小濾鏡
*
* 在紋理對映的過程中,如果圖元的大小不等於紋理的大小,OpenGL便會對紋理進行縮放以適應圖元的尺寸。
* 我們可以通過設定紋理濾鏡來決定OpenGL對某個紋理採用的放大、縮小的演算法。
*
* 第三個參數列示使用的濾鏡
*
* 第三個引數可選項如下:
*
* GL_NEAREST 取最鄰近畫素
* GL_LINEAR 線性內部插值
* GL_NEAREST_MIPMAP_NEAREST 最近多貼圖等級的最鄰近畫素
* GL_NEAREST_MIPMAP_LINEAR 在最近多貼圖等級的內部線性插值
* GL_LINEAR_MIPMAP_NEAREST 在最近多貼圖等級的外部線性插值
* GL_LINEAR_MIPMAP_LINEAR 在最近多貼圖等級的外部和內部線性插值
*
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//設定放大濾鏡
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexImage2D函式將Pixels陣列中的畫素值傳給當前繫結的紋理物件,於是便建立了紋理,Pixels是最後一個引數
glTexImage2D(
//紋理的型別
GL_TEXTURE_2D,
//紋理的等級 0預設 級的解析度最大
0,
//gpu內部格式 亮度,灰度圖
GL_LUMINANCE,
//紋理影像的寬度和高度 拉昇到全屏
width, height,
//邊框大小
0,
//畫素資料的格式 亮度,灰度圖 要與上面一致
GL_LUMINANCE,
//畫素值的資料型別
GL_UNSIGNED_BYTE,
//紋理的資料(畫素資料)
NULL
);
//使用glBindTexture將建立的紋理繫結到當前紋理。這樣所有的紋理函式都將針對當前紋理。
glBindTexture(GL_TEXTURE_2D, texts[1]);
//呼叫glTexParameter來設定紋理濾鏡
//設定縮小濾鏡
/**
* 第一個參數列明是針對何種紋理進行設定
* 第二個參數列示要設定放大濾鏡還是縮小濾鏡
* 第三個參數列示使用的濾鏡
*
* 第三個引數可選項如下:
*
* GL_NEAREST 取最鄰近畫素
* GL_LINEAR 線性內部插值
* GL_NEAREST_MIPMAP_NEAREST 最近多貼圖等級的最鄰近畫素
* GL_NEAREST_MIPMAP_LINEAR 在最近多貼圖等級的內部線性插值
* GL_LINEAR_MIPMAP_NEAREST 在最近多貼圖等級的外部線性插值
* GL_LINEAR_MIPMAP_LINEAR 在最近多貼圖等級的外部和內部線性插值
*
* 多貼圖紋理(Mip Mapping)為一個紋理物件生成不同尺寸的影像。在需要時,根據繪製圖形的大小來決定採用的紋理
* 等級或者在不同的紋理等級之間進行線性內插。使用多貼圖紋理的好處在於消除紋理躁動。這種情況在所繪製的景物
* 離觀察者較遠時常常發生(如圖6.6-1和6.6-2)。由於多貼圖紋理現在的渲染速度已經很快,以至於和普通紋理沒有
* 什麼區別,我們現在一般都使用多貼圖紋理。
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//設定放大濾鏡
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexImage2D函式將Pixels陣列中的畫素值傳給當前繫結的紋理物件,於是便建立了紋理,Pixels是最後一個引數
glTexImage2D(
//紋理的型別
GL_TEXTURE_2D,
//紋理的等級 0預設 級的解析度最大
0,
//gpu內部格式 亮度,灰度圖
GL_LUMINANCE,
//紋理影像的寬度和高度 拉昇到全屏
width, height,
//邊框大小
0,
//畫素資料的格式 亮度,灰度圖 要與上面一致
GL_LUMINANCE,
//畫素值的資料型別
GL_UNSIGNED_BYTE,
//紋理的資料(畫素資料)
NULL
);
//使用glBindTexture將建立的紋理繫結到當前紋理。這樣所有的紋理函式都將針對當前紋理。
glBindTexture(GL_TEXTURE_2D, texts[2]);
//縮小的過濾器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexImage2D函式將Pixels陣列中的畫素值傳給當前繫結的紋理物件,於是便建立了紋理,Pixels是最後一個引數
glTexImage2D(
//紋理的型別
GL_TEXTURE_2D,
//紋理的等級 0預設 級的解析度最大
0,
//gpu內部格式 亮度,灰度圖
GL_LUMINANCE,
//紋理影像的寬度和高度 拉昇到全屏
width, height,
//邊框大小
0,
//畫素資料的格式 亮度,灰度圖 要與上面一致
GL_LUMINANCE,
//畫素值的資料型別
GL_UNSIGNED_BYTE,
//紋理的資料(畫素資料)
NULL
);
LOGI("glTexImage2D success");
/**************************紋理設定********************************************/
unsigned char *buf[3] = {0};
buf[0] = new unsigned char[width * height];
buf[1] = new unsigned char[width * height / 4];
buf[2] = new unsigned char[width * height / 4];
for (int i = 0; i < 10000; i++) {
//memset(buf[0],i,width*height);
// memset(buf[1],i,width*height/4);
//memset(buf[2],i,width*height/4);
//420p yyyyyyyy uu vv
if (feof(fp) == 0) {
//yyyyyyyy
fread(buf[0], 1, width * height, fp);
fread(buf[1], 1, width * height / 4, fp);
fread(buf[2], 1, width * height / 4, fp);
}
//啟用第1層紋理,繫結到建立的opengl紋理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texts[0]);
//替換紋理內容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE,
buf[0]);
//啟用第2層紋理,繫結到建立的opengl紋理
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, texts[1]);
//替換紋理內容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
GL_UNSIGNED_BYTE, buf[1]);
//啟用第2層紋理,繫結到建立的opengl紋理
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, texts[2]);
//替換紋理內容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
GL_UNSIGNED_BYTE, buf[2]);
//三維繪製
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//視窗顯示
eglSwapBuffers(eglDisplay, eglSurface);
}
/**************************紋理顯示********************************************/
LOGI("eglSwapBuffers success");
env->ReleaseStringUTFChars(url_, url);
}
相關文章
- Electron實現跨平臺全能視訊播放器播放器
- ffmpeg播放器實現詳解 - 視訊顯示播放器
- 29.FFmpeg+OpenGLES+OpenSLES播放器實現(三.FFmpeg配置和編譯指令碼)播放器編譯指令碼
- Dplayer Html5 彈幕視訊播放器的實現HTML播放器
- 自定義視訊播放器播放器
- 視訊播放器工具Infuse播放器
- 阿里雲視訊播放器阿里播放器
- 使用海康H5視訊播放器開發包實現監控播放H5播放器
- MPV 播放器:Linux 下的極簡視訊播放器播放器Linux
- FFmpeg+SDL2實現簡易音視訊同步播放器播放器
- Vue + WebRTC 實現音視訊直播(附自定義播放器樣式)VueWeb播放器
- 使用VideoView做個實用的視訊播放器IDEView播放器
- [- 多媒體 -] OpenGLES3.0 接入視訊實現特效 - 引言S3特效
- IOS 整合 Bilibili IJKPlayer播放器,播放rtmp視訊流iOS播放器
- 開源視訊播放器介紹播放器
- H5視訊播放器特性H5播放器
- 視訊播放器全屏旋轉方案播放器
- 視訊播放器Infuse PRO mac版播放器Mac
- Mac視訊播放器哪個好用?Mac播放器
- Movist Pro for Mac(Mac視訊播放器)Mac播放器
- MediaCodec、OpenGL、OpenSL/AudioTrack 實現一款簡單的視訊播放器播放器
- 11.QT-ffmpeg+QAudioOutput實現音訊播放器QT音訊播放器
- Win10播放器無法播放視訊如何解決_win10自帶播放器不能播放視訊怎麼修復Win10播放器
- 實現播放視訊及彈幕
- ReactNative乾貨分享——視訊播放器AppReact播放器APP
- vs2017使用vlc視訊播放器播放器
- Cisdem Video Player for mac(高清視訊播放器)IDEMac播放器
- Mac強大的視訊播放器:InfuseMac播放器
- vue2.x 音訊播放器 使用element ui + Audio實現一款完整的音訊播放器Vue音訊播放器UI
- win10自帶視訊播放器在哪_怎麼開啟win10自帶的視訊播放器Win10播放器
- rmvb用什麼視訊播放器win10_rmvb怎麼用視訊播放器開啟win10播放器Win10
- iOS--React Native視訊播放器外掛iOSReact Native播放器
- 知乎視訊播放器 Griffith 開源了~播放器
- ArtVideoPlayer:一個靈活的視訊播放器IDE播放器
- Infuse PRO for Mac(強大的視訊播放器)Mac播放器
- 專業藍光視訊播放器軟體播放器
- Infuse PRO for Mac(強大的視訊播放器)Mac播放器
- video自定義實現視訊播放功能IDE