Android音视频处理之MediaCodec

2023-05-16

MediaCodec是Android中媒体编解码器,可以对媒体进行编/解码

MediaCodec采用同步/异步方式处理数据,并且使用了一组输入输出缓存(ByteBuffer)。通过请求一个空的输入缓存(ByteBuffer),向其中填充满数据并将它传递给编解码器处理。编解码器处理完这些数据并将处理结果输出至一个空的输出缓存(ByteBuffer)中。使用完输出缓存的数据之后,将其释放回编解码器

 

MediaCodec的生命周期有三种状态:停止态-Stopped、执行态-Executing、释放态-Released

停止状态(Stopped)包括了三种子状态:未初始化(Uninitialized)、配置(Configured)、错误(Error)。

执行状态(Executing)会经历三种子状态:刷新(Flushed)、运行(Running)、流结束(End-of-Stream)

(1)当创建了一个MediaCodec对象,此时MediaCodec处于Uninitialized状态。

MediaCodec主要提供了createEncoderByType(String type)、createDecoderByType(String type)两个方法来创建编解码器,它们均需要传入一个MIME类型多媒体格式。常见的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 
String AUDIO_MIME = "audio/mp4a-latm";
MediaCodec mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME);

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;
 }

(2)创建完MediaCodec之后,需要使用configure(…)方法对MediaCodec进行配置,这时MediaCodec转为Configured状态。 

编解码器配置使用的是MediaCodec的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);

(3)配置完MediaCodec之后,调用MediaCodec的start()方法使其转入Executing状态。

在调用start()方法后MediaCodec立即进入Flushed子状态,此时MediaCodec会拥有所有的缓存。一旦第一个输入缓存(input buffer)被移出队列,MediaCodec就转入Running子状态,这种状态占据了MediaCodec的大部分生命周期。当你将一个带有end-of-stream marker标记的输入缓存入队列时,MediaCodec将转入End-of-Stream子状态。在这种状态下,MediaCodec不再接收之后的输入缓存,但它仍然产生输出缓存直到end-of- stream标记输出。你可以在Executing状态的任何时候通过调用flush()方法返回到Flushed子状态。

下面看看整个过程:

调用MediaCodec的start()方法,此时MediaCodec处于Executing状态,可以通过getInputBuffers()方法和getOutputBuffers()方法获取缓存队列:

mAudioEncoder.start();
mAudioInputBuffers = mAudioEncoder.getInputBuffers();
mAudioOutputBuffers = mAudioEncoder.getOutputBuffers();

当MediaCodec处于Executing状态之后就可以对数据进行处理了。

  • 首先通过dequeueInputBuffer(long timeoutUs)请求一个输入缓存,timeoutUs代表等待时间,设置为-1代表无限等待:
int inputBufIndex = mAudioEncoder.dequeueInputBuffer(1000);

返回的整型变量为请求到的输入缓存的index,通过getInputBuffers()得到的是输入缓存数组,通过index和输入缓存数组可以得到当前请求的输入缓存,在使用之前要clear一下,避免之前的缓存数据影响当前数据

mInputBuffer = mAudioInputBuffers[inputBufIndex];
mInputBuffer.clear();

也可以直接通过mAudioEncoder.getInputBuffer(inputBufIndex)得到输入缓存

  • 接着就是把数据添加到输入缓存中,并调用queueInputBuffer(...)把缓存数据入队
mInputBuffer.put(bytes, 0, BUFFER_SIZE_IN_BYTES);
mAudioEncoder.queueInputBuffer(inputBufIndex, 0, BUFFER_SIZE_IN_BYTES, (1000000 * mEncodedSize / AUDIO_BYTE_PER_SAMPLE), 0);

获取输出缓存和获取输入缓存类似

  • 首先通过dequeueOutputBuffer(BufferInfo info, long timeoutUs)来请求一个输出缓存,这里需要传入一个BufferInfo对象,用于存储ByteBuffer的信息
int outputBufIndex = mAudioEncoder.dequeueOutputBuffer(mOutBufferInfo, 1000);
public final static class BufferInfo {
    public void set(
            int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) {
        offset = newOffset;
        size = newSize;
        presentationTimeUs = newTimeUs;
        flags = newFlags;
    }
    public int offset // 偏移量
    public int size;    // 缓存区有效数据大小
    public long presentationTimeUs; // 显示时间戳
    public int flags;                   // 缓存区标志

