Android Mediacodec硬解H264并显示
2016-04-14 09:41
567 查看
4000
本文以rtsp流为例,指导如何使用该类进行H264的硬解码,并最终显示图片到指定的SurfaceView 上。
当Surface被创建成功后才能进行配置Mediacodec 解码器的动作
2.如果送来的流的第一帧Frame有pps和sps,可以不需要配置format.setByteBuffer的”csd-0” (sps) 和”csd-1”(pps);
否则必须配置相应的pps和sps,通常情况下sps和pps如下:
byte[] sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] pps = { 0, 0, 0, 1, 104, -18, 60, -128 };。
3.获取SurfaceView 的holder,SurfaceHolder surfaceHolder = surfaceView.getHolder();
绑定显示的surfaceView并配置format。
4.配置完成后,启动Mediacodec 解码线程。
其中myFrame = MyService.getNextVideoFrame(); 表示从rtsp的服务器取得一帧数据,再解析出其中的pts,size喂给解码器。
解码器decode的结果:int outIndex = decoder.dequeueOutputBuffer(info, outtime);
根据outIndex的值可以判断当前数据是否可以显示到surfaceView。
2.dequeueAndRenderOutputBuffer 中进入default分支后:通过decoder.releaseOutputBuffer(outIndex, true),来显示到绑定的surfaceView上(注意第二个参数如果改为false,这表示释放数据,但是不显示到surfaceView)。
Android Mediacodec硬解H264并显示
从API 16(Android 4.1)开始,Android提供了Mediacodec类以便开发者更加灵活的处理音视频的编解码。本文以rtsp流为例,指导如何使用该类进行H264的硬解码,并最终显示图片到指定的SurfaceView 上。
一、准备工作
SurfaceView:作为Mediacodec的图像显示的绑定对象,需要事先布局:<SurfaceView android:id="@+id/surface_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_centerInParent="true"/>
当Surface被创建成功后才能进行配置Mediacodec 解码器的动作
二、配置Mediacodec 解码器并启动解码器
1.获取rtsp流Frame的宽高:frameWidth,frameHeight2.如果送来的流的第一帧Frame有pps和sps,可以不需要配置format.setByteBuffer的”csd-0” (sps) 和”csd-1”(pps);
否则必须配置相应的pps和sps,通常情况下sps和pps如下:
byte[] sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] pps = { 0, 0, 0, 1, 104, -18, 60, -128 };。
3.获取SurfaceView 的holder,SurfaceHolder surfaceHolder = surfaceView.getHolder();
绑定显示的surfaceView并配置format。
4.配置完成后,启动Mediacodec 解码线程。
MediaFormat format = MediaFormat.createVideoFormat("video/avc", frameWidth, frameHeight); format.setByteBuffer("csd-0" , ByteBuffer.wrap(sps))); format.setByteBuffer("csd-1", ByteBuffer.wrap(pps))); /* create & config android.media.MediaCodec */ MediaCodec decoder; try { decoder = MediaCodec.createDecoderByType(ret); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } decoder.configure(format, surfaceHolder.getSurface(), null, 0);//blind surfaceView decoder.start(); //start decode thread
三、Mediacodec 解码过程
新建立一个线程,专门用于解码。其中myFrame = MyService.getNextVideoFrame(); 表示从rtsp的服务器取得一帧数据,再解析出其中的pts,size喂给解码器。
解码器decode的结果:int outIndex = decoder.dequeueOutputBuffer(info, outtime);
根据outIndex的值可以判断当前数据是否可以显示到surfaceView。
private class VideoThread extends Thread { private boolean done = false; private MediaCodec.BufferInfo info; VideoThread() { super(); done = false; } @Override public void run() { ByteBuffer[] inputBuffers = decoder.getInputBuffers(); info = new MediaCodec.BufferInfo(); int inIndex = -1; int sampleSize = 0; long pts = 0; MyFrame myFrame; while (!done) {//init done = false; //get Frame data from service,you should customize this line... myFrame = MyService.getNextVideoFrame(); if(myFrame == null){ continue; } inIndex = decoder.dequeueInputBuffer(0); if (inIndex >= 0) { sampleSize = myFrame.getFrameSize(); pts = (long) myFrame.getPresentationTime(); ByteBuffer buffer = inputBuffers[inIndex]; buffer.clear(); buffer.rewind(); buffer.put(myFrame.getBuffer(), 0, sampleSize); decoder.queueInputBuffer(inIndex, 0, sampleSize, pts, 0); dequeueAndRenderOutputBuffer(0); } else { if (!dequeueAndRenderOutputBuffer(timeout) && !dequeueAndRenderOutputBuffer(30 * 1000)) { } } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } } } decoder.stop(); decoder.release(); } public boolean dequeueAndRenderOutputBuffer(int outtime) { int outIndex = decoder.dequeueOutputBuffer(info, outtime); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: return false; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: return false; case MediaCodec.INFO_TRY_AGAIN_LATER: return false; case MediaCodec.BUFFER_FLAG_SYNC_FRAME: return false; default: decoder.releaseOutputBuffer(outIndex, true);//show image right now return true; } }
四、其他
1.Mediacode 的h264视频解码不会收到一帧解出一帧,可能会缓冲几帧后才会显示一帧。2.dequeueAndRenderOutputBuffer 中进入default分支后:通过decoder.releaseOutputBuffer(outIndex, true),来显示到绑定的surfaceView上(注意第二个参数如果改为false,这表示释放数据,但是不显示到surfaceView)。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories