全部產品
Search
文件中心

ApsaraVideo VOD:Android播放器常見問題

更新時間:Oct 22, 2024

本文介紹Android播放器SDK在使用過程中的常見問題及解決方案。

如何擷取當前播放進度

播放器SDK預設每500 ms回調一次播放進度。您可以修改回調間隔時間,通過將間隔時間縮短,以更高頻率地擷取當前播放進度,範例程式碼如下:

//修改回調間隔
PlayerConfig config = mAliyunLivePlayer.getConfig();
config.mPositionTimerIntervalMs = 100;//回調間隔時間,單位:ms。
mAliyunLivePlayer.setConfig(config);

mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
        @Override
        public void onInfo(InfoBean infoBean) {
        if(infoBean.getCode() == InfoCode.CurrentPosition){
            //當前播放進度
            long currentPosition = infoBean.getExtraValue();
        }
    }
});

建立播放器時發生crash

可通過以下步驟排查問題產生原因:

  1. 檢查CPU架構是否為x86架構。

    播放器SDK目前僅支援arm64-v8a架構和armeabi-v7a架構,不支援x86架構。

  2. 檢查專案中是否混合式整合了播放器SDK相關的.so和Maven依賴。

    例如:在build.gradle中通過Maven依賴整合了播放器SDK,又在專案的Module中libs相關目錄下也整合了播放器相關的動態庫。

    解決方案推薦:如果存在混用的情況,建議刪除動態庫,使用Maven方式依賴。如果必須要整合動態庫,則請排查播放器SDK相關的.so檔案是否統一(需要保證是同一個版本下的.so檔案)。播放器SDK的整合操作及動態庫擷取,請參見快速整合。播放器相關的動態庫如下圖所示:crash

  3. 如果整合的是part包,請確認AlivcFFmpeg的版本依賴是否正確。

    AlivcFFmpeg的版本依賴資訊請參見AlivcFFmpeg版本依賴

播放器運行過程中發生crash

可通過以下步驟排查問題產生原因:

  1. 確認crash是否產生在播放器SDK。

    查看是否有帶AliyunPlayer首碼的崩潰堆棧,如果有,則說明問題產生在播放器SDK。

  2. 升級至最新版播放器SDK並驗證,確認問題是否已經修複。

  3. 如果問題依然存在,請準備相關的崩潰檔案(包含全部線程)、崩潰日誌、崩潰情境等資訊,如何擷取問題日誌

播放視頻時畫面有黑邊

可通過以下步驟排查問題產生原因:

  1. 檢查視頻源本身是否存在黑邊問題。

  2. 可通過如下介面調節播放器的縮放模式。

    /*
    SCALE_ASPECT_FILL:按比例填充,畫面會有裁剪。
    SCALE_ASPECT_FIT:按比例縮放,會有黑邊。
    SCALE_TO_FILL:不按比例填充,畫面會變形。
    */
    mAliPlayer.setScaleMode();
  3. 若縮放模式依然無法滿足需求,則可以在應用程式層調節SurfaceView/TextureView的大小。

播放視頻時有聲音無畫面

可通過以下步驟排查問題產生原因:

  1. 使用其他播放器播放,檢查視頻是否是純音頻。

  2. 確認渲染視頻的View設定是否有異常,例如沒有給播放器設定顯示View,或者顯示View被移出播放介面。設定顯示View的方法請參見建立播放器步驟4

播放已擷取讀取許可權的本地視頻時報錯Invalid argument

當播放已擷取讀取許可權的本地視頻時報錯Invalid argument,建議檢查一下檔案名稱和檔案絕對路徑,避免路徑中出現中文和空格混雜的情況。

播放已擷取讀取許可權的本地視頻時報錯Permission denied

因Android系統從Android 10(Android Q)版本開始引入了分區儲存特性,需要在AndroidManifest.xml的application標籤下新增android:requestLegacyExternalStorage="true"後,才能正常使用Android裝置的儲存許可權。

全屏播放時劉海屏閃現一個黑色通知欄

