实现音视频编解码工具 MediaCodec 创建解码器

前言

Android 开发中提供了实现音视频编解码工具 MediaCodec针对对应音视频解码类型通过该类创建对应解码器就能实现对数据进行解码操作

MediaCodec

MediaCodec 所支持的数据类型: 压缩的音视频数据、原始音频数据和原始视频数据;首先 show 代码,紧接着之前 MediaExtactor 提取资源, MediaCodecList 遍历支持格式,确认设置支持该资源格式后通过 MediaCodec 创建解码器(这里是做视频解码播放)

MediaCodec 编码过程

在整个编解码过程中, MediaCodec 的使用会经历配置、启动、数据处理、停止、释放几个过程,相应的状态可归纳为停止(Stopped),执行(Executing)以及释放(Released)三个状态,而Stopped状态又可细分为未初始化(Uninitialized)、配置(Configured)、异常( Error),Executing状态也可细分为读写数据(Flushed)、运行(Running)和流结束(End-of-Stream)

MediaCodec 整个状态结构图如下:

从上图可知,当 MediaCodec 被创建后会进入未初始化状态,待设置好配置信息并调用 start() 启动后, MediaCodec 会进入运行状态,并且可进行数据读写操作

如果在这个过程中出现了错误MediaCodec 会进入 Stopped 状态,我们就是要使用 reset 方法来重置编解码器,否则 MediaCodec 所持有的资源最终会被释放

当然,如果 MediaCodec 正常使用完毕,我们也可以向编解码器发送 EOS 指令,同时调用 stop 和 release 方法终止编解码器的使用

MediaCodec API 说明

MediaCodec可以处理具体的视频流,主要有这几个方法:

  • getInputBuffers:获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组
  • queueInputBuffer:输入流入队列
  • dequeueInputBuffer:从输入流队列中取数据进行编码操作
  • getOutputBuffers:获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组
  • dequeueOutputBuffer:从输出队列中取出编码操作之后的数据
  • releaseOutputBuffer:处理完成,释放ByteBuffer数据

基本使用

所有的同步模式的 MediaCodec API都遵循一个模式:

  • 创建并配置一个 MediaCodec 对象
  • 循环直到完成
  • 如果输入缓冲区就绪,读取一个输入块,并复制到输入缓冲区中
  • 如果输出缓冲区就绪,复制输出缓冲区的数据 释放 MediaCodec 对象

(1) 创建编/解码器

MediaCodec 主要提供了 createEncoderByType(String type)、createDecoderByType(String type) 两个方法来创建编解码器,它们均需要传入一个MIME类型多媒体格式

扫描二维码关注公众号,回复: 14508719 查看本文章

常见的MIME类型多媒体格式如下:

● “video/x-vnd.on2.vp8” - VP8 video (i.e. video in .webm)
● “video/x-vnd.on2.vp9” - VP9 video (i.e. video in .webm)
● “video/avc” - H.264/AVC video
● “video/mp4v-es” - MPEG4 video
● “video/3gpp” - H.263 video
● “audio/3gpp” - AMR narrowband audio
● “audio/amr-wb” - AMR wideband audio
● “audio/mpeg” - MPEG1/2 audio layer III
● “audio/mp4a-latm” - AAC audio (note, this is raw AAC packets, not packaged in LATM!)
● “audio/vorbis” - vorbis audio
● “audio/g711-alaw” - G.711 alaw audio
● “audio/g711-mlaw” - G.711 ulaw audio

当然,MediaCodec 还提供了一个 createByCodecName (String name) 方法,支持使用组件的具体名称来创建编解码器;但是该方法使用起来有些麻烦,且官方是建议最好是配合 MediaCodecList 使用,因为 MediaCodecList 记录了所有可用的编解码器

当然,我们也可以使用该类对传入的 minmeType 参数进行判断,以匹配出 MediaCodec 对该mineType 类型的编解码器是否支持

以指定MIME类型为 “video/avc” 为例,代码如下:

private static MediaCodecInfo selectCodec(String mimeType) {
    
    
     // 获取所有支持编解码器数量
     int numCodecs = MediaCodecList.getCodecCount();
     for (int i = 0; i < numCodecs; i++) {
    
    
        // 编解码器相关性信息存储在MediaCodecInfo中
         MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
         // 判断是否为编码器
         if (!codecInfo.isEncoder()) {
    
    
             continue;
         }
        // 获取编码器支持的MIME类型,并进行匹配
         String[] types = codecInfo.getSupportedTypes();
         for (int j = 0; j < types.length; j++) {
    
    
             if (types[j].equalsIgnoreCase(mimeType)) {
    
    
                 return codecInfo;
             }
         }
     }
     return null;
 }
123456789101112131415161718192021

(2) 配置、启动编/解码器

编解码器配置使用的是 MediaCodec 的 configure 方法,该方法首先对 MediaFormat 存储的数据 map 进行提取,然后调用本地方法 native-configure 实现对编解码器的配置工作

在配置时,configure 方法需要传入format、surface、crypto、flags 参数,其中 format 为 MediaFormat 的实例,它使用 ”key-value” 键值对的形式存储多媒体数据格式信息; surface 用于指明解码器的数据源来自于该 surface

crypto 用于指定一个 MediaCrypto 对象,以便对媒体数据进行安全解密;flags指明配置的是编码器(CONFIGURE_FLAG_ENCODE)

MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480);     // 创建MediaFormat
mFormat.setInteger(MediaFormat.KEY_BIT_RATE,600);       // 指定比特率
mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30);  // 指定帧率
mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat);  // 指定编码器颜色格式  
mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定关键帧时间间隔
mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE); 
1234567

从上面代码可知,当编解码器 start 后,会进入一个 for(;;)循环,该循环是一个死循环,以实现不断地去从编解码器的输入缓存池中获取包含数据的一个缓存区,然后再从输出缓存池中获取编解码好的输出数据

  • 在一个 Java 应用程序的执行过程中,只要在同一个对象上多次调用它 hashCode 方法必须始终返回相同的整数,前提是在equals对象上的比较被修改;这个整数不需要从一个应用的一次执行到同一应用的另一次执行保持一致

  • 如果两个对象根据 equals(Object) 方法,然后调用 hashCode 方法必须产生相同的整数结果

  • 这是要求如果两个对象根据 equals(java.lang.Object) 方法,然后调用 hashCode 方法必须产生不同的整数结果;但是,程序员应该知道,为不相等的对象生成不同的整数结果可能会提高性能

尽可能实际地,由类定义的 hashCode 方法 Object 为不同的对象返回不同的整数(这通常是通过将对象的内部地址转换成整数来实现的,但是Java编程语言并不要求这种实现技术)

本文 全面介绍了 LiveData 的生命周期是如何变化的 希望大家在开发时尽量避免出现 因生命周期变化而出现的问题

为了是大家能够更好的学习 音视频开发, 在这里特别提供一份 高级音视频开发学习笔记;里面包含了这些年学习 音视频开发所遇到的难题及其解决方案; 有需要这份 高级音视频开发学习笔记 的朋友: 可以私信 发送 “笔记” 即可 免费获取; 希望大家阅读过后,能够 查漏补缺;早日成为高级音视频开发者

内容展示如下:

高级音视频开发学习笔记目录

H264 编码基础 06 – 编码标准之战

  • 一个视频标准的普及其实是商业利益博弈的结果。
  • 一个视频标准能普及还在于他的对手
  • WMV
  • VP8
  • AV1

H264 编码基础 01 帧与场

  • 视频序列帧、场编码方式
  • H264 编码(帧内预测)
  • 视频流 H264 的组装
  • 三种帧的说明
  • 压缩算法的说明
  • 手写 H264 编码器

有需要完整版高级音视频开发学习笔记的朋友: 可以 私信 发送 “笔记” 即可 免费获取

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

加油!让我们早日成为音视频高级开发者

猜你喜欢

转载自blog.csdn.net/m0_70748845/article/details/126607966