问题排查


修正“不允许使用明文 HTTP 流量”错误

如果您的应用的网络安全配置不允许您的应用请求明文 HTTP 流量(即 http://,而不是 https://),就会发生此错误。如果您的应用以 Android 9(API 级别 28)或更高版本为目标平台,默认情况下,明文 HTTP 流量会停用。

如果您的应用需要使用明文 HTTP 流量,那么您需要使用允许这种流量的网络安全配置。如需了解详情,请参阅 Android 的网络安全文档。如需启用所有明文 HTTP 流量,只需将 android:usesCleartextTraffic="true" 添加到应用的 AndroidManifest.xmlapplication 元素中即可。

ExoPlayer 演示版应用使用默认的网络安全配置,因此不允许明文 HTTP 流量。您可以按照上述说明启用它。

修复“SSLHandshakeException”、“CertPathValidatorException”和“ERR_CERT_AUTHORITY_INVALID”错误

SSLHandshakeExceptionCertPathValidatorExceptionERR_CERT_AUTHORITY_INVALID 都表示服务器的 SSL 证书存在问题。这些错误与 ExoPlayer 无关。如需了解详情,请参阅 Android 的 SSL 文档

为什么某些媒体文件无法查找?

默认情况下,如果执行准确跳转操作的唯一方法是让播放器扫描整个文件并将其编入索引,ExoPlayer 不支持在媒体中进行跳转。ExoPlayer 将此类文件视为不可搜索文件。大多数现代媒体容器格式都包含用于跳转的元数据(例如样本索引),具有明确定义的跳转算法(例如,针对 Ogg 的插值对分搜索),或指明其内容是恒定比特率。在这些情况下,ExoPlayer 可以实现高效的跳转操作。

如果您需要跳转但有无法跳转的媒体,我们建议您转换内容以使用更合适的容器格式。对于 MP3、ADS 和 AMR 文件,您还可以在假设这些文件具有恒定比特率的情况下启用跳转功能,如此处所述。

为什么某些 MP3 文件中的跳转模式不准确?

可变比特率 (VBR) MP3 文件从根本上不适合需要精确跳转的用例。这样处理有两个考虑:

  1. 对于精确跳转,理想情况下,容器格式将在标头中提供精确的时间到字节映射。此映射允许播放器将请求的搜寻时间映射到相应的字节偏移量,并开始从该偏移量请求、解析和播放媒体。遗憾的是,可用于在 MP3 中指定此映射的标头(例如 XING 标头)通常并不精确。
  2. 对于不提供精确时间到字节映射(或根本无法进行任何时间到字节映射)的容器格式,如果容器在数据流中包含绝对样本时间戳,仍然可以执行精确搜寻。在这种情况下,播放器可以将寻道时间映射到对相应字节偏移量的最佳猜测,开始从该偏移量请求媒体,解析第一个绝对样本时间戳,并有效地对媒体执行引导式二进制搜索,直到找到正确的样本。遗憾的是,MP3 在流中不包含绝对采样时间戳,因此此方法不可行。

出于这些原因,对 VBR MP3 文件执行精确跳转的唯一方法是扫描整个文件并在播放器中手动建立时间到字节的映射。您可以使用 FLAG_ENABLE_INDEX_SEEKING 启用此策略,使用 setMp3ExtractorFlags DefaultExtractorsFactory 上设置。请注意,它不能很好地缩放为大型 MP3 文件,特别是如果用户在开始播放后不久尝试跳转到靠近流结尾的位置,这就需要播放器等到下载完毕并将整个流编入索引后再执行搜寻。在这种情况下,在 ExoPlayer 中,我们决定针对速度而非准确性进行优化,因此 FLAG_ENABLE_INDEX_SEEKING 默认处于停用状态。

如果您控制正在播放的媒体,我们强烈建议您使用更合适的容器格式,例如 MP4。没有我们所知的哪些用例 MP3 是媒体格式的理想选择

为什么我的视频中的跳转速度变慢?

当播放器在视频中寻找新的播放位置时,需要执行以下两项操作:

  1. 将与新播放位置对应的数据加载到缓冲区中(如果数据已缓冲,则可能不需要加载)。
  2. 由于大多数视频压缩格式都会采用帧内编码,请在新的播放位置之前刷新视频解码器并开始从 I-frame(关键帧)解码。为了确保跳转准确(即播放正好在跳转位置),前一 I-frame 和跳转位置之间的所有帧都需要进行解码,并立即丢弃(不会显示在屏幕上)。

通过增加播放器在内存中缓冲的数据量或将数据预缓存到磁盘,可以缓解 (1) 引入的延迟时间。

可以通过使用 ExoPlayer.setSeekParameters 降低跳转精度,或者对视频重新编码以获得更频繁的 I 帧(这将产生更大的输出文件),来缓解 (2) 带来的延迟时间。

为什么某些 MPEG-TS 文件无法播放?

某些 MPEG-TS 文件不包含访问单元分隔符 (AUD)。默认情况下,ExoPlayer 依靠 AUD 以低成本检测帧边界。同样,某些 MPEG-TS 文件也不包含 IDR 关键帧。默认情况下,这些是 ExoPlayer 考虑的唯一关键帧类型。

当 ExoPlayer 被要求播放缺少 AUD 或 IDR 关键帧的 MPEG-TS 文件时,它似乎卡在缓冲状态。如果您需要播放此类文件,可以分别使用 FLAG_DETECT_ACCESS_UNITSFLAG_ALLOW_NON_IDR_KEYFRAMES 执行此操作。可以使用 setTsExtractorFlagsDefaultExtractorsFactory 上设置这些标记,也可以使用构造函数DefaultHlsExtractorFactory 上设置这些标记。 除了相对于基于 AUD 的帧边界检测相比,使用 FLAG_DETECT_ACCESS_UNITS 的计算成本高昂之外,使用 FLAG_DETECT_ACCESS_UNITS 不会产生副作用。使用 FLAG_ALLOW_NON_IDR_KEYFRAMES 可能会导致在播放某些 MPEG-TS 文件时,在开始播放时以及定位后立即出现暂时性的视觉损坏。

为什么在某些 MPEG-TS 文件中找不到字幕?

某些 MPEG-TS 文件包含 CEA-608 轨道,但未在容器元数据中声明它们,因此 ExoPlayer 无法检测到它们。您可以手动指定任何字幕轨道,方法是向 DefaultExtractorsFactory 提供预期字幕格式列表,包括可用于在 MPEG-TS 流中识别这些字幕的无障碍频道:

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory()
    .setTsSubtitleFormats(
      listOf(
        Format.Builder()
          .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
          .setAccessibilityChannel(accessibilityChannel)
          // Set other subtitle format info, such as language.
          .build()
      )
    )
val player: Player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, extractorsFactory)).build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory()
        .setTsSubtitleFormats(
            ImmutableList.of(
                new Format.Builder()
                    .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
                    .setAccessibilityChannel(accessibilityChannel)
                    // Set other subtitle format info, such as language.
                    .build()));