可以通過設定為沈浸式的狀態列解決。

播放MOV視頻失敗

Android播放器SDK支援MOV格式的視頻。如果播放MOV視頻失敗,可考慮是否由源視頻的moov(音視頻資料索引)在mdat(音視頻資料)之後造成,可通過轉碼源視頻將moov移到mdat之後解決。更多資訊,請參見步驟二:排查流

初始化或播放視頻時報錯找不到播放器SDK的.so動態庫

可通過以下步驟排查問題產生原因:

  1. 檢查CPU架構是否滿足要求。

    播放器SDK目前僅支援arm64-v8a架構和armeabi-v7a架構的動態庫。

  2. 檢查播放器SDK的版本是否過低。

    如果播放器SDK版本是5.4.6.0-full或者更低的版本,建議升級到5.4.6.0-full-15467853或者更高的版本。播放器SDK的最新及歷史版本請參見Android播放器SDK

使用列表播放器AliListPlayer播放HLS(m3u8)視頻報錯

V5.4.5.0及之前版本的播放器SDK不支援使用列表播放器AliListPlayer播放HLS(m3u8)格式的視頻。從V5.4.5.0之後版本開始支援播放HLS(m3u8)格式的視頻,但是需要開啟本機快取,開啟本機快取的方法請參見本機快取

使用Android播放器SDK是否支援播放Android工程中assets和raw檔案夾下的視頻?

不支援。需要您將視頻拷貝到手機儲存中,然後使用絕對路徑來播放視頻。

播放HLS視頻流並配置本機快取後,播放失敗報錯403

問題現象:當以VidAuth播放方式,播放HLS(M3U8)協議的視頻流時,開啟本機快取後,出現播放失敗並報錯403。

問題原因:由於開啟本機快取後,如果在沒有充分緩衝的狀態下退出播放,當下一次起播時,未緩衝的部分因仍然使用上一次到期的Vid鑒權資訊請求,從而導致鑒權失敗報錯403。

處理方法:針對5.5.4.0及以後版本的播放器SDK,如果視頻播放URL帶有鑒權參數且播放協議為HLS,可以通過設定PlayerConfig.mEnableStrictAuthMode欄位,進行不同鑒權模式的選擇(預設值為false)。

  • 非嚴格鑒權(false):鑒權也緩衝,若上一次只緩衝了部分媒體,下次播放至非緩衝部分時,播放器會用緩衝的鑒權發起請求,如果URL鑒權設定的有效期間很短,會導致播放異常。

  • 嚴格鑒權(true):鑒權不緩衝,每次起播都進行鑒權,無網路下會導致起播失敗。

Android播放器SDK是否支援邊下載邊播放?

不支援。Android播放器SDK支援在播放的過程中通過開啟本機快取功能緩衝下載視頻檔案,下次再播放時,直接播放緩衝好的視頻檔案;目前不支援將放在檔案目錄下的本機快取視頻檔案單獨拿出來進行播放。

Android播放器SDK是否支援擷取播放視頻的緩衝速度?

支援。Android播放器SDK支援擷取緩衝速度、即時渲染幀率、音視頻碼率、網路下行碼率等,詳細說明請參見擷取播放資訊

HDR視頻播放異常

Android播放器SDK暫不支援帶旋轉角度的HDR視頻,因此播放此類視頻時會有異常。

seek相關問題

seek後,進度條異常回跳

問題原因:播放器預設使用不精準seek。seek後播放器會從seek點附近的主要畫面格開始播放。

解決方案:切換為精準seek模式。

如何切換精準seek和非精準seek模式

切換seek模式的範例程式碼如下:

//非精準seek。
mAliPlayer.seekTo(1000);
mAliPlayer.seekTo(1000, IPlayer.SeekMode.Inaccurate);
//精準seek。
mAliPlayer.seekTo(1000,IPlayer.SeekMode.Accurate);

使用精準seek模式後,進度條也會異常回跳

問題原因:由於精準seek耗時比非精準seek耗時更長,當seek點距離主要畫面格間隔過大,超過精準seek的最大間隔時,播放器SDK會自動切換成非精準seek,導致進度條回跳。

解決方案:可以通過介面設定精準seek的最大間隔,將精準seek的最大間隔調大,降低精準seek被切換成非精準seek的頻率,以提高seek精準度,但當seek的點距離主要畫面格間隔太長時,耗時也將更長,請根據業務特點合理設定精準seek的最大間隔。範例程式碼如下:

//單位:ms。
mAliPlayer.setMaxAccurateSeekDelta(1000);

緩衝相關問題

使用邊播邊緩衝(CacheConfig)時緩衝失敗

可以在onInfo回調中,擷取緩衝失敗的原因。onInfo回調的範例程式碼如下:

mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
        @Override
        public void onInfo(InfoBean infoBean) {
        if(infoBean.getCode() == InfoCode.CacheSuccess){
            //邊播邊緩衝成功
        }else if(infoBean.getCode() == InfoCode.CacheError){
            //邊播邊緩衝失敗
            String error = infoBean.getExtraMsg();
        }
    }
});

常見的失敗原因如下:

  • CacheConfig中設定的大小和時間長度與視頻不匹配導致緩衝失敗。

  • 在播放過程中,stop或者seek到buffer外導致緩衝失敗。

說明

播放器SDK V5.4.4.0版本,新增支援的預先載入和本機快取能力,可以完全替代邊播邊緩衝的使用情境。建議使用新增加的本機快取能力,更多內容請參見進階功能

本機快取,緩衝目錄是否可以指定到內部儲存目錄

可以指定。您可以將Android裝置的外部儲存目錄換成內部儲存目錄,請確保內部儲存目錄有存取權限即可正常緩衝。

視頻緩衝時報錯encrypt check fail

緩衝即下載,如果設定了安全下載,請確認加密校正檔案與App資訊是否匹配(即需要將離線下載中產生的加密校正檔案下載並儲存到播放器SDK中,詳細內容請參見視頻下載),否則會導致緩衝或下載失敗。

擷取音視頻來源資料

解碼方式需要切換為軟解碼,並且播放不加密的視頻,才會返迴音視頻的未經處理資料。範例程式碼如下:

//切換到軟解碼。
mAliPlayer.enableHardwareDecoder(false);
IPlayer.RenderFrameCallbackConfig renderFrameCallbackConfig = new IPlayer.RenderFrameCallbackConfig();
//是否只返回底層視頻資料地址,預設為true。
renderFrameCallbackConfig.mVideoDataAddr = false;
//是否只返回底層音頻資料地址,預設為false。
renderFrameCallbackConfig.mAudioDataAddr = false;
mAliPlayer.setRenderFrameCallbackConfig(renderFrameCallbackConfig);

mAliPlayer.setOnRenderFrameCallback(new IPlayer.OnRenderFrameCallback() {
    @Override
    public boolean onRenderFrame(FrameInfo frameInfo) {
        return false;
    }
});

擷取視頻寬高

您可以通過以下三種方式擷取視頻寬高:

  • 阿里雲播放器AliPlayer執行個體執行完prepare方法後,至少確保處於準備完成狀態後,通過如下方法擷取:

    mAliyunPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
        @Override
        public void onPrepared() {
              mAliyunPlayer.getVideoWidth();
                  mAliyunPlayer.getVideoHeight();
        }
    });
  • 監聽視頻大小變化回調方法:

    mAliyunPlayer.setOnVideoSizeChangedListener(new IPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(int width, int height) {
    
        }
    });
  • 通過track方式擷取:

    mAliyunPlayer.setOnTrackReadyListener(new IPlayer.OnTrackReadyListener() {
        @Override
        public void onTrackReady(MediaInfo mediaInfo) {
        List<TrackInfo> trackInfos = mediaInfo.getTrackInfos();
            for (TrackInfo trackInfo : trackInfos) {
            if(trackInfo.getType() == TrackInfo.Type.TYPE_VIDEO){
            trackInfo.getVideoWidth();
            trackInfo.getVideoHeight();
            }
        }
        }
    });

