全部产品
Search
文档中心

视频点播:Android播放器常见问题

更新时间:Oct 21, 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中使用缩略图前,请确保已为视频配置缩略图,即视频已生成雪碧截图(在视频点播控制台创建类型为雪碧图的截图模板,通过工作流将视频用该雪碧图的截图模板处理后,生成雪碧图数据),详细内容请参见视频截图。播放器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. 将生成的日志提供给阿里云技术支持。