日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

解密视频魔法:将ExternalOES纹理转化为TEXTURE_2D纹理

發布時間:2023/12/24 windows 31 coder
生活随笔 收集整理的這篇文章主要介紹了 解密视频魔法:将ExternalOES纹理转化为TEXTURE_2D纹理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在使用OpenGL ES進行圖形圖像開發時,我們常使用GL_TEXTURE_2D紋理類型,它提供了對標準2D圖像的處理能力。這種紋理類型適用于大多數場景,可以用于展示靜態貼圖、渲染2D圖形和進行圖像處理等操作。
另外,有時我們需要從Camera或外部視頻源讀取數據幀并進行處理。這時,我們會使用GL_TEXTURE_EXTERNAL_OES紋理類型。其專門用于對外部圖像或實時視頻流進行處理,可以直接從 BufferQueue?中接收的數據渲染紋理多邊形,從而提供更高效的視頻處理和渲染性能。

在實際應用中,我們通常將GL_TEXTURE_2DGL_TEXTURE_EXTERNAL_OES這兩種紋理類型分開使用,并且它們互不干擾。實際上,這種情況占據了80%的使用場景。我們可以根據具體需求選擇合適的紋理類型進行處理和渲染。
然而,有時候我們也會遇到一些特殊情況,需要將GL_TEXTURE_EXTERNAL_OES紋理轉化為GL_TEXTURE_2D紋理進行視頻處理或計算。這種情況可能出現在需要對視頻數據進行特殊的圖像處理或者與GL_TEXTURE_2D紋理類型的其他渲染操作進行交互時。

當以上情況出現時,我們該如何處理呢?難道是直接將GL_TEXTURE_EXTERNAL_OES紋理賦值給GL_TEXTURE_2D紋理使用(經過實驗這種方式是不可用的)?
這里對此情況,先給出解決方案,一般我們可以通過一些技術手段,如離屏渲染或FrameBuffer幀緩沖區對象,將GL_TEXTURE_EXTERNAL_OES紋理轉換為GL_TEXTURE_2D紋理,并進行后續的處理和計算。
而此篇文章主要記錄,我是如何通過FrameBuffer幀緩沖區對象,將GL_TEXTURE_EXTERNAL_OES紋理數據轉化為GL_TEXTURE_2D紋理數據的!

  • 首先 回顧一下GL_TEXTURE_2D紋理與GL_TEXTURE_EXTERNAL_OES紋理;
  • GL_TEXTURE_EXTERNAL_OES紋理數據通過FrameBuffer轉化為GL_TEXTURE_2D紋理數據

一、TEXTURE_2DEXTERNAL_OES

在正式研究 “GL_TEXTURE_EXTERNAL_OES紋理數據轉化為GL_TEXTURE_2D紋理數據” 之前,先要搞清楚:

  • 什么是GL_TEXTURE_2D紋理?
  • 什么又是GL_TEXTURE_EXTERNAL_OES紋理?
  • GL_TEXTURE_2D紋理與GL_TEXTURE_EXTERNAL_OES紋理有什么樣的區別?

1.1 GL_TEXTURE_2D紋理

GL_TEXTURE_2D 提供了對標準2D圖像的處理能力,可以存儲靜態的貼圖圖像或者幀緩沖區的渲染結果
其使用二維的紋理坐標系,通過將紋理坐標映射到紋理圖像上的對應位置,可以實現紋理貼圖、紋理過濾、紋理環繞等操作

GL_TEXTURE_2D紋理的特點包括:

  • 使用二維紋理坐標系進行操作;
  • 使用glTexImage2D函數加載紋理數據;
  • 通過紋理過濾和紋理環繞等方式進行紋理的采樣和處理;

GL_TEXTURE_2D紋理:創建、綁定、采樣、加載紋理圖像

public static int createDrawableTexture2D(Context context, int drawableId) {
    // 生成紋理ID
    int[] textures = new int[1];
    GLES30.glGenTextures(1, textures, 0);
    // 綁定紋理
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0]);
    // 紋理采樣方式
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
    // texImage2D加載圖像數據
    InputStream is = context.getResources().openRawResource(drawableId);
    Bitmap bitmapTmp;
    try {
        bitmapTmp = BitmapFactory.decodeStream(is);
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    GLUtils.texImage2D(GLES30.GL_TEXTURE_2D,0, GLUtils.getInternalFormat(bitmapTmp), bitmapTmp, GLUtils.getType(bitmapTmp), 0 );
    bitmapTmp.recycle();
    return textures[0];
}

