大家好,我是程序员kenney,今天给大家说说在android上如何做视频编码。

所谓视频编码就是将每帧的图片内容通过某种方式编码成视频,今天给大家介绍的是用android自带的MediaCodec进行硬编码,与前一篇文章的硬解码类似,硬编码就是利用硬件进行编码。

下面我们就来看看如何一步步实现视频硬编码:

1. 创建并配置MediaCodec

private val MIME_TYPE = "video/avc"

...

val format = MediaFormat.createVideoFormat("video/avc", width, height).apply {

setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)

setInteger(MediaFormat.KEY_BIT_RATE, 5120000)

setInteger(MediaFormat.KEY_FRAME_RATE, 25)

setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)

}

mediaCodec = MediaCodec.createEncoderByType(MIME_TYPE)

mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

mediaCodec.start()

复制代码

这里我们通过将帧渲染到surface上的方式向MediaCodec提供编码数据,因此KEY_COLOR_FORMAT是COLOR_FormatSurface,另外码率、帧率及关键帧间隔可根据需要设置。

2. 创建EGL

什么是EGL?关于EGL的概念可以参考我的一篇文章《OpenGL ES 高级进阶:EGL及GL线程》,这里EGL的作用主要是2个,一个是向MediaCodec提供编码帧是通过将帧内容渲染到一个EGL Surface上,这个EGL Surface需要通过MediaCodec给出的input surface来创建,另一个作用是做texture的共享,因为编码通常会放到另一个线程里,和提供帧texture的线程不是同一个线程。

egl = EncodeEGL(shareContext, mediaCodec.createInputSurface()).apply {

init()

makeCurrent()

}

复制代码

然后将这个EGL绑定到调用线程中,我的demo是将编码放到一个独立的线程上,所以只makeCurrent就可以了,不需要之后再restore回来。

3. 创建MediaMuxer

这个东西是用来进行视频容器封装的,编码只是得到了一堆视频帧数据,那播放器怎么知道怎样去播这堆数据呢?这时就需要容器这个东西,这里用的是mp4:

mediaMuxer = MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

复制代码

4. 渲染编码帧

这里渲染编码帧就是很简单地渲染一个texture,没什么好说的,然后还要设置帧时间戳,这样播放器播的时候才知道什么时间播哪帧,最后swapBuffers完成渲染,这样就向MediaCodec提供了一个待编码的帧。

encodeRenderer.drawFrame(texture)

egl.setTimestamp(timestamp)

egl.swapBuffers()

复制代码

5. 获取编码好的帧并将数据写入文件

这一步有点复杂,因为要处理的情况比较多,这里只把一些关键的步骤列出来。

首先dequeueOutputBuffer()将编码好的一帧的index拿出来,可以设置超时时间,如果在超时时间到达时还未获取到编好的一帧,就会返回,可以通过返回的值来判断是否成功拿到了编码好的帧index。

得到了index就从encoderOutputBuffers中取出编码好的帧数据,然后通过MediaMuxer将数据写入文件。

最后把dequeue出来的buffer再归还回去。

val ret = mediaCodec.dequeueOutputBuffer(bufferInfo, 0)

...

encoderOutputBuffers = mediaCodec.outputBuffers

...

val encodedData = encoderOutputBuffers[ret]

...

encodedData.position(bufferInfo.offset)

encodedData.limit(bufferInfo.offset + bufferInfo.size)

mediaMuxer.writeSampleData(trackIndex, encodedData, bufferInfo)

mediaCodec.releaseOutputBuffer(ret, false)

复制代码

现在来看一下demo:

Thread {

val egl = EGL().apply {

init()

bind()

}

val bitmap = decodeBitmapFromAssets("test.png")

Thread {

val videoEncoder = VideoEncoder()

videoEncoder.init("/sdcard/test.mp4", 540, 540, egl.eglContext)

for (i in 0 until 100) {

val texture = GLUtil.bitmap2Texture(rotateBitmap(bitmap, i * 2f))

videoEncoder.encodeFrame(texture, 100 * i * 1000000L)

GLUtil.deleteTexture(texture)

}

videoEncoder.encodeFrame(0, 0)

videoEncoder.release()

}.start()

}.start()

复制代码

视频编码比较常见的使用场景是相机录像和将一个视频生成带特效的视频,简单起见,这里我把一张图旋转成不的角度来做为视频的帧,编码出一个540*540的mp4视频,帧间隔为100ms,编好的视频就可以用播放器来播放了,效果是这样的:

感谢阅读!

