Android使用OpenGL和MediaCodec錄制功能
一,什么是opengl
Open Graphics Library 圖形領(lǐng)域的工業(yè)標(biāo)準(zhǔn),是一套跨編程語言、跨平臺的、專業(yè)的圖形編程(軟件)接口。它用于二維、三維圖像,是一個(gè)功能強(qiáng)大,調(diào)用方便的底層圖形庫。
與硬件無關(guān)??梢栽诓煌钠脚_如Windows、Linux、Mac、Android、IOS之間進(jìn)行移植。因此,支持OpenGL的軟件具有很好的移植性,可以獲得非常廣泛的應(yīng)用。
OpenGL ES 1.0 和 1.1 :Android 1.0和更高的版本支持這個(gè)API規(guī)范。 OpenGL ES 2.0 :Android 2.2(API 8)和更高的版本支持這個(gè)API規(guī)范。 OpenGL ES 3.0 :Android 4.3(API 18)和更高的版本支持這個(gè)API規(guī)范。 OpenGL ES 3.1 : Android 5.0(API 21)和更高的版本支持這個(gè)API規(guī)范
還須要由設(shè)備制造商提供了實(shí)現(xiàn)支持 目前廣泛支持的是2.0 <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
二,什么是Android OpenGL ES
針對手機(jī)、PDA和游戲主機(jī)等嵌入式設(shè)備而設(shè)計(jì)的OpenGL API 子集。
GLSurfaceView
繼承至SurfaceView,它內(nèi)嵌的surface專門負(fù)責(zé)OpenGL渲染。 管理Surface與EGL 允許自定義渲染器(render)。 讓渲染器在獨(dú)立的線程里運(yùn)作,和UI線程分離。 支持按需渲染(on-demand)和連續(xù)渲染(continuous)。
OpenGL是一個(gè)跨平臺的操作GPU的API,但OpenGL需要本地視窗系統(tǒng)進(jìn)行交互,這就需要一個(gè)中間控制層, EGL就是連接OpenGL ES和本地窗口系統(tǒng)的接口,引入EGL就是為了屏蔽不同平臺上的區(qū)別。
三, OpenGL 繪制流程

其實(shí)就是一個(gè)可編程管線pipline

渲染管線做的事情就是讓計(jì)算機(jī)完成圖形功能。 固定管線:程序員只能設(shè)置參數(shù)。比如 f(x)=axx + bx + c程序員只能設(shè)置a,b,c的值,卻不能修改這個(gè)公式。 可編程管線:程序員掌控一切。
四, OpenGL坐標(biāo)系



五, OpenGL 著色器
著色器(Shader)是運(yùn)行在GPU上的小程序。頂點(diǎn)著色器(vertex shader) 如何處理頂點(diǎn)、法線等數(shù)據(jù)的小程序。片元著色器(fragment shader) 如何處理光、陰影、遮擋、環(huán)境等等對物體表面的影響,最終生成一副圖像的小程序

六, GLSL編程語言


七,使用MediaCodec錄制在Opengl中渲染架構(gòu)

