前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TRTCSDK自定义采集Texture2D视频通话

TRTCSDK自定义采集Texture2D视频通话

作者头像
腾讯云-chaoli
修改2021-08-06 10:19:54
1.3K1
修改2021-08-06 10:19:54
举报

一、适用场景

腾讯TRTCSDK,提供了摄像头通话、录屏通话、基础美颜、高级美颜功能。

摄像头通话功能,是TRTCSDK对系统摄像头进行了封装,采集摄像头数据,编码传输通话。

如果您自研(或者购买第三方)美颜和特效处理模块,则需要自己采集和处理摄像头拍摄画面,对采集到的YUV数据、纹理数据进行操作处理,将处理后的数据,交给TRTCSDK编码传输通话。TRTCSDK是有提供自定义采集功能接口的。

二、API介绍:

enableCustomVideoCapture

sendCustomVideoData

如官网api文档介绍:

enableCustomVideoCapture( boolean enable )启用视频自定义采集模式

开启该模式后,SDK 不在运行原有的视频采集流程,只保留编码和发送能力。 您需要用 sendCustomVideoData() 不断地向 SDK 塞入自己采集的视频画面。

sendCustomVideoData( TRTCCloudDef.TRTCVideoFrame frame )向 SDK 投送自己采集的视频数据

Android 平台有两种的方案:

  • buffer 方案:对接起来比较简单,但是性能较差,不适合分辨率较高的场景。
  • texture 方案:对接需要一定的 OpenGL 基础,但是性能较好,640 × 360 以上的分辨率请采用该方案。

TRTCVideoFrame参数释义如下

写法示例:

代码语言:javascript
复制
//YUV buffer方案
        TRTCCloudDef.TRTCVideoFrame frame = new TRTCCloudDef.TRTCVideoFrame();
        frame.bufferType = TRTC_VIDEO_BUFFER_TYPE_BYTE_ARRAY;
        frame.pixelFormat = TRTC_VIDEO_PIXEL_FORMAT_I420;
//        frame.pixelFormat = TRTC_VIDEO_PIXEL_FORMAT_NV21;
        frame.data = byte[](i420Data);
        frame.width = 320;
        frame.height = 240;
        
        TRTCCloud.sendCustomVideoData(frame);
        
//纹理 texture方案
        TRTCCloudDef.TRTCVideoFrame frame = new TRTCCloudDef.TRTCVideoFrame();
        frame.bufferType = TRTC_VIDEO_BUFFER_TYPE_TEXTURE;
        frame.pixelFormat = TRTCCloudDef.TRTC_VIDEO_PIXEL_FORMAT_Texture_2D;
        frame.texture = new TRTCCloudDef.TRTCTexture();
        frame.texture.textureId = textureId;
        frame.texture.eglContext14 = eglContext;
        frame.width = 960;
        frame.height = 720;
        frame.timestamp = 0;
        
        TRTCCloud.sendCustomVideoData(frame);

三、Texture2D方案:

本篇主要介绍Texture方案:使用安卓系统封装的camera2+GLSurefaceView,采集到OES纹理,使用FBO复制成Texture2D纹理离屏渲染,将纹理ID交给TRTCSDK编码传输。

0、通话效果

写成的demo效果如下,源码地址点击下载

两个图中,都用采集到了超高清画质1280X960,使用OpenGL方式渲染,直接操作手机GPU,渲染流畅,通话过程中无卡帧现象。这是Texture方案比buffer方案最大的优势:性能好。

在开始讲demo代码实现过程之前,我们先回顾一下几个知识点:OpenGL、安卓端OpenGL ES、FBO离屏渲染。

这三个知识点,是demo中需要用的音视频基础,下面讲串起来讲一下。

1、OpenGL

OpenGL这个状态机,大家平时应该都有接触过,学起来也并不简单,想要用三两句简介,把OpenGL说出个所以然来,实在是考验笔者的总结能力。在写本篇之前,有新开一篇专门讲OpenGL,有需要的同学可以跳过去阅览一番先。《OpenGL入门》

