系列文章:

前几篇文章已经讲完了摄像头画面的捕捉和特效渲染,这篇文章我们来讲一讲最后的视频录制部分。

我们这里将使用MediaRecorder去录制视频。MediaRecorder可以同时录制视频和音频。我们将音频源直接设置成摄像头,让它从摄像头里面读取音频数据:

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);

mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);

但是视频源并不能直接设置成摄像头,因为摄像头捕捉到的画面是原始的视频画面,我们上上一篇文章中讲到了如何将这个原始画面绘制到纹理,然后通过特效处理现实到TextureView上:

1.png

所以如果我们直接将MediaRecorder的视频源设置成摄像头的话录制下来的视频并没有带上特效。

那要怎么做呢? MediaRecorder有一种视频源叫做MediaRecorder.VideoSource.SURFACE,意思是从Surface里面读取画面去录制。那我们是不是直接吧TextureView的SurfaceTexture创建的Surface传给MediaRecorder让它捕捉TextureView的内容就行了呢?

可惜的是如果直接用MediaRecorder.setInputSurface将Surface设置进去,会抛出异常:

09-22 14:53:47.473 897 943 E AndroidRuntime: java.lang.IllegalArgumentException: not a PersistentSurface

09-22 14:53:47.473 897 943 E AndroidRuntime: at android.media.MediaRecorder.setInputSurface(MediaRecorder.java:165)

原因是只能设置MediaCodec.PersistentSurface类型的Surface:

/**

* Configures the recorder to use a persistent surface when using SURFACE video source.

*

May only be called before {@link #prepare}. If called, {@link #getSurface} should

* not be used and will throw IllegalStateException. Frames rendered to the Surface

* before {@link #start} will be discarded.

* @param surface a persistent input surface created by

* {@link MediaCodec#createPersistentInputSurface}

* @throws IllegalStateException if it is called after {@link #prepare} and before

* {@link #stop}.

* @throws IllegalArgumentException if the surface was not created by

* {@link MediaCodec#createPersistentInputSurface}.

* @see MediaCodec#createPersistentInputSurface

* @see MediaRecorder.VideoSource

*/

public void setInputSurface(@NonNull Surface surface) {

if (!(surface instanceof MediaCodec.PersistentSurface)) {

throw new IllegalArgumentException("not a PersistentSurface");

}

native_setInputSurface(surface);

}

好吧直接滴干活不行那我们就悄悄滴干活。

首先还是需要视频源设置成MediaRecorder.VideoSource.SURFACE,然后配置一堆的视频信息。这些设置项具体是什么意思讲起来比较费劲,我就不展开了,大家感兴趣的可以自行搜索:

mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

mMediaRecorder.setOutputFile(mLastVideo.getPath());

mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);

mMediaRecorder.setVideoSize(mPreview.getWidth(), mPreview.getHeight());

mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);

mMediaRecorder.setOrientationHint(0);

配置完之后开启录制:

try {

mMediaRecorder.prepare();

} catch (IOException e) {

Toast.makeText(this, "failed to prepare MediaRecorder", Toast.LENGTH_LONG)

.show();

}

mMediaRecorder.start();

上面的都是一些常规操作,大部分使用MediaRecorder的代码都是这样用的,下面我们来看正片:

return mGLRender.createEGLSurface(mMediaRecorder.getSurface());

这里拿到MediaRecorder的那个视频源Surface,给它创建了一个EGLSurface。我们在之前那篇EGL基础里面介绍过它。

我们可以用EGL14.eglMakeCurrent方法指定OpenGL往哪个Surface里面绘制,所以我们直接修改代码将OpenGL的目标Suface设置成这个视频源Surface就可以了吗?

恭喜你,得到了一个BUG。

现在视频是可以录制了,但是预览画面黑了。为什么,回顾下这幅图:

1.png

我们需要要将OpenGL的画面绘制到TextureView上才能在屏幕上看到特效渲染后的预览画面。

那怎么办?TextureView和MediaRecorder只能二选一了吗?不,小孩子才做选择题,成年人当然是全都要。

我们让OpengGL辛苦点,画两次...

2.png

首先修改下GLRender.render方法, EGLSurface由外面传进来,这样我们就能在外面控制它往TextureView和MediaRecord绘制了:

public void render(float[] matrix, EGLSurface eglSurface) {

makeCurrent(eglSurface);

GLES20.glUniformMatrix4fv(mTransformMatrixId, 1, false, matrix, 0);

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

GLES20.glBindTexture(GLES11Ext.GL_SAMPLER_EXTERNAL_OES, mGLTextureId);

GLES20.glUniform1i(mTexPreviewId, 0);

GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

GLES20.glDrawElements(GLES20.GL_TRIANGLES, ORDERS.length, GLES20.GL_UNSIGNED_SHORT, mOrder);

EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);

}

然后在绘制的时候绘制两次:

mCameraTexture.updateTexImage();

mCameraTexture.getTransformMatrix(mTransformMatrix);

mGLRender.render(mTransformMatrix, mGLRender.getDefaultEGLSurface());