GL_TEXTURE_2D紋理:Shader處理階段(片元著色器)

precision mediump float;  
varying vec2 v_texture_coord;  
uniform sampler2D MAIN;  
void main() {  
   vec4 color=texture2D(MAIN, v_texture_coord);  
   gl_FragColor=color;  
}

GL_TEXTURE_2D紋理:紋理渲染

GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);

1.2 GL_TEXTURE_EXTERNAL_OES紋理類型

根據AOSP: SurfaceTexture 文檔描述,GL_TEXTURE_EXTERNAL_OES 是一種特殊的紋理類型,主要用于處理外部圖像或視頻數據,如從攝像頭捕捉的實時圖像和外部視頻流。
GL_TEXTURE_EXTERNAL_OES 相對于 GL_TEXTURE_2D 最大的特點就是 GL_TEXTURE_EXTERNAL_OES可直接從 BufferQueue?中接收的數據渲染紋理多邊形

GL_TEXTURE_EXTERNAL_OES紋理類型的特點包括:

  • 需采用特殊的采樣器類型紋理著色器擴展
  • 使用二維紋理坐標系進行操作,與GL_TEXTURE_2D相似。
  • 專門用于處理外部圖像或視頻數據,可直接從 BufferQueue?中接收的數據渲染紋理多邊形,從而提供更高效的視頻處理和渲染性能。

對于此,官方文檔中提供了一個 Grafika 的連續拍攝案例工程,并給出了如下參考流程圖。

通過閱讀 Grafika 的連續拍攝案例,我們得知:

  • 首先,需創建一個OES紋理ID,用于接收Camera圖像數據
// GL_TEXTURE_EXTERNAL_OES: 紋理創建、綁定、采樣
public static int createTextureOES() {
    // 創建OES紋理ID
    int[] textures = new int[1];
    GLES30.glGenTextures(1, textures, 0);
    TextureUtil.checkGlError("glGenTextures");
    // 綁定OES紋理ID
    int texId = textures[0];
    GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
    TextureUtil.checkGlError("glBindTexture " + texId);
    // OES紋理采樣
    GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
    GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
    GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
    GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
    TextureUtil.checkGlError("glTexParameter");
    return texId;
}
  • 完成OES紋理ID創建后,通過oesTextureId創建一個圖像消費者SurfaceTexture,將SurfaceTexture設定為預覽的PreviewTexture;
// 傳入一個OES紋理ID
SurfaceTexture mSurfaceTexture = new SurfaceTexture(oesTextureId);  
// 將 SurfaceTexture 設置為預覽的 PreviewTexture
Camera.setPreviewTexture(mSurfaceTexture);
  • 或者通過SurfaceTexture創建Surface,將Surface對象傳遞給MediaPlayerMediaCodec進行視頻幀數據獲取;
// 傳入一個OES紋理ID
SurfaceTexture mSurfaceTexture = new SurfaceTexture(oesTextureId);  
// 創建 Surface 
Surface mSurface = new Surface(mSurfaceTexture);
// 將 Surface 設置給 MediaPlayer 外部視頻播放器,獲取視頻幀數據
MediaPlayer.setSurface(surface);
  • 前文已經提到GL_TEXTURE_EXTERNAL_OES紋理類型 可直接從Surface對應的BufferQueue中獲取視頻流數據;
  • 在獲取到視頻幀數據后:
    一方面,可通過OpenGL的渲染管線,將GL_TEXTURE_EXTERNAL_OES紋理渲染到GLSurfaceView上,完成圖像數據的預覽
    另一方面,可將GL_TEXTURE_EXTERNAL_OES紋理,通過離屏渲染的形式,寫入到 MediaCodeC,硬編碼生成MP4視頻。
// GL_TEXTURE_EXTERNAL_OES紋理:Shader處理階段(片元著色器)
#extension GL_OES_EGL_image_external : require  
precision mediump float;  
varying vec2 v_texture_coord;  
uniform samplerExternalOES MAIN;  
void main() {  
   vec4 color=texture2D(MAIN, v_texture_coord);  
   gl_FragColor=color;  
}

GL_TEXTURE_EXTERNAL_OES紋理:紋理渲染

// 紋理渲染階段:GL_TEXTURE_EXTERNAL_OES紋理
GLES30.glBindTexture(GLES30.GL_TEXTURE_EXTERNAL_OES, texId);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);

1.3 關于兩者的區別 我的個人理解