为了方便理解下面demo中代码流程,这里总结一下OpenGL渲染流程(注意并不一定全是这种顺序)

  • 申明OpenGl状态机上下文
  • 设置视图展示窗口(viewport)
  • 创建图形类,确定好顶点位置和图形颜色,将顶点和颜色数据转换为OpenGl使用的数据格式
  • 加载顶点着色器和片段着色器用来修改图形的颜色,纹理,坐标等属性
  • 创建程式(Program),连接顶点着色器片段着色器。
  • 将坐标数据传入到OpenGl 程式中:

2、安卓端OpenGL ES

OpenGl一般用于在图形工作站,在PC端使用,由于性能各方面原因,在移动端使用OpenGl基本带不动。为此,Khronos公司就为OpenGl提供了一个子集,OpenGl ES(OpenGl for Embedded System)

OpenGl ES是免费的跨平台的功能完善的2D/3D图形库接口的API,是OpenGL的一个子集。

移动端使用到的基本上都是OpenGl ES,当然Android开发下还专门为OpenGl提供了android.opengl包,并且提供了GlSurfaceView,GLU,GlUtils等工具类。

我们需要了解两个基本类别的Android框架:GlSurfaceView和GlSurfaceView.Renderer

GlSurfaceView是什么? GLSurfaceView的作用是什么? GLSurfaceView如何使用?

GlSurfaceView从名字就可以看出,它是一个SurfaceView,看源码可知,GlSurfaceView继承自SurfaceView。并增加了Renderer.它的作用就是专门为OpenGl显示渲染使用的。

GLSurfaceView的使用方法: 可以通过创建的实例使用这个类,并增加你的Renderer.

代码语言:javascript
复制
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView glSurfaceView = new GLSurfaceView(this);
        glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {
            @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config) {

            }

            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height) {

            }

            @Override
            public void onDrawFrame(GL10 gl) {

            }
        });
        setContentView(glSurfaceView);
    }

GlSurfaceView.Renderer是什么?GLSurfaceView.Renderer的作用?GLSurfaceView.Renderer的用法?

该接口定义了用于绘制在图形所需的方法GLSurfaceView。你必须提供这个接口作为一个单独的类的实现,并将其连接到您的GLSurfaceView使用实例 GLSurfaceView.setRenderer()。如上面的代码所示。作用就是提供各种渲染方法,OpenGl的渲染操作均在此接口中实习。下面说下实现该接口的方法含义:

  • onSurfaceCreated():系统调用这个方法一次创建时GLSurfaceView。使用此方法来执行只需要发生一次的操作,比如设置OpenGL的环境参数或初始化的OpenGL图形对象。
  • onDrawFrame():系统调用上的每个重绘此方法GLSurfaceView。使用此方法作为主要执行点用于绘制(和重新绘制)的图形对象。
  • 系统调用此方法时的GLSurfaceView几何形状的变化,包括尺寸变化GLSurfaceView或设备屏幕的取向。例如,当设备从纵向变为横向的系统调用这个方法。使用此方法可以在变化做出反应GLSurfaceView容器。

介绍完了GlSurfaceView和GlSurfaceView.renderer之后,接下来说下如何使用GlSurfaceView; 1. 创建一个GlSurfaceView 2. 为这个GlSurfaceView设置渲染 3. 在GlSurfaceView.renderer中绘制处理显示数据

代码语言:javascript
复制
public class GlRenderView extends GLSurfaceView {

    public GlRenderView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public void setRender(){
        //设置EGL 版本
        setEGLContextClientVersion(2);
        glRender = new GlRenderWrapper(this, size);

        setRenderer(glRender);
        //手动渲染模式
        setRenderMode(RENDERMODE_WHEN_DIRTY);
    }
}

