Android 用MediaCodec实现视频硬解码
http://www.360doc.com/content/14/0119/10/8122810_346350456.shtml
本文向你讲述如何用Android标准的API (MediaCodec)实现视频的硬件编解码。例程将从摄像头采集视频开始,然后进行H264编码,再解码,然后显示。我将尽量讲得简短而清晰,不展示那些不相关的代码。但是,我不建议你读这篇文章,也不建议你开发这类应用,而应该转而开发一些戳鱼、打鸟、其乐融融的程序。好吧,下面的内容是写给那些执迷不悟的人的,看完之后也许你会同意我的说法:Android只是一个玩具,很难指望它来做靠谱的应用。
1、从摄像头采集视频
可以通过摄像头Preview的回调,来获取视频数据。
首先创建摄像头,并设置参数:
cam = Camera.open();
cam.setPreviewDisplay(holder);
Camera.Parameters parameters = cam.getParameters();
parameters.setFlashMode("off"); // 无闪光灯
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
parameters.setPreviewFormat(ImageFormat.YV12);
parameters.setPictureSize(camWidth, camHeight);
parameters.setPreviewSize(camWidth, camHeight);
//这两个属性 如果这两个属性设置的和真实手机的不一样时,就会报错
cam.setParameters(parameters);
宽度和高度必须是摄像头支持的尺寸,否则会报错。要获得所有支持的尺寸,可用getSupportedPreviewSizes,这里不再累述。据说所有的参数必须设全,漏掉一个就可能报错,不过只是据说,我只设了几个属性也没出错。 然后就开始Preview了:
buf = new byte[camWidth * camHeight * / ];
cam.addCallbackBuffer(buf);
cam.setPreviewCallbackWithBuffer(this);
cam.startPreview();
setPreviewCallbackWithBuffer是很有必要的,不然每次回调系统都重新分配缓冲区,效率会很低。
在onPreviewFrame中就可以获得原始的图片了(当然,this 肯定要 implements PreviewCallback了)。这里我们是把它传给编码器:
public void onPreviewFrame(byte[] data, Camera camera) {
if (frameListener != null) {
frameListener.onFrame(data, , data.length, );
}
cam.addCallbackBuffer(buf);
}
2、编码
首先要初始化编码器:
mediaCodec = MediaCodec.createEncoderByType("Video/AVC");
MediaFormat mediaFormat = MediaFormat.createVideoFormat(type, width, height);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, );
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, );
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, );
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
然后就是给他喂数据了,这里的数据是来自摄像头的:
public void onFrame(byte[] buf, int offset, int length, int flag) {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-);
if (inputBufferIndex >= )
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buf, offset, length);
mediaCodec.queueInputBuffer(inputBufferIndex, , length, , );
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,);
while (outputBufferIndex >= ) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
if (frameListener != null)
frameListener.onFrame(outputBuffer, , length, flag);
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, );
}
先把来自摄像头的数据喂给它,然后从它里面取压缩好的数据喂给解码器。
3、解码和显示
首先初始化解码器:
mediaCodec = MediaCodec.createDecoderByType("Video/AVC");
MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height);
mediaCodec.configure(mediaFormat, surface, null, );
mediaCodec.start();
这里通过给解码器一个surface,解码器就能直接显示画面。
然后就是处理数据了:
public void onFrame(byte[] buf, int offset, int length, int flag) {
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buf, offset, length);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount * 1000000 / FRAME_RATE, 0);
mCount++;
} MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
while (outputBufferIndex >= 0) {
mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
}
queueInputBuffer第三个参数是时间戳,其实怎么写都无所谓,只要是按时间线性增加的就可以,这里就随便弄一个了。后面一段的代码就是把缓冲区给释放掉,因为我们直接让解码器显示,就不需要解码出来的数据了,但是必须要这么释放一下,否则解码器始终给你留着,内存就该不够用了。
好了,到现在,基本上就可以了。如果你运气够好,现在就能看到视频了,比如在我的三星手机上这样就可以了。但是,我试过几个其他平台,多数都不可以,总是有各种各样的问题,如果要开发一个不依赖平台的应用,还有很多的问题要解决。说说我遇到的一些情况:
1、视频尺寸
一般都能支持176X144/352X288这种尺寸,但是大一些的,640X480就有很多机子不行了,至于为什么,我也不知道。当然,这个尺寸必须和摄像头预览的尺寸一致,预览的尺寸可以枚举一下。
2、颜色空间
根据ANdroid SDK文档,确保所有硬件平台都支持的颜色,在摄像头预览输出是YUV12,在编码器输入是COLOR_FormatYUV420Planar,也就是前面代码中设置的那样。 不过,文档终究是文档,否则安卓就不是安卓。
在有的平台上,这两个颜色格式是一样的,摄像头的输出可以直接作为编码器的输入。也有的平台,两个是不一样的,前者就是YUV12,后者等于I420,需要把前者的UV分量颠倒一下。下面的代码效率不高,可供参考。
byte[] i420bytes = null;
private byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
if (i420bytes == null)
i420bytes = new byte[yv12bytes.length];
for (int i = 0; i < width*height; i++)
i420bytes[i] = yv12bytes[i];
for (int i = width*height; i < width*height + (width/2*height/2); i++)
i420bytes[i] = yv12bytes[i + (width/2*height/2)];
for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)
i420bytes[i] = yv12bytes[i - (width/2*height/2)];
return i420bytes;
}
怎样去判断是否需要这个转换。据说,Android 4.3不用再从摄像头的PreView里面取图像,避开了这个问题。这里有个例子http://bigflake.com/mediacodec/CameraToMpegTest.java.txt
3、输入输出缓冲区的格式
SDK里并没有规定格式,但是,这种情况H264的格式基本上就是附录B。但是,也有比较有特色的,它就是不带那个StartCode,就是那个0x000001,搞得把他编码器编出来的东西送给他的解码器,他自己都解不出来。还好,我们可以自己加。
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size + ];
outputBuffer.get(outData, , bufferInfo.size);
if (frameListener != null) {
if ((outData[]== && outData[]== && outData[]==)
|| (outData[]== && outData[]== && outData[]== && outData[]==))
{
frameListener.onFrame(outData, , outData.length-, bufferInfo.flags);
}
else
{
outData[] = ;
outData[] = ;
outData[] = ;
frameListener.onFrame(outData, , outData.length, bufferInfo.flags);
}
}
4、有时候会死在dequeueInputBuffer(-1)上面
根据SDK文档,dequeueInputBuffer 的参数表示等待的时间(毫秒),-1表示一直等,0表示不等。按常理传-1就行,但实际上在很多机子上会挂掉,没办法,还是传0吧,丢帧总比挂掉好。当然也可以传一个具体的毫秒数,不过没什么大意思吧。
Android 用MediaCodec实现视频硬解码的更多相关文章
- Android 用MediaCodec实现视频硬解码(转)
本文向你讲述如何用android标准的API (MediaCodec)实现视频的硬件编解码.例程将从摄像头采集视频开始,然后进行H264编码,再解码,然后显示.我将尽量讲得简短而清晰,不展示 那些不相 ...
- Android平台对H264视频硬解码
本文讲述如何使用Android标准的API (MediaCodec)实现H264的硬件解码. 原本我们是用JNI调用平台提供的硬件解码接口得到YUV帧,再放入opengl脚本里处理渲染的.可是换了新平 ...
- MediaCodec在Android视频硬解码组件的应用
https://yq.aliyun.com/articles/632892 云栖社区> 博客列表> 正文 MediaCodec在Android视频硬解码组件的应用 cheenc 201 ...
- 【Android 直播软件开发:音视频硬解码篇】
开篇 炙手可热,望而生畏的音视频开发 时至今日,短视频App可谓是如日中天,一片兴兴向荣.随着短视频的兴起,音视频开发也越来越受到重视,但是由于音视频开发涉及知识面比较广,入门门槛相对较高,让许许多多 ...
- WPF 视频硬解码渲染播放(无空域)(支持4K、8K、高帧率视频)
MediaWPF 基于 .NET 6 实现视频硬解码渲染Demo(无空域问题) 代码实现仅供学习参考 本项目视频渲染通过显卡进行视频解码,CPU几乎不参与工作,并且不存在令人烦躁的空域问题. 在播放摄 ...
- Android平台MediaCodec避坑指北
https://www.jianshu.com/p/5d62a3cf0741 最近使用MediaCodec做编解码H264,写一点东西以免自己再次掉坑. 先说一下具体环境,使用的是,Windows10 ...
- vlc源码分析(六) 调用OpenMAX硬解码H.265
H.265(HEVC)编码格式能够在得到相同编码质量视频的前提下,使用相当于H.264(AVC)一半的存储容量,虽然H.265的算法复杂度比H.264高一个数量级,但是硬件水平在不断提高,因此H.26 ...
- 【GPU编解码】GPU硬解码---DXVA
前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...
- 【GPU编解码】GPU硬解码---DXVA (转)
前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...
随机推荐
- IOS 命令行安装备忘
1. 首先要越狱 2. 新增BIGBOSS或者苹果核的源 3. 安装MobileTerminal和MobileTerm Backgrounder (用于后台运行命令),最好r520以上版本,R530还 ...
- .NET序列化的一点技巧
介绍 序列化是将对象状态转换为可保持或传输的形式的过程.序列化的补集是反序列化,后者将流转换为对象.这两个过程一起保证数据易于存储和传输. .NET Framework 提供了两个序列化技术: 二进制 ...
- MySQL zip版安装配置
文章出处:http://www.cnblogs.com/winstic/,请保留此连接 这段时间在学习Python 数据库操作知识,简单整理MySQL zip文件安装方法 下载 在MySQL官网htt ...
- DbUtils组件
DbUtils组件 DbUtils组件, 1. 简化jdbc操作 2. 下载组件,引入jar文件 : commons-dbutils-1.6.jar |-- DbUtils 关闭资源.加载驱动 |-- ...
- MySQL查询优化:连接查询排序limit
MySQL查询优化:连接查询排序limit(join.order by.limit语句) 2013-02-27 个评论 收藏 我要投稿 MySQL查询优化:连接查询排序 ...
- Egret HTTP网络
HTTP 请求与响应: private createGameScene():void { //HTTP 1.0 var request = new egret.HttpRequest(); reque ...
- Ruby自学笔记(六)— 循环
循环结构在编程语言中是不可或缺的,所以Ruby中的循环也有其自定义的规则. 而我们关注循环结构,要知道两个因素:1) 循环的条件:2) 循环执行的内容 Ruby有一些方式来实现循环结构体: 1. ti ...
- OSSchedLock()函数透析
uC/OS-II的OSSchedLock()和OSSchedUnlock()函数允许应用程序锁定当前任务不被其它任务抢占. 使用时应当注意的是:当你调用了OSSchedLock()之后,而在调用OSS ...
- 转:2014年最酷的30个JavaScript库
原文来自于:http://www.gbtags.com/gb/share/3701.htm 使用JavaScript库将会使开发变的更简单,大部分JavaScript库提供的功能都是极好的,当我们在为 ...
- 【Linux】 诊断工具-strace
1,别人家的总结: http://www.cnblogs.com/bangerlee/archive/2012/02/20/2356818.html 2,我自己碰到问题: 暂无. 3,使用场景: 程序 ...