最全Android MediaCodec 硬编码 H264 文件,2024年最新高级程序员面试题及答案

最后,如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。

小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

if (inputBufferId >= 0) {

ByteBuffer inputBuffer = codec.getInputBuffer(…);

// fill inputBuffer with valid data

codec.queueInputBuffer(inputBufferId, …);

}

// 从输出缓冲区队列中拿到编解码后的内容,进行相应操作后释放,供下一次使用

int outputBufferId = codec.dequeueOutputBuffer(…);

if (outputBufferId >= 0) {

ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);

MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A

// bufferFormat is identical to outputFormat

// outputBuffer is ready to be processed or rendered.

codec.releaseOutputBuffer(outputBufferId, …);

} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

// Subsequent data will conform to new format.

// Can ignore if using getOutputFormat(outputBufferId)

outputFormat = codec.getOutputFormat(); // option B

}

}

// 调用 stop 方法进入 Uninitialized 状态

codec.stop();

// 调用 release 方法释放,结束操作

codec.release();

代码解析


MediaFormat 设置

首先需要创建并设置好 MediaFormat 对象,它表示媒体数据格式的相关信息,对于视频主要有以下信息要设置:

  • 颜色格式

  • 码率

  • 码率控制模式

  • 帧率

  • I 帧间隔

其中,码率就是指单位传输时间传送的数据位数,一般用 kbps 即千位每秒来表示。而帧率就是指每秒显示的帧数。

其实对于码率有三种模式可以控制:

  • BITRATE_MODE_CQ

  • 表示不控制码率,尽最大可能保证图像质量

  • BITRATE_MODE_VBR

  • 表示 MediaCodec 会根据图像内容的复杂度来动态调整输出码率,图像负责则码率高,图像简单则码率低

  • BITRATE_MODE_CBR

  • 表示 MediaCodec 会把输出的码率控制为设定的大小

对于颜色格式,由于是将 YUV 数据编码成 H264,而 YUV 格式又有很多,这又涉及到机型兼容性问题。在对相机编码时要做好格式的处理,比如相机使用的是 NV21 格式,MediaFormat 使用的是 COLOR_FormatYUV420SemiPlanar,也就是 NV12 模式,那么就得做一个转换,把 NV21 转换到 NV12 。

对于 I 帧间隔,也就是隔多久出现一个 H264 编码中的 I 帧。

完整 MediaFormat 设置示例:

MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);

mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);

// 马率

mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5);

// 调整码率的控流模式

mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);

// 设置帧率

mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);

// 设置 I 帧间隔

mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);

当开始编解码操作时,开启编解码线程,处理相机预览返回的 YUV 数据。

在这里用到了相机的一个封装库:

https://github.com/glumes/EzCameraKit

编解码操作

编解码操作代码如下:

while (isEncoding) {

// YUV 颜色格式转换

if (!mEncodeDataQueue.isEmpty()) {

input = mEncodeDataQueue.poll();

byte[] yuv420sp = new byte[mWidth * mHeight * 3 / 2];

NV21ToNV12(input, yuv420sp, mWidth, mHeight);

input = yuv420sp;

}

if (input != null) {

try {

// 从输入缓冲区队列中拿到可用缓冲区,填充数据,再入队

ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();

ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();

int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);

if (inputBufferIndex >= 0) {

// 计算时间戳

pts = computePresentationTime(generateIndex);

ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];

inputBuffer.clear();

inputBuffer.put(input);

mMediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, pts, 0);

generateIndex += 1;

}

MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);

// 从输出缓冲区队列中拿到编码好的内容,对内容进行相应处理后在释放

while (outputBufferIndex >= 0) {

ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];

byte[] outData = new byte[bufferInfo.size];

outputBuffer.get(outData);

// flags 利用位操作,定义的 flag 都是 2 的倍数

if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // 配置相关的内容,也就是 SPS,PPS

mOutputStream.write(outData, 0, outData.length);

} else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // 关键帧

mOutputStream.write(outData, 0, outData.length);

} else {

// 非关键帧和SPS、PPS,直接写入文件,可能是B帧或者P帧

mOutputStream.write(outData, 0, outData.length);

}

mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);

outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);

}

} catch (IOException e) {

Log.e(TAG, e.getMessage());

}

} else {

try {

Thread.sleep(500);

} catch (InterruptedException e) {

Log.e(TAG, e.getMessage());

}

}

}

首先,要把要把相机的 NV21 格式转换成 NV12 格式,然后 通过 dequeueInputBuffer 方法去从可用的输入缓冲区队列中出队取出缓冲区,填充完数据后再通过 queueInputBuffer方法入队。

dequeueInputBuffer 返回缓冲区索引,如果索引小于 0 ,则表示当前没有可用的缓冲区。它的参数 timeoutUs 表示超时时间 ,毕竟用的是 MediaCodec 的同步模式,如果没有可用缓冲区,就会阻塞指定参数时间,如果参数为负数,则会一直阻塞下去。

queueInputBuffer 方法将数据入队时,除了要传递出队时的索引值,然后还需要传入当前缓冲区的时间戳 presentationTimeUs 和当前缓冲区的一个标识 flag 。

其中,时间戳通常是缓冲区渲染的时间,而标识则有多种标识,标识当前缓冲区属于那种类型:

  • BUFFER_FLAG_CODEC_CONFIG

写在最后

本次我的分享也接近尾声了,感谢你们在百忙中花上一下午来这里聆听我的宣讲,希望在接下来的日子,我们共同成长,一起进步!!!

最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:

对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android架构师之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

之路很漫长,一起共勉吧!

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

[外链图片转存中…(img-1xexGlsU-1715407455258)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值