我们配置的是手动渲染模式,这里如果想执行onDrawFrame方法,需要每来一帧图像,调用 requestRender()。

代码语言:javascript
复制
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    camera2Helper = new Camera2Helper((Activity) glRenderView.getContext(),size);

    mTextures = new int[1];
    //创建一个纹理
    GLES20.glGenTextures(mTextures.length, mTextures, 0);
    //将纹理和离屏buffer绑定
    mSurfaceTexture = new SurfaceTexture(mTextures[0]);
    //监听有新图像到来
    mSurfaceTexture.setOnFrameAvailableListener(this);

    //使用fbo 将samplerExternalOES 输入到sampler2D中
    cameraFilter = new CameraFilter(glRenderView.getContext());
    //负责将图像绘制到屏幕上
    screenFilter = new ScreenFilter(glRenderView.getContext());
    //获取OpenGL上下文对象
    eglContext = EGL14.eglGetCurrentContext();

}

onSurfaceCreated中,实例Camera2Helper对象,创建一个SurfaceTexture和纹理,并进行绑定。这个SurfaceTexture会传给Camera2中,camera2负责输入图像到SurfaceTexture中,这里的SurfaceTexture是一个离屏buffer。并且实例CameraFilter和ScreenFilter。

当有新图像来了,会执行onFrameAvailable,更新图像。

代码语言:javascript
复制
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
    glRenderView.requestRender();
}

问什么要将 samplerExternalOES 输入到sampler2D中

ScreenFilter:负责将图像绘制到屏幕上(加完滤镜美颜等效果,也是用这个类进行展示的)

CameraFilter的顶点着色器。

代码语言:javascript
复制
// 把顶点坐标给这个变量, 确定要画画的形状
attribute vec4 vPosition;
//接收纹理坐标,接收采样器采样图片的坐标
attribute vec4 vCoord;
//变换矩阵, 需要将原本的vCoord(01,11,00,10) 与矩阵相乘 才能够得到 surfacetexure(特殊)的正确的采样坐标
uniform mat4 vMatrix;
//传给片段着色器 像素点
varying vec2 aCoord;

void main(){
    //内置变量 gl_Position ,我们把顶点数据赋值给这个变量 opengl就知道它要画什么形状了
    gl_Position = vPosition;
    // 进过测试 和设备有关(有些设备直接就采集不到图像,有些呢则会镜像)
    aCoord = (vMatrix*vCoord).xy;
    //aCoord =  vec2((vCoord*vMatrix).x,(vCoord*vMatrix).y);
}

片段着色器:

代码语言:javascript
复制
#extension GL_OES_EGL_image_external : require
//SurfaceTexture比较特殊
//float数据是什么精度的
precision mediump float;
 
//采样点的坐标
varying vec2 aCoord;
 
//采样器
uniform samplerExternalOES vTexture;
 
void main(){
    //变量 接收像素值
    // texture2D:采样器 采集 aCoord的像素
    //赋值给 gl_FragColor 就可以了
    gl_FragColor = texture2D(vTexture,aCoord);
}

再实例化CameraFilter,会创建着色器程序。拿到着色器中的变量。

代码语言:javascript
复制
public BaseFilter(Context mContext, int mVertexShaderId, int mFragShaderId) {
        this.mContext = mContext;
        this.mVertexShaderId = mVertexShaderId;
        this.mFragShaderId = mFragShaderId;


        mGlVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGlVertexBuffer.clear();

//        float[] VERTEXT = {
//                -1.0f, -1.0f,
//                1.0f, -1.0f,
//                -1.0f, 1.0f,
//                1.0f, 1.0f
//        };

        float[] VERTEXT = {
                -1.0f, 1.0f,
                1.0f, 1.0f,
                -1.0f, -1.0f,
                1.0f, -1.0f
        };


        mGlVertexBuffer.put(VERTEXT);

        mGlTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGlTextureBuffer.clear();

//        float[] TEXTURE = {
//                0.0f, 1.0f,
//                1.0f, 1.0f,
//                0.0f, 0.0f,
//                1.0f, 0.0f,
//        };

        float[] TEXTURE = {
                0.0f, 0.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                1.0f, 1.0f,
        };


        mGlTextureBuffer.put(TEXTURE);

        initilize(mContext);

        resetCoordinate();

    }

