开发指南

1 开发准备

开始前请确保下述前提条件已准备就绪:

解压后 SDK 包的 libs 文件夹中,包含了jar 文件,so 库文件以及第三方库,文件列表如下:

libs
├── armeabi
│   ├── libneliveplayer.so
│   ├── libnelpengine.so
│   ├── libnelprender.so
├── armeabi-v7a
│   ├── libneliveplayer.so
│   ├── libnelpengine.so
│   ├── libnelprender.so
├── x86
│   ├── libneliveplayer.so
│   ├── libnelpengine.so
│   ├── libnelprender.so
├── arm64-v8a
│   ├── libneliveplayer.so
│   ├── libnelpengine.so
│   ├── libnelprender.so
└── NELivePlayer.jar (Java层代码)

2 集成SDK

本文是根据官网的播放器 Demo 来介绍 SDK 的集成,可在网易云视频官网下载最新的播放器 Android Demo,来查看更多的实现细节。

2.1 导入库文件

集成添加播放器 SDK 到 App 工程,如下图红色框中所示。
(1) 创建应用程序的 Android 工程。
(2) 将 SDK 中的 Jar 包添加到 App 工程的 libs 目录下,并在工程属性中设置依赖此 Jar 包。
(3) 选取同 CPU 类型匹配的 so 动态库压缩包,并解压到工程目录的 libs 目录。

pic

需要修改build.gradle文件,确保添加了NELivePlayer.jar文件和动态库文件的依赖:

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
}

sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
}

2.2 配置工程权限

SDK 需要访问网络、读写 SD 卡、获取设备信息等权限。在 AndroidManifest.xml 中加入以下配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.netease.neliveplayer.demo" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="22" />

    <!-- 权限声明 -->
    <!-- 允许程序打开网络套接字 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- 允许程序使用PowerManager WakeLocks以防止处理器休眠或者屏幕锁屏 -->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- 允许程序向外部存储设备写数据 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 允许程序向外部存储设备读数据 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <!-- 允许程序获取网络相关信息 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 允许程序获取Wifi网络状态信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 允许程序获得设备信息 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />


    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <!-- 欢迎页面activity,默认竖屏模式 -->
        <activity
            android:name=".NEWelcomeActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- 播放设置页面activity,默认竖屏模式 -->
        <activity 
            android:name=".NEMainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:configChanges="orientation|keyboardHidden|screenSize"
            />
        <!-- 播放页面activity,默认横屏模式 -->
        <activity 
            android:name=".NEVideoPlayerActivity"
            android:label="@string/app_name"
            android:screenOrientation="landscape"
            android:configChanges="orientation|keyboardHidden|screenSize"
            />
        <!-- 申明播放器的广播接收器,第三方APP集成时,action中的com.netease.neliveplayer.demo请替换为自己的包名 -->
        <receiver
            android:name="com.netease.neliveplayer.demo.receiver.NELivePlayerReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.netease.neliveplayer.demo.ACTION.RECEIVE_NELP_RELEASE_SUCCESS_NOTIFICATION" />
            </intent-filter>
        </receiver>

        <!-- 申明本地电话状态(通话状态)的广播接收器,第三方APP集成时,如果需要在App中处理播放器与本地电话的交互请加上此接收器 -->
        <!-- action中的com.netease.neliveplayer.demo请替换为自己的包名 -->
        <receiver android:name="com.netease.neliveplayer.demo.receiver.NEIncomingCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

2.3 实现播放功能

Android SDK 的 API 形式与 MediaPlayer 保持一致,通过 NELivePlayer 跟底层 so 动态库进行交互。详细的流程可参见 Demo 源代码:

通过自定义 VideoView 调用 API 来实现媒体播放功能,通过自定义 MediaController 来进行媒体播放控制

(1) 创建播放器实例
首先在 VideoView 中创建播放器实例,NELivePlayer为播放器提供的接口类;

private NELivePlayer  mLivePlayer = null;
mLivePlayer = NELivePlayer.create(mContext); // mContext为VideoView的上下文信息

(2) 设置播放缓冲策略

mLivePlayer.setBufferStrategy(mBufferStrategy); //0为直播极速模式,1为直播低延时模式,2为直播流畅模式, 3为点播抗抖动模式

(3) 设置是否开启硬件解码模式,默认是软件解码

mLivePlayer.setHardwareDecoder(mHardwareDecoder); // false 为软件解码, true 为硬件解码

(4) 注册 listener 对播放过程进行监听,并根据监听结果做出相应的处理

