Android MediaCodec踩坑笔记

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

关于编解码,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/]


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233064.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MyBatis-Plus 如何实现连表查询[通俗易懂]

    MyBatis-Plus 如何实现连表查询[通俗易懂]MyBatis-Plus如何实现连表查询安装使用简单的3表查询分页查询还可以这么操作,但不建议骚操作简单的3表查询分页查询项目地址:giteegithub安装在项目中添加依赖,依赖已经包含了mybatis-plus-boot-starter<3.4.2>依赖后无需再次引入mybatis-plus<dependency><groupId>com.github.yulichang</groupId><artifactI

    2022年9月14日
    2
  • docker 删除_Docker删除镜像和容器[通俗易懂]

    docker 删除_Docker删除镜像和容器[通俗易懂]当一个host中镜像和容器较多,需要重置时可选择删除其中部分或全部的镜像和容器。那么你就需要下面的操作了。1、删除容器1)首先需要停止所有的容器dockerstop$(dockerps-a-q)2)删除所有的容器(只删除单个时把后面的变量改为containerid即可)dockerrm$(dockerps-a-q)2、删除镜像1)查看host中的镜像dockeri…

    2022年4月19日
    262
  • 微软OneDrive云盘5T大容量免费申请「建议收藏」

    微软OneDrive云盘5T大容量免费申请「建议收藏」博主分享一个亲测有效的OneDrive获取5T的方法,OneDrive云盘在国内的下载速度还是相当可以的,5T的大空间相信也能满足绝大部分人,教育版的子账号还是很稳的,欢迎上车,但是本人不提供任何技术支持,有需要还是请百度吧,因为我也没有搞得很明白。自助申请教程获取临时邮箱打开:http://mail.hrka.net/,获取临时邮箱地址,打开后复制右上角邮箱地址,不要关闭或刷…

    2025年10月15日
    3
  • PINN学习记录(2)

    PINN学习记录(2)PINN 学习记录 2 PINN 基于解物理的方程的应用 所以我自己学习了一段时间 参考了网上很多的开源项目 末尾会贴出一些 自己总结了一下思路解微分方程 1 ODEf x f x f x f x f x f x f 0 1f 0 1f 0 1 网络构造这里说明一下 之后用 nn module 来解决 这只是建立一个通用网络 importtorchi nnasnnimport nn Module

    2025年6月11日
    2
  • nmf java_NMF & pLSA[通俗易懂]

    nmf java_NMF & pLSA[通俗易懂]今天在围脖中看到自动化所刘康的围脖中给了一个关于NMF和pLSA对比分析的链接http://ezcodesample.com/plsaidiots/NMFPLSA.html,大体读了一下,下面用中文对文中的思想重新解释,版权归原作者AndrewPolar。一、名词解释(1)NMF,nonnegativematrixfactorization,直观理解即非负矩阵分解,通常用于矩阵填充,推荐系…

    2022年6月29日
    37
  • 使用 MQTTnet 实现 MQTT 通信示例

    使用 MQTTnet 实现 MQTT 通信示例一、什么是MQTT?MQTT(MessageQueuingTelemetryTransport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。MQTT是基于二进制消息的发布/订阅编程模式的消息协议,如今已经成为OASIS规范,由于规范很简单,非常适合需要低功耗和网络带宽有限的IoT场景。二、MQTTnetMQ…

    2022年6月25日
    31

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号