文档首页 > > 播放器SDK> Android播放器> SDK使用

SDK使用

分享
更新时间:2020/09/17 GMT+08:00

实现播放功能

  1. 创建播放器。

    mMediaPlayer = new IjkMediaPlayer(Context context,String domainId,String userId,Boolean isLive); 
    mMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
    mMediaPlayer.setOnPreparedListener(onPreparedListener);
    • context:上下文。
    • isLive:是否为直播。
    • domainId:租户id,自定义,不支持中文字符,用于播放器数据统计。
    • userId:用户id,自定义,不支持中文字符,用于播放器数据统计。

  2. 设置播放地址。

    mMediaPlayer.setDataSource(mAppContext, url, headers);
    • mAppContext:上下文。
    • url:播放片源的地址,为快速起播,该参数可以设为""字符串。
    • headers:请求头信息,没有可设置为null。

    直播服务必须设置片源URL,在播放HLS时移片源时,必须将直播地址与时移地址用“|”进行拼接,格式为“直播地址”|“时移地址”。

  3. 设置显示承载的VIEW。

    播放器支持两种view(SurfaceView和TextureView),SurfaceView采用setDisplay接口,TextureView采用setSurface接口。

    • SurfaceView采用setDisplay接口
      mMediaPlayer.setDisplay(SurfaceHolder holder);
    • TextureView采用setSurface接口
      mMediaPlayer.setSurface(Surface surface);

    播放加密视频尽量使用surfaceView。非加密视频可以使用TextureView。surfaceview是新建了一个新的window,不受原始的view的一切属性约束,只有View是可用的,才能设置给播放器,否则无法播放

  4. 准备播放。

    使用prepareAsync函数。

    mMediaPlayer.prepareAsync();

    准备完成后会触发OnPreparedListener监听事件。

  5. 结束播放。

    if (mMediaPlayer != null) 
    mMediaPlayer.release();

拖拽播放

播放GOP过大的视频时,调用seekTo会跳回到拖动前的位置,这是因为视频的关键帧问题(GOP导致的),视频压缩率比较高,而seek只支持关键帧,出现这个情况就是原始的视频文件中i帧比较少,播放器会在拖动的位置找最近的关键帧。

1
2
3
4
5
6
7
 public void seekTo(long time) {
try {  
mMediaPlayer.seekTo((int) time); 
} catch (IllegalStateException e) {
        e.printStackTrace(); 
} 
}

外挂字幕功能

  1. 加载字幕文件。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    private SubtitleReader mSubtitleReader;
    private List<SubtitleLineInfo> mSubtitleLineInfos;
    private SubtitleView mSubtitleView;
    //加载文件
    InputStream inputStream = getResources().openRawResource(R.raw.shame_ass);
    AssSubtitleFileReader assSubtitleFileReader = new AssSubtitleFileReader();
    SubtitleInfo subtitleInfo = assSubtitleFileReader.readInputStream(inputStream);
    mSubtitleReader = new SubtitleReader();
    mSubtitleReader.setSubtitleInfo(subtitleInfo);
    mSubtitleLineInfos = subtitleInfo.getSubtitleLineInfos();
    

  2. 根据进度条的改变显示对应的字幕。

    long progress = getCurrentPosition();
    int lineNumber = SubtitleUtil.getLineNumber(mSubtitleLineInfos, progress, mSubtitleReader.getPlayOffset());
    	if (lineNumber == -1) {
    		mSubtitleView.setText("");
    	} 
    	else {
    		SubtitleLineInfo subtitleLineInfo = mSubtitleLineInfos.get(lineNumber);
    		mSubtitleView.setText(Html.fromHtml(subtitleLineInfo.getSubtitleHtml()))
    	};

弹幕功能

  1. 添加依赖。

    dependencies {
    ...
    implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
    }

  2. 新建播放器并添加配置。

    mDefinitionWeVideoView.openDanmukuView(true);

  3. 设置弹幕可见与隐藏。

    public void setDanmuku(boolean isShow) {
             if(mDanmakuView == null){
                 initDanMuView();
              }else if(isShow){
                 mDanmakuView.show();
             }else {
                 mDanmakuView.hide();
             }
    }

  4. 发表弹幕。

    mDefinitionWeVideoView.addDanmaku("普通字幕");
    mDefinitionWeVideoView.addDanmakuWithDrawable("自定义字幕",drawable);

m3u8边播边缓存

  • 初始化
    private EncryptM3U8Server m3u8Server = new EncryptM3U8Server();//加密版m3u8 httpServer
    private String encryptKey = "63F06F99D823D33AAB89A0A93DECFEE0"; //get the key by AES128Utils.getAESKey()
    m3u8Server.execute();//启动服务
    
    private void initM3u8Manger() {
    dirPath = StorageUtils.getCacheDirectory(this).getPath() + "/m3u8Cache";//缓存目录
    //common config !
    M3U8CacheConfig.build(getApplicationContext())
    .setSaveDir(dirPath)
    .setDebugMode(true);
    // add listener
    M3U8Cache.getInstance().setOnM3U8CacheListener(onM3U8CacheListener);
    M3U8Cache.getInstance().setEncryptKey(encryptKey);//encryptKey 为空则不会加密
    }
  • 生命周期
    @Override
    protected void onPause() {
    super.onPause();
    m3u8Server.encrypt();//加密m3u8
    }
    
    @Override
    protected void onResume() {
    super.onResume();
    m3u8Server.decrypt();//解密m3u8
    }
    
    @Override
    protected void onDestroy() {
    super.onDestroy();
    m3u8Server.finish();//关闭服务
    }
  • 边播边缓存功能使用
    M3U8Cache.getInstance().cache(url);//开始缓存m3u8视频,再次调用则为暂停 调用cache2(url)则不会触发暂停只有开始缓存功能
    url = M3U8Cache.getInstance().getM3U8Path(url);
    m3u8 = m3u8Server.createLocalHttpUrl(url);//获取本地代理的m3u8地址 传递给播放器进行边播边缓存

