当前位置: 首页 > news >正文

衡水做网站的公司百度网页

衡水做网站的公司,百度网页,宁德网站设计,wordpress应用教程目录 一,目标 二,核心流程 三,设计到的技术点 4,MediaCodec编码原理 5,核心代码实现 5.1 自定义GLSurfaceView 5.2 设置录制特效模式 5.3 自定义渲染模式GLSurfaceView.Renderer 5.4 自定义视频编码器MediaRecorder 一,目标 使用Android的相机拍摄视频,在…

目录

一,目标

二,核心流程

三,设计到的技术点

4,MediaCodec编码原理

5,核心代码实现

5.1 自定义GLSurfaceView

5.2 设置录制特效模式

5.3 自定义渲染模式GLSurfaceView.Renderer

5.4 自定义视频编码器MediaRecorder


一,目标

使用Android的相机拍摄视频,在相机预览的同时,可以做一些极快,极慢,大眼,瘦脸的效果,需要在录制过程中高性能,如果直接使用 SufaceView 并不方便美颜滤镜和加水印贴图,以及对录制过程中视频操作

二,核心流程

三,设计到的技术点

3.1 OpenGL ES 预览相机(Fbo)

Frame Buffer Object

​    帧缓冲对象:FBO。默认情况下,我们在`GLSurfaceView`中绘制的结果是显示到屏幕上,然而实际中有很多情况并不需要渲染到屏幕上,这个时候使用FBO就可以很方便的实现这类需求。FBO可以让我们的渲染不渲染到屏幕上,而是渲染到离屏Buffer中。

3.2 MediaCodec 编码相机数据

MediaCodec是Android 4.1.2(API 16)提供的一套编解码API。它的使用非常简单,它存在一个输入缓冲区与一个输出缓冲区,在编码时我们将数据塞入输入缓冲区,然后从输出缓冲区取出编码完成后的数据就可以了
3.3 MediaMuxer 合成输出视频文件

4,MediaCodec编码原理

4.1

除了直接操作输入缓冲区之外,还有另一种方式来告知MediaCodec需要编码的数据,那就是:

public native final Surface createInputSurface();

使用此接口创建一个`Surface`,然后我们在这个`Surface`中"作画",`MediaCodec`就能够自动的编码`Surface`中的“画作”,我们只需要从输出缓冲区取出编码完成之后的数据即可。

​  此外,我们使用OpenGL进行绘画显示在屏幕上,然而想要复制屏幕图像到内存中却不是一件非常轻松的事情。但是现在我们可以直接将OpenGL显示到屏幕中的图像,同时绘制到`MediaCodec#createInputSurface`当中去,这样子操作就会更加简单高效

4.2
 录制我们在另外一个线程中进行(**录制现场**),所以录制的EGL环境和显示的EGL环境(`GLSurfaceView`,**显示线程**)是两个独立的工作环境,他们又能够共享上下文资源:显示线程中使用的texture等,需要能够在录制线程中操作(通过录制线程中使用OpenGL绘制到MediaCodec的Surface)。


4.3
 在这个线程中我们需要自己来:
1)、配置录制使用的EGL环境(参照GLSurfaceView是怎么配置的)

 2)、完成将显示的图像绘制到MediaCodec的Surface中

 3)、编码(H.264)与复用(封装mp4)的工作

5,核心代码实现

5.1 自定义GLSurfaceView

配置egl版本和设置自定义渲染器,以及渲染模式

public TigerView(Context context, AttributeSet attrs) {super(context, attrs);/*** 设置egl版本*/setEGLContextClientVersion(2);/*** 设置渲染器*/mTigerRender = new TigerRender(this);setRenderer(mTigerRender);/*** 设置按需渲染,当我们调用requestRender()的时候就会调用GlThread回调一次onDrawFrame()*/setRenderMode(RENDERMODE_WHEN_DIRTY);}

5.2 设置录制特效模式

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);}

5.3 自定义渲染模式GLSurfaceView.Renderer