android 获取视频编码,Android视频编码相关推荐

  1. android获取图片缩略图,Android系获取图片和视频的缩略图

    获取手机里视频缩略图: public static Bitmap getVideoThumbnail(ContentResolver cr,Uri uri) { Bitmap bitmap = nul ...

  2. android获取多媒体库的视频、音频、图片

    从媒体库中查找音频.视频.图片文件的相关信息,并获取视频.图片.专辑图片的缩略图 和一些文件操作 package com.uwatch.swconnectservice.util; import ja ...

  3. android获取键盘状态,Android获取屏幕方向及键盘状态的小例子

    Android获取屏幕方向及键盘状态的小例子 复制代码 代码如下: Configuration config = getResources().getConfiguration(); if (conf ...

  4. android获取activity截图,Android Activity 不能被截屏的解决方法

    Android Activity 不能被截屏的解决方法 在Activity 添加即可 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECU ...

  5. Android获取Linux图像信息,Android系统信息获取 之十三:Linux内核版本信息获取

    Android系统信息获取 之十三:Linux内核版本信息获取 Android系统是基于Linux的,各个Android版本对应的Linux版本不尽相同,我们这里不去追究各个Android对应的Lin ...

  6. android 获取对话框对象,Android 基本Dialog和自定义Dialog

    Android 基本Dialog和自定义Dialog Dialog类是对话框的基类,但你应该避免直接实例化Dialog ,可以使用子类 1.AlertDialog 此对话框可以显示标题,最多三个按钮, ...

  7. android获取程序名称,Android获取应用程序名称(ApplicationName)示例

    MainActivity如下: 代码如下: package cn.testapplicationname; import android.os.Bundle; import android.widge ...

  8. android获取详细地址,Android获取当前子网掩码地址(亲测可用)

    Android 获取当前子网掩码地址(亲测可用),现在网上好多都是通过 DhcpInfo 来获取,但是通过这种方法有 Bug,很多人用 DhcpInfo 的方式都是获取不到,都是为 0.0.0.0. ...

  9. android获取apk版本号,android 获取apk的版本信息

    释放双眼,带上耳机,听听看~! 今天,简单讲讲android如何获取apk的版本信息. 这个很简单,但是之前还是查找了资料,所以记录一下. 一.应用程序得到自己的版本信息 /** * 得到当前应用版本 ...

  10. android 获取权限管理,Android获取超级管理员权限的实现

    Android获取超级管理员权限的实现 发布时间:2020-10-14 18:54:35 来源:脚本之家 阅读:86 作者:柚子君. 1.定义特殊的广播接收者,系统超级管理员的广播接收者 public ...

最新文章

  1. 展望未来:使用 PostCSS 和 cssnext 书写 CSS
  2. 软件开发模式对比(瀑布、迭代、螺旋、敏捷)
  3. 让Web站点崩溃最常见的七大原因
  4. 会导致所有者权益减少的项目是_第二章:会计要素的确认(11)所有者权益
  5. Error: could not open `C:\Java\jre7\lib\i386\jvm.cfg
  6. Oracle Parallel Execution(并行执行)
  7. linux mint 忘记密码,在Linux Mint中如何提醒mysql localhost base的密码?
  8. libaio源码安装_MySQL5.7.17 编译安装及二进制安装详解
  9. 属于微型计算机主要性能指示,2014年兰大入学测试题--计算机基础
  10. 软件设计师-数据库( 分布式)
  11. androidstudio 3 Android Device Monitor
  12. 【自用】手工编译lnmp环境
  13. 【个人笔记】OpenCV4 C++ 快速入门 14课
  14. 《软件需求分析》读书笔记NO.4
  15. ios计算机错误,用iTunes更新IOS14失败,显示发生未知错误(4000)的简单解决办法!...
  16. 网络安全与渗透:漏洞攻击—— msf(四)此生无悔入华夏,男儿何不带吴钩
  17. YouTube上的版权保护
  18. 【干货资料 】简单的交换机光模块故障排查步骤
  19. 2021-08-22
  20. 矩阵对角化(Diagonalizing a Matrix)

热门文章

  1. 图深度学习——复杂图嵌入:异质图,二分图,多维图,超图,符号图,动态图
  2. 用心去爱那些爱过我们的人
  3. 牙医管家口腔管理软件DSD微笑设计3.8版本更新
  4. C语言高级教程-C语言数组(六):变长数组
  5. css cursor 鼠标指针样式总结
  6. python递推公式_线性代数求解递推形式数列的通项公式
  7. 300以内的蓝牙耳机哪款好?半入耳蓝牙耳机南卡和漫步者测评
  8. 人生若只如初见服务器维护,「北京服务器」人生若只如初见
  9. Nginx 单IP绑定多域名配置 顶级域名重定向到www域名
  10. 盛大是中国互联网最耀眼的流星