opengl es 繪制視頻 使用原生代碼

前言

像正真做這種關(guān)于opengl 的開發(fā),一般建議都是直接使用原生代碼,而不是使用android已經(jīng)封裝好的opengl代碼捡絮, 對于像opengl這么偏底層的圖形接口是很有必要去深入到native層去一趟究竟的暖混。
寫作本文的目的也只是與大家一起去學(xué)習(xí)與交流,還是那句話(talk is cheap参淫,show me the code)救湖,如果不想聽我在這里瞎bb,盡可將我已經(jīng)寫好的DEMO copy下來涎才,然后運行起來鞋既,像看看效果, 這比說一大堆廢話管用耍铜。此代碼主要參考了oculus mobile sdk, 我只是將其中一部分代碼裁剪出來邑闺,再自己加以修改,來作為案例學(xué)習(xí)之用棕兼,僅供大家參考陡舅。

環(huán)境

  • 構(gòu)建環(huán)境

簡單說說我的開發(fā)環(huán)境,首先NDK開發(fā)工具包必不可少伴挚,目前還是采取通用的Android.mk 與 Application.mk 這樣的Mikefile 文件進行構(gòu)建靶衍,這種方式網(wǎng)上有大量教程,建議去google developer 去查閱用法, ** 有一點需要注意下**茎芋,在Application.mk 中的NDK_TOOLCHAIN_VERSION 的版本是隨你的ndk版本而定的颅眶,比如我ndk版本是r10e,這里用的toolchan版本是4.8田弥,如果你用的其它ndk版本涛酗,那么你需要改一下toolchan的版本號。 google的新的ndk demo中,已經(jīng)采取直接將ndk的配置寫在gradle腳本里面來代替現(xiàn)在這種傳統(tǒng)的配置了煤杀。

  • 繪制環(huán)境
    我這里是通過調(diào)用opengl es api函數(shù)向java層提供的GLSurfaceView輸出并渲染圖像眷蜈。也可以使用NDK提供的原生window api 去進行渲染圖像。
  1. 1 執(zhí)行下面步驟以在原生代碼使用opengl es 2.0/3.0 的API:
  • 修改Android.mk構(gòu)建文件沈自。添加 LOCAL_LDLIBS += -lGLESv2 -lGLESv3
  • 代碼中包含opengl es頭文件酌儒。 #include<GLES3/gl3.h> #include<GLES2/gl2ext.h> , 我是用的gles 3.0的庫,因為2.0 是沒有我需要用到的 glGenVertexArrays API的枯途, 不過繪制視頻需要binding使用gl2ext庫提供的GL_TEXTURE_EXTERNAL_OES 作為圖像映射的紋理目標(biāo)忌怎。有的人可能會問,為什么不是一般常用的GL_TEXTURE_2D 呢酪夷? 其實原因在于SurfaceTexture榴啸,我查了下SurfaceTexture源碼, 發(fā)現(xiàn)它內(nèi)部使用的是eglCreateImageKHR去創(chuàng)建的image晚岭, 而這個api必須使用TEXTURE_EXTERNAL_OES鸥印,并且用了TEXTURE_EXTERNAL_OES 就不能使用TexImage2D等系列接口去生成紋理image,所以說坦报,SurfaceTexture類的功能是起到替代opengl 的TexImageXX接口的作用库说, 因此視頻圖像的捕獲與圖像更新還是用的java層封裝好的SurfaceTexture , 那么這就涉及到了naive代碼與java代碼的交互片择。
  1. 2 Native代碼與Java的交互
    一個簡單的步驟:
    2.1 通過隨意一個JNIEnv指針去獲取一個JavaVM在jni層的一個指針潜的,注意,一個應(yīng)用代表一個javaVM字管。但JNIEnv指針在不同的線程中有不同JNIEnv
    JavaVM *gJavaVM; //// 通常是設(shè)置的一個全局的對象 env->GetJavaVM(&gJavaVM);
    2.2 獲取JavaVM在opengl的代表JNIEnv指針啰挪。由于我們需要在opengl 繪制線程 的上下文 中與javaVM交互,因此JNIEnv指針的獲取必須在opengl上下文中嘲叔。
    具體代碼:
JNIEnv *AttachJava()
{      
        JavaVMAttachArgs args = {
        JNI_VERSION_1_4,   // 表示jni版本號亡呵,還有1_1,1_2, 1_6這幾種
        0,    /* NULL or name of thread as modified UTF-8 str */
        0    /* global ref of a ThreadGroup object, or NULL */
}; 
    JNIEnv* jni;
    int status = gJavaVM->AttachCurrentThread( &jni, &args);
    if (status < 0) {
        LOG_ERROR("<SurfaceTexture> faild to attach current thread!");
        return NULL;
    }
    return jni;
}

2.3 在native代碼中,通過jni創(chuàng)建一個運行在javaVM環(huán)境中的SurfaceTexture的實例 jobject

const char *stClassPath = "android/graphics/SurfaceTexture";
    const jclass surfaceTextureClass = jni->FindClass(stClassPath);
    if (surfaceTextureClass == 0) {
        LOG_ERROR("FindClass (%s) failed", stClassPath);
    }

//    // find the constructor that takes an int
    const jmethodID constructor = jni->GetMethodID( surfaceTextureClass, "<init>", "(I)V" );
    if (constructor == 0) {
        LOG_ERROR("GetMethonID(<init>) failed");
    }

    jobject  obj = jni->NewObject(surfaceTextureClass, constructor, texId);
    if (obj == 0) {
        LOG_ERROR("NewObject() failed");
    }

    javaSurfaceTextureObj = jni->NewGlobalRef(obj);
    if (javaSurfaceTextureObj == 0) {
        LOG_ERROR("NewGlobalRef() failed");
    }

    //Now that we have a globalRef, we can free the localRef
    jni->DeleteLocalRef(obj);

這個過程等價于在java代碼中new 一個SurfaceTexture':surfaceTexture = new SurfaceTexture(textId)硫戈。其實锰什,現(xiàn)在我們只是將SurfaceTexture的創(chuàng)建工作從Java層搬到了Native層,其余的操作莫過于下面這個幾個動作了:
在native中實現(xiàn) 像在java代碼做的下面這個幾個動作掏愁,實現(xiàn)視頻幀的捕獲與更新:

  surfaceTexture .setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
            @Override
            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                frameAvailable = ture;
            }
        });

// 在繪制線程循環(huán)中執(zhí)行下面操作
   if (frameAvailable) {
                surfaceTexture .updateTexImage();
                surfaceTexture .getTransformMatrix(textureTransform);
                frameAvailable = false;
            }

在native中

  1. 獲取updateTexImage方法的MethodID,updateTexImageMethodId = jni->GetMethodID( surfaceTextureClass, "updateTexImage", "()V")
  2. 然后 在需要調(diào)用這個方法時歇由,通過這個MethodID卵牍,找到這個方法果港,使用jni->CallVoidMethod(javaSurfaceTextureObj, updateTexImageMethodId), 從而實現(xiàn)了native調(diào)用java的非靜態(tài)方法。
    ok, opengl進行紋理映射綁定的的texture已經(jīng)有了糊昙,那么下面就是圖像的渲染工作了辛掠。

圖形渲染

看過我在DEMO 中的關(guān)于java代碼中使用opengl的步驟可以看出繪制的套路來:

  1. 創(chuàng)建一個與shader代碼鏈接的shader program 對象。

program = createProgram(vextexShader, fragmentShader);

  1. 獲取vertex shader代碼中聲明的屬性變量的 handle。

因為頂點帶有傳輸?shù)綀D形管線上的屬性萝衩。要繪制一個對象回挽,我們需要指定它的頂點,以及頂點如何定義平面猩谊。這些屬性包括頂點千劈,法線,紋理坐標(biāo)以及其它屬性牌捷。
positionHandle = glGetAttribLocation(shaderProgram, "aPosition");
textureParamHandle = glGetUniformLocation(shaderProgram, "texture");

  1. 初始化頂點數(shù)據(jù)墙牌。

