MediaCodec 从Surface编码及android锁屏录像和后台录像实现
息屏录像模块
基于以下原理, 做出了完整的后头录像锁屏app.不同于其它监控软件的伪后台(伪装窗口运行),此模块完全后台运行,即使锁屏状态也能监测和录像. 支持高清录像/录音/选择摄像头.
锁屏app介绍
当前功能有:
移动侦测录像. 此软件可以侦测摄像头范围内画面.当发生画面变化时自动开始录像,当动作停止一分钟后自动停止录像并保存;
晃动手机启动录像;
可定制做 脸部识别侦测录像.侦测到人脸时自动开始录像. 用usb红外摄像头作为输入源(已测试部分设备可行), 录像后自动上传到网盘, 多路摄像头录像. 这些功能demo做过, 但是没时间集成到这个应用.
使用场景:
可用于室内防盗/宠物记录/隐蔽拍摄等.
后台录像已做成 androidStudio 模块. 直接调用使用. 需要此模块请私信或联系邮箱 ilotuo@163.com 咨询.
演示视频
http://v.youku.com/v_show/id_XMTY3NTA1NDk4MA==.html?spm=a2hzp.8253869.0.0.4C43Bp&from=y1.7-2
整体框架原理
模块录像时一共有3个线程;
- preview 线程一直运行, 从camera获得数据到surfacetexture;
- 一个控制线程 Server,也是一直运行, 控制相机初始化,回调,录像源选择,启动录像;
- 编码线程实现从surface编码;
预览线程要创建一个”离屏egl surface";
编码线程要从mediacodec返回的surface创建egl surface;
开始录像和停止录像的时候,要让渲染环境在两个线程间互相切换。
离屏渲染
preview 线程做的事情是将Camera预览的内容渲染到一个离屏渲染环境. 如何创建一个离屏渲染环境?
- 创建一个offscreen egl surface;
- 获得默认display 设备和缓存等;
- makeCurrent,使用该surface和egl display设备,缓存等创建egl上下文,这是个共享上下文,可以在多个渲染线程使用;
- opengl初始化,纹理初始化,surfacetexture初始化,得到对应surface。
关于egl是什么见引用文章。
Android从Surface编码原理
这是api 18之后的功能。下面尝试把手机摄像头preview渲染到Input Surface.然后对该Surface编码.
首先该Surface由MediaCodec创建:
mSurface = mMediaCodec.createInputSurface(); // API >= 18
然后手动为该Surface初始化EGL后,渲染GL画面,编码。
add on 06/30/2016 SurfaceEncode Example .
编码输出
先看编码是怎么输出的.典型的MediaCodec Buffer编码,dequeueOutputBuffer的流水线顺序是这样:
1: changeFormat .
2: deque sps,pps buffer . BufferInfo 的flags被置位为:BUFFER_FLAG_CODEC_CONFIG.(如果是编码为h264,此时应写入h264文件)
3: IFrame (2+3 = IDR Frame)
4: many pFrame …
但是当使用InputSurface时,第二步被跳过,也就是没有BUFFER_FLAG_CODEC_CONFIG buffer,此时应该手动取sps和pps :
ByteBuffer sps = newFormat.getByteBuffer("csd-0");ByteBuffer pps = newFormat.getByteBuffer("csd-1");
如果是H264裸流要先把这两个buffer写到文件头或推流.
详见SufraceEncoder.md drainAllEncoderMuxer函数.
初始化EGL
本例以手机摄像头录像为例. 下面的代码来自SufraceEncoder.md(上一节有开源链接). 在preview渲染线程增加录像线程:
mSurfaceEncoder = new SurfaceEncoder(thread.mVideoSource.mCols,thread.mVideoSource.mRows);Surface sur = mSurfaceEncoder.getInputSurface();mRendererHolder.setRecordingSurface(sur);
其中 mRendererHolder为preivew主线程.
seRecordingSSurface函数:
public void setRecordingSurface(final Surface surface){RecordSurfaceRenderHandler rh = RecordSurfaceRenderHandler.createHandler();rh.setEglContext(mMasterEgl.getContext(), mTexId, surface, true);mClients.add(rh);}
RecordSurfaceRenderHandler 完成创建录制线程,为Surafce初始化EGL,在帧更新回调时切换EGL Context,并绘制到Surface.
源码 RecordSurfaceRenderHandler.md
关于EGL初始化,首先了解下EGL是什么,我这里看过两篇文章讲的比较好. 引用文章[1]和[2]
第一篇讲EGL原理,本质.第二篇将EGL应用.
通过以上文章我们知道,我们将要共享一块纹理, 那么EGL context 是唯一. 任何EGL Surface 渲染前都要和这个Context 绑定.使成为渲染目标.如果你的渲染主线程来自GLSurfaceView ,那么要先获得它的EGLContext, 我还没有实践过,也许可以参考这个提问:android - How can GLSurfaceView use my EGLDisplay, EGLContext and eglSurface? - Stack Overflow
handleSetEglContext 函数为surface初始化EGL环境,EGLBase在grafika工程有,过程和原理结合源码参考上面的#2链接.可以认为其所做的都是为makeCurrent(切换渲染对象)时做准备. EGLBase#makeCurrent实现就调用了一句:
EGL14.eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)
将共享Context和我们的Surface绑定在一起.
Draw
把编码线程当成主线程的一个client,在主渲染线程帧回调时,向编码线程发送消息:
handler.sendMessage(handler.obtainMessage(MSG_RENDER_DRAW2, (int) (timestamp >> 32), (int) timestamp,mTxtMat));
编码线程的处理: GLDrawer2D 负责编译shader 和调用GLES接口进行渲染,以及Surface swap交换帧缓存.每次更新自动给MediaCodec输入帧数据.RencordSurfaceRenderHandler#handleFrameAvailable 完成渲染和drain编码:
private void handleFrameAvailable(int tex_id,float[] transform, long timestampNanos) {Log.v(TAG,"handleFrameAvailable #0");SurfaceEncoder mVideoEncoder = SurfaceEncoder.getInstance();if(mVideoEncoder==null || !mVideoEncoder.isRecording())return;Log.d(TAG, "handleDrain: #3");mVideoEncoder.drainAllEncoderMuxer(false);mDrawer.draw(tex_id, transform);mTargetSurface.setPresentationTime(timestampNanos);mTargetSurface.swap();Log.v(TAG,"handleFrameAvailable #1");}
mVideoEncoder.drainAllEncoderMuxer 编码输出前面讲过了,和BufferInput差不多,去掉Buffer输入和注意sps和pps保存即可.
录制线程的帧回调只是多了mTargetSurface.swap().其作用就是调用GL14.eglSwapBuffers(mEglDisplay, surface) 将画到EglDisplay上的缓存换到Surface的帧缓存,录制线程的绘制.
小结
如何从surface编码?
- mediacodec 创建inputsurface;
- 设置一个录像线程,但是要求使用同一个shard_context,使用input surface创建EGLSruface;
- 初始化openGL,shader等;
- makeCurrent, 把当前(录像线程)设置为渲染线程;
- 渲染线程只更新surfaceTexture,不画出来,把SurfaceTexture传给录像线程;
参考文档
#1 科学网—EGL资源的数据共享应用和底层驱动实现 - 郭叶军的博文
#2 学习OpenGL-ES: 2 - EGL解析 - kiffa - 博客园
saki4510t/UVCCamera
google/grafika: Grafika test app
MediaCodec 从Surface编码及android锁屏录像和后台录像实现相关推荐
- android锁屏显示应用程序,今日应用:微软又给 Android 做了一款锁屏应用
微软又做了一款 Android 锁屏应用,质量还不错.如果你已经设置了锁屏,Picturesque可能让你再解锁一次你真的需要在锁屏就处理这么多任务吗? 微软又来给 Android 提供应用了,他们昨 ...
- android 锁屏音量,Android锁屏状态获取音量按键事件
Android系统没有提供音量按键的广播,而Activity的onKeyDown方法只有在界面显示时才能捕获音量变化, 要在锁屏状态或后台获得音量按键事件,可以通过判断音量值的改变来判断是否按下了音量 ...
- [Android] Android 锁屏实现与总结 (一)
实现锁屏的方式有多种(锁屏应用.悬浮窗.普通Activity伪造锁屏等等).但国内比较主流并且被广泛应用的Activity伪造锁屏方式. 实例演示图片如下: 系列文章链接如下: [Android] A ...
- Android 锁屏键和home键分开处理
在做视频直播的时候遇到一个问题,就是Android锁屏状态下与home键状态下SurfaceView的生命周期发生的改变是不相同的. 因为home键与锁屏的时候activity都会走onPause() ...
- android锁屏应用系统排行榜,重塑安卓手机的20大锁屏应用程序
1. AcDisplay 它是一个简单的设计android锁屏应用程序,以简约的方式处理通知.您可以直接从锁定屏幕访问应用程序.它具有使用传感器唤醒设备的活动模式. 兼容性 - Android 4.1 ...
- jQuery仿Android锁屏图案应用
jQuery仿Android锁屏图案应用 在线演示 本地下载 posted @ 2018-12-03 14:08 栖息地 阅读(...) 评论(...) 编辑 收藏
- jQuery仿Android锁屏图案应用插件
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- Android锁屏实现与总结
Android锁屏实现与总结 Android锁屏实现与总结(网易云阅读) 一.自定义锁屏基本原理 二.重要步骤 1.广播注册 2.Activity设置 3.按键的屏蔽 4.滑屏解锁 5.Event b ...
- android 锁屏通知
最近有个需求,说要弄个锁屏通知,通知倒是做过很多了,锁屏通知还真没弄过,经过一番研究,这里做个记录,方便搬砖. 话不多少,直接上效果图: 直接上代码: 安卓系统7以及以下: Notification. ...
最新文章
- 利用XSL对XML数据进行加密和大小写转换
- JavaScript公共运行库
- .net随笔-vb.net打开外部程序发送键盘信号(1)
- boost::geometry::default_distance_result用法的测试程序
- 如何处理JCO版本太旧引起的问题
- qq群发信息显示服务器检测到,关于如何突破QQ群发消息屏蔽或限制经验总结
- pytorch线性回归代码_[PyTorch 学习笔记] 1.3 张量操作与线性回归
- 删库不必跑路,谈数据库删除设计
- OpenShift Security (8) - 安装并运行 DevSecOps 应用
- 使用 pycharm安装各个模块
- ContentPresenter
- 吴恩达机器学习与深度学习作业目录 [图片已修复]
- Apache Nutch 1.3 学习笔记十一(页面评分机制 LinkRank 介绍)
- 报头中的偏移量作用_网络中BN层的作用
- DirectX8编程指南-1 (转)
- 修改elementUI 表格透明度,字体颜色
- 数据分析 第二章 1.数据清洗及特征处理
- 短语、直接短语、句柄、素短语
- 夏斌:半年宏观调控思路的建议
- Java波斯王子时之沙攻略_《波斯王子:时之砂》剧情攻略