关于编解码,FFMpeg不香吗,为什么要吊死在Android的MediaCodec上?对于这个问题,我也很无奈,FFMpeg很香,但是因为包体积、效率等问题引发的工作业务的需要,使我不得不在Android MediaCodec的摧残下苟且偷生。MediaCodec的api比较简单,用来写demo毫无难度,让人痛不欲生的是它的兼容性问题。使用MediaCodec遇到的问题,往往都是和机型、版本、某类媒体文件相关的问题。从开始使用MediaCodec到现在,遇到了许多问题,很多解决了后过了许久又忘记了,这篇博客作为一个记录,也为后来不得不趟坑的人提供一点点经验吧。

编码器或者解码器Config时候崩溃

这个问题对于MediaCodec的使用者来说应该是一个比较普遍的问题了,在MediaCodec进行config崩溃,往往会给出具体的崩溃信息,比较常见的是格式不支持,最多的是宽高不支持或者编解码器数量限制。

一个手机能够解码或者编码视频能力是有限的,在手机的/etc/media_codec.xml中是有说明的,比如使用一个15年左右的低端机型去解码1080p的视频,大概率会失败。当然,时代发展,限制的低端机型也大多支持1080p,那不妨解码4k视频试试。

另外,在现在的一些能够支持1080p解码的机型上,使用MediaCodec同时解码两路1080p视频也会出现config失败的问题,比如SM-J250f、SM-J700f等手机。这种情况,就只能避免这么使用了,引入软解,或者把1080p视频先转成小视频等。

解码视频,渲染出来的图像异常

这个一般是新手可能会遇到的问题,不是MediaCodec的问题,而是使用者的问题。有时候为了加速解码或者是其他的原因,使用者在进行解码的时候,会对一些帧进行丢弃,但是如果丢弃后,解码帧不是从关键帧开始的话,到下一个关键帧之前,解码出来的图像,都会是花屏的图像。所以需要做的就是要保证每次解码的流程开始时,塞入给解码的数据要从关键帧开始,并且不要随意丢输入帧。否则不会只有MediaCodec解码会花屏,FFMpeg同样无解。

在使用MediaCodec解码到ByteBuffer中,然后拿出来渲染,没有丢帧也有可能会遇到这个问题。这种情况下一般也不是解码器的问题,而是渲染的问题,比如stride和width不匹配,在渲染的时候需要对数据进行处理。或者是颜色空间不正确,导致了渲染的色彩不正确等问题。

解码Mp4中的音频,seek到0后解码,解码出的音频数据不是从头开始的

遇到了使用MediaCodec进行Mp4音频解码,MediaExtractor已经选中音频轨道,并且seek到0后,播放出来的音频并不是从头开始的问题。并且同样的视频,在有的手机上是好的,有的手机上又有问题,不仅仅是使用MediaCodec,使用MediaPlayer直接来播放也会遇到同样的问题。调试后发现,解码出来的数据前面有很多帧给出来的pts是对的,但是数据的size一直为0,而塞入的数据又的确没啥问题。

在有问题的手机上,使用另外一个不存在问题的视频,和存在问题的视频对比了下,解码器解码时候的MediaFormat,发现有问题的视频带有"encoder-delay"标记,值为1,而正常的视频是没有的。google了下发现罪魁祸首果然是它,Android P此问题存在,Android Q版本进行了修复。本地解决方案将"encoder-delay"标记值设置为0即可修复音频不是从头开始的问题。

音频解码无数据

三星J200G解码mp4中的音频,格式为mp4a-latm时遇到的偶现的问题。MediaCodec未返回错误,dequeueInputBuffer返回值正常,可以请求到buffer,但是dequeueOutputBuffer一直无法获得输出。log中有error信息:

4-18 12:27:47.926 32443-2244/com.uc.vmate E/ACodec: OMXCodec::onEvent, OMX_ErrorStreamCorrupt
04-18 12:27:47.931 2175-2266/? E/SEC_AAC_DEC: saacd_decode() failed ret_val: -5, Indata 0x 21 1b 94 a5, length : 1326
04-18 12:27:47.931 2175-2266/? E/SEC_AAC_DEC: ASI 0x 11, 90 00 00
04-18 12:27:47.931 32443-2244/com.uc.vmate E/ACodec: OMXCodec::onEvent, OMX_ErrorStreamCorrupt
04-18 12:27:47.936 2175-2266/? E/SEC_AAC_DEC: saacd_decode() failed ret_val: -5, Indata 0x 21 1b 94 bd, length : 1314

最后发现问题不是出在解码器上,而是出在MediaExtractor上,MediaExtractor使用的时候,内部可能出错,导致MediaExtractor解封装的时候给的数据存在异常。

视频解码时,获取输入输出Buffer始终为-1

在Oppo A37wf 5.1.1系统的手机上进行视频解码的时候,遇到了一个问题。同时解码两路1080p视频,结果一路正常,一路解码出一帧后,dequeueInputBuffer和dequeueOutputBuffer始终返回-1,无报错。而看grafika是可以同时解码的。对比了下我的使用和grafika中的示例,发现最终的原因是我在使用MediaCodec进行解码的时候,并不是dequeueOutputBuffer后就会releaseOutputBuffer,保证同时只会持有一帧数据。而是同时持有了两帧的outputBufer。

把代码改成只持有一个outputBuffer就正常了。回头再看了下Android cts中MediaCodec相关代码,并没有对同时多路持有多个Buffer进行测试,所以最终只能认命了,避免掉持有多帧不立即释放的情况。这里也就不得不提一个建议了,要想使用MediaCodec时,代码无bug,使用尽量不超出Android cts的范围,超出范围的使用就要做好面对一堆bug的准备了。

借助SurfaceTexture解码,图像方向不正确

MediaCodec解码视频可以直接将视频图像解码到Surface上,而Surface又可以通过SurfaceTexture来进行构建,也就是说MediaCodec可以直接将图像解码到SurfaceTexture上,当我们需要对解码后的视频进行图像处理时,通常会选择这样的方式。

对于带有旋转或者裁边信息的视频,我们通常需要在渲染SurfaceTexture的纹理时,将其纹理矩阵信息通过getTransformMatrix拿到,然后去做渲染处理。但是在一些情况下,我们通过getTransformMatrix并不能拿到正确的矩阵。依旧是在Oppo A37wf这个手机上,使用两路1080p视频解码,会遇到一路视频正常,一路视频的SurfaceTexture无法通过getTransformMatrix获得正确的Matrix的情况。在grafika上也存在同样的问题。

进一步跟进,发现在出错的那一路解码器中,codec.getOutputFormat()得到的Format中有一个using-sw-renderer字段被设为1了,所以根本原因就是这个手机用Surface的方式同时硬解两路1080p视频,会有一路渲染使用的软件渲染的方式进行渲染,而这个垃圾手机软件渲染又有bug。 在MediaCodec.cpp可以找到设置和处理相关源码,但是找到可以强制改回使用硬件渲染的方式。

我使用的解决方案是在发现解码使用的是软件渲染的OutputFormat时,对获取的矩阵进行判断,如果矩阵信息和OutputFormat中的信息不匹配的话,重新计算一个渲染矩阵出来,然后渲染时候使用重新计算的矩阵。计算方式可以参考native层源码GLConsumer.cpp中的计算。直接copy过来就能用了。


欢迎转载,转载请保留文章出处。湖广午王的博客[http://blog.csdn.net/junzia/article/details/106036509]


Android MediaCodec踩坑笔记相关推荐

  1. iphone se 一代 不完美越狱 14.6 视频壁纸教程(踩坑笔记)

    iphone se 一代 不完美越狱 14.6 加 视频壁纸教程-踩坑笔记 越狱流程 1.爱思助手制作启动u盘 坑点: 2.越狱好后 视频壁纸软件 1.源 2.软件安装 越狱流程 1.爱思助手制作启动 ...

  2. Linux内核踩坑笔记

    systemtap embedded C踩坑笔记戳这: https://blog.csdn.net/qq_41961459/article/details/103093912 task_struct的 ...

  3. Android applink 踩坑指南

    Android applink 踩坑指南 原理 接入步骤 将链接与activity关联起来 加入meta data 生成身份验证JSON 真机测试 结论 官方文档 原理 与url scheme不同的地 ...

  4. 阿里云部署Tiny Tiny RSS踩坑笔记

    阿里云部署Tiny Tiny RSS踩坑笔记 前言 入坑了RSS,之前的配置是阿里云部署RSSHub,配合Inoreader进行文章阅读,详情见RSS入坑指南.阿里云部署RSSHub踩坑笔记.在202 ...

  5. 「Java」基于Mirai的qq机器人开发踩坑笔记(其一)

    目录 0. 前置操作 I. 安装MCL II. MCL自动登录配置 III. 安装IDEA插件 1. 新建Mirai项目 2. 编写主类 3. 添加外部依赖 4. IDEA运行 5. 插件打包 6. ...

  6. 「Java」基于Mirai的qq机器人开发踩坑笔记(其二)

    目录 0. 配置机器人 1. onLoad方法 2. onEnable方法 3. 消息属性 4. 消息监听 I. 好友消息 II. 群聊消息 III. 无差别消息 5. 发送消息 I. 文本消息 II ...

  7. 昆仑通态触摸屏1003故障码,踩坑笔记

    昆仑通态触摸屏1003故障码,踩坑笔记 第一次使用这个昆仑通态触摸屏,使用modbusRTU与金田变频器做通讯. 触摸屏在线后报1003通讯错误代码,现象是控制指令正常,但是读取不正常.读取变频器状态 ...

  8. EDUSOHO踩坑笔记之四十二:资讯

    EDUSOHO踩坑笔记之四十二:资讯 获取资讯列表信息 GET /articles/{id} 权限 老API,需要认证 参数 字段 是否必填 描述 sort string 否 排序,'created' ...

  9. EDUSOHO踩坑笔记之三十三:班级

    EDUSOHO踩坑笔记之三十三:班级 班级 班级 获取班级信息 获取班级列表 班级成员 获取班级计划 加入班级 营销平台加入班级 班级 班级 获取班级信息 GET /classrooms/{class ...

最新文章

  1. day9 线程与进程、队列
  2. mysql和java区别_java和mysql的length()区别及char_length()
  3. java 集合框架(一)
  4. Eclipse启动项目时,删除workspaces无用的工作区间
  5. mysql中的类型与java_mysql与java数据类型对应关系
  6. P2P之UDP穿透NAT的原理与实现(转)
  7. 如何在Windows中打开和使用命令提示符
  8. python读取txt文件每一行存为列表,从txt文件中读取一定数量的行,并以python方式转换为list...
  9. 韩顺平HTML5教程www.gis520.com
  10. 小样本点云深度学习库_NeurIPS2019 | MIT与上海交大提出新型点云深度学习框架Point-Voxel CNN...
  11. c#多线程thread实例详解
  12. HitPaw Watermark Remover for mac(视频图片去水印)
  13. asp毕业设计——基于asp+access的WEB网上留言板设计与实现(毕业论文+程序源码)——网上留言板
  14. windows 程序员计算器
  15. GAN (Generative Adversarial Nets 生成对抗网络)
  16. rasp 系统_RASP 类接口
  17. 二叉平衡树之AVL树【手动实现代码】
  18. 解决Win7笔记本下玩游戏的显示问题
  19. 〖Python自动化办公篇⑫〗- Excel 文件自动化 - 读取 excel 数据
  20. 【BP靶场portswigger-服务端4】操作系统命令注入-5个实验(全)

热门文章

  1. Linux中搭建FTP服务器,匿名用户访问、本地用户访问、虚拟用户访问(详细解答安装配置步骤)
  2. 创意摄影:用胶片拼贴人像
  3. 图解人工智能发展史和人工智能全景图
  4. openEuler进化记:一颗探索宇宙的生态之星
  5. 寻找局部最高点-1D
  6. 家装服务市场现状研究分析预测报告-
  7. Android面试Java问题整理
  8. html中纵向分块的代码,html标签 —— 块级元素
  9. 厕所堵了用什么通最方便最快 2022
  10. WinXP内核驱动调试