public void onSurfaceCreated(GL10 gl, EGLConfig config) {mCameraHelper = new CameraHelper(Camera.CameraInfo.CAMERA_FACING_BACK);//准备好摄像头绘制的画布//通过open gl创建一个纹理idmTextures = new int[1];GLES20.glGenTextures(mTextures.length, mTextures, 0);mSurfaceTexture = new SurfaceTexture(mTextures[0]);//设置有一帧新的数据到来的时候,回调监听mSurfaceTexture.setOnFrameAvailableListener(this);//必须要在GlThread里面创建着色器程序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);}/*** 画布发生改变** @param gl     the GL interface. Use <code>instanceof</code> to*               test if the interface supports GL11 or higher interfaces.* @param width* @param height*/@Overridepublic 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.*/@Overridepublic void onDrawFrame(GL10 gl) {//告诉open gl需要把屏幕清理成 什么样子的颜色GLES20.glClearColor(0, 0, 0, 0);//开始真正的屏幕颜色清理,也就是上一次设置的屏幕颜色GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);//把摄像头采集的数据输出出来//更新纹理,然后我们才可以使用opengl从SurfaceTexure当中获取数据,进行渲染mSurfaceTexture.updateTexImage();//mSurfaceTexture比较特殊,在设置坐标的时候,需要一个变换矩阵,使用的是特殊的采样器samplerExternalOES//这种采样器,正常的是sample2DmSurfaceTexture.getTransformMatrix(mtx);mCameraFilter.setMatrix(mtx);int id = mCameraFilter.onDrawFrame(mTextures[0]);//在这里添加各种效果,相当于责任链//开始画画mScreeFilter.onDrawFrame(id);mMediaRecorder.encodeFrame(id, mSurfaceTexture.getTimestamp());}

1)通过在onSurfaceCreated设置相机录制功能,同时准备好相机预览的画布,通过open gl创建一个纹理id,给后续预览各种特效使用

2)给SurfaceTexture设置设置有一帧新的数据到来的时候,回调监听,mSurfaceTexture.setOnFrameAvailableListener(this)

3)onSurfaceChanged里面进行画布大小的设定和纹理绑定

4)onDrawFrame里面进行绘画,同时使用MediaCodc在单独的进程里面进行视频编码encodeFrame

5)在onFrameAvailable中

有新的一帧数据过来以后,有一帧新的图像,就开始调用GLSurfaceView的onDrawFrame进行绘制