八,代碼實(shí)現(xiàn)
8.1 自定義渲染view繼承GLSurfaceView
public class TigerView extends GLSurfaceView {
private TigerRender mTigerRender;
//默認(rèn)正常速度
private Speed mSpeed = Speed.MODE_NORMAL;
public void setSpeed(Speed speed) {
mSpeed = speed;
}
public enum Speed {
MODE_EXTRA_SLOW, MODE_SLOW, MODE_NORMAL, MODE_FAST, MODE_EXTRA_FAST
}
public TigerView(Context context) {
super(context);
}
public TigerView(Context context, AttributeSet attrs) {
super(context, attrs);
/**
* 設(shè)置egl版本
*/
setEGLContextClientVersion(2);
/**
* 設(shè)置渲染器
*/
mTigerRender = new TigerRender(this);
setRenderer(mTigerRender);
/**
* 設(shè)置按需渲染,當(dāng)我們調(diào)用requestRender()的時(shí)候就會調(diào)用GlThread回調(diào)一次onDrawFrame()
*/
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
public void startRecord() {
float speed = 1.f;
switch (mSpeed) {
case MODE_EXTRA_SLOW:
speed = 0.3f;
break;
case MODE_SLOW:
speed = 0.5f;
break;
case MODE_NORMAL:
speed = 1.f;
break;
case MODE_FAST:
speed = 1.5f;
break;
case MODE_EXTRA_FAST:
speed = 3.f;
break;
}
mTigerRender.startRecord(speed);
}
public void stopRecord() {
mTigerRender.stopRecord();
}
}8.2 自定義渲染器TigerRender
public class TigerRender implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
private final TigerView mView;
private CameraHelper mCameraHelper;
private SurfaceTexture mSurfaceTexture;
float[] mtx = new float[16];
private ScreeFilter mScreeFilter;
private int[] mTextures;
private CameraFilter mCameraFilter;
private MediaRecorder mMediaRecorder;
public TigerRender(TigerView tigerView) {
mView = tigerView;
}
/**
* 畫布創(chuàng)建好了
*
* @param gl the GL interface. Use <code>instanceof</code> to
* test if the interface supports GL11 or higher interfaces.
* @param config the EGLConfig of the created surface. Can be used
* to create matching pbuffers.
*/
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mCameraHelper = new CameraHelper(Camera.CameraInfo.CAMERA_FACING_BACK);
//準(zhǔn)備好攝像頭繪制的畫布
//通過open gl創(chuàng)建一個(gè)紋理id
mTextures = new int[1];
GLES20.glGenTextures(mTextures.length, mTextures, 0);
mSurfaceTexture = new SurfaceTexture(mTextures[0]);
//設(shè)置有一幀新的數(shù)據(jù)到來的時(shí)候,回調(diào)監(jiān)聽
mSurfaceTexture.setOnFrameAvailableListener(this);
//必須要在GlThread里面創(chuàng)建著色器程序
mCameraFilter = new CameraFilter(mView.getContext());
mScreeFilter = new ScreeFilter(mView.getContext());
EGLContext eglContext = EGL14.eglGetCurrentContext();
mMediaRecorder = new MediaRecorder(mView.getContext(), "/mnt/sdcard/test.mp4", CameraHelper.HEIGHT, CameraHelper.WIDTH, eglContext);
}
/**
* 畫布發(fā)生改變
*
* @param gl the GL interface. Use <code>instanceof</code> to
* test if the interface supports GL11 or higher interfaces.
* @param width
* @param height
*/
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mCameraHelper.startPreview(mSurfaceTexture);
mCameraFilter.onReady(width, height);
mScreeFilter.onReady(width, height);
}
/**
* 畫畫
*
* @param gl the GL interface. Use <code>instanceof</code> to
* test if the interface supports GL11 or higher interfaces.
*/
@Override
public void onDrawFrame(GL10 gl) {
//告訴open gl需要把屏幕清理成 什么樣子的顏色
GLES20.glClearColor(0, 0, 0, 0);
//開始真正的屏幕顏色清理,也就是上一次設(shè)置的屏幕顏色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//把攝像頭采集的數(shù)據(jù)輸出出來
//更新紋理,然后我們才可以使用opengl從SurfaceTexure當(dāng)中獲取數(shù)據(jù),進(jìn)行渲染
mSurfaceTexture.updateTexImage();
//mSurfaceTexture比較特殊,在設(shè)置坐標(biāo)的時(shí)候,需要一個(gè)變換矩陣,使用的是特殊的采樣器samplerExternalOES
//這種采樣器,正常的是sample2D
mSurfaceTexture.getTransformMatrix(mtx);
mCameraFilter.setMatrix(mtx);
int id = mCameraFilter.onDrawFrame(mTextures[0]);
//在這里添加各種效果,相當(dāng)于責(zé)任鏈
//開始畫畫
mScreeFilter.onDrawFrame(id);
mMediaRecorder.encodeFrame(id, mSurfaceTexture.getTimestamp());
}
/**
* SurfaceTexture有一個(gè)新的有效的圖片的時(shí)候會被回調(diào),此時(shí)可以把這個(gè)數(shù)據(jù)回調(diào)給GLSurfaceView的onDrawFrame
*
* @param surfaceTexture
*/
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
if (mView != null) {
//開始渲染,有一幀新的圖像,就開始調(diào)用GLSurfaceView的onDrawFrame進(jìn)行繪制
mView.requestRender();
}
}
public void startRecord(float speed) {
try {
mMediaRecorder.start(speed);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void stopRecord() {
mMediaRecorder.stop();
}
}8.3 創(chuàng)建編碼器MediaRecorder
/**
* 視頻錄制
*/
public class MediaRecorder {
private final Context mContext;
private final String mPath;
private final int mWidth;
private final int mHeight;
private final EGLContext mEglContext;
private MediaCodec mMediaCodec;
private Surface mInputSurface;
private MediaMuxer mMediaMuxer;
private Handler mHandler;
private EGLBase mEglBase;
private boolean isStart;
private int index;
private float mSpeed;
/**
* @param context
* @param path 視頻保存地址
* @param width 視頻寬
* @param height 視頻高
*/
public MediaRecorder(Context context, String path, int width, int height, EGLContext eglContext) {
mContext = context.getApplicationContext();
mPath = path;
mWidth = width;
mHeight = height;
mEglContext = eglContext;
}
/**
* 開始錄制視頻
*/
public void start(float speed) throws IOException {
/**
* 配置MediaCodec編碼器,視頻編碼的寬,高,幀率,碼率
* 錄制成mp4格式,視頻編碼格式是h264 MIMETYPE_VIDEO_AVC 高級編碼
*/
mSpeed = speed;
MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
//配置碼率 1500kbs
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_000);
//幀率
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
//關(guān)鍵字間隔
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);
//創(chuàng)建視頻高級編碼器
mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
//因?yàn)槭菑腟urface中讀取的,所以不需要設(shè)置這個(gè)顏色格式
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
//配置編碼器,CONFIGURE_FLAG_ENCODE,
mMediaCodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
//交給虛擬屏幕,通過opengl 將預(yù)覽的紋理繪制到這一個(gè)虛擬屏幕中,這樣子Mediacodc 就會自動(dòng)編碼這一幀圖像
mInputSurface = mMediaCodec.createInputSurface();
//mp4 播放流程 解復(fù)用--》解碼 》繪制
//mp4 編碼流程 封裝器--》編碼
//MUXER_OUTPUT_MPEG_4 MP4格式封裝器,將h.264通過他寫出到文件就可以了
mMediaMuxer = new MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
/**
* 配置EGL環(huán)境,也就是配置我們的虛擬屏幕環(huán)境
*/
HandlerThread handlerThread = new HandlerThread("ViewoCodec");
handlerThread.start();
Looper looper = handlerThread.getLooper();
//子線程和子線程之間的通信
mHandler = new Handler(looper);
//EGl的綁定線程,對我們自己創(chuàng)建的EGl環(huán)境,都是在這個(gè)線程里面進(jìn)行
mHandler.post(() -> {
//創(chuàng)建我們的EGL環(huán)境(虛擬設(shè)備,EGL上下文 )
mEglBase = new EGLBase(mContext, mWidth, mHeight, mInputSurface, mEglContext);
//啟動(dòng)編碼器
mMediaCodec.start();
isStart = true;
});
}
/**
* textureId 紋理id
* 調(diào)用一次,就有一個(gè)新的圖片需要編碼
*/
public void encodeFrame(int textureId, long timesnap) {
if (!isStart) {
return;
}
//切換到子線程中編碼
mHandler.post(() -> {
//把圖像紋理畫到虛擬屏幕里面
mEglBase.draw(textureId, timesnap);
//此時(shí)我們需要從編碼器里面的輸出緩沖區(qū)獲取編碼以后的數(shù)據(jù)就可以了,
getCodec(false);
});
}
private void getCodec(boolean endOfStream) {
if (endOfStream) {
//表示停止錄制,此時(shí)我們不錄制了,需要給mediacoic 通知
mMediaCodec.signalEndOfInputStream();
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
//希望將已經(jīng)編碼完成的數(shù)據(jù)都獲取到,然后寫出到mp4文件中
while (true) {
//并不是傳給mediacodec一幀數(shù)據(jù)就表示可以編碼出一幀數(shù)據(jù),有可能需要好多幀數(shù)據(jù)才可以同時(shí)編碼出數(shù)據(jù)出來
//輸出緩沖區(qū)
//傳遞-1表示一直等到輸出緩沖區(qū)有一個(gè)編碼好的有效的數(shù)據(jù)以后才會繼續(xù)向下走,不然就會一直卡在127行,
//10_000超時(shí)時(shí)間
int status = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);
if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
//如果是停止,就繼續(xù)循環(huán),
// 繼續(xù)循環(huán)就表示不會接收到新的等待編碼的圖像了
//相當(dāng)于保證mediacodic中所有待編碼的數(shù)據(jù)都編碼完成
// 標(biāo)記不是停止,我們退出,下一輪接收到更多的數(shù)據(jù)才來輸出編碼以后的數(shù)據(jù),我們就讓繼續(xù)走
// 表示需要更多數(shù)據(jù)才可以編碼出圖像 false是繼續(xù)錄制,未來還有機(jī)會在調(diào)用getCodec
if (!endOfStream) {
//結(jié)束錄制了
break;
}
//否則繼續(xù)
} else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
//開始編碼,就會調(diào)用一次
MediaFormat outputFormat = mMediaCodec.getOutputFormat();
//配置封裝器,增加一路指定格式的媒體流
index = mMediaMuxer.addTrack(outputFormat);
//啟動(dòng)封裝器
mMediaMuxer.start();
} else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
//忽略
} else {
//成功取出一個(gè)有效的輸出
ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(status);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
//如果獲取的ByteBuffer是配置信息,那么就不需要寫出到mp4文件中
bufferInfo.size = 0;
}
if (bufferInfo.size != 0) {
bufferInfo.presentationTimeUs = (long) (bufferInfo.presentationTimeUs / mSpeed);
//寫出到 mp4文件中
//根據(jù)偏移定位去獲取數(shù)據(jù),而不是從0開始
outputBuffer.position(bufferInfo.offset);
//設(shè)置可讀可寫的總長度
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
mMediaMuxer.writeSampleData(index, outputBuffer, bufferInfo);
}
//輸出緩沖區(qū)使用完畢了, 此時(shí)就可以回收了,讓mediacodec繼續(xù)使用
mMediaCodec.releaseOutputBuffer(status, false);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
//結(jié)束了,
break;
}
}
}
}
public void stop() {
isStart = false;
mHandler.post(() -> {
getCodec(true);
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
mMediaMuxer.stop();
mMediaMuxer.release();
mMediaMuxer = null;
mEglBase.release();
mEglBase = null;
mInputSurface = null;
mHandler.getLooper().quitSafely();
mHandler = null;
});
}
}8.4 配置egl環(huán)境
/**
* EGL配置 和錄制opengl的操作
*/
public class EGLBase {
private final EGLSurface mEglSurface;
private final ScreeFilter mScreeFilter;
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
private EGLContext mEGLContext;
/**
* @param context
* @param width
* @param height
* @param surface MediaCodec創(chuàng)建的surface, 我們需要將這個(gè)surface貼到虛擬屏幕里面
*/
public EGLBase(Context context, int width, int height, Surface surface, EGLContext eglContext) {
createEGL(eglContext);
//把surface貼到EGLDisplay 虛擬屏幕里面
int[] attrib_list = {
//不需要配置什么屬性
EGL14.EGL_NONE};
//就是向mEglDisplay這個(gè)虛擬屏幕上面畫畫
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, attrib_list, 0);
//必須要綁定當(dāng)前線程的顯示上下文,不然就繪制不上去,這樣子之后操作的opelgl就是在這個(gè)虛擬屏幕上操作,讀和寫都是在同一個(gè)surface里面
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
//向虛擬屏幕畫畫
mScreeFilter = new ScreeFilter(context);
mScreeFilter.onReady(width, height);
}
private void createEGL(EGLContext eglContext) {
//創(chuàng)建虛擬顯示器
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
int[] version = new int[2];
//初始化虛擬設(shè)備
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
throw new RuntimeException("eglInitialize failed");
}
int[] attrib_list = new int[]{//rgba 紅綠藍(lán)透明度
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_ALPHA_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,//和egl的版本有關(guān)系
EGL14.EGL_NONE//這個(gè)很重要,一定要配置為NONE,表示配置結(jié)束了
};
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
boolean eglChooseConfig = EGL14.eglChooseConfig(mEglDisplay, attrib_list, 0, configs, 0, configs.length, num_config, 0);
if (!eglChooseConfig) {
//如果配置失敗
throw new IllegalArgumentException("eglChooseConfig failed");
}
mEglConfig = configs[0];
int[] attriblist = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
//創(chuàng)建EGL上下文
//share_context 共享上下,傳遞繪制線程的(GLThread)EGL上下文,達(dá)到共享資源的目的
mEGLContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, eglContext, attriblist, 0);
if (mEGLContext == null || mEGLContext == EGL14.EGL_NO_CONTEXT) {
mEGLContext = null;
throw new RuntimeException("createContex error !");
}
}
/**
* @param textureId 紋理id,代表一張圖片
* @param timesnap 時(shí)間戳
*/
public void draw(int textureId, long timesnap) {
//必須要綁定當(dāng)前線程的顯示上下文,不然就繪制不上去,這樣子之后操作的opelgl就是在這個(gè)虛擬屏幕上操作,讀和寫都是在同一個(gè)surface里面
//畫畫之前也必須要綁定
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
//向虛擬屏幕畫畫
mScreeFilter.onDrawFrame(textureId);
//刷新eglSurface時(shí)間戳
EGLExt.eglPresentationTimeANDROID(mEglDisplay, mEglSurface, timesnap);
//交換數(shù)據(jù),EGL工作模式,雙緩存模式,內(nèi)部有兩個(gè)frameBuff,當(dāng)EGL將一個(gè)frame顯示到屏幕上以后,
// 另一個(gè)frame就在后臺等待opengl進(jìn)行交換
//也就是畫完一次,交換一次
EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
}
public void release() {
EGL14.eglDestroySurface(mEglDisplay, mEglSurface);
EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroyContext(mEglDisplay, mEGLContext);
EGL14.eglReleaseThread();
EGL14.eglTerminate(mEglDisplay);
}
}8.5 配置shader
public class AbstractFilter {
protected FloatBuffer mGLVertexBuffer;
protected FloatBuffer mGLTextureBuffer;
//頂點(diǎn)著色
protected int mVertexShaderId;
//片段著色
protected int mFragmentShaderId;
protected int mGLProgramId;
/**
* 頂點(diǎn)著色器
* attribute vec4 position;
* 賦值給gl_Position(頂點(diǎn))
*/
protected int vPosition;
/**
* varying vec2 textureCoordinate;
*/
protected int vCoord;
/**
* uniform mat4 vMatrix;
*/
protected int vMatrix;
/**
* 片元著色器
* Samlpe2D 擴(kuò)展 samplerExternalOES
*/
protected int vTexture;
protected int mOutputWidth;
protected int mOutputHeight;
public AbstractFilter(Context context, int vertexShaderId, int fragmentShaderId) {
this.mVertexShaderId = vertexShaderId;
this.mFragmentShaderId = fragmentShaderId;
// 4個(gè)點(diǎn) x,y = 4*2 float 4字節(jié) 所以 4*2*4
mGLVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mGLVertexBuffer.clear();
float[] VERTEX = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
mGLVertexBuffer.put(VERTEX);
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};
mGLTextureBuffer.put(TEXTURE);
initilize(context);
initCoordinate();
}
protected void initilize(Context context) {
String vertexSharder = OpenGLUtils.readRawTextFile(context, mVertexShaderId);
String framentShader = OpenGLUtils.readRawTextFile(context, mFragmentShaderId);
mGLProgramId = OpenGLUtils.loadProgram(vertexSharder, framentShader);
// 獲得著色器中的 attribute 變量 position 的索引值
vPosition = GLES20.glGetAttribLocation(mGLProgramId, "vPosition");
vCoord = GLES20.glGetAttribLocation(mGLProgramId, "vCoord");
vMatrix = GLES20.glGetUniformLocation(mGLProgramId, "vMatrix");
// 獲得Uniform變量的索引值
vTexture = GLES20.glGetUniformLocation(mGLProgramId, "vTexture");
}
public void onReady(int width, int height) {
mOutputWidth = width;
mOutputHeight = height;
}
public void release() {
GLES20.glDeleteProgram(mGLProgramId);
}
public int onDrawFrame(int textureId) {
//設(shè)置顯示窗口
GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);
//使用著色器
GLES20.glUseProgram(mGLProgramId);
//傳遞坐標(biāo)
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);
//綁定
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(vTexture, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//解綁
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureId;
}
//修改坐標(biāo)
protected void initCoordinate() {
}
}到此這篇關(guān)于Android使用OpenGL和MediaCodec錄制的文章就介紹到這了,更多相關(guān)Android使用OpenGL和MediaCodec內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android OpenGL入門之GLSurfaceView
- Android使用Opengl錄像時(shí)添加水印
- Android OpenGLES如何給相機(jī)添加濾鏡詳解
- Android利用OpenGLES繪制天空盒實(shí)例教程
- Android OpenGLES2.0等腰直角三角形和彩色的三角形(三)
- Android基于OpenGL的GLSurfaceView創(chuàng)建一個(gè)Activity實(shí)現(xiàn)方法
- Android編程之OpenGL繪圖技巧總結(jié)
- Android硬件解碼組件MediaCodec使用教程
- Android音視頻開發(fā)之MediaCodec的使用教程
相關(guān)文章
Flutter實(shí)現(xiàn)心動(dòng)的動(dòng)畫特效
為了追求更好的用戶體驗(yàn),有時(shí)候我們需要一個(gè)類似心跳一樣跳動(dòng)著的控件來吸引用戶的注意力。本文將利用Flutter實(shí)現(xiàn)這一動(dòng)畫特效,需要的可以參考一下2022-04-04
Android編程之界面跳動(dòng)提示動(dòng)畫效果實(shí)現(xiàn)方法
這篇文章主要介紹了Android編程之界面跳動(dòng)提示動(dòng)畫效果實(shí)現(xiàn)方法,實(shí)例分析了Android動(dòng)畫效果的布局及功能相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
解決Android Studio 格式化 Format代碼快捷鍵問題
這篇文章主要介紹了解決Android Studio 格式化 Format代碼快捷鍵問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android Studio 合并module到統(tǒng)一文件夾的方法
這篇文章主要介紹了Android Studio 合并module到統(tǒng)一文件夾的方法,補(bǔ)充介紹了android studio關(guān)于同名資源文件的合并技巧,需要的朋友可以參考下2018-04-04
Android的八種對話框的實(shí)現(xiàn)代碼示例
本篇文章主要介紹了Android的八種對話框的實(shí)現(xiàn)代碼示例,這里整理了詳細(xì)的代碼,非常具有實(shí)用價(jià)值,有需要的小伙伴可以參考下。2017-09-09
設(shè)置Android系統(tǒng)永不鎖屏永不休眠的方法
在進(jìn)行Android系統(tǒng)開發(fā)的時(shí)候,有些特定的情況需要設(shè)置系統(tǒng)永不鎖屏,永不休眠。本篇文章給大家介紹Android 永不鎖屏,開機(jī)不鎖屏,刪除設(shè)置中休眠時(shí)間選項(xiàng),需要的朋友一起學(xué)習(xí)吧2016-03-03
Android開發(fā)之TimePicker控件用法實(shí)例詳解
這篇文章主要介紹了Android開發(fā)之TimePicker控件用法,結(jié)合實(shí)例形式詳細(xì)分析了Android項(xiàng)目的建立及TimePicker控件的具體使用技巧,需要的朋友可以參考下2016-02-02