BaseFilter 是父类,首先初始化了顶点坐标和纹理坐标的值,

代码语言:javascript
复制
private void initilize(Context mContext) {
    //读取着色器信息
    mVertexShader = OpenGlUtils.readRawShaderFile(mContext, mVertexShaderId);
    mFragShader = OpenGlUtils.readRawShaderFile(mContext, mFragShaderId);
    //创建着色器程序
    mProgramId = OpenGlUtils.loadProgram(mVertexShader, mFragShader);
    //获取着色器变量,需要赋值
    vPosition = GLES20.glGetAttribLocation(mProgramId, "vPosition");
    vCoord = GLES20.glGetAttribLocation(mProgramId, "vCoord");
    vMatrix = GLES20.glGetUniformLocation(mProgramId, "vMatrix");
    vTexture = GLES20.glGetUniformLocation(mProgramId, "vTexture");

}

会把着色器里的信息以String读取出来,OpenGlUtils是个工具类,OpenGlUtils.loadProgram创建着色器程序。

ScreenFilter也是一样的,但是不同的是在片段着色器中,接收的纹理是Sampler2D,而不是 samplerExternalOES。这是因为,在CameraFilter中,传入的直接是SurfaceTexture,它不属于OpenGL中定义的东西,所以使用samplerExternalOES,经过CameraFilter使用FBO处理后,后续的所有操作都使用Sampler2D就可以了。

代码语言:javascript
复制
precision mediump float;

    varying vec2 aCoord;

    uniform sampler2D vTexture;

    void main(){
    gl_FragColor=texture2D(vTexture,aCoord);
    }

onSurfaceChanged

代码语言:javascript
复制
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {

    camera2Helper.setPreviewSizeListener(this);
    camera2Helper.setOnPreviewListener(this);
    //打开相机
    camera2Helper.openCamera(width, height, mSurfaceTexture);



    float scaleX = (float) mPreviewHeight / (float) width;
    float scaleY = (float) mPreviewWdith / (float) height;

    float max = Math.max(scaleX, scaleY);

    screenSurfaceWid = (int) (mPreviewHeight / max);
    screenSurfaceHeight = (int) (mPreviewWdith / max);
    screenX = width - (int) (mPreviewHeight / max);
    screenY = height - (int) (mPreviewWdith / max);

   //prepare 传如 绘制到屏幕上的宽 高 起始点的X坐标 起使点的Y坐标
    cameraFilter.prepare(screenSurfaceWid, screenSurfaceHeight, screenX, screenY);
    screenFilter.prepare(screenSurfaceWid, screenSurfaceHeight, screenX, screenY);

}

 onSurfaceChanged中开启相机,传入screenFilter要预览的大小参数。但对于cameraFilter..prepare中,还会创建FBO。

创建过程在 loadFBO中

代码语言:javascript
复制
private void loadFOB() {

    if (mFrameBuffers != null) {
        destroyFrameBuffers();
    }
    //创建FrameBuffer
    mFrameBuffers = new int[1];
    GLES20.glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);
    //创建FBO中的纹理
    mFBOTextures = new int[1];
    OpenGlUtils.glGenTextures(mFBOTextures);

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFBOTextures[0]);
    //指定FBO纹理的输出图像的格式 RGBA
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mOutputWidth, mOutputHeight,
            0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);

    //将fbo绑定到2d的纹理上
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
            GLES20.GL_TEXTURE_2D, mFBOTextures[0], 0);

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

}

onDrawFrame方法