關于兩者的區別,我的個人理解:
GL_TEXTURE_2D紋理類型與GL_TEXTURE_EXTERNAL_OES紋理類型,在數據來源紋理數據的存儲格式上存在差異。

  • 數據來源方面
    一個來源于glTexImage2D加載的二維圖像數據
    一個來源與圖像消費者Surface對應的BufferQueue
  • 紋理存儲格式
    GL_TEXTURE_EXTERNAL_OES數據來源于外部視頻源或Camera,其數據格式可能為YUV或RGB;
    GL_TEXTURE_2D的數據格式則依賴于開發中setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)的配置,可能是RGBA8888,也可能是RGBA4444等等。

由于兩者數據來源和紋理存儲格式的差異,兩種紋理類型是不能直接進行轉化的。

  • 首先,其在紋理采樣階段Shader處理階段紋理渲染階段均不同程度的存在差異(這一點在上一節的兩者對比的代碼舉例中可以證明)。
  • 其次,如果需要在處理和計算階段將GL_TEXTURE_EXTERNAL_OES紋理轉換GL_TEXTURE_2D紋理,通常需要使用離屏渲染或幀緩沖區對象等技術手段。

二、EXTERNAL_OES轉化為TEXTURE_2D紋理數據

這里直接介紹轉化過程

  • 首先,需創建一個OES紋理ID(相關代碼舉例在前文已經給出);
  • 完成OES紋理ID創建后,通過oesTextureId創建一個圖像消費者SurfaceTexture(相關代碼舉例在前文已經給出);
  • 通過SurfaceTexture創建Surface,將Surface對象傳遞給MediaPlayer,獲取Sdcard中對應路徑的視頻幀數據獲取(相關代碼舉例在前文已經給出);
  • 創建FRAMEBUFFER幀緩沖區,并綁定GL_TEXTURE_2D空白紋理對象;
public static int createEmptyTexture2DBindFrameBuffer(int[] frameBuffer, int texPixWidth, int texPixHeight) {
    // 創建紋理ID
    int[] textures = new int[1];
    GLES30.glGenTextures(1, textures, 0);
    // 綁定GL_TEXTURE_2D紋理
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0]);
    // 紋理采樣
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
    GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
    // 創建一個空的2D紋理對象,指定其基本參數,并綁定到對應的紋理ID上
    GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, texPixWidth, texPixHeight,0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
    // 取消綁定紋理
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);

    /**
     * 幀緩沖區
     */
    // 創建幀緩沖區
    GLES30.glGenFramebuffers(1, frameBuffer, 0);
    // 將幀緩沖對象綁定到OpenGL ES上下文的幀緩沖目標上
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBuffer[0]);
    // 使用GLES30.GL_COLOR_ATTACHMENT0將紋理作為顏色附著點附加到幀緩沖對象上
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textures[0], 0);
    // 取消綁定緩沖區
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);

    return textures[0];
}
  • GL_TEXTURE_EXTERNAL_OES紋理渲染FRAMEBUFFER幀緩沖區中;
// 激活紋理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
// 將所需的紋理對象綁定到Shader中紋理單元0上
GLES30.glUniform1i(mOesTextureIdHandle, 0);
// 綁定紋理
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
// 綁定FRAMEBUFFER緩沖區
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, tex2DFrameBufferId);
// 繪制矩形
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);
// 取消FRAMEBUFFER的綁定
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);
  • 最后,繪制渲染GL_TEXTURE_2D紋理,完成紋理圖像的顯示。
// 激活紋理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
// 將所需的紋理對象綁定到Shader中紋理單元0上
GLES30.glUniform1i(mTex2DIdHandle, 0);
// 綁定紋理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, tex2DId);
// 繪制矩形
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);

三、源碼下載

ExternalOES紋理數據 轉換為 TEXTURE-2D紋理數據:
https://download.csdn.net/download/aiwusheng/88650498

參考

AOSP:SurfaceTexture
https://source.android.google.cn/docs/core/graphics/arch-st?hl=zh-c

Github:Google Grafika
https://github.com/google/grafika/blob/master/app/src/main/java/com/android/grafika/ContinuousCaptureActivity.java

OpenGL渲染管線:
https://xiaxl.blog.csdn.net/article/details/121467207

紋理ID 離屏渲染 寫入到Surface中:
https://xiaxl.blog.csdn.net/article/details/131682521

MediaCodeC與OpenGL硬編碼錄制mp4:
https://xiaxl.blog.csdn.net/article/details/72530314

總結

以上是生活随笔為你收集整理的解密视频魔法:将ExternalOES纹理转化为TEXTURE_2D纹理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。