(包括頂點坐標(biāo),紋理坐標(biāo)暗甥,以及數(shù)組的一個索引(因為這里使用了glDrawElements)喜滨。

// 聲明一個結(jié)構(gòu)體,保存頂點數(shù)據(jù)
 struct Vertices
    {
        float positions[4][4];
        float texCoords[4][4];
    };
// 指定頂點連接順序
 unsigned short indices[] = {
      0, 1, 2,  0, 2 ,3
    };
  1. 通過這些頂點數(shù)據(jù)來填充vertex shader中的屬性撤防。

這里用到了VBO去緩存頂點數(shù)據(jù)(也就是將主存儲客戶端(CPU端)中保存的頂點數(shù)據(jù)虽风,緩存到GPU中, 提高了GPU渲染效率)寄月, 每一組緩沖區(qū)就是 OpenGL 的頂點數(shù)組對象辜膝,叫做VAO。

glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
glGenBuffers( 1, &vb );
glBindBuffer( GL_ARRAY_BUFFER, vb );
glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), &vertices, GL_STATIC_DRAW );
glGenBuffers( 1, &ib ) ;
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ib );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( indices ), indices, GL_STATIC_DRAW );
glEnableVertexAttribArray ( positionHandle );
glVertexAttribPointer ( positionHandle, 3, GL_FLOAT, false, 0, (const GLvoid *)offsetof( Vertices, positions ));
glEnableVertexAttribArray ( textureCoordHandle );
glVertexAttribPointer ( textureCoordHandle, 4, GL_FLOAT, false, 0, (const GLvoid *)offsetof( Vertices, texCoords ));
  1. OpenGL 渲染緩沖區(qū)中的數(shù)據(jù)剥懒。

我們可以使用下面的代碼來讓 VAO 包含索引緩沖區(qū):

glUseProgram(program);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, index.size(), GL_UNSIGNED_INT, NULL); 

ok内舟, opengl 繪制圖形的大致套路就是這樣。詳情請參考源碼初橘!

總結(jié)

由于才疏學(xué)淺验游,本文只起到一個拋磚引玉的作用, 簡單的描述了一下native代碼中使用opengl的列子保檐,只在場景中畫出了一個簡單的長方行貼圖來顯示圖像耕蝉。如果 深入一點,做一個球形貼圖夜只,然后再通過一連串的矩陣運算(MVP)垒在,就可以做個全景視頻了。說實話扔亥,在工作中是很難有機會寫到這么原始opengl代碼场躯,我都已經(jīng)很久沒有寫過opengl相關(guān)代碼了,現(xiàn)在基本就是在別人已經(jīng)封裝好的代碼基礎(chǔ)上寫寫業(yè)務(wù)邏輯旅挤,要不是因為一年前寫的幾篇文字踢关,引起這么多人的關(guān)注,可能我都很難再把以前擼過的代碼再重溫一遍來跟大家一起分享粘茄,感謝大家的關(guān)注签舞!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秕脓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子儒搭,更是在濱河造成了極大的恐慌吠架,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搂鲫,死亡現(xiàn)場離奇詭異傍药,居然都是意外死亡,警方通過查閱死者的電腦和手機魂仍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門怔檩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蓄诽,你說我怎么就攤上這事薛训。” “怎么了仑氛?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵乙埃,是天一觀的道長。 經(jīng)常有香客問我锯岖,道長介袜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任出吹,我火速辦了婚禮遇伞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捶牢。我一直安慰自己鸠珠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布秋麸。 她就那樣靜靜地躺著渐排,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灸蟆。 梳的紋絲不亂的頭發(fā)上驯耻,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音炒考,去河邊找鬼可缚。 笑死,一個胖子當(dāng)著我的面吹牛斋枢,可吹牛的內(nèi)容都是我干的帘靡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼杏慰,長吁一口氣:“原來是場噩夢啊……” “哼测柠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缘滥,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤轰胁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后朝扼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赃阀,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年擎颖,在試婚紗的時候發(fā)現(xiàn)自己被綠了榛斯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡搂捧,死狀恐怖驮俗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情允跑,我是刑警寧澤王凑,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站聋丝,受9級特大地震影響索烹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弱睦,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一百姓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧况木,春花似錦垒拢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矗晃,卻和暖如春仑嗅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背张症。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工仓技, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俗他。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓脖捻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兆衅。 傳聞我的和親對象是個殘疾皇子地沮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容