- 修正“不允许明文 HTTP 流量”错误
- 修复“SSLHandshakeException”“CertPathValidatorException”和“ERR_CERT_AUTHORITY_INVALID”错误
- 为什么有些媒体文件无法查找?
- 为什么某些 MP3 文件中的跳转不准确?
- 为什么我的视频跳转速度变慢?
- 为什么某些 MPEG-TS 文件无法播放?
- 为什么在某些 MPEG-TS 文件中找不到字幕?
- 为什么某些 MP4/FMP4 文件无法正常播放?
- 为什么有些视频流会失败,并显示 HTTP 响应代码 301 或 302?
- 为什么某些数据流会失败并显示 UnrecognizedInputFormatException?
- 为什么 setPlaybackParameters 在某些设备上无法正常运行?
- “在错误的线程上访问播放器”错误意味着什么?
- 如何解决“意外状态行:ICY 200 OK”这一问题?
- 如何查询正在播放的直播是否为直播?
- 如何在应用在后台运行时继续播放音频?
- 为什么 ExoPlayer 支持我的内容,但 ExoPlayer Cast 库不支持?
- 为什么内容无法播放,但未显示错误?
- 如何让解码库加载并用于播放?
- 我可以直接使用 ExoPlayer 播放 YouTube 视频吗?
- 视频播放卡顿
- API 不稳定的 lint 错误
修正“不允许明文 HTTP 流量”错误
如果您的应用的网络安全配置不允许时请求明文 HTTP 流量(即 http://
,而不是 https://
),则会出现此错误。如果您的应用以 Android 9(API 级别 28)或更高版本为目标平台,则默认配置会停用明文 HTTP 流量。
如果您的应用需要使用明文 HTTP 流量,则您需要使用允许该流量的网络安全配置。如需了解详情,请参阅 Android 的网络安全文档。如需启用所有明文 HTTP 流量,只需将 android:usesCleartextTraffic="true"
添加到应用的 AndroidManifest.xml
的 application
元素中即可。
ExoPlayer 演示版应用使用默认的网络安全配置,因此不允许明文 HTTP 流量。您可以按照上述说明启用它。
修复“SSLHandshakeException”“CertPathValidatorException”和“ERR_CERT_AUTHORITY_INVALID”错误
SSLHandshakeException
、CertPathValidatorException
和 ERR_CERT_AUTHORITY_INVALID
都表示服务器的 SSL 证书存在问题。这些错误与 ExoPlayer 无关。如需了解详情,请参阅 Android 的 SSL 文档。
为什么有些媒体文件是不可查找的?
默认情况下,在执行准确跳转操作的唯一方法是让播放器扫描整个文件并将其编入索引时,ExoPlayer 不支持媒体跳转。ExoPlayer 将此类文件视为无法搜索。大多数现代媒体容器格式都包含用于跳转的元数据(例如样本索引),具有明确定义的跳转算法(例如,针对 Ogg 的插值对分搜索),或指明其内容是恒定的比特率。在这些情况下,ExoPlayer 可以高效地执行跳转操作并提供支持。
如果您需要跳转,但有无法跳转的媒体内容,我们建议您将内容转换为使用更合适的容器格式。对于 MP3、ADS 和 AMR 文件,您还可以在这些文件具有恒定比特率的情况下启用跳转功能,如此处所述。
为什么某些 MP3 文件中的播放进度会不准确?
可变比特率 (VBR) MP3 文件从根本上不适合需要精确跳转的用例。这样处理有两个考虑:
- 对于精确跳转,理想情况下,容器格式将在标头中提供精确的时间到字节映射。此映射允许播放器将请求的跳转时间映射到相应的字节偏移量,并开始从该偏移量请求、解析和播放媒体。遗憾的是,可用于在 MP3 中指定此映射的标头(例如 XING 标头)通常并不精确。
- 对于不提供精确时间到字节映射(或任何时间到字节映射)的容器格式,如果容器在数据流中包含绝对样本时间戳,仍然可以执行精确搜索。在这种情况下,播放器可以将跳转时间映射到相应字节偏移量的最佳猜测结果,从该偏移量开始请求媒体,解析第一个绝对样本时间戳,并有效地对媒体执行引导式二进制搜索,直到找到正确的样本。遗憾的是,MP3 未在数据流中包含绝对样本时间戳,因此这种方法不可行。
出于这些原因,对 VBR MP3 文件执行精确跳转的唯一方法是扫描整个文件,并在播放器中手动建立时间到字节的映射。您可以使用 FLAG_ENABLE_INDEX_SEEKING
(可使用 setMp3ExtractorFlags
在 DefaultExtractorsFactory
上设置)启用此策略。请注意,它不能很好地缩放为大型 MP3 文件,尤其是当用户尝试在开始播放后不久就尝试跳转到视频流末尾时,这会要求播放器等到它下载完毕并将整个视频流编入索引后再执行跳转。在 ExoPlayer 中,在这种情况下,我们决定针对速度而非准确性进行优化,因此 FLAG_ENABLE_INDEX_SEEKING
默认处于停用状态。
如果您要控制正在播放的媒体,强烈建议您使用更合适的容器格式,例如 MP4。我们并不了解 MP3 是最佳媒体格式的使用情形。
为什么我的视频跳转速度变慢?
当播放器在视频中寻找新的播放位置时,需要执行以下两项操作:
- 将与新播放位置对应的数据加载到缓冲区中(如果此数据已缓冲,则可能不需要此操作)。
- 由于大多数视频压缩格式都采用帧内编码,因此在新的播放位置之前刷新视频解码器并开始从 I 帧(关键帧)进行解码。为了确保跳转准确(即播放从跳转位置开始),上一个 I 帧和跳转位置之间的所有帧都需要解码并立即舍弃(不会显示在屏幕上)。
通过增加播放器在内存中缓冲的数据量或将数据预缓存到磁盘,可以缓解 (1) 引入的延迟时间。
可通过使用 ExoPlayer.setSeekParameters
降低跳转精度,或对视频重新编码以采用更频繁的 I 帧(这会导致输出文件更大)来缓解 (2) 带来的延迟时间。
为什么某些 MPEG-TS 文件无法播放?
某些 MPEG-TS 文件不包含访问单元分隔符 (AUD)。默认情况下,ExoPlayer 依靠 AUD 以低成本检测帧边界。同样,某些 MPEG-TS 文件不包含 IDR 关键帧。默认情况下,ExoPlayer 考虑的唯一关键帧类型。
当 ExoPlayer 被要求播放缺少 AUD 或 IDR 关键帧的 MPEG-TS 文件时,它会显示为卡在缓冲状态。如果您需要播放此类文件,可以分别使用 FLAG_DETECT_ACCESS_UNITS
和 FLAG_ALLOW_NON_IDR_KEYFRAMES
执行此操作。您可以在 DefaultExtractorsFactory
上使用 setTsExtractorFlags
设置这些标志,也可以使用构造函数在 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_LISTS
或 FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
,这会导致提取器完全忽略修改列表。它们可以使用 setMp4ExtractorFlags
或 setFragmentedMp4ExtractorFlags
在 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.mp3
到 https://second.com/test.mp3
。两者都是 HTTPS,因此这不是跨协议重定向。第二个重定向是从 https://second.com/test.mp3
到 http://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_MPD
、MimeTypes.APPLICATION_M3U8
或 MimeTypes.APPLICATION_SS
传递给 MediaItem.Builder
的 setMimeType
,以明确指定流的类型。
第二个不太常见的原因是 ExoPlayer 不支持您尝试播放的媒体的容器格式。在这种情况下,测试失败会按预期运行,不过您可以随时向我们的问题跟踪器提交功能请求,包括容器格式和测试数据流的详细信息。请先搜索现有的功能请求,然后再提交新的功能请求。
为什么 setPlaybackParameters 在某些设备上无法正常运行?
在 Android M 及更低版本上运行应用的调试 build 时,使用 setPlaybackParameters
API 时,您可能会遇到性能断断续续、声音失真和 CPU 利用率高的情况。这是因为对这些 Android 版本上运行的调试 build 停用了对此 API 非常重要的优化。
请务必注意,此问题仅影响调试 build。而不影响发布 build,因为发布 build 始终会启用优化。因此,您提供给最终用户的版本不应受此问题影响。
“在错误的线程上访问播放器”错误是什么意思?
请参阅使用入门页面上的关于线程的注意事项。
如何解决“意外状态行:ICY 200 OK”问题?
如果服务器响应包含 ICY 状态行,而不是与 HTTP 兼容的状态行,则可能会出现此问题。ICY 状态行已弃用,不应继续使用,因此,如果您控制的是服务器,则应更新它以提供符合 HTTP 规范的响应。如果您无法执行此操作,则使用 ExoPlayer OkHttp 库可解决此问题,因为它能够正确处理 ICY 状态行。
如何查询正在播放的流是否为直播?
您可以查询播放器的 isCurrentWindowLive
方法。此外,您可以检查 isCurrentWindowDynamic
,确定窗口是否为动态窗口(即仍在不断更新)。
如何在应用在后台运行时继续播放音频?
请按照以下步骤操作,确保在应用位于后台时继续播放音频:
- 您需要有一个正在运行的前台服务。这可以防止系统终止您的进程以释放资源。
- 您需要存储
WifiLock
和WakeLock
。这样可以确保系统让 Wi-Fi 无线装置和 CPU 保持唤醒状态。如果使用ExoPlayer
,可以通过调用setWakeMode
来轻松做到这一点,这将在正确的时间自动获取和释放所需的锁。
请务必在音频不再播放后立即释放锁定(如果不使用 setWakeMode
)并停止服务。
为什么 ExoPlayer 支持我的内容,但 ExoPlayer Cast 库不支持?
您尝试播放的内容可能尚未启用 CORS。Cast 框架要求内容启用 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 中的说明进行操作,包括将配置标志传递给为要播放的格式启用解码器。
- 对于包含原生代码的库,请确保您使用的 Android NDK 版本正确无误(如 README 中所指定),并留意配置和构建期间出现的任何错误。按照 README 中的步骤进行操作后,您应该会在每个受支持架构的库路径的
libs
子目录中看到.so
文件。 - 如需尝试使用演示版应用中的库进行播放,请参阅启用捆绑解码器。如需了解如何在自己的应用中使用该库,请参阅该库的 README 文件。
- 如果您使用的是
DefaultRenderersFactory
,则当解码器加载时,您应该会在 Logcat 中看到类似于“Loaded FfmpegAudioRenderer”的信息级日志行。如果缺少该依赖项,请确保应用依赖于解码库。 - 如果您在 Logcat 中看到来自
LibraryLoader
的警告级日志,则表示加载库的原生组件失败。如果发生这种情况,请检查您是否正确按照库的 README 中的步骤进行操作,并且在按照说明执行操作时是否未输出任何错误。
如果您在使用解码库时仍然遇到问题,请查看 Media3 问题跟踪器,了解近期是否有任何相关问题。如果您需要提交与构建库的原生部分相关的新问题,请添加运行 README 说明后的完整命令行输出,以帮助我们诊断问题。
我可以直接使用 ExoPlayer 播放 YouTube 视频吗?
不可以,ExoPlayer 无法播放 YouTube 中的视频,例如格式为 https://www.youtube.com/watch?v=...
的网址。您应改用 YouTube iframe Player API,这是在 Android 上播放 YouTube 视频的官方方式。
视频播放卡顿
例如,如果内容比特率或分辨率超出设备功能,设备可能无法足够快地解码内容。您可能需要使用较低质量的内容才能在此类设备上获得良好的性能。
如果您在运行从 Android 6.0(API 级别 23)到 Android 11(API 级别 30)及包括的 Android 版本的设备上遇到视频卡顿现象(尤其是在播放受 DRM 保护或高帧速率的内容时),可以尝试启用异步缓冲区队列。
不稳定的 API lint 错误
Media3 可保证 API Surface 子集的二进制文件兼容性。不保证二进制文件兼容性的部分标有 @UnstableApi
。为明确这一区别,除非带有 @OptIn
注解,否则使用不稳定的 API 符号会导致 lint 错误。
@UnstableApi
注解未暗示 API 的质量或性能,只表明它没有“API 冻结”。
您可以通过以下两种方式处理 API lint 不稳定的错误:
- 请改用可实现相同结果的稳定 API。
- 继续使用不稳定的 API,并使用
@OptIn
为使用情况添加注解,如后文所示。
添加 @OptIn
注解
Android Studio 可以帮助您添加注释:
![屏幕截图:如何添加“选择启用”注释](https://developer.android.google.cn/static/media/media3/exoplayer/images/unstable-api.png?authuser=6&hl=da)
您还可以在 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
文件中抑制特定 lint 错误,为整个项目选择启用该功能:
<?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
注解。