Android | 萤石SDK接入

本文档详尽介绍了如何在Android应用中成功接入萤石SDK,包括Module的build.gradle配置、Manifests.xml权限设置、代码混淆、界面XML布局以及Activity的播放初始化等关键步骤,旨在帮助开发者避免常见问题,实现流畅的视频播放功能。
摘要由CSDN通过智能技术生成

参考链接
Android SDK 使用说明 · 萤石开放平台API文档
常量字段值

每次按照官方文档接入都有一些小坑,在此记录一下
这篇我尽量详细介绍一下,跟着做百分百成功接入。

0.总结

配置流程:

  1. 配置Module中的build.gradle时 记得加上上okhttp3和gson的包
  2. 配置Manifests时按权限需配置
  3. 代码混淆一定要加上

使用流程

xml界面无需多言,使用原生SurfaceView组件

java代码播放时

  1. 要先在onCreate中初始化SDK
  2. 然后设置SurfaceHolder的回调函数
  3. 不需要时记得停止播放和销毁

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时调用停止播放和释放资源,或者在不需要播放时调用。

Vue.js是一个流行的前端框架,用于构建用户界面。萤石云(EgisTrend Open Platform)是一款智能视频监控平台。在Vue项目中集成萤石云通常涉及API调用和组件交互。 要接入萤石云并实现实时通道切换,你需要完成以下步骤: 1. **注册并获取API密钥**: 首先,你需要在萤石云官网注册账号,并创建应用以便获得API Key和Secret Key。 2. **安装依赖**: 安装axios库作为HTTP请求工具,可以方便地与萤石云的REST API通信。 ```bash npm install axios ``` 3. **创建服务**: - 创建一个Vue的服务文件(如`vue-egistrend-api.js`),封装萤石云相关的函数,例如登录、设备列表获取和通道切换等。 ```javascript import axios from 'axios'; export const egistrendApi = { login(username, password) { // 发送POST请求登录 }, getDeviceList() { return axios.get('your-endpoint/device/list'); }, switchChannel(deviceId, channelId) { return axios.put(`your-endpoint/device/${deviceId}/channel`, { channel_id: channelId }); } }; ``` 4. **在组件中使用**: - 在需要显示和切换通道的组件里,注入这个服务,然后处理用户的操作,比如点击事件触发通道切换。 ```javascript <template> <button @click="switchToChannel(2)">切换到通道2</button> </template> <script> import { egistrendApi } from '@/services/vue-egistrend-api'; export default { methods: { switchToChannel(channelId) { egistrendApi.switchChannel('device-id', channelId).then(response => { console.log('切换成功:', response); }).catch(error => { console.error('切换失败:', error); }); } } } </script> ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

指针不南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值