if (mRecordSurface != null) {

mGLRender.render(mTransformMatrix, mRecordSurface);

mGLRender.setPresentationTime(mRecordSurface, mCameraTexture.getTimestamp());

}

这里需要注意的是我们需要给这一帧设置下时间戳,用于录制视频的时间同步:

public void setPresentationTime(EGLSurface eglSurface, long nsecs) {

EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);

}

好了,这个录像的实现方法比较简单。到此整个特效相机的教程就结束了,希望对大家有用。

这篇文章的demo依然在github(注意是feature_record分支)

android 视频特效,安卓特效相机(四) 视频录制相关推荐

  1. android 小视频添加水印,安卓手机怎么给视频加水印 视频加水印的手机软件|微信小视频怎么加水印...

    感觉中午一个小时的午休时间更本不够似的,以至于现在的我还头昏脑胀的厉害,睡眼惺忪的我还得默默的敲击着键盘,全都是为了生活啊,算了不传递这些负能量了,来说说咱们今天的教程,是关于如何用手机给视频加水印的 ...

  2. android 视频相册,安卓11版本保存视频到相册,提示保存成功,相册里没有视频...

    安卓11版本,下载视频uni.downloadFile并保存到相册uni.saveImageToPhotosAlbum提示成功,但相册里没有视频 其他安卓版本和iphone,可以正常保存 save(v ...

  3. android手机视频编辑,安卓手机如何使用视频编辑器给自己录制的视频去除原声并添加配乐?安卓手机视频编辑器...

    点击"视频编辑"就可以开始来进行视频编辑操作了,首先我们要来选择自己想要来进行配乐的视频文件.也可以点击上方的"录制"按钮来直接录制视频素材.这里小编选择了图库 ...

  4. 黄油相机如何为视频添加贴纸 黄油相机为视频新增贴纸方法

    黄油相机在哪为视频添加贴纸?很多小伙伴不知道如何操作,下面小编给大家带来了图文教程,感兴趣的朋友快来看看吧. 黄油相机给视频新增个性化贴纸的方法 1.第一步打开黄油相机,进去之后,点击拍摄图标 2.第 ...

  5. 音视频7——安卓硬编音视频数据推送到rtmp服务器

    音视频开发路线: Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门 demo地址: videoPath/Demo8Activity.j ...

  6. 音视频7——安卓软编音视频数据推送到rtmp服务器

    音视频开发路线: Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门 demo地址: videoPath/Demo8Activity.j ...

  7. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  8. 【Android RTMP】安卓直播推流总结 ( 直播服务器搭建 | NV21 图像采集 | H.264 视频编码 | PCM 音频采集 | AAC 音频编码 | RTMP 包封装推流 )

    文章目录 一. 安卓直播推流专栏博客总结 二. 相关资源介绍 三. GitHub 源码地址 四. 整体 Android 直播推流数据到服务器并观看直播演示过程 Android 直播推流流程 : 手机采 ...

  9. 安卓 camera 调用流程_音视频开发之旅(四)Camera视频采集

    目录 Camera基础知识 视频采集的流程 遇到的问题和常见的坑(重点) 收获 一. Camera基础知识 Camera 有几个重要的基础概念. facing相机的方向,一般后置摄像头和前置摄像头. ...

最新文章

  1. 算法学习:常用排序方法
  2. 基于EasyNVR二次开发实现业务需求:直接集成EasyNVR播放页面到自身项目
  3. DPDK helloworld 源码阅读
  4. 二十八种未授权访问漏洞合集(暂时最全)
  5. Mobile-LPR——面向移动端的准商业级车牌识别库
  6. 面试必备TCP三次握手
  7. 门槛回归模型_stata15:门槛模型
  8. 关于修改CentOS7(64位)环境变量
  9. AMD ATI Radeon 显卡被曝多个漏洞
  10. ResNeXt核心思想
  11. linux sqlplus 历史命令,SQLPLUS下历史命令查找
  12. c51流水灯实验报告汇编语言,51单片机流水灯实验报告.doc
  13. 前端时尚好用的图标项目-Font Awesome
  14. 游戏功能模块——新手引导
  15. <第六、七周>新店日记,shopee怎么怎么开广告?怎么定价比较合理?
  16. MT6737 PCB设计指南资料分享
  17. 二手苹果电脑交易的坑和辨别真伪的一些方法总结(下篇)
  18. OpenLayers 6 如何优雅的使用天地图WMTS服务“经纬度投影(CGCS2000)”和“球面墨卡托投影(EPSG:3857)”
  19. RuoYi-Flowable 工作流管理平台
  20. c语言ascii码字符集共有多少个编码,标准ascii码字符集共有多少个编码

热门文章

  1. Session与Cookie(自定义Session)
  2. Application Verifier使用起步
  3. 如何让你pycharm用起来更舒服,看起来更美观
  4. SIC8833芯片开发厨房电子秤方案
  5. CA认证原理与演化过程
  6. Flutter v1,大厂面试必问
  7. 如何快速体验腾迅信鸽推送
  8. 电子设计解决方案透视
  9. LA@分块矩阵@初等变换@初等矩阵#逆矩阵计算@初等变换法
  10. 10进制转2进制,js实现