代码语言:javascript
复制
@Override
public void onDrawFrame(GL10 gl) {
    int textureId;
    // 配置屏幕
    //清理屏幕 :告诉opengl 需要把屏幕清理成什么颜色
    GLES20.glClearColor(0, 0, 0, 1.0f);
    //执行上一个:glClearColor配置的屏幕颜色
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    //更新获取一张图
    mSurfaceTexture.updateTexImage();

    mSurfaceTexture.getTransformMatrix(mtx);
    //cameraFiler需要一个矩阵,是Surface和我们手机屏幕的一个坐标之间的关系
    cameraFilter.setMatrix(mtx);

    textureId = cameraFilter.onDrawFrame(mTextures[0]);

    int id = screenFilter.onDrawFrame(textureId);
    //将TRTCSDK 发送纹理frame的关键参数,回调给外部
    onPreviewListener.onTexture(id, eglContext,mPreviewWdith,mPreviewHeight);
}

cameraFilter.onDrawFrame(mTextures[0])

mTextures[0]是SUrfaceTexture绑定的纹理ID

代码语言:javascript
复制
@Override
public int onDrawFrame(int textureId) {

    //锁定绘制的区域  绘制是从左下角开始的
    GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);
    //绑定FBO,在FBO上操作
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);

    //使用着色器
    GLES20.glUseProgram(mProgramId);

    //赋值vPosition
    mGlVertexBuffer.position(0);
    GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGlVertexBuffer);
    GLES20.glEnableVertexAttribArray(vPosition);

    //赋值vCoord
    mGlTextureBuffer.position(0);
    GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGlTextureBuffer);
    GLES20.glEnableVertexAttribArray(vCoord);
    //赋值vMatrix
    GLES20.glUniformMatrix4fv(vMatrix, 1, false, matrix, 0);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    //SurfaceTexture 对应 GL_TEXTURE_EXTERNAL_OES 类型
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
    //赋值vTexture
    GLES20.glUniform1i(vTexture, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

    return mFBOTextures[0];
}

在FBO上操作,把输入的纹理ID上的图像,输出到FBO的纹理ID上然后返回。

screenFilter.onDrawFrame(textureId);

代码语言:javascript
复制
public int onDrawFrame(int textureId) {

    GLES20.glViewport(x, y, mOutputWidth, mOutputHeight);

    GLES20.glUseProgram(mProgramId);

    mGlVertexBuffer.position(0);

    GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGlVertexBuffer);
    GLES20.glEnableVertexAttribArray(vPosition);

    mGlTextureBuffer.position(0);
    GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGlTextureBuffer);
    GLES20.glEnableVertexAttribArray(vCoord);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    //传入的是GL_TEXTURE_2D类型
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glUniform1i(vTexture, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    return textureId;


}

这里拿到的textureId是CameraFilter中FBO的纹理ID,赋值着色器变量,直接glDrawArrays绘制。这里

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);的意思是绘制三角形,从第一个点开始,总共有4个点。

1 2 3 画一个三角,2 3 4画一个三角,这四个点就是顶点坐标。这里两个三角正好绘制出来的是一个矩形。

3、FBO离屏渲染

如上文中代码所示,在cameraFilter.onDrawFrame(mTextures[0])方法里面,已经将纹理id绘制出来了GLES20.glDrawArrays

,为什么还要FBO离屏渲染呢,什么是FBO呢。

帧缓冲对象FBO(Frame buffer Object)。

OpenGL默认情况下,在GLSurfaceView中绘制的结果是显示到屏幕上的,但是实际情况中大部分时候都不需要渲染到屏幕中去,这个FBO就是来实现这个需求的,FBO可以让不渲染到屏幕当中去,而是渲染到离屏的buffer中。比如美颜操作、水印操作等,都需要处理纹理,将处理之后的Texture2D纹理渲染出来。

注意,目前TRTCSDK,传输纹理格式,仅支持Texture2D格式,不支持OES格式,而android系统carmera2采集的纹理格式,是OES格式的。

安卓端FBO写法

