您的位置:首页 > 移动开发 > Android开发

Android Mediacodec硬解H264并显示

2016-04-14 09:41 567 查看
4000

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,frameHeight

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 解码线程。

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)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android