mLivePlayer.setOnPreparedListener(mPreparedListener);
mLivePlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mLivePlayer.setOnCompletionListener(mCompletionListener);
mLivePlayer.setOnErrorListener(mErrorListener);
mLivePlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mLivePlayer.setOnInfoListener(mInfoListener);
mLivePlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mLivePlayer.setOnVideoParseErrorListener(mVideoParseErrorListener);
mLivePlayer.setOnDefinitionListener(mOnDefinitionListener);

其中,各种 listener 的描述可参见 API 文档。

(5) 设置数据源

mLivePlayer.setDataSource(mUri.toString());//设置数据源,返回 true 成功,返回 false 失败

(6) 设置显示用的surface 需要将 SurfaceHolder 传入NEMediaPlayer用于显示,实例如下:
若 VideoView 继承 SurfaceView,则通过 setDisplay() 接口将 SurfaceHolder 传到底层用于显示。

mLivePlayer.setDisplay(mSurfaceHolder); //设置显示surface

若 VideoView 继承 TextureView,则通过 setSurface() 接口将 SurfaceTexture 传到底层用于显示。

mLivePlayer.setSurface(mSurface); //设置显示surface

(7) 设置播放过程中屏幕是否保持长亮

mLivePlayer.setScreenOnWhilePlaying(true); //true:播放过程中屏幕长亮

(8) flv点播加密视频获取密钥(直播流和非flv点播视频可以忽略该步骤)
对于flv点播的加密视频,需要密钥进行解密,然后再进行播放,用户需要调用该接口来获取密钥并验证密钥的正确性。

mLivePlayer.initDecryption(transferToken, accid, token, appKey, decryptionListener);

其中:
transferToken为获取密钥的令牌;
accid为视频云用户创建的其子用户id;
token为视频云用户子用户的token;
appKey为视频云用户子用户的appKey
decryptionListener为密钥校验结果的回调,校验正确才能继续prepareAsync操作。 用户需要在收到密钥校验结果的回调后根据不同的结果做出相应的处理,下面为示例代码:

OnDecryptionListener decryptionListener = new OnDecryptionListener() {
    @Override
    public void onDecryption(int ret) {
        Log.i(TAG, " ret = " + ret);
        switch (ret) {
            case NEType.NELP_NO_ENCRYPTION:
            case NEType.NELP_ENCRYPTION_CHECK_OK:
                mLivePlayer.prepareAsync(mContext);
                mCurrState = PREPARING;
                break;
            case NEType.NELP_ENCRYPTION_UNSUPPORT_PROTOCAL:
                break;
            case NEType.NELP_ENCRYPTION_KEY_CHECK_ERROR:
                break;
            case NEType.NELP_ENCRYPTION_INPUT_INVALIED:
                break;
            case NEType.NELP_ENCRYPTION_GET_KEY_TIMEOUT:
                break;
            case NEType.NELP_ENCRYPTION_UNKNOWN_ERROR:
                break;
            default:
                break;
        }
    }
};

对于未加密的flv视频以及密钥获取并校验正确的flv加密视频,可以继续步骤9的操作。否则需要退出并根据返回值检查错误的原因,然后再重复上述操作进行播放。

(9) 预处理视频文件,准备播放

mLivePlayer.prepareAsync();

预处理完成后,sdk会有一个回调,需要先注册一个 OnPreparedListener 获取准备完成的回调,步骤(4)已经注册了。

OnPreparedListener mPreparedListener = new OnPreparedListener() {
    public void onPrepared(NELivePlayer mp) {
        mLivePlayer.start();
};

(10) 调用 start() 开始播放。

使用 MediaController 进行媒体播放控制需要注意:
(1) 在OnPreparedListener监听到prepare完成后,可以通过 getDuration() 接口来获取文件总时长,通过 getCurrentPosition() 来获取当前播放位置,并用于设置播放进度条,拖动操作可以通过调用 mVideoView.seekTo() 接口。
注意:上述几个接口均只适用于点播

(2) 可通过 setVideoScalingMode(int videoScalingMode) 接口来设置画面显示模式,其中:

VIDEO_SCALING_MODE_NONE = 0; //原始大小
VIDEO_SCALING_MODE_FIT  = 1; //等比例放大,有一边贴黑边
VIDEO_SCALING_MODE_FILL = 2; //全屏,画面会拉伸
VIDEO_SCALING_MODE_FULL = 3; //全屏,等比例放大,有一边有裁剪

注意:该方法为demo中提供的示例,用户可以参考该示例进行设置,也可以根据自己的实际需求进行计算并设置,该部分不属于SDK的范畴。

(3) 退出播放调用 release() 接口。

mLivePlayer.release();

退出播放并释放相关资源,资源释放成功后会有相应的通知,通知以广播的形式进行接收,具体接收方式见下述2.4节 第9条。若退出后再次进入播放,需要等待资源释放成功后才能进入。

2.4 播放状态回调

NELivePlayer SDK 提供了播放过程中各个状态监听器,定义在 NELivePlayer 类中。在使用监听器之前要先注册所有的监听器,如下所示:

mLivePlayer.setOnPreparedListener(mPreparedListener);
mLivePlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mLivePlayer.setOnCompletionListener(mCompletionListener);
mLivePlayer.setOnErrorListener(mErrorListener);
mLivePlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mLivePlayer.setOnInfoListener(mInfoListener);
mLivePlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mLivePlayer.setOnVideoParseErrorListener(mVideoParseErrorListener);
mLivePlayer.setOnDefinitionListener(mOnDefinitionListener);

(1) OnPreparedListener

interface OnPreparedListener {
        /**
         * 预处理完成后调用,可以在该函数内添加处理逻辑
         * @param  mp 播放器实例
         */
        void onPrepared(NELivePlayer mp);
}

该监听器用于监听播放器的预处理过程,主要包括:创建播放所需资源、获取媒体流等,当 预处理完成后,SDK 会回调该对象的 onPrepared 接口,若setShouldAutoplay设置为ture,则会在收到该回调的同时自动播放,若setShouldAutoplay设置为false,则需要在该回调中调用start()接口手动播放。

(2) OnVideoSizeChangedListener

interface OnVideoSizeChangedListener {
        /**
         * 视频大小发生变化时调用,可以在该函数内添加处理逻辑
         * @param  mp 播放器实例
         * @param  width 视频宽度
         * @param  height 视频高度
         * @param  sar_num 像素宽高比的分子
         * @param  sar_den 像素宽高比的分母
         */
        void onVideoSizeChanged(NELivePlayer mp, int width, int height, int sar_num, int sar_den);
}

该监听器用于监听当前播放的视频文件的分辨率大小,在视频文件的分辨率发生变化时会触发该回调,用户可以根据分辨率信息来调整UI布局。

(3) OnCompletionListener

interface OnCompletionListener {
        /**
         * 播放完成后调用,可以在该函数内添加处理逻辑
         * @param  mp 播放器实例
         */
        void onCompletion(NELivePlayer mp);
}

该监听器用于监听播放结束的消息,对于点播文件或本地文件,播放结束后会触发该回调。对于直播来说,播放器无法判断直播是否结束,只能通过业务服务器来进行通知。若主播推流结束,播放器可能会读取不到数据超时退出,进入onError回调。

(4) OnErrorListener

interface OnErrorListener {
        /**
         * 播放发生错误时调用,可以在该函数内添加处理逻辑
         * @param mp 播放器实例
         * @param what 错误类型
         * @param extra 附加信息
         */
        boolean onError(NELivePlayer mp, int what, int extra);
}

该监听器用于监听播放过程中发生的错误消息,发生任何错误,都会触发该回调。在播放发生错误的时候,用户想继续播放,则可以在收到该回调后释放上次播放的资源,重新初始化,播放。

(5) OnBufferingUpdateListener

注意:暂未支持,保留

interface OnBufferingUpdateListener {
        /**
         * 网络视频流缓冲发生变化时调用,可以在该函数内添加处理逻辑
         * @param  mp 播放器实例
         * @param  percent 缓冲百分比
         */
        void onBufferingUpdate(NELivePlayer mp, int percent);
}

该监听器用于监听当前播放器已经缓冲的百分比,当缓冲达到100%时,则可以开始播放。

(6) OnInfoListener

interface OnInfoListener {
        /**
         * 在状态变化时调用,可以在该函数内添加处理逻辑
         * @param mp 播放器实例
         * @param what 状态类型,参考 NEType 中的状态类型
         * @param extra 附加信息
         */
        boolean onInfo(NELivePlayer mp, int what, int extra);
}

info类型参考 NEType 中的状态类型,下面为部分info类型的说明:

// 标识缓冲开始
NELP_BUFFERING_START;
// 标识缓冲结束
NELP_BUFFERING_END;
// 标识视频第一帧显示
NELP_FIRST_VIDEO_RENDERED;
// 标识音频第一帧显示
NELP_FIRST_AUDIO_RENDERED;

该监听器用于监听播放器的状态消息,播放过程中状态发生变化时会触发该回调,用户可以根据自己应用层的业务逻辑来做相应的处理。比如收到NELP_BUFFERING_START消息,说明此时网络状况不好,播放处于卡顿状态,此时可以在播放界面上加载一个缓冲中的动画,等收到NELP_BUFFERING_END的消息,说明缓冲结束,可以开始播放了,此时可以把缓冲动画关闭。

(7) OnSeekCompleteListener

interface OnSeekCompleteListener {
        /**
         * Seek操作完成后调用,可以在该函数内添加处理逻辑
         * @param  mp 播放器实例
         */
        void onSeekComplete(NELivePlayer mp);
}

该监听器用于监听 seek 操作完成的消息,当调用的 seekTo 方法到指定的位置播放,seek成功后会触发该回调。

注意:该监听器只适用于点播

(8) OnVideoParseErrorListener

interface OnVideoParseErrorListener {
        /**
         * 视频码流解析发生错误时调用, 此时音频播放正常, 无画面, 可以在该函数内添加处理逻辑。
         * @param mp 播放器实例
         */
        void onVideoParseError(NELivePlayer mp);
}

该监听器在视频码流解析失败时的消息,此时音频播放正常,视频可能无画面,用户可以在此处添加处理逻辑,比如退出重新播放。

(9) OnDefinitionListener

public interface OnDefinitionListener {
        /**
         * 不同清晰度数据的回调
         * @param definitionDataList 不同清晰度数据,包括清晰度类型、是否正在使用等参数
         */
        void onParseDefinition(List<NEDefinitionData> definitionDataList);
        /**
         * 自动切换清晰度时回调的结果,当设置的地址是清晰度文件地址时,如果使用的是高清晰度的地址播放而网络状态较差,那么自动切换到清晰度最低的地址播放时的回调
         * @param definitionType 清晰度类型
         */
        void onAutoSwitchDefinition(NEDefinitionData.DefinitionType definitionType);
}

该监听器在解析完清晰度信息文件后回调 onParseDefinition,在自动切换到清晰度最低的地址播放时回调 onAutoSwitchDefinition。

(10) 接收资源释放成功的消息 资源释放结束的消息通过 BroadcastReceiver 进行接收。播放之前在需要的类中注册监听器(例如:在Activity的onCreate注册、onDestroy注销)。具体过程如下: 1、在manifest文件中添加用于接收资源释放结束的receiver,然后创建一个接收广播消息的类

public class NELivePlayerReceiver extends BroadcastReceiver {
    private final static String TAG = NELivePlayerReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        //接收播放器资源释放结束消息
        if (intent.getAction().equals(context.getPackageName() + NEType.NELP_ACTION_RECEIVE_RELEASE_SUCCESS_NOTIFICATION)) {
            Log.i(TAG, "NELivePlayer RELEASE SUCCESS!");
            NELivePlayerObserver.getInstance().onReceive();

        }
    }
}