前文代码示例中,已经给出了FBO的写法了,这里再展示FBO的OpenGL.API

1、创建FBO

代码语言:javascript
复制
//创建FrameBuffer
mFrameBuffers = new int[1];
GLES20.glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);

2、绑定FBO

通过绑定纹理对象来锁定挂接渲染区

代码语言:javascript
复制
//绑定FBO,在FBO上操作
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
//将fbo绑定到2d的纹理上
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                GLES20.GL_TEXTURE_2D, mFBOTextures[0], 0);
                
//这个Texture2D的纹理id需要事先创建
    private int createFBOTexture(int width, int height, int format) {
        final int[] textureIds = new int[1];
        GLES20.glGenTextures(1, textureIds, 0);
        if(textureIds[0] == 0){
            int i = GLES20.glGetError();
            throw new RuntimeException("Could not create a new texture buffer object, glErrorString : "+ GLES20.glGetString(i));
        }
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, format, width, height,
                0, format, GLES20.GL_UNSIGNED_BYTE, null);
        return textureIds[0];
    }

3、接着我们就可以离屏渲染了(draw)

代码语言:javascript
复制
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

4、screenFilter.onDrawFrame(textureId)将FBO中的纹理Texture2D正常渲染到屏幕上

代码语言:javascript
复制
//传入的是GL_TEXTURE_2D类型
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(vTexture, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

5、预览结束,销毁当前帧的FBO。下一帧回调出来再继续循环。

代码语言:javascript
复制
public void destroyFrameBuffers() {
    //删除fbo的纹理
    if (mFBOTextures != null) {
        GLES20.glDeleteTextures(1, mFBOTextures, 0);
        mFBOTextures = null;
    }
    //删除fbo
    if (mFrameBuffers != null) {
        GLES20.glDeleteFramebuffers(1, mFrameBuffers, 0);
        mFrameBuffers = null;
    }
}

4、TRTCSDK 视频通话

将Texture2D纹理数据,经过您三方美颜数据处理之后,就可以交给TRTCSDK的sendCustomVideoData接口了,即可实现自定义采集视频通话。

TRTCSDK集成、初始化等写法,官网有比较详细的介绍,写法比较简单,结合demo看代码、文档即可。

代码语言:javascript
复制
@Override
public void onTexture(int textureId, android.opengl.EGLContext eglContext, int width, int height) {

    frame.texture = new TRTCCloudDef.TRTCTexture();
    frame.bufferType = TRTC_VIDEO_BUFFER_TYPE_TEXTURE;
    frame.timestamp = 0;
    frame.texture.textureId = textureId;
    frame.texture.eglContext14 = eglContext;
    frame.width = height;
    frame.height = width;
    frame.pixelFormat = TRTCCloudDef.TRTC_VIDEO_PIXEL_FORMAT_Texture_2D;
    mTRTCCloud.sendCustomVideoData(frame);


}

四、TRTC通话状态

使用TRTC提供的监控仪表盘,看到通话正常

五、demo下载

myCustomVideoCaptureDemo

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、适用场景
  • 二、API介绍:
  • 三、Texture2D方案:
    • 0、通话效果
      • 1、OpenGL
        • 2、安卓端OpenGL ES
          • GlSurfaceView是什么? GLSurfaceView的作用是什么? GLSurfaceView如何使用?
          • GlSurfaceView.Renderer是什么?GLSurfaceView.Renderer的作用?GLSurfaceView.Renderer的用法?
          • 问什么要将 samplerExternalOES 输入到sampler2D中
          • onSurfaceChanged
          • 创建过程在 loadFBO中
          • cameraFilter.onDrawFrame(mTextures[0])
          • screenFilter.onDrawFrame(textureId);
        • 3、FBO离屏渲染
          • 安卓端FBO写法
        • 4、TRTCSDK 视频通话
        • 四、TRTC通话状态
        • 五、demo下载
        相关产品与服务
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档