Player player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

为什么有些 MP4/FMP4 文件无法正常播放?

某些 MP4/FMP4 文件包含的编辑列表通过跳过、移动或重复示例列表来重写媒体时间轴。ExoPlayer 部分支持应用修改列表。例如,它可以延迟或重复从同步样本开始的样本组,但对于并非从同步样本开始的修改,它不会截断音频样本或前贴片广告媒体。

如果您发现媒体的某部分意外缺失或重复,请尝试设置 Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTSFragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS,这会导致提取器完全忽略修改列表。您可以使用 setMp4ExtractorFlagssetFragmentedMp4ExtractorFlags DefaultExtractorsFactory 上设置

为什么有些视频流会失败并显示 HTTP 响应代码 301 或 302?

HTTP 响应代码 301 和 302 都表示重定向。有关简要说明,请访问维基百科。当 ExoPlayer 发出请求并收到状态代码为 301 或 302 的响应时,它通常会遵循重定向并照常开始播放。默认不会发生此情况的一种情况是跨协议重定向。跨协议重定向是指从 HTTPS 到 HTTP 或从 HTTP 到 HTTPS 重定向(或者,在另一对协议之间重定向,这种情况不太常见)。您可以使用 wget 命令行工具测试某个网址是否会导致跨协议重定向,如下所示:

wget "https://yourserver.com/test.mp3" 2>&1  | grep Location

输出应类似如下所示:

Location: https://second.com/test.mp3 [following]
Location: http://third.com/test.mp3 [following]

本示例中有两个重定向。第一个重定向是从 https://yourserver.com/test.mp3https://second.com/test.mp3。两者都是 HTTPS,因此这不是跨协议重定向。第二个重定向是从 https://second.com/test.mp3http://third.com/test.mp3。这会从 HTTPS 重定向到 HTTP,属于跨协议重定向。ExoPlayer 不会在其默认配置中跟踪此重定向,这意味着播放将失败。

如果需要,您可以将 ExoPlayer 配置为在实例化应用中使用的 DefaultHttpDataSource.Factory 实例时遵循跨协议重定向。如需了解如何选择和配置网络堆栈,请点击此处

为什么有些数据流会失败并显示 Un 观察 InputFormatException?

此问题与以下形式的播放失败有关:

UnrecognizedInputFormatException: None of the available extractors
(MatroskaExtractor, FragmentedMp4Extractor, ...) could read the stream.

此故障可能有两个原因。最常见的原因是您尝试播放 DASH (mpd)、HLS (m3u8) 或 SmoothStreaming(ism、isml)内容,但播放器尝试将其作为渐进式视频流进行播放。如需播放此类流,您必须依赖于相应的 ExoPlayer 模块。如果流 URI 未以标准文件扩展名结尾,您还可以将 MimeTypes.APPLICATION_MPDMimeTypes.APPLICATION_M3U8MimeTypes.APPLICATION_SS 传递给 MediaItem.BuildersetMimeType,以明确指定流的类型。

第二个不太常见的原因是 ExoPlayer 不支持您尝试播放的媒体的容器格式。在这种情况下,测试失败是正常情况。不过,您可以随时向我们的问题跟踪器提交功能请求,包括容器格式和测试数据流的详细信息。请先搜索现有的功能请求,然后再提交新的请求。

为什么 setPlaybackParameters 在某些设备上无法正常运行?

在 Android M 及更低版本上运行应用的调试 build 时,使用 setPlaybackParameters API 时可能会遇到性能不连贯、可听见的杂音和 CPU 利用率过高的问题。这是因为在这些 Android 版本上运行的调试 build 已停用对此 API 非常重要的优化。

请务必注意,此问题只会影响调试 build。它影响始终为其启用优化的发布 build。因此,您提供给最终用户的版本不应受此问题影响。

“在错误的线程上访问播放器”错误是什么意思?

请参阅使用入门页面上的关于线程的注意事项

如何解决“意外状态行:ICY 200 OK”这一问题?

如果服务器响应包含 ICY 状态行,而不是与 HTTP 兼容的状态行,则可能会出现此问题。ICY 状态行已弃用,不应使用,因此,如果您控制服务器,则应对其进行更新,以提供符合 HTTP 规范的响应。如果您无法执行此操作,则使用 ExoPlayer OkHttp 库可以解决此问题,因为它能够正确处理 ICY 状态行。

如何查询正在播放的流是否为直播?

您可以查询播放器的 isCurrentWindowLive 方法。此外,您还可以检查 isCurrentWindowDynamic,以确定窗口是否为动态窗口(即仍在不断更新)。

如何在应用在后台运行时继续播放音频?

请按照以下步骤操作,确保在应用位于后台时继续播放音频:

  1. 您需要有一个正在运行的前台服务。这可以防止系统终止您的进程以释放资源。
  2. 您需要存储 WifiLockWakeLock。它们可确保系统使 Wi-Fi 无线装置和 CPU 保持唤醒状态。如果通过调用 setWakeMode 来使用 ExoPlayer,您可以轻松做到这一点,该方法会自动在正确的时间获取和释放所需的锁。

请务必释放锁(如果不使用 setWakeMode)并在不再播放音频后立即停止该服务。

为什么 ExoPlayer 支持我的内容,但 ExoPlayer Cast 库不支持?

您尝试播放的内容可能未启用 CORSCast 框架要求内容启用 CORS 才能播放。

为什么内容无法播放,但未显示任何错误?

您用于播放内容的设备可能不支持特定的媒体样本格式。将 EventLogger 作为监听器添加到播放器,并在 Logcat 中查找类似于以下内容的代码行,即可轻松确认这一点:

[ ] Track:x, id=x, mimeType=mime/type, ... , supported=NO_UNSUPPORTED_TYPE

NO_UNSUPPORTED_TYPE 表示设备无法解码 mimeType 指定的媒体样本格式。如需了解支持的示例格式,请参阅 Android 媒体格式文档如何获取要加载并用于播放的解码库? 可能也很有用。

如何获取要加载并用于播放的解码库?

  • 大多数解码器库都有手动检查和构建依赖项的步骤,因此请务必按照相关库自述文件中的步骤进行操作。例如,对于 ExoPlayer FFmpeg 库,您必须按照 libraries/decoder_ffmpeg/README.md 中的说明进行操作,包括将配置标志传递给启用解码器,以便针对您要播放的任何格式进行播放。
  • 对于包含原生代码的库,请确保您使用的是 README 中指定的正确版本的 Android NDK,并注意在配置和构建期间出现的任何错误。按照 README 中的步骤执行操作后,您应该会在每个受支持架构的库路径的 libs 子目录中看到 .so 文件。
  • 如需尝试使用演示版应用中的库进行播放,请参阅启用捆绑式解码器。如需了解如何在您自己的应用中使用该库,请参阅该库的 README 文件。
  • 如果您使用的是 DefaultRenderersFactory,那么在解码器加载时,应该会在 Logcat 中看到信息级日志行,例如“Load FfmpegAudioRenderer”。如果缺少该依赖项,请确保应用依赖于解码库。
  • 如果您在 Logcat 中看到来自 LibraryLoader 的警告级日志,则表示无法加载库的原生组件。如果发生这种情况,请检查您是否正确地按照库的 README 中的步骤进行操作,并且在按照说明执行操作时是否未输出任何错误。

如果您在使用解码库时仍然遇到问题,请查看 Media3 问题跟踪器,了解近期是否遇到了任何相关问题。如果您需要提交新问题,并且该问题与构建库的原生部分相关,请添加运行 README 指令后的完整命令行输出,以帮助我们诊断问题。

我可以使用 ExoPlayer 直接播放 YouTube 视频吗?

不可以,ExoPlayer 无法播放 YouTube 中的视频,例如 https://www.youtube.com/watch?v=... 格式的网址。您应改用 YouTube Android Player API,这是在 Android 上播放 YouTube 视频的官方方式。

视频播放卡顿

例如,如果内容比特率或分辨率超出设备功能,设备可能无法足够快地解码内容。您可能需要使用较低质量的内容才能在此类设备上获得良好性能。

如果您在运行从 Android 6.0(API 级别 23)到 Android 11(API 级别 30)的 Android 版本的设备上遇到视频卡顿,尤其是在播放受 DRM 保护或高帧速率的内容时,可以尝试启用异步缓冲区队列