Android平台对H264视频硬解码

Android平台对H264视频硬解码

  本文讲述如何使用Android标准的API (MediaCodec)实现H264的硬件解码。

  原本我们是用JNI调用平台提供的硬件解码接口得到YUV帧,再放入opengl脚本里处理渲染的。可是换了新平台之后,没有拿到底层的接口,所以这两天找在Android上的H264解码方案。前天在友人的提示下找到了MediaCodec这个类,Android developer上面有MediaCodec的描述和用法,还算详细可以慢慢摸索。但是在网上关于这个类的用法是比较少。

  那在这里贴代码介绍一下。

复制代码
 1 // Video Constants
 2     private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video
 3     private final static int VIDEO_WIDTH = 1280;
 4     private final static int VIDEO_HEIGHT = 720;
 5     private final static int TIME_INTERNAL = 30;
 6 
 7     public void initDecoder() {
 8 
 9         mCodec = MediaCodec.createDecoderByType(MIME_TYPE);
10         MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,
11                 VIDEO_WIDTH, VIDEO_HEIGHT);
12         mCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(),
13                 null, 0);
14         mCodec.start();
15     }
复制代码

 

  这是初始化解码器操作,具体要设置解码类型,高度,宽度,还有一个用于显示视频的surface。

 

复制代码
 1     public boolean onFrame(byte[] buf, int offset, int length) {
 2         Log.e("Media", "onFrame start");
 3         Log.e("Media", "onFrame Thread:" + Thread.currentThread().getId());
 4         // Get input buffer index
 5         ByteBuffer[] inputBuffers = mCodec.getInputBuffers();
 6         int inputBufferIndex = mCodec.dequeueInputBuffer(100);
 7 
 8         Log.e("Media", "onFrame index:" + inputBufferIndex);
 9         if (inputBufferIndex >= 0) {
10             ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
11             inputBuffer.clear();
12             inputBuffer.put(buf, offset, length);
13             mCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount
14                     * TIME_INTERNAL, 0);
15             mCount++;
16         } else {
17             return false;
18         }
19 
20         // Get output buffer index
21         MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
22         int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 100);
23         while (outputBufferIndex >= 0) {
24             mCodec.releaseOutputBuffer(outputBufferIndex, true);
25             outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 0);
26         }
27         Log.e("Media", "onFrame end");
28         return true;
29     }
复制代码

具体流程:

  1.获取一个可用的inputBuffer的索引(出列)

  2.将一帧数据放入inputBuffer

  3.将inputBuffer入列进行解码

  4.获得一个outputBuffer的索引(出列)

  5.释放outputBuffer

  6.在4,5间循环直到没有outputBuffer可出列为止

  这里解码器有多个输入缓冲区(我测试是有3个),实现不卡顿。

常见的问题:

  dequeueInputBuffer和dequeueOutputBuffer经常会获取不了缓冲区(跟机器的性能有关),如果参数为-1,则会一直等待;如果参数为正数,则等待相应的微秒后返回,没有可用缓冲区就会返回-1。

结论:
  测试程序是读取一个H264裸流文件,识别每一帧然后将其放入解码器解码以及渲染。读取和识别的代码在Demo中。经测试,不同平台的解码效果还挺大。我播放的是720p的H264文件。最出色的居然是RK(瑞芯微)平台,缓冲区获取很少失败,但是会因为过热重启机器;高通平台试了几台机器(锤子、小米4、还有个别定制机),经常有拿不到缓冲区的情况。MTK的只有一台(TCL么么哒),直接初始化不了解码器。。。
  这里跟GPU性能有关,我们解的是720p8m码率的视频,换成较低码率较低分辨率的,大部分机器还是可以正常运行的(除了么么哒)。当然或许有些平台的实现是软解码(使用cpu,ffmpeg方案),这我暂时还没遇到。

Demo链接如下(注意裸流文件要放置的路径):
http://files.cnblogs.com/files/superping/H264Demo.zip
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android 上加载 H264 视频,可以使用 Android 提供的 MediaCodec API 和 SurfaceView 控件。 首先,需要创建一个 MediaExtractor 对象来读取 H264 视频文件。然后,通过 MediaFormat 获取 H264 视频的参数,例如视频的宽度、高度、码率等。接下来,创建一个 MediaCodec 对象,将 H264 视频的参数设置到 MediaCodec 中,并且将一个 SurfaceView 控件传递给 MediaCodec 作为输出。最后,将 H264 视频数据传递给 MediaCodec 进行解码解码后的视频数据将通过 SurfaceView 控件进行显示。 以下是一个简单的示例代码: ``` MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(videoFilePath); int videoTrackIndex = -1; for (int i = 0; i < extractor.getTrackCount(); i++) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (mime.startsWith("video/")) { videoTrackIndex = i; break; } } extractor.selectTrack(videoTrackIndex); MediaFormat format = extractor.getTrackFormat(videoTrackIndex); int width = format.getInteger(MediaFormat.KEY_WIDTH); int height = format.getInteger(MediaFormat.KEY_HEIGHT); int bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE); SurfaceView surfaceView = findViewById(R.id.surface_view); MediaCodec codec = MediaCodec.createDecoderByType("video/avc"); codec.configure(format, surfaceView.getHolder().getSurface(), null, 0); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); BufferInfo info = new BufferInfo(); boolean isEOS = false; while (!isEOS) { int inputBufferIndex = codec.dequeueInputBuffer(10000); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; int sampleSize = extractor.readSampleData(inputBuffer, 0); if (sampleSize < 0) { isEOS = true; codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); } else { codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0); extractor.advance(); } } int outputBufferIndex = codec.dequeueOutputBuffer(info, 10000); switch (outputBufferIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: outputBuffers = codec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: format = codec.getOutputFormat(); break; case MediaCodec.INFO_TRY_AGAIN_LATER: break; default: ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; codec.releaseOutputBuffer(outputBufferIndex, true); break; } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { isEOS = true; } } codec.stop(); codec.release(); extractor.release(); ``` 注意:上述代码仅为示例代码,实际使用时需要进行适当的修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值