从 ExoPlayer 源码分析视频无法播放问题

2023-05-16

image.png

What happened

最近负责的项目中碰到了在部分手机上无法播放视频的问题,我们接入的是 ExoPlayer 三方库,从 log 看出现的是 Decoder init failed,也是网上常见的 4001 (ERROR_CODE_DECODER_INIT_FAILED)的问题。
在这里插入图片描述

在 Google 搜索无果后,决定深入源码中去一步一步探究,找到问题的所在,果然功夫不负有心人,最终从源码中找到了解决方案,分享出来希望也能帮助到大家。

Google 上相关问题: github.com/google/ExoP…

Find the bug

应用对比

由于我之前负责其他的项目中也使用了 ExoPlayer 来播放的动态壁纸的,在这几个机型上测试发是可以正常播放动态壁纸,这样可以大概率排除是机型的问题。

随即引入 ExoPlayer 库写了一个简单的 Demo,测试对比发现在该机型上可以播放网上找一个视频链接,但无法播放我们的视频链接,初步怀疑视频格式在某些机型上不支持。

源码分析

从 log 中可以看出是 MediaCodecVideoRenderer 抛出了 ExoPlaybackException,从调用栈关系可以发现最终是调用到了 MediaCodecRenderer -> maybeInitCodecWithFallback() ,然后再去源码中分析其逻辑。

private void maybeInitCodecWithFallback(...) {
  ...

  while (codec == null) {
    ...

    try {
      initCodec(codecInfo, crypto); 
    } catch (Exception e) {
      Log.w(TAG, "Failed to initialize decoder: " + codecInfo, e);

      DecoderInitializationException exception = new DecoderInitializationException(inputFormat, e, mediaCryptoRequiresSecureDecoder, codecInfo); 

      ...
    }
  }
}

public DecoderInitializationException(
    Format format,
    @Nullable Throwable cause,
    boolean secureDecoderRequired,
    MediaCodecInfo mediaCodecInfo) {
  this(
       "Decoder init failed: " + mediaCodecInfo.name + ", " + format ,
      cause,
      format.sampleMimeType,
      secureDecoderRequired,
      mediaCodecInfo,
      Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null,
      /* fallbackDecoderInitializationException= */ null);
}
复制代码

从以上源码可以看出,正是调用了 initCodec() 出现了异常,然后抛出了 DecoderInitializationException其打印的异常信息也和 log 中的一致,继续追 initCodec() 中的逻辑。

private void initCodec(...) {
  ...

  codec = codecAdapterFactory.createAdapter(configuration);

  ...
}
复制代码

通过打断点调试发现,其逻辑走到了 DefaultMediaCodecAdapterFactory 的 createAdapter() 中,继续跟到了 SynchronousMediaCodecAdapter.Factory 中的 createAdapter() 中,最终调用了 MediaCodec 中的 configure() 导致的异常。(从源码中可以看出,在DefaultMediaCodecAdapterFactory 中有 if 逻辑,但其实最终逻辑都会调用到 MediaCodec 中,所以无需关注该 if 逻辑)

public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.Factory {
  ...

  @Override
  public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
      throws IOException {

    if ((asynchronousMode == MODE_ENABLED && Util.SDK_INT >= 23)
        || (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31)) {
      ...

      AsynchronousMediaCodecAdapter.Factory factory =
          new AsynchronousMediaCodecAdapter.Factory(
              trackType,
              enableSynchronizeCodecInteractionsWithQueueing,
              enableImmediateCodecStartAfterFlush);
      return factory.createAdapter(configuration);
    }

 return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration); 
  }
}

public class SynchronousMediaCodecAdapter implements MediaCodecAdapter {

 public static class Factory implements MediaCodecAdapter.Factory {

    @Override
    public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException {
      ...

      try {
        codec = createCodec(configuration);
        TraceUtil.beginSection("configureCodec");
        codec.configure(
            configuration.mediaFormat,
            configuration.surface,
            configuration.crypto,
            configuration.flags);
        ...

        return new SynchronousMediaCodecAdapter(codec, inputSurface);
      } catch (IOException | RuntimeException e) {
        ...
      }
    }
 }
复制代码
final public class MediaCodec {
    ...