    @NonNull
    public BufferInfo dup() {
        BufferInfo copy = new BufferInfo();
        copy.set(offset, size, presentationTimeUs, flags);
        return copy;
    }
};

然后通过返回的index得到输出缓存,并通过BufferInfo获取ByteBuffer的信息,我们可以得到当前数据是否Codec-specific Data:

mOutBuffer = mAudioOutputBuffers[outputBufIndex];
if ((mOutBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
    // Codec-specific Data, 这里可以从ByteBuffer中获取csd参数
    // audioFormat.setByteBuffer("csd-0", mOutBuffer);
} else {
    // 处理数据
}
mAudioEncoder.releaseOutputBuffer(outputBufIndex, false);

也可以直接通过mAudioEncoder.getOutputBuffer(outputBufIndex)得到输出缓存

注意一定要调用releaseOutputBuffer方法。

通过调用stop()方法使MediaCodec返回到Uninitialized状态,因此这个MediaCodec可以再次重新配置 。

当使用完MediaCodec后,必须调用release()方法释放其资源。

在极少情况下MediaCodec会遇到错误并进入Error状态。这个错误可能是在队列操作时返回一个错误的值或者有时候产生了一个异常导致的。通过调用 reset()方法使MediaCodec再次可用。你可以在任何状态调用reset()方法使MediaCodec返回到Uninitialized状态。否则,调用 release()方法进入最终的Released状态。

 

 

 

 

 

 


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android音视频处理之MediaCodec 的相关文章

  • PhoneGap/Cordova 应用程序通知

    我是 PhoneGap Cordova 的新手 我希望向我的应用程序添加一些通知 推送通知 因此当应用程序上发布新文章时 它会提醒用户 本地通知 在设定的时间间隔 日期和时间 我可以提示用户我的应用程序上的最新文章 我进行了大量搜索 但找不
  • Android-如何将 android.net.Uri 对象转换为 java.net.URI 对象?

    我正在尝试获得一个FileInputStream用户从图片库中选择的图像上的对象 这是安卓URI由返回android provider MediaStore Images Media INTERNAL CONTENT URI content
  • Android主线程的IO操作

    我的问题有两个 是否建议在 Android 的主线程上进行 IO 操作 或者它是否有可能导致我的应用程序崩溃 如果在主线程上执行 IO 操作不理想 我可以使用哪些其他框架 以便当我的应用程序加载时它可以执行一些基本的 IO 文件读取并将值存
  • 如何替换 Android 中已弃用的 Bundle/Argument get(key) 调用

    我有以下扩展函数 允许我在应用程序活动和片段之间传递捆绑数据项 inline fun
  • 如何使用 gradle 从 3 个子模块构建 1 个 jar

    I have 安卓工作室3 gradle 4 1 梯度工具3 classpath com android tools build gradle 3 0 1 当我有一个模块并使用 gradle 工具 2 时 我使用了 task makeJar
  • 如何在应用程序关闭时在 Android 通知中显示操作按钮?

    我有一个安卓应用程序 对于通知 我们必须显示一些操作按钮 当应用程序打开时 我们可以自由地构建通知并显示操作按钮 但是当应用程序关闭时 通知会在 Android 的通知托盘中收到 应用程序开发人员无法控制构建用户界面和操作按钮 我们现在如何
  • 当满足条件时,如何以编程方式更改 ImageButton src 目标?

    我有一个学校项目 我正在尝试开发一个手电筒应用程序 对于开 关 ImageButton 我想要 4 个自定义图像 如果手电筒关闭 turn on png 默认 turn on pressing png 按下状态 true 如果手电筒打开 t
  • 吉夫伦致命信号11

    我正在尝试使用一些本机代码来创建 Gif 我使用绘画绘制图像 创建一些笔画 单击 保存 绘制的图像将保存为 JPG 格式 当我单击 创建 Gif 时 它会获取所有图像并开始创建 gif 这是当我收到致命信号 11 并且应用程序重新启动时 我
  • Fragment 问题中的 ExpandableListView

    我正在尝试在片段中实现可扩展列表视图 没有错误出现 当我尝试记录两个的输出时List
  • 需要 Android webview window.open() 和 window.close() 的信息

    我正在开发一个安卓应用程序 这是我网站的 WebView 该网站包含一个弹出按钮 单击该按钮后 将打开一个新窗口并显示内容 该链接可以来自外部站点 然而 当我实现此操作时 新选项卡正在打开 之后它会弹出以打开浏览器 尽管在 Web 视图中打
  • Android上如何获取ImageView的Drawable的矩形?

    我想要获取将包裹 ImageView 的 Drawable 的矩形对象 而不是包裹 ImageView 的矩形 我将使用该矩形在 Drawable 周围绘制一些奇特的矩形 我怎样才能得到那个矩形 Rect rect new Rect Ima
  • React Native Expo StackNavigator 重叠通知栏

    我正在尝试为我的 React Native Expo 应用程序实现导航栏 这里有一个问题 dependencies expo 18 0 3 react 16 0 0 alpha 12 react native 0 45 1 react na
  • 选项卡主机内的 Android Fragment 视图状态 [重复]

    这个问题在这里已经有答案了 可能的重复 使用 Fragment 为 Android 中的每个选项卡单独的返回堆栈 https stackoverflow com questions 6987334 separate back stack f
  • MIUI 权限被拒绝活动 KeyguardLocked

    当应用程序处于后台且屏幕被锁定时 我无法启动活动 没有异常或警告 只是不调用 onCreate 我一直在与这个问题作斗争 我想我终于找到了它的根源 日志中有一行 D com android server am ExtraActivityMa
  • 是否可以通过 Android 应用程序来录音?

    我是一名开发人员 希望创建一个 Android 应用程序来记录电话 这是出于我个人的需要 为了我自己的目的和记录而记录电话 是否有可能做到这一点 是否可以访问麦克风以及通过扬声器发出的声音 我对 Android 开发有点陌生 所以请耐心等待
  • 如何将 Android 添加到 Phonegap 平台版本 3

    经过大量挖掘 我相信这个问题 https stackoverflow com questions 18423444 phonegap 3 doesnt work with andriod studio与我没有添加任何用于构建phonegap
  • 如何为我的 Android Market APK 创建证书?

    我想将我的第一个 APK 应用程序上传到 Android Market 但我收到了此错误 顺便说一下 在 stackoverflow 中搜索时并没有引导我找到正确的链接 市场不接受使用调试证书签名的 APK 创建有效期至少 50 年的新证书
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • 如何在android中通过蓝牙向配对设备发送短信?

    在我的应用程序中 我想通过蓝牙发送和接收短信 我可以在列表视图中看到配对设备名称和地址的列表 但是当我尝试向配对设备发送文本时 什么也没有发生 在其他设备中没有收到文本 这是我向配对设备发送消息的代码 private void sendDa
  • Android MulticastSocket.joinGroup 不会触发发送 IGMP 消息

    Code MulticastSocket s new MulticastSocket InetAddress addr InetAddress getByName 230 230 230 1 s joinGroup addr 在 Ubunt

随机推荐

  • 【嵌入式系统】二、初识 Tiva TM4C123G系列开发板

    大二电赛小白 思考 主要偏向于嵌入式的应用 xff0c 请大家多多指教 xff01 TM4C123x系列是TI公司推出的一款32位基于ARM Cortex M4的处理器 1 TM4C123GH6PM的M4内核 超低功耗 耗电量370 A M
  • fdisk用法

    NAME fdisk Partition table manipulator for Linux SYNOPSIS fdisk u b sectorsize C cyls H heads S sects device fdisk l u d
  • 用策略模式优化代码的实例

    实例一 xff1a 利用利用策略模式实际开发中 if else 条件判断过多的问题 xff0c 条件少还好 xff0c 一旦 else if 过多这里的逻辑将会比较混乱 xff0c 并很容易出错 比如 xff1a 刚开始条件较少 xff0c
  • 灰度处理与二值化的关系

    当开始接触图像处理的童鞋可能跟我一样对这两个概念迷惑过 xff0c 在图像处理中 xff0c 用RGB三个分量 xff08 R xff1a Red xff0c G xff1a Green xff0c B xff1a Blue xff09 x
  • ucos2历程——信号量集

    信号量集 信号量集由两部分组成 xff1a 标识组和等待任务列表 xff1b 标识组由三部分组成 xff1a 1 OSFlagType 识别是否为信号量集的标志 2 OSFlagWaitList 指向等待任务列表的指针 3 OSFlagFl
  • 人体姿态估计资源大列表(Human Pose Estimation)

    基础 xff1a Human Pose Estimation人体姿态估计综述调研人体姿态估计数据集整理 xff08 Pose Estimation Keypoint xff09 姿态估计的两个数据集COCO和MPII的认识 Human Po
  • DIY小四轴之电路设计(一)

    DIY小四轴之电路设计 xff08 一 xff09 写在前面 前一阵时间一直在做四轴飞行器 xff0c 略有一点收获吧 xff0c 在这里分享出来 xff0c 一方面算是对自己的总结 xff0c 另一方面希望能给想做小四轴的读者一些思路 本
  • DIY小四轴之电路设计(二)

    DIY小四轴之电路设计 xff08 二 xff09 上次我分析了四轴电源的电路 xff0c 这次我们来看电机驱动与传感器电路 三 空心杯电机驱动电路 一般的小型四轴都选用空心杯电机来驱动旋翼 xff0c 空心杯电机不仅节能而且灵敏 xff0
  • ubuntu 18.04 vnc server开机自启动

    转自 xff1a https blog csdn net lixiaotao 1 article details 90140979 1 首先确定vncserver 以正确安装到linux系统 xff1b 2 设置vncserver随系统自启
  • vnc viewer灰屏的解决方法

    vnc能够连接上 xff0c 但是进入界面灰屏 先关闭当前打开的vnc xff1a vncserver kill 88 然后修改权限 xff1a chmod 43 x vnc xstartup 然后重新打开vnc vncserver geo
  • samba 常用命令

    没怎么修改配置 xff0c 但有时需要修改时 xff0c 又是搜索一番 xff0c 故将常用的在此备份一下 修改samba配置 xff1a span class token function sudo span span class tok
  • .rst 语法+简明教程

    reStructuredText 是扩展名为 rst的纯文本文件 xff0c 含义为 34 重新构建的文本 34 xff0c 也被简称为 xff1a RST或reST xff1b 是Python编程语言的Docutils项目的一部分 xff
  • TG_7100b准备开发环境

    请在 64 位 Ubuntu 下搭建开发环境 Win10 系统可以在应用商店下载安装 Ubuntu18 04 LTS 其他用户可以安装虚拟机软件 以下为基于 Ubuntu 环境开发和编译 SDK 时需要用到的库和依赖包 xff0c 请您按顺
  • C++ STL中各容器内存、优劣的分析

    STL有三大核心部分 xff1a 容器 xff08 Container xff09 算法 xff08 Algorithms xff09 迭代器 xff08 Iterator xff09 以下介绍容器相关内容 xff1a 各种容器的元素在内存
  • 给自己时间沉淀下来

    像很多学长学姐当初一样 xff0c 我也到了繁忙的大四 这个尴尬的时间 xff0c 要选择 xff0c 要放弃 开始实习 xff0c 去窥探一下外面的世界 经过一个月的测试工作 xff0c 开始发现自己与别人的差距还是很大 再继续试水 xf
  • docker安装使用系列二之容器、镜像、仓库使用实例分析

    可能大家对docker了解不深 xff0c 这里再简单赘述一下docker这款利器 1 什么是docker Doker是基于GO语言实现的云开源项目 xff0c 通过对应用组件的封装 分发 部署 运行等生命周期的管理 xff0c 达到应用组
  • 图像处理之opencv库使用小结

    OpenCV是一个基于BSD许可 xff08 开源 xff09 发行的跨平台计算机视觉库 xff0c 可以运行在Linux Windows Android和Mac OS操作系统上 它轻量级而且高效 由一系列 C 函数和少量 C 43 43
  • react 启动项目遇到的问题

    当启动react 项目时遇到 xff1a 39 react scripts 39 不是内部或外部命令 xff0c 也不是可运行的程序 npm install npm install 下载依赖遇到安装失败 xff0c 则依赖包删除不干净 xf
  • Android LED电子表时钟字体digital font

    字体效果如下图所示 xff1a 这种类型的字体样式会被一些UI设计用于Android APP中时钟显示 xff0c 比如交通灯倒计时 实现这种字体样式 xff0c 先导入一个字体包 xff1a digital ttf 这个digital t
  • Android音视频处理之MediaCodec

    MediaCodec是Android中媒体编解码器 xff0c 可以对媒体进行编 解码 MediaCodec采用同步 异步方式处理数据 xff0c 并且使用了一组输入输出缓存 xff08 ByteBuffer xff09 通过请求一个空的输