播放器优化

  • 软硬编解码的选择。
    表1 软硬编解码说明

    名称

    描述

    优点

    缺点

    软编解码

    主要使用CPU进行编解码,通常情况下使用FFmpeg进行编码和解压音视频数据。

    • 在不同的设备、系统版本中兼容性极高。
    • 解码时,色彩还原度更高。
    • 编解码过程可扩展性强。

    CPU占用高,手机易发热,耗电量大。

    硬编解码

    主要使用非CPU进行编解码,如GPU等,通常情况下直接调用系统API进行音视频编解码处理。

    系统占用少,执行效率高。

    • 兼容性低,需根据硬件厂商和系统版本单独适配。
    • 可控性比较差。
    • iOS硬件芯片为统一的videoToolBox解码后的NV12数据格式。
    • Android使用的mediacodec解码的数据格式跟手机硬件厂商有关,无法统一mediacodec解码出来的数据格式。

    综合以上情况,在推流方面,iOS系统和硬件设备统一性高,使用全硬编方案效果更好;Android因机型繁杂,支持程度不一,推荐4.3以上版本使用硬编。在播放解码方面,iOS硬解和软解支持性都较高,软解功耗更高,但是在部分细节方面表现较优,可控性强,具体视项目情况选择;Android推荐4.1版本以上使用硬解,4.1以下版本使用软解。

  • 直播与点播建议设置参数如下所示。
    if (mIsLive) {
    // Param for living
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 3000);//  最大缓存大小是3秒,可以根据实际需求进行修改。
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);//无限读。
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);//关闭播放器缓冲。
    } else {
    // Param for playback
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 0);
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 0);
    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1);
    }
  • 累积延迟。
    • RTMP到达CDN时存在4.0到5.0秒的延迟,可以通过设置IJKPlayer相关参数进行优化。
      表2 参数说明

      参数

      说明

      skip_loop_filter

      对所选帧进行跳过环路滤波。

      skip_frame

      编解码器丢弃所有非关键帧。

    • 优化延迟问题还可以从推流端将编码器调低GOP,如1秒一个GOP,这样可以减小延迟时长,但会导致编码器压缩率降低,图像质量下降,并且会加大服务端的数据压力。
    • 导致延迟还有一个主要原因是ffplay默认的帧率控制没有追帧策略,若网络发生抖动,延时会累加。优化策略就是调整帧率控制部分,保障流畅度和实时性。

监听函数实现

具体的监听器函数以及参数说明请参见监听器接口

实现函数如下:

mMediaPlayer.setOnPreparedListener(onPreparedListener); 
mMediaPlayer.setOnVideoSizeChangedListener(onSizeChangedListener); 
mMediaPlayer.setOnCompletionListener(onCompletionListener); 
mMediaPlayer.setOnErrorListener(onErrorListener );
mMediaPlayer.setOnInfoListener(onInfoListener); 
mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener); 
mMediaPlayer.setOnSeekCompleteListener(onSeekCompleteListener);
mMediaPlayer.setOnTimedTextListener(OnTimedTextListener);

括号内为实现了播放器事件监听器的类:OnPreparedListener、OnCompletionListener、OnBufferingUpdateListener、OnErrorListener、OnInfoListener、OnSeekListener、OnVideoSizeChangedListener,您需要根据实际需求实现这些监听器类接口。

例如OnErrorListener:
private IMediaPlayer.OnErrorListener onErrorListener = new IMediaPlayer.OnErrorListener() {
@Override
public boolean onError(IMediaPlayer iMediaPlayer, int framework_err, int impl_err) {
//根据实际需求实现对应的功能。
return true;
}
};

播放器挂起与恢复

应用切换至后台时调用播放器pause挂起播放。

@Override 
public void surfaceDestroyed(SurfaceHolder holder){ 
 if (player!= null) 
 { 
   mMediaPlayer.pause();
  } 
}

应用切换至前台时调用播放器start继续播放。

@Override 
public void surfaceCreated(SurfaceHolder holder) 
{ 
 if (mMediaPlayer != null) 
 {  
   mMediaPlayer.start(); 
 }  
}

错误码处理

您可以通过player.setOnErrorListener接口设置错误监听,当播放器出现错误导致无法继续播放时,会通过设置的监听回调函数onError获取详细的错误码。

调节音量和静音

设置播放器音量或者静音。

mMediaPlayer.setVolume(float leftVolume, float rightVolume)

音量设置范围:0.0f-1.0f,0.0f为静音。

切换片源

片源播放束或者退出播放后,放播放器。

if (mMediaPlayer != null) 
mMediaPlayer.release();

然后执行一遍实现播放功能

分享:

    相关文档

    相关产品

文档是否有解决您的问题?

提交成功!非常感谢您的反馈,我们会继续努力做到更好!
反馈提交失败,请稍后再试!

*必选

请至少选择或填写一项反馈信息

字符长度不能超过200

提交反馈 取消

如您有其它疑问,您也可以通过华为云社区问答频道来与我们联系探讨

智能客服提问云社区提问