每次按照官方文档接入都有一些小坑,在此记录一下
这篇我尽量详细介绍一下,跟着做百分百成功接入。
安卓 接入 萤石 SDK 入门步骤
0.总结
配置流程:
- 配置Module中的build.gradle时 记得加上上okhttp3和gson的包
- 配置Manifests时按权限需配置
- 代码混淆一定要加上
使用流程
xml界面无需多言,使用原生SurfaceView组件
java代码播放时
- 要先在onCreate中初始化SDK
- 然后设置SurfaceHolder的回调函数
- 不需要时记得停止播放和销毁
1.配置 Module的build.gradle
官方文档只给出了三个EZVIZ的包,实测仍需okhttp3和gson的包
dependencies {
/*这两个是EZVIZSDK需要*/
implementation 'com.squareup.okhttp3:okhttp:3.14.7'
implementation 'com.google.code.gson:gson:2.8.6'
/*萤石SDK核心模块,必须依赖*/
implementation 'com.ezviz.sdk:ezviz-sdk:4.16.1'
/*视频通话模块,按需使用*/
implementation 'com.ezviz.sdk:videotalk:1.2.0'
/*码流获取模块,按需使用*/
implementation 'com.ezviz.sdk:streamctrl:1.2.0'
}
2.Manifests.xml配置
如果有需要登录页面登录账号的,需要配置Activity和receiver,
如果只是通过AppKey、AccessToken、摄像头序列号、通道号、验证码来播放的,可以只配置权限,Activity和receiver不用管。
权限
按需添加,这里我都需要用到,所以全部添加
<!--基础功能所需权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!--配网所需权限-->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!--对讲所需权限-->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--视频通话权限所需-->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Activity
用于启动萤石登录页,如果无需登录页可以忽略
<activity
android:name="com.videogo.main.EzvizWebViewActivity"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
</activity>
广播接收器
用于检测是否登陆成功以及参数的获取,同上,也可以不添加
<receiver
android:name="you_BroadcastReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.videogo.action.OAUTH_SUCCESS_ACTION" />
</intent-filter>
</receiver>
因为本项目还需要接受其他的广播,所以使用过滤器将广播信息接收到自定义的广播接收器中去了
和我有一样需求的,可以将下面代码添加到receiver中去
<intent-filter>
<action android:name="com.videogo.action.OAUTH_SUCCESS_ACTION" />
</intent-filter>
最终manifasts.xml终配置如下所示
android:usesCleartextTraffic="true"// 允许访问http链接
因为项目中需要访问http链接,所以加上了这一句,不确定EZVIZSDK是否需要此权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="*.*.automaticequipment">
<!-- 基础功能所需权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 配网所需权限 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!-- 对讲所需权限 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 视频通话权限所需 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AutoMaticEquipment"
android:usesCleartextTraffic="true"
tools:targetApi="m">
<activity android:name=".*" />
<activity android:name=".*" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.videogo.main.EzvizWebViewActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait" />
<service android:name=".Services.ControlService" />
<receiver
android:name=".Receivers.ControlReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
<action android:name="cn.lyom.CUSTOM_INTENT" />
</intent-filter>
<intent-filter>
<action android:name="com.videogo.action.OAUTH_SUCCESS_ACTION" />
</intent-filter>
</receiver>
</application>
</manifest>
3.代码混淆配置
打开proguard-rules.pro
点击1处改成project,点击2处打开文件
将下列代码粘贴进此文件
#========SDK对外接口=======#
-keep class com.ezviz.opensdk.** { *;}
#========以下是hik二方库=======#
-dontwarn com.ezviz.**
-keep class com.ezviz.** { *;}
-dontwarn com.ez.**
-keep class com.ez.** { *;}
-dontwarn com.hc.CASClient.**
-keep class com.hc.CASClient.** { *;}
-dontwarn com.videogo.**
-keep class com.videogo.** { *;}
-dontwarn com.hik.TTSClient.**
-keep class com.hik.TTSClient.** { *;}
-dontwarn com.hik.stunclient.**
-keep class com.hik.stunclient.** { *;}
-dontwarn com.hik.streamclient.**
-keep class com.hik.streamclient.** { *;}
-dontwarn com.hikvision.sadp.**
-keep class com.hikvision.sadp.** { *;}
-dontwarn com.hikvision.netsdk.**
-keep class com.hikvision.netsdk.** { *;}
-dontwarn com.neutral.netsdk.**
-keep class com.neutral.netsdk.** { *;}
-dontwarn com.hikvision.audio.**
-keep class com.hikvision.audio.** { *;}
-dontwarn com.mediaplayer.audio.**
-keep class com.mediaplayer.audio.** { *;}
-dontwarn com.hikvision.wifi.**
-keep class com.hikvision.wifi.** { *;}
-dontwarn com.hikvision.keyprotect.**
-keep class com.hikvision.keyprotect.** { *;}
-dontwarn com.hikvision.audio.**
-keep class com.hikvision.audio.** { *;}
-dontwarn org.MediaPlayer.PlayM4.**
-keep class org.MediaPlayer.PlayM4.** { *;}
#========以上是hik二方库=======#
#========以下是第三方开源库=======#
# JNA
-dontwarn com.sun.jna.**
-keep class com.sun.jna.** { *;}
# Gson
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.idea.fifaalarmclock.entity.***
-keep class com.google.gson.stream.** { *; }
# OkHttp
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
# 必须额外加的,否则编译无法通过
-dontwarn okio.**
#========以上是第三方开源库=======#
4.界面XML
Activity的xml中添加如下代码用作播放
<SurfaceView
android:id="@+id/mSv"
android:layout_width="352dp"
android:layout_height="288dp"/>
5.Activity播放
初始化SDK
SDK初始化需要放在onCreate中,建议封装成函数,在onCreate中调用
private void EZinit(){
/** * sdk日志开关,正式发布需要去掉 */
EZOpenSDK.showSDKLog(true);
/** * 设置是否支持P2P取流,详见api */
EZOpenSDK.enableP2P(true);
/** * APP_KEY请替换成自己申请的 */
String AppKey= "";
String AccessToken = "";
EZOpenSDK.initLib(getApplication(), AppKey);
EZOpenSDK.getInstance().setAccessToken(AccessToken);
}
设置SurfaceHolder回调
此处不设置将无法进行播放,在onCreate中添加如下代码
private void EZAddSvCallBack(){
binding.mSv.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (player != null) {
player.setSurfaceHold(holder);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (player != null) {
player.setSurfaceHold(null);
}
}
});
}
创建handler
Handler需要用来接收播放结果消息
可以根据官方文档进行排错
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what){
case 102: // 成功消息
break;
case 103: // 失败消息,附加代码
//播放失败,得到失败信息
ErrorInfo errorinfo = (ErrorInfo) msg.obj;
//得到播放失败错误码
int code = errorinfo.errorCode;
//得到播放失败模块错误码
String codeStr = errorinfo.moduleCode;
//得到播放失败描述
String description = errorinfo.description;
//得到播放失败解决方方案
String description1 = errorinfo.sulution;
Toast.makeText(getBaseContext(), "code:" + code , Toast.LENGTH_LONG).show();
break;
case 134: // 解析出视频画面分辨率回调
try {
String temp = (String) msg.obj;
String[] strings = temp.split(":");
int mVideoWidth = Integer.parseInt(strings[0]);
int mVideoHeight = Integer.parseInt(strings[1]);
//解析出视频分辨率
} catch (Exception e) {
e.printStackTrace();
}
break;
case 300: // 获取到当前取流类型
break;
default:
Toast.makeText(getBaseContext(), "what:" + String.valueOf(msg.what), Toast.LENGTH_LONG).show();
break;
}
return false;
}
});
创建摄像头
创建外部变量java ZPlayer player;
即在onCreate外边创建此变量
因为播放摄像头需要牵扯到停止,以及资源释放,所以需要外部变量
private void EZreadyStart(){
String deviceSerial = ""; // 摄像头序列号
int cameraNo = 1; // 摄像头通道号
player = EZOpenSDK.getInstance().createPlayer(deviceSerial, cameraNo);
//设置Handler, 该handler将被用于从播放器向handler传递消息
player.setHandler(handler);
//设置播放器的显示Surface
player.setSurfaceHold(binding.mSv.getHolder());
}
创建 播放、停止、释放资源函数
private void EZStart(){
player.startRealPlay();
}
private void EZStop(){
player.stopRealPlay();
}
private void EZRelease(){
player.release();
}
最终播放代码看起来像这样
package *.*.*;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.shy.rockerview.MyRockerView;
import com.videogo.errorlayer.ErrorInfo;
import com.videogo.openapi.EZConstants;
import com.videogo.openapi.EZOpenSDK;
import com.videogo.openapi.EZPlayer;
import java.util.Arrays;
import *.*.*.*.ControlService;
import *.*.*.*.DataConvert;
import *.*.*.Tools.Tools;
import *.*.*.Utils.TCPClient;
import *.*.*.bean.Order;
import *.*.*.databinding.ActivityControlEpBinding;
public class ControlEp extends AppCompatActivity {
ActivityControlEpBinding binding;
EZPlayer player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityControlEpBinding.inflate(getLayoutInflater());
Tools.FullScreen(this);
setContentView(binding.getRoot());
EZinit();
EZAddSvCallBack();
EZreadyStart();
EZStart();
}
private void EZinit(){
/** * sdk日志开关,正式发布需要去掉 */
EZOpenSDK.showSDKLog(true);
/** * 设置是否支持P2P取流,详见api */
EZOpenSDK.enableP2P(true);
/** * APP_KEY请替换成自己申请的 */
String AppKey= "";
String AccessToken = "";
EZOpenSDK.initLib(getApplication(), AppKey);
EZOpenSDK.getInstance().setAccessToken(AccessToken);
}
private void EZAddSvCallBack(){
binding.mSv.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (player != null) {
player.setSurfaceHold(holder);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (player != null) {
player.setSurfaceHold(null);
}
}
});
}
private void EZreadyStart(){
String deviceSerial = "D00304533"; // 摄像头序列号
int cameraNo = 1; // 摄像头通道号
player = EZOpenSDK.getInstance().createPlayer(deviceSerial, cameraNo);
// /**
// * 设备加密的需要传入密码
// * 传入视频加密密码,用于加密视频的解码,该接口可以在收到ERROR_INNER_VERIFYCODE_NEED或ERROR_INNER_VERIFYCODE_ERROR错误回调时调用
// * @param verifyCode 视频加密密码,默认为设备的6位验证码
// */
// player.setPlayVerifyCode(String verifyCode);
//设置Handler, 该handler将被用于从播放器向handler传递消息
player.setHandler(handler);
//设置播放器的显示Surface
player.setSurfaceHold(binding.mSv.getHolder());
}
private void EZStart(){
player.startRealPlay();
}
private void EZStop(){
player.stopRealPlay();
}
private void EZRelease(){
player.release();
}
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what){
case 102: // 成功消息
break;
case 103: // 失败消息,附加代码
//播放失败,得到失败信息
ErrorInfo errorinfo = (ErrorInfo) msg.obj;
//得到播放失败错误码
int code = errorinfo.errorCode;
//得到播放失败模块错误码
String codeStr = errorinfo.moduleCode;
//得到播放失败描述
String description = errorinfo.description;
//得到播放失败解决方方案
String description1 = errorinfo.sulution;
Toast.makeText(getBaseContext(), "code:" + code , Toast.LENGTH_LONG).show();
break;
case 134: // 解析出视频画面分辨率回调
try {
String temp = (String) msg.obj;
String[] strings = temp.split(":");
int mVideoWidth = Integer.parseInt(strings[0]);
int mVideoHeight = Integer.parseInt(strings[1]);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 300: // 获取到当前取流类型
break;
default:
Toast.makeText(getBaseContext(), "what:" + String.valueOf(msg.what), Toast.LENGTH_LONG).show();
break;
}
return false;
}
});
@Override
protected void onDestroy() {
socketRecvThread.interrupt();
socketSendThread.interrupt();
EZStop();
EZRelease();
super.onDestroy();
}
}
记得在销毁当前Activity时调用停止播放和释放资源,或者在不需要播放时调用。