2、添加注册监听器的方法

NELivePlayerObserver.getInstance().observeNELivePlayerObserver(observer,true);

3、添加注销监听器的方法

NELivePlayerObserver.getInstance().observeNELivePlayerObserver(observer,false);

通过以上步骤,我们就可以接收到资源释放成功的消息,开发者可以在收到该通知后做自定义的处理。具体实现可参见demo。

2.5 切换播放地址功能

在播放过程中,用户可以直接调用 switchContentUrl(url)来切换播放地址。

String url = "http://xxx.xxx.xxx.xxx/xxx.flv";
mLivePlayer.switchContentUrl(url);

注意:该接口仅限于播放过程中切换播放地址或播放完成后播放下一个视频,第一次播放时不能调用该接口

2.6 切换视频清晰度功能

首先需要调用setDataSource时设置不同清晰度信息的文件地址,播放器内部会对该文件进行解析,然后播放过程中,用户可以直接调用 switchDefinition(definition, callback)来切换播放的视频清晰度。

String url = "http://xxx.xxx.xxx.xxx/xxx.idx";
mLivePlayer.setDataSource(url);
mLivePlayer.switchDefinition(definition,callback);

2.7 截图功能

在软件解码的条件下,SDK 支持截图功能,可以在 VideoView 中调用 getSnapshot(bitmap) 接口来实现截图功能。

mLivePlayer.getSnapshot(bitmap)

截图结果以 bitmap 格式返回,开发者可根据自己的需求保存成 jpg 或 png 格式,并保存到指定位置,demo 默认以 jpg 格式保存在 /sdcard/ 目录下。

通过以上操作,最终的播放效果下图所示:

pic

3 API说明

有关API的详细说明,可参见SDK包中docs,打开index.html查看,或者打开下面的在线文档。

网易云视频播放器NELivePlayer Android SDK API详细文档