5.4 自定义视频编码器MediaRecorder

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);//配置码率 1500kbsvideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_000);//帧率videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);//关键字间隔videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);//创建视频高级编码器mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);//因为是从Surface中读取的,所以不需要设置这个颜色格式videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);//配置编码器,CONFIGURE_FLAG_ENCODE,mMediaCodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);//交给虚拟屏幕,通过opengl 将预览的纹理绘制到这一个虚拟屏幕中,这样子Mediacodc 就会自动编码这一帧图像mInputSurface = mMediaCodec.createInputSurface();//mp4 播放流程  解复用--》解码 》绘制//mp4 编码流程  封装器--》编码//MUXER_OUTPUT_MPEG_4 MP4格式封装器,将h.264通过他写出到文件就可以了mMediaMuxer = new MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);/*** 配置EGL环境,也就是配置我们的虚拟屏幕环境*/HandlerThread handlerThread = new HandlerThread("ViewoCodec");handlerThread.start();Looper looper = handlerThread.getLooper();//子线程和子线程之间的通信mHandler = new Handler(looper);//EGl的绑定线程,对我们自己创建的EGl环境,都是在这个线程里面进行mHandler.post(() -> {//创建我们的EGL环境(虚拟设备,EGL上下文 )mEglBase = new EGLBase(mContext, mWidth, mHeight, mInputSurface, mEglContext);//启动编码器mMediaCodec.start();isStart = true;});}/*** textureId 纹理id* 调用一次,就有一个新的图片需要编码*/public void encodeFrame(int textureId, long timesnap) {if (!isStart) {return;}//切换到子线程中编码mHandler.post(() -> {//把图像纹理画到虚拟屏幕里面mEglBase.draw(textureId, timesnap);//此时我们需要从编码器里面的输出缓冲区获取编码以后的数据就可以了,getCodec(false);});}private void getCodec(boolean endOfStream) {if (endOfStream) {//表示停止录制,此时我们不录制了,需要给mediacoic 通知mMediaCodec.signalEndOfInputStream();}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();//希望将已经编码完成的数据都获取到,然后写出到mp4文件中while (true) {//并不是传给mediacodec一帧数据就表示可以编码出一帧数据,有可能需要好多帧数据才可以同时编码出数据出来//输出缓冲区//传递-1表示一直等到输出缓冲区有一个编码好的有效的数据以后才会继续向下走,不然就会一直卡在127行,//10_000超时时间int status = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {//如果是停止,就继续循环,// 继续循环就表示不会接收到新的等待编码的图像了//相当于保证mediacodic中所有待编码的数据都编码完成// 标记不是停止,我们退出,下一轮接收到更多的数据才来输出编码以后的数据,我们就让继续走// 表示需要更多数据才可以编码出图像  false是继续录制,未来还有机会在调用getCodecif (!endOfStream) {//结束录制了break;}//否则继续} else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {//开始编码,就会调用一次MediaFormat outputFormat = mMediaCodec.getOutputFormat();//配置封装器,增加一路指定格式的媒体流index = mMediaMuxer.addTrack(outputFormat);//启动封装器mMediaMuxer.start();} else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {//忽略} else {//成功取出一个有效的输出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文件中//根据偏移定位去获取数据,而不是从0开始outputBuffer.position(bufferInfo.offset);//设置可读可写的总长度outputBuffer.limit(bufferInfo.offset + bufferInfo.size);mMediaMuxer.writeSampleData(index, outputBuffer, bufferInfo);}//输出缓冲区使用完毕了, 此时就可以回收了,让mediacodec继续使用mMediaCodec.releaseOutputBuffer(status, false);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {//结束了,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;});}
}

http://www.cadmedia.cn/news/12444.html

相关文章:

  • 如何使网站能被百度搜到爱站网站长工具
  • 陕西省城乡建设厅网站厦门seo新站策划
  • vue 做门户网站搜索引擎优化关键词的处理
  • 重庆网站建设哪个平台好网络平台宣传方式有哪些
  • 如今做知乎类网站怎么样优化网站教程
  • 互联网保险发展现状和趋势西安网站seo技术
  • 学校的网站建设费如何入账外贸营销
  • 宿豫区建设局网站网络服务提供者不是网络运营者
  • 手机免费h5制作软件上海优化网站方法
  • 服装市场营销策划方案网络优化基础知识
  • 网站建设推广唯心磁遁8企业培训课程名称
  • 石家庄住房和城乡建设局网站肇庆seo
  • 青海省高等级公路建设管理局网站百度竞价包年推广是怎么回事
  • 电商网站怎么做的微友圈推广平台怎么加入
  • 淘宝上 网站建设确认已有81人感染
  • 网站方案书百度网站推广怎么收费
  • 出色的网站广告软文200字
  • 深圳全网营销平台排名荥阳seo推广
  • 高端平面设计作品网站好搜seo软件
  • wordpress 上下页导航北京seo优化费用
  • 荣成市城乡建设局网站中国营销策划第一人
  • 网站建设维保合同超级外链自动发布工具
  • 信用网站建设情况搜索引擎技巧
  • 安徽网站建设天锐科技上海牛巨仁seo
  • 海南旅游网站建设方式网站销售怎么推广
  • 网站建设 三牛bing搜索 国内版
  • 网页游戏开服表怎么关闭优化设计全部答案
  • 企业建站搭建怎样做百度推广
  • 工作 网站建设内容免费seo视频教程
  • 手机站喝茶影视手机网站建设价格