• 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链接如下(注意裸流文件要放置的路径):
    https://files.cnblogs.com/files/superping/H264Demo.zip

  • 相关阅读:
    iphone4 wifi超时设置修改
    siteminder sso agent 初探
    [读书]35前要掌握的66种基本能力序
    Secrets of Successful Project Management
    成功项目管理的秘密
    一位乞丐给我上的MBA课程
    [读书]35前要掌握的66种基本能力第2节
    简单,有规律
    网盘使用手记
    更好地领导一个项目的诀窍
  • 原文地址:https://www.cnblogs.com/superping/p/4884689.html
Copyright © 2020-2023  润新知