自動碼率切換邏輯

當開啟自動碼率切換,即調用了mAliPlayer.selectTrack(TrackInfo.AUTO_SELECT_INDEX)介面後,播放器SDK內部會統計當前網速,當10秒內網速達到下一個碼率時,就會切換到下一個碼率。若10秒內網速未達到下一個碼率,則不切換。

  • 從高碼率切換到低碼率的情境,當10秒內網速達到下一個碼率時,會將已緩衝的高碼率內容播放完成後才切換。

  • 從低碼率切換到高碼率的情境,當10秒內網速達到下一個碼率時,會直接切換。

自動碼率切換,首先需要在控制台轉碼為自適應流,然後指定播放器擷取自適應流。以VidAuth播放方式為例,樣本如下:

VidAuth vidAuth = new VidAuth();
List<Definition> list = new ArrayList<>();
list.add(Definition.DEFINITION_AUTO);
vidAuth.setDefinition(list);

自訂重試邏輯

在網路重試情境下,播放器SDK預設重試次數為2次,網路逾時時間為15秒,當重試失敗後會觸發Error回調。

可以自訂重試邏輯,當觸發重試時,將重試事件通知到外部,由外部決定具體的重試邏輯。範例程式碼如下:

PlayerConfig config = mAliPlayer.getConfig();
//1.設定重試次數,此處以設定為0為例。
config.mNetworkRetryCount = 0;
mAliPlayer.setConfig(config);

mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
    @Override
    public void onInfo(InfoBean infoBean) {
        //2.監聽retry事件。
       if(infoBean.getCode() == InfoCode.NetworkRetry){
            //TODO做相應的邏輯處理。
        }
    }
});

播放RTS流時,報錯協議不支援

可通過以下步驟排查報錯原因:

  1. 檢查3個SDK(AliyunPlayer、AlivcArtc、Rts)是否全部都已整合。

    使用阿里雲播放器播放RTS流,需要整合3個SDK:AliyunPlayer、AlivcArtc、Rts。整合操作可參見阿里雲播放器SDK整合Native RTS SDK實現說明

  2. 檢查Native RTS SDK(Rts)和阿里雲播放器SDK(AliyunPlayer)的版本是否配套。

    Native RTS SDK和阿里雲播放器SDK有版本配套關係,需要整合對應的版本,版本配套關係請參見SDK下載

  3. 檢查AliyunPlayer和AlivcArtc的版本是否一致,需保持一致。

  4. 整合Native RTS SDK後,需要手動載入。

    load RtsSDK:System.loadLibrary("RtsSDK");
  5. 可選:經上述步驟排查驗證後,若仍然報錯。請在AndroidManifest.xml檔案中或在app/build.gradle檔案的android{}閉包中,配置android:extractNativeLibs="true",範例程式碼如下:

    packagingOptions {
        jniLibs {
        useLegacyPackaging true
        }
    }

縮圖使用

在播放器SDK中使用縮圖前,請確保已為視頻配置縮圖,即視頻已產生雪碧截圖(在ApsaraVideo for VOD控制台建立類型為雪碧圖的截圖模板,通過工作流程將視頻用該雪碧圖的截圖模板處理後,產生雪碧圖資料),詳細內容請參見視頻截圖。播放器SDK中使用縮圖的範例程式碼如下:

mAliPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
    @Override
    public void onPrepared() {
        //1.擷取縮圖URL。
        List<Thumbnail> thumbnailList = mAliPlayer.getMediaInfo().getThumbnailList();
        //2.建立縮圖協助類。
        ThumbnailHelper mThumbnailHelper = new ThumbnailHelper(thumbnailList.get(0).mURL);
        //3.設定監聽。
        mThumbnailHelper.setOnPrepareListener(new ThumbnailHelper.OnPrepareListener() {
            @Override
            public void onPrepareSuccess() {
                //5.縮圖載入成功後,可以請求擷取指定位置的縮圖。
                mThumbnailHelper.requestBitmapAtPosition(1000);

            }
            @Override
            public void onPrepareFail() {}
        });

        mThumbnailHelper.setOnThumbnailGetListener(new ThumbnailHelper.OnThumbnailGetListener() {
            @Override
            public void onThumbnailGetSuccess(long positionMs, ThumbnailBitmapInfo thumbnailBitmapInfo) {
                //6.擷取指定位置縮圖的Bitmap。
                Bitmap thumbnailBitmap = thumbnailBitmapInfo.getThumbnailBitmap();
            }
            @Override
            public void onThumbnailGetFail(long positionMs, String errorMsg) {}
        });

        //4.載入縮圖。
        mThumbnailHelper.prepare();
    }
});

請注意:如果播放方式為URL的時候,AliPlayer 可能並不能擷取到ThumbnailList,建議使用者直接指定縮圖的URL。

mAliPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
    @Override
    public void onPrepared() {
        //1.建立縮圖協助類。
        ThumbnailHelper mThumbnailHelper = new ThumbnailHelper(URL);
        //2.設定監聽。
        mThumbnailHelper.setOnPrepareListener(new ThumbnailHelper.OnPrepareListener() {
            @Override
            public void onPrepareSuccess() {
                //4.縮圖載入成功後,可以請求擷取指定位置的縮圖。
                mThumbnailHelper.requestBitmapAtPosition(1000);

            }
            @Override
            public void onPrepareFail() {}
        });

        mThumbnailHelper.setOnThumbnailGetListener(new ThumbnailHelper.OnThumbnailGetListener() {
            @Override
            public void onThumbnailGetSuccess(long positionMs, ThumbnailBitmapInfo thumbnailBitmapInfo) {
                //5.擷取指定位置縮圖的Bitmap。
                Bitmap thumbnailBitmap = thumbnailBitmapInfo.getThumbnailBitmap();
            }
            @Override
            public void onThumbnailGetFail(long positionMs, String errorMsg) {}
        });

        //3.載入縮圖。
        mThumbnailHelper.prepare();
    }
});

清晰度相關問題

視頻轉碼了多個清晰度,播放器SDK預設會播放視頻的哪個清晰度?

預設清晰度播放順序:FD、LD、SD、HD、2K、4K、OD,清晰度的定義請參見清晰度:Definition。播放器SDK會從左至右依次尋找,找到哪個清晰度就播放哪個清晰度。

如何指定視頻播放預設清晰度?

範例程式碼如下:

//以VidSts播放方式為例
VidSts vidSts = new VidSts();
//設定vid、AccessKeyId、AccessKeySecret、token等參數的代碼此處省略,可參見基礎功能文檔中的建立播放器設定。
/*
    參數1:期望播放的清晰度,取值為FD、LD、SD、HD、2K、4K、OD。
    參數2:是否強制使用期望播放的清晰度播放。false:不強制使用期望的清晰度播放,按播放器SDK的預設清晰度播放順序尋找播放;true:強制使用設定的期望清晰度播放,如果找不到期望播放的清晰度就不播放。
*/
vidSts.setQuality("",false);

如果同一個清晰度有多個碼流,播放器SDK會播放哪個碼流?

當同一個清晰度有多個碼流時,播放器SDK會播放最新的碼流。

如何設定同一個視頻,在播放時無浮水印,下載後帶浮水印?

將視頻轉碼多個清晰度,播放時播放無浮水印的清晰度,下載時下載有浮水印的清晰度。

如何擷取問題日誌

當您向阿里雲擷取支援人員時,請將您的問題日誌一併提交,以便更高效地為您解決問題。問題日誌的擷取方法如下:

  1. 擷取問題日誌。

    建議將記錄層級設定為AF_LOG_LEVEL_TRACE後再擷取問題日誌。具體方法請參見擷取SDK日誌

  2. 將產生的日誌提供給阿里雲支援人員。