问题排查


解决“不允许明文 HTTP 流量”问题错误

如果您的应用请求明文 HTTP 流量(即 http://,而不是 https://) 不允许。如果您的应用以 Android 9(API 级别 28)或更高版本为目标平台,则应使用明文 HTTP 流量默认处于停用状态。

如果您的应用需要处理明文 HTTP 流量,则您需要使用 网络安全配置。查看 Android 的 网络安全文档 了解详情。要启用所有 HTTP 明文流量,只需将 android:usesCleartextTraffic="true" 添加到应用的 application 元素中 AndroidManifest.xml

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 启用此策略, 可DefaultExtractorsFactory 上设置,方法是使用 setMp3ExtractorFlags。请注意,在压缩为大型 MP3 文件时 尤其是当用户尝试很快接近直播结尾时 在开始播放后(这需要播放器等到其下载完成) 并在执行跳转之前将整个流编入索引。在 ExoPlayer 中, 在这种情况下,他们决定优化速度而不是准确性, 因此,FLAG_ENABLE_INDEX_SEEKING 默认处于停用状态。

如果您要控制正在播放的媒体,我们强烈建议您使用 相应的容器格式,如 MP4。没有我们已知的应用场景 其中 MP3 是最佳的媒体格式

为什么视频的跳转速度很慢?

当播放器尝试在视频中找到新的播放位置时,它需要执行以下两项操作: 事情:

  1. 将与新播放位置对应的数据加载到缓冲区中 (如果该数据已缓冲,则可能不需要这样做)。
  2. 刷新视频解码器并开始从 I-frame(关键帧)解码,然后 新的播放位置,因为大多数视频都采用帧内编码 压缩格式。为了确保定位准确(也就是说, 播放正好从定位位置开始), 需要对上一个 I 帧和定位位置进行解码, (不会显示在屏幕上)。

可以通过以下两种方式之一来缓解第 (1) 项带来的延迟时间: 或将数据预缓存到磁盘

可以通过降低准确率和延迟, 或使用 ExoPlayer.setSeekParameters 对视频进行重新编码, 具有更高频率的 I-frame(这会导致输出文件越大)。

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

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

当系统要求您播放 缺少 AUD 或 IDR 关键帧的 MPEG-TS 文件。如果您需要播放此类文件 您可以使用 FLAG_DETECT_ACCESS_UNITSFLAG_ALLOW_NON_IDR_KEYFRAMES。这些标记可在 DefaultExtractorsFactory,使用 setTsExtractorFlags 或在 DefaultHlsExtractorFactory使用 构造函数。 使用 FLAG_DETECT_ACCESS_UNITS 不会产生任何副作用, 相较于基于 AUD 的帧边界检测,计算开销非常大。使用 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,这会导致 提取器以完全忽略修改列表。这些可设置 DefaultExtractorsFactory(使用 setMp4ExtractorFlagssetFragmentedMp4ExtractorFlags

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

HTTP 响应代码 301 和 302 均表示重定向。简要说明 可在维基百科上找到。当 ExoPlayer 发出请求并收到 状态代码为 301 或 302 的响应,则通常遵循重定向 并正常开始播放。默认情况下不会出现这种情况 适用于跨协议重定向跨协议重定向是指 或从 HTTPS 到 HTTP,反之亦然(或者 协议)。您可以使用 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 实例时, 应用。了解如何选择和配置网络堆栈 此处

为什么有些流会因 UnrecognizedInputFormatException 而失败?

此问题涉及以下形式的播放失败:

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_SSsetMimeType,共 MediaItem.Builder 以明确 指定流的类型。

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

为什么 setPlaybackParameters 在某些设备上无法正常工作?

在 Android M 及更低版本上运行应用的调试 build 时,您可能 运行不稳定、有声音的伪像和高 CPU 利用率 使用 setPlaybackParameters API。这是因为 对于在这些设备上运行的调试 build 停用 Android 版本。

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

“在错误的线程上访问播放器”怎么办错误意味着什么?

请参阅“使用入门”页面上的关于线程处理的说明

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

如果服务器响应包含 ICY 状态行、 而不是与 HTTP 协议兼容的ICY 状态行已弃用, 因此,如果您控制服务器,就应该更新服务器, 返回符合 HTTP 协议的响应。如果您无法执行此操作,请使用 ExoPlayer OkHttp 库可以解决此问题,因为它能够处理 ICY 状态行。

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

您可以查询玩家的 isCurrentWindowLive 方法。此外,您还可以 可通过检查 isCurrentWindowDynamic 了解窗口是否为动态窗口 (即,仍在随时间更新)。

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

请按照以下步骤操作,确保在打开应用后,音频继续播放 背景:

  1. 您需要有一个正在运行的前台服务。这样可以防止 从而避免终止进程以释放资源
  2. 你需要同时持有一个 WifiLock 和一个 WakeLock。这可确保 系统会使 Wi-Fi 无线装置和 CPU 保持唤醒状态。如果使用 ExoPlayer,请致电setWakeMode, 在正确的时间获取和释放所需的锁。

请务必释放锁定(如果不使用 setWakeMode)并停止 服务。

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

您尝试播放的内容可能并非 已启用 CORSCast 框架要求在 才能播放相应内容

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

您播放内容的设备可能 支持特定的媒体样本格式。只需添加 一个 EventLogger 作为播放器的监听器,并查找 类似于 Logcat 中的以下代码:

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

NO_UNSUPPORTED_TYPE 表示设备无法解码媒体 mimeType 指定的示例格式。请参阅 Android 媒体格式 文档了解支持的示例格式。如何获得 解码库来加载并用于播放?可能也会有帮助。

如何加载解码库以用于播放?

  • 大多数解码器库都有手动检出和构建依赖项的步骤, 请确保您已按照相关库的 README 文件中的步骤执行操作。 例如,对于 ExoPlayer FFmpeg 库,必须遵循 library/decoder_ffmpeg/README.md 中的说明,包括通过 配置标记,以便为您要播放的任何格式启用解码器
  • 对于包含原生代码的库,请确保您使用的是正确的 Android NDK 的版本,并查找任何 配置和构建期间出现的错误。您应该会看到 .so 文件会显示在每个文件库路径的 libs 子目录中 按照 README 中的步骤进行操作。
  • 如需尝试使用演示版应用中的库进行播放,请参阅 启用捆绑式解码器。有关库的 README 文件,请参阅 从您自己的应用中使用该库的说明。
  • 如果您使用的是DefaultRenderersFactory,您应该会看到一个信息级 类似“Loaded FfmpegAudioRenderer”的日志行在 Logcat 中加载。 如果缺少此参数,请确保应用依赖于 解码库。
  • 如果您在 Logcat 中看到来自 LibraryLoader 的警告级日志,则 表示加载库的原生组件失败。如果 发生了什么情况,检查您是否已正确执行了库的 README 文件中的步骤 并且按照说明操作时未输出任何错误。

如果您在使用解码库时仍然遇到问题,请检查 Media3 问题跟踪器。如果您需要提交 一个新问题,它与构建库的原生部分有关,请 包含运行 README 说明后的完整命令行输出,以帮助我们 诊断问题。

我可以直接通过 ExoPlayer 播放 YouTube 视频吗?

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

视频播放卡顿

在某些情况下,设备可能无法足够快地解码内容,例如 内容比特率或分辨率超出了设备处理能力。您可能需要 使用低质量内容在此类设备上获得良好性能。

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

不稳定的 API lint 错误

Media3 可保证 API Surface 的子集的二进制文件兼容性。通过 保证二进制兼容性的部分带有 @UnstableApi。为了明确这一区别,“不稳定”的用法 除非带有 @OptIn 注解,否则 API 符号会生成 lint 错误。

@UnstableApi 注解并不表示任何 API 的质量或性能,只是表明它并未“API 冻结”。

您可以通过以下两种方式处理不稳定的 API lint 错误:

  • 改用可达到相同结果的稳定 API。
  • 继续使用不稳定的 API,并为用法添加 @OptIn 注解,如下所示 稍后会展示。
添加 @OptIn 注解

Android Studio 可以帮助您添加注释:

<ph type="x-smartling-placeholder">
</ph> 屏幕截图:如何添加“选择启用”注释
图 2:使用 Android Studio 添加 @androidx.annotations.OptIn 注解。

您也可以在 Kotlin 中手动为特定的用法网站添加注解:

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

@OptIn(UnstableApi::class)
fun functionUsingUnstableApi() { ... }

以及在 Java 中:

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

@OptIn(markerClass = UnstableApi.class)
private void methodUsingUnstableApis() { ... }

您可以通过添加 package-info.java 文件来选择启用整个软件包:

@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

若要为整个项目选择启用此功能,请在相应项目的 lint.xml 文件

 <?xml version="1.0" encoding="utf-8"?>
 <lint>
   <issue id="UnsafeOptInUsageError">
     <option name="opt-in" value="androidx.media3.common.util.UnstableApi" />
   </issue>
 </lint>

此外,还有一个不应使用的 kotlin.OptIn 注解。时间是 务必要使用 androidx.annotation.OptIn 注解。