疑難排解


修正「不允許明文 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、ADTS 和 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 畫面 (關鍵影格) 開始解碼。為確保搜尋準確 (也就是說,播放會從搜尋位置開始),必須解碼前一個 I 畫面格和搜尋位置之間的所有畫面格,並立即捨棄 (不會顯示在畫面上)。

如要減少 (1) 造成的延遲,可以增加播放器在記憶體中緩衝處理的資料量,或是預先將資料快取到磁碟

如要減少 (2) 造成的延遲,可以使用 ExoPlayer.setSeekParameters 降低搜尋準確度,或是重新編碼影片,增加 I 影格的頻率 (這會導致輸出檔案變大)。

為什麼有些 MPEG-TS 檔案無法播放?

部分 MPEG-TS 檔案不含存取單元分隔符 (AUD)。根據預設,ExoPlayer 會依賴 AUD 偵測影格邊界,成本較低。同樣地,部分 MPEG-TS 檔案不含 IDR 關鍵影格。根據預設,ExoPlayer 只會考量這類關鍵影格。

如果要求播放缺少 AUD 或 IDR 關鍵影格的 MPEG-TS 檔案,ExoPlayer 會顯示停滯在緩衝狀態。如要播放這類檔案,請分別使用 FLAG_DETECT_ACCESS_UNITSFLAG_ALLOW_NON_IDR_KEYFRAMES。這些標記可透過 setTsExtractorFlagsDefaultExtractorsFactory 上設定,或透過建構函式DefaultHlsExtractorFactory 上設定。使用 FLAG_DETECT_ACCESS_UNITS 不會有任何副作用,但相較於以 AUD 為準的影格邊界偵測,運算成本較高。播放部分 MPEG-TS 檔案時,使用 FLAG_ALLOW_NON_IDR_KEYFRAMES 可能會導致播放開始時和搜尋後立即發生暫時的影像損毀情形。

為什麼部分 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 都表示重新導向。簡短說明請參閱 Wikipedia。ExoPlayer 發出要求並收到狀態碼為 301 或 302 的回應時,通常會按照重新導向指示操作,並照常開始播放。預設情況下,只有跨通訊協定的重新導向不會發生這種情況。跨通訊協定重新導向是指從 HTTPS 重新導向至 HTTP,或從 HTTP 重新導向至 HTTPS (或較少見的,在其他通訊協定之間重新導向)。如要測試網址是否會導致跨通訊協定重新導向,請使用 wget 指令列工具,如下所示:

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

輸出內容應如下所示:

Location: https://secondserver.example.net/test.mp3 [following]
Location: http://thirdserver.example.org/test.mp3 [following]

在這個範例中,有兩次重新導向。第一次重新導向是從 https://yourserver.example.com/test.mp3https://secondserver.example.net/test.mp3。兩者都是 HTTPS,因此這不是跨通訊協定重新導向。第二個重新導向是從 https://secondserver.example.net/test.mp3http://thirdserver.example.org/test.mp3。這會將 HTTPS 重新導向至 HTTP,因此屬於跨通訊協定重新導向。ExoPlayer 不會採用預設設定中的這項重新導向,因此播放作業會失敗。

如有需要,您可以在例項化應用程式中使用的 DefaultHttpDataSource.Factory 例項時,將 ExoPlayer 設為追蹤跨通訊協定重新導向。瞭解如何選取及設定網路堆疊,請參閱這篇文章

為什麼部分串流會因 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_SS 傳遞至 MediaItem.BuildersetMimeType,明確指定串流類型。

第二個較不常見的原因是,ExoPlayer 不支援您嘗試播放的媒體容器格式。在這種情況下,失敗是預期行為,但歡迎透過問題追蹤工具提交功能要求,並附上容器格式和測試串流的詳細資料。提交新要求前,請先搜尋現有要求。

為什麼 setPlaybackParameters 無法在部分裝置上正常運作?

在 Android M 和更早版本上執行應用程式的偵錯版本時,使用 setPlaybackParameters API 可能會導致效能不穩、出現可聽見的失真,以及 CPU 使用率偏高。這是因為在這些 Android 版本上執行的偵錯建構版本,會停用對這個 API 很重要的最佳化功能。

請注意,這個問題只會影響偵錯版本。這不會影響發布版本,因為系統一律會為發布版本啟用最佳化功能。因此,您提供給使用者的版本不會受到這個問題影響。

「Player is accessed on the wrong thread」錯誤是什麼意思?

請參閱入門頁面的「有關執行緒的注意事項」。

如何修正「Unexpected status line: ICY 200 OK」?

如果伺服器回應包含 ICY 狀態行,而非符合 HTTP 規範的狀態行,就可能發生這個問題。ICY 狀態行已遭淘汰,不應使用。因此,如果您控管伺服器,請更新伺服器,提供符合 HTTP 規範的回應。如果無法執行這項操作,請使用 ExoPlayer OkHttp 程式庫,因為該程式庫可以正確處理 ICY 狀態行,因此能解決問題。

如何查詢播放的串流是否為直播?

您可以查詢玩家的 isCurrentWindowLive 方法。此外,您也可以檢查 isCurrentWindowDynamic,瞭解視窗是否為動態 (也就是說,是否會隨著時間更新)。

應用程式在背景執行時,如何繼續播放音訊?

如要確保應用程式在背景執行時能繼續播放音訊,請按照下列步驟操作:

  1. 您必須執行前景服務。這樣一來,系統就不會為了釋出資源而終止程序。
  2. 您必須持有WifiLockWakeLock。這些設定可確保系統讓 Wi-Fi 無線電和 CPU 保持喚醒狀態。如果使用 ExoPlayer,只要呼叫 setWakeMode,系統就會在正確時間自動取得及釋放必要鎖定,因此可以輕鬆完成這項作業。

如果不再播放音訊,請務必釋放鎖定 (如果未使用 setWakeMode) 並停止服務。

為什麼 ExoPlayer 支援我的內容,但 ExoPlayer Cast 程式庫不支援?

您嘗試播放的內容可能未啟用 CORSCast 架構需要啟用 CORS 的內容才能播放。

為什麼內容無法播放,但系統未顯示任何錯誤?

你播放內容的裝置可能不支援特定媒體樣本格式。只要將 EventLogger 新增為播放器的接聽程式,並在 Logcat 中尋找類似下列的行,即可輕鬆確認:

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

NO_UNSUPPORTED_TYPE 表示裝置無法解碼 mimeType 指定的媒體樣本格式。如要瞭解支援的樣本格式,請參閱 Android 媒體格式文件如何取得解碼程式庫,以便載入及用於播放?一文也可能對您有幫助。

如何載入解碼程式庫,並用於播放影片?

  • 大多數解碼器程式庫都有手動步驟,可檢查及建構依附元件,因此請務必按照相關程式庫的 README 檔案中的步驟操作。舉例來說,如要使用 ExoPlayer FFmpeg 程式庫,必須按照 libraries/decoder_ffmpeg/README.md 中的操作說明,包括將設定標記傳遞至啟用解碼器,以便播放任何格式。
  • 如果是含有原生程式碼的程式庫,請務必使用 README 中指定的正確 Android NDK 版本,並留意設定和建構期間出現的任何錯誤。按照 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 檢查錯誤

Media3 保證部分 API 介面的二進位檔相容性。凡是「不」保證二進位檔相容性的部分,都會標示 @UnstableApi。為明確區分,不穩定的 API 符號用法會產生 Lint 錯誤,除非使用 @OptIn 註解。

@UnstableApi 註解並不代表 API 的品質或效能,只表示 API 並非「API 凍結」。

處理不穩定的 API 檢查錯誤有兩種方式:

  • 改用可達到相同結果的穩定版 API。
  • 繼續使用不穩定的 API,並以 @OptIn 註解用法,如下所示。
新增 @OptIn 註解

Android Studio 可協助您新增註解:

螢幕截圖:如何新增 Optin 註解
圖 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 檔案中抑制特定 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 註解。