    public void configure(...) {
        configure(format, surface, crypto, null, flags);
    }

    private void configure(...) {
        if (crypto != null && descramblerBinder != null) {
            throw new IllegalArgumentException("Can't use crypto and descrambler together!");
        }

        ...

        native_configure(keys, values, surface, crypto, descramblerBinder, flags);
    }

    private native final void native_configure(...); 

    ...
}
复制代码

可以看出最终调用的是 C/C++ 的代码,一般在这里出现了异常,那对于 Android 端看似是无能为力的,但此时我又从另一个角度去思考,正常能播放的机型和无法播放的机型,到底是哪些参数有差别呢? 于是又一步一步回退去排查整个流程中 MediaCodecInfo 对象的中值,经过不断排查,最终发现以下核心逻辑代码:

public abstract class MediaCodecRenderer extends BaseRenderer {
    ...

    private void maybeInitCodecWithFallback(
        MediaCrypto crypto, boolean mediaCryptoRequiresSecureDecoder)
        throws DecoderInitializationException {
        ...     

        try {
          // 获取可用的解码器 list
          List<MediaCodecInfo> allAvailableCodecInfos =
     getAvailableCodecInfos(mediaCryptoRequiresSecureDecoder); 
          availableCodecInfos = new ArrayDeque<>();

          // 默认为false,所以走的只获取可用 list 中的第一个数据
          if (enableDecoderFallback) { 
            availableCodecInfos.addAll(allAvailableCodecInfos) ;
          } else if (!allAvailableCodecInfos.isEmpty()) {
            availableCodecInfos.add(allAvailableCodecInfos.get(0)) ;
          }

         ...
      }
      ...

      // 循环去找可用的 list 中是否能有解码器初始化成功
      while (codec == null) {
        MediaCodecInfo codecInfo = availableCodecInfos.peekFirst();

        if (!shouldInitCodec(codecInfo)) {
          return;
        }

        try {
          initCodec(codecInfo, crypto);
        } catch (Exception e) {
          ...
        }
      }

      availableCodecInfos = null;
    } 
    ...    
}
复制代码

从中可以看出,首先会通过 getAvailableCodecInfos() 获取一组可用的解码器 list,然后通过逻辑判断将该 list 中全部还是第一个加到队列 availableCodecInfos 中,接下来通过 while 循环,不断的从 availableCodecInfos 队列中取第一个,去尝试初始化看能否成功,直到找到了成功初始化的解码器。

/*
  @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder initialization fails. This may result in using a decoder that is less efficient or slower than the primary decoder.
/
复制代码

从上面注释可以了解到 enableDecoderFallback 参数的含义,如果设置为true,可能会导致性能降低(软解性能不如硬解),默认相当于优先初始化硬解。

解决方案

其实非常简单就能解决了,设置 setEnableDecoderFallback(true), 大功告成!

ExoPlayer player = new ExoPlayer.Builder(context)
        .setRenderersFactory(new DefaultRenderersFactory(context).setEnableDecoderFallback(true))
        .build();

转载:https://juejin.cn/post/7170363954265325576#heading-2

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

从 ExoPlayer 源码分析视频无法播放问题 的相关文章

  • 从 ExoPlayer 源码分析视频无法播放问题

    What happened 最近负责的项目中碰到了在部分手机上无法播放视频的问题 xff0c 我们接入的是 ExoPlayer 三方库 xff0c 从 log 看出现的是 Decoder init failed xff0c 也是网上常见的
  • ExoPlayer结构分析

    最近要做音乐播放器 经过一周的调研 最终决定使用google的ExoPlayer 对ExoPlayer的架构 流程做个分析 梳理一下调用逻辑 先做个笔记 等项目做完了写一个多媒体播放的大型攻略 媒体资源的获取 MediaSource类代表媒
  • 纹理视图获取表面

    我正在使用 ExoPlayer 库 它需要一个 Surface 但是我找不到任何方法来检索 Textureview 的底层 Surface 有任何想法吗 Surfaceview 有一个方法 surfaceView getHolder get
  • 如何在整个应用程序中显示底部的迷你控制器或持久媒体控件(例如 Spotify 或 Google 音乐)[关闭]

    Closed 这个问题需要多问focused 目前不接受答案 我正在开发一个应用程序 可以播放来自 youtube 或其他实时流媒体的视频 如果用户按下后退 主页按钮 用户只能听到音频 然后他们可以返回再次观看视频 我创建了一个可以容纳玩家
  • 如何在 android ExoPlayer 中显示字幕

    我正在使用 Exoplayer 来播放 URL 我想在 exoplayer 中添加 srt 文件 但我认为播放器不支持srt文件 所以我将文件内容放入 1 个变量中 如何在 android exoplayer 中显示字幕 有点晚了 但它可能
  • Exoplayer如何知道要使用自定义视频视图播放哪个URL

    我正在开发一个在线视频播放器 我正在使用TextureVideoViewMute extends TextureView implements TextureView SurfaceTextureListener自定义类作为自定义视频播放器
  • 在 RecyclerView 中发布 ExoPlayer

    我正在 RecyclerView 中实现 ExoPlayer 但滚动时视频会停止 但音频不会停止 如何在RecyclerView中释放ExoPlayer 或者我怎样才能获得回收对象的位置 以便我能够释放 ExoPlayer 这是我的数据对象
  • 如何在 Exoplayer 中添加下一个、上一个、快退和前进的侦听器

    我正在 ExoPlayer 上工作 我想自定义 ExoPlayer 并监听事件下一个 上一个 倒带 前进 以便当用户单击下一个按钮时播放列表中的下一个视频将播放 而使用上一个时将播放播放列表中的上一个视频将播放播放列表等 我正在使用自定义布
  • 是否有使用 ExoPlayer 实现 DASH 的非 YouTube 示例?

    您好 我正在寻找配置示例ExoPlayer for DASH 但我发现的例子使用 Youtube 视频 有没有 YouTube 上没有的视频示例 能DASH可以针对互联网上的任何视频进行配置吗 是的 ExoPlayer 可以通过 HTTP
  • 如何使用 ExoPlayer IMA 扩展在特定点展示广告?

    我在用ExoPlayer 2 7 3带有 IMA 扩展名 我必须以一定的时间间隔展示广告 我已经成功地整合了AdsLoader and AdsMediaSource 我正在接收并展示广告 但广告只出现在电影的开头 如何让广告在我想要的时间点
  • 防止 Flutter 中通过 video_player 使用的 ExoPlayer 发送垃圾邮件日志

    每当我在我的播放器中播放新文件时视频播放器我得到一长串日志 这使得查看我自己的日志变得更加困难 其中很多都包含记录值信息和嗨有没有办法让 video player 不那么喋喋不休 I ExoPlayerImpl 21717 Init d44
  • Exoplayer 视频加载速度

    我正在寻找在我的项目中实现 exoplayer 我已经成功实现了 但是视频加载时间很慢 如何在exoplayer中实现或实现视频的快速加载 就像抖音一样 立即加载视频并开始播放 尝试下面的代码 它使缓冲时间变小 因此您可以更快地加载视频 M
  • ExoPlayer 2 的质量选择器

    我目前正在开发一个现场和电影播放器 应用程序 我选择了Exo播放器版本2播放这部电影 我对此了解不多 我想让用户选择电影的质量在播放器屏幕上 例如 720p 或 1080p 等 但我不知道如何获取现有品质的列表并将其显示给用户 下面的代码是
  • Exoplayer 从字节数组播放音频 - ByteArrayDataSource

    使用 Exoplayer 我尝试播放字节数组中的音频文件 我正在尝试使用 ByteArrayDataSource 但在调用构造函数时出现错误 new ByteArrayDataSource data 这是我想出的代码 private voi
  • 我们如何在流式传输后缓存 HLS 视频 url

    我正在使用 exomedia 库通过 hls 播放视频 我想在视频流式传输时缓存视频 AndroidVideoCache 库很好地完成了这项工作 但它不支持 HLS 可以通过配置使用的okhttp客户端来完成外媒体 https github
  • ExoPlayer - 如何播放本地mp3文件

    我正在尝试使用 ExoPlayer 而不是 MediaPlayer 因为 MediaPlayer 返回错误的 getCurrentPosition 是一个常见错误 我需要一个替代品 但我无法在任何地方找到如何通过与 MediaPlayer
  • Android Media3 会话和控制器 - 播放未开始

    我正在尝试实现AndroidMedia3 https developer android com guide topics media media3MediaSessionService 和 MediaController 但由于某种原因播
  • ExoPlayer - 奇怪的阿拉伯语/波斯语字幕格式

    我正在尝试创建一个带字幕的视频播放器 除了一件事之外 一切都已设置并正常工作 我的阿拉伯语字幕没有正确显示 它们的符号和东西看起来很奇怪 像这样 这是我的带有字幕的 ExoPlayer 设置 Uri srt Uri parse http d
  • 通过 ExoPlayer 进行 Android 流传输

    我正在尝试从MediaPlayer有利于ExoPlayer但我找不到任何更新的演示如何使用它 我认为他们已经删除了FrameworkSampleSource方法 我从 Github 下载了演示 但找不到播放器的实现 我可以找到添加 URL
  • 设备锁定时,互联网音乐播放器无法加载歌曲(打瞌睡模式?)

    我正在构建一个音乐播放器 可以播放互联网上的歌曲 我注意到 通常 当一首歌曲结束并且必须加载另一首歌曲时 应用程序不会播放下一首歌曲 我等啊等 终于决定解锁手机以了解发生了什么 令人惊讶的是 设备解锁后立即开始播放以下歌曲 第一次我以为这只

随机推荐

  • 快速记忆常用排序

    选泡插 xff0c 快归堆希桶计基 xff0c 恩方恩老恩一三 xff0c 对恩加k恩乘k xff0c 不稳稳稳不稳稳 xff0c 不稳不稳稳稳稳
  • ssh常用命令50条

    SSH xff08 Secure Shell xff09 是一种用于远程登录 数据传输和命令执行的安全协议 下面列举了 SSH 命令的一些常见用法 xff1a 连接到远程主机 xff1a ssh username 64 hostname 其
  • Kail虚拟机的安装教程

    本指南是关于在 VMware 内部虚拟化 Kali Linux xff0c 让您拥有 Kali VM 这是使用 Kali 的好方法 xff0c 因为它与主机完全分离 xff0c 允许您与其他虚拟机 xff08 以及主机和网络上的其他机器 x
  • JDBC Connection Reset问题分析

    2014年 7 月 13 日 半年前开始 xff0c 项目组测试MM 在验证功能时 xff0c 经常报怨讲测试环境上的应用在启动时很慢 xff0c 偶尔会报失败 xff0c 遇到类似问题多数情况下重新启动一次就可以启动成功 xff0c 但少
  • 安卓DataBinding出现ActivityMainBinding类不存在

    安卓中并不存在ActivityMainBinding这个类 xff0c 这个类是在XML布局的最外层加入 lt layout gt lt layout gt 就会自动生成 如果你的XML布局的名字叫做 34 activity main 34
  • Ubuntu18.04配置交叉编译器,解压安装以后无法查到编译器的版本号

    在进行嵌入式linux开发时 xff0c 有时需要安装特定交版本的交叉编译器 xff0c 这就需要我们手动安装 xff0c 在解压安装安好并且设置了路径 xff0c 我们会发现查不到编译器的版本号 xff0c 也就是仍然无法使用交叉编译器
  • MySQL万字精华总结!你连基础的JVM运行时内存布局都忘了?含面试题+答案

    前言 Netty 是一款基于 Java 的网络编程框架 xff0c 能为应用程序管理复杂的网络编程 多线程处理以及并发 Netty 隐藏了样板和底层代码 xff0c 让业务逻辑保持分离 xff0c 更加易于复用 使用 Netty 可以得到一
  • 获取Spring中@PathVariable注解里带点的完整参数

    x1f680 优质资源分享 x1f680 学习路线指引 xff08 点击解锁 xff09 知识定位人群定位 x1f9e1 Python实战微信订餐小程序 x1f9e1 进阶级本课程是python flask 43 微信小程序的完美结合 xf
  • 使用 oh-my-posh 美化 windows terminal,让其接近oh-my-zsh

    x1f680 优质资源分享 x1f680 学习路线指引 xff08 点击解锁 xff09 知识定位人群定位 x1f9e1 Python实战微信订餐小程序 x1f9e1 进阶级本课程是python flask 43 微信小程序的完美结合 xf
  • 数据结构实验报告-01

    一 实验目的和要求 要求 1 按照群文件中的报告内容 xff0c 首先进行设计工作并加以文字记录 xff0c 然后编写代码 xff0c 测试 xff0c 分析 xff0c 再二次完善报告 xff0c 形成完整的文字记录 2 再次强调 xff
  • flutter面试题,覆盖所有面试知识点,面试必会

    为什么想跳槽 xff1f 简单说一下当时的状况 xff0c 我在这家公司做了两年多 xff0c 这两年多完成了一个大项目 xff0c 作为开发的核心主力 xff0c 开发压力很大 xff0c 特别是项目上线前的几个月是非常辛苦 xff0c
  • 删除docker容器中的文件,你还看不明白?

    原理讲解前 xff0c 先看一个最经典的业务场景 xff0c 如开发一个电商网站 xff0c 要实现支付订单的功能 xff0c 流程如下 xff1a 创建一个订单之后 xff0c 如果用户立刻支付了这个订单 xff0c 我们需要将订单状态更
  • 实战Compose——做个简洁却不简单的星球打卡App

    Focus Focus是一款帮助你集中的app 为自己的目标建立星球 xff0c 将时间投入在上面 花在星球上的每一分钟都会被记录 xff0c 每颗星球可以定制颜色与外观 为了贯彻简单干净不让人分心的设计理念 xff0c app采用白灰为主
  • android 截屏实现的几种方式

    Android 截屏分为四种 xff1a View 截屏 WebView 截屏 系统截屏 和 adb 截屏 1 View 截屏 View 截图是将当前 View 界面截取下来 xff0c 而对于屏幕上其他信息比如 xff1a 状态栏或其他应
  • iBatis使用log4j2输出日志

    原文链接这里 iBatis是一个老项目 xff0c 2 3 4 726版本发布之后 xff0c 项目改名为MyBatis xff0c 项目主页目前为http mybatis github io 我从08年开始接触iBatis xff0c 一
  • Android搭建无线调试环境

    ADB 安卓官网对此工具的介绍 Android 调试桥 adb 是一种功能多样的命令行工具 xff0c 可让您与设备进行通信 adb 命令可用于执行各种设备操作 xff08 例如安装和调试应用 xff09 xff0c 并提供对 Unix s
  • Golang:使用 FCM 实现推送通知

    firebase和设备令牌基本介绍 设备令牌是主要参赛者 xff0c 负责推送通知 这不是一个花哨的词 xff0c 它只是每个设备唯一的 id 当我们安装任何应用程序时 xff0c 我们设备的唯一令牌会存储在他们的服务器上 xff0c 他们
  • 适用于 Flutter 的 AWS Amplify

    关于Amplify Amplify 于 2017 年推出 xff0c 是面向移动和前端 Web 开发人员的端到端 AWS 解决方案 它是服务和工具的组合 xff0c 可以一起使用或单独使用 xff0c 以帮助移动和前端开发人员使用 AWS
  • Android Native 异常捕获库

    现状 发生native异常时 xff0c 安卓系统会将native异常信息输出到logcat中 xff0c 但是java层无法感知到native异常的发生 xff0c 进而无法获取这些异常信息并上报到业务的异常监控系统 业务部门可以快速实现
  • 从 ExoPlayer 源码分析视频无法播放问题

    What happened 最近负责的项目中碰到了在部分手机上无法播放视频的问题 xff0c 我们接入的是 ExoPlayer 三方库 xff0c 从 log 看出现的是 Decoder init failed xff0c 也是网上常见的