网站设计制作要多少钱,合肥庐阳区建设局网站,电子商务网站建设考题,网站建设上线多久声网 SDK项目集成与api使用整理
遥想约4年前#xff0c;也自行调研过#xff0c;虽然最终没有在实际项目中落地。 声网Android端集成与一对一音视频功能实现 现在#xff0c;终于要开始在项目中正式落地了#xff0c;而声网也从原来的v3.x升级到了v4.x版本了。根据官网介绍…声网 SDK项目集成与api使用整理
遥想约4年前也自行调研过虽然最终没有在实际项目中落地。 声网Android端集成与一对一音视频功能实现 现在终于要开始在项目中正式落地了而声网也从原来的v3.x升级到了v4.x版本了。根据官网介绍两大版本间改动还是比较大的。本次集成落地会直接用v4.x版本。 迁移指南
demo
示例项目 API-Examples 跑通 API 示例项目
生成token
进来这个地址 点击总览这里点击临时Token生成器生成。24小时有效期
水晶球
点击旁边的水晶球进入水晶球页面可以查看频道列表 点击对应频道可以进入其通话详情。可以看到对应用户的uid等信息
集成
发版说明 /app/build.gradle
//声网dependencies {...// x.y.z 替换为具体的 SDK 版本号如4.0.0 或 4.1.0-1implementation io.agora.rtc:full-sdk:x.y.z}//implementation io.agora.rtc:agora-special-full:4.1.1.26这里的集成可能会存在一些问题。 比如不同版本可能会有些api用不了比如说旧版本有些功能有些bug所以还是使用最新的推荐版本稳妥。
混淆
/app/proguard-rules.pro 文件
#声网
-keep class io.agora.**{*;}权限
/app/src/main/AndroidManifest.xml
!--必要权限--
uses-permission android:nameandroid.permission.INTERNET/!--可选权限--
uses-permission android:nameandroid.permission.CAMERA/
uses-permission android:nameandroid.permission.RECORD_AUDIO/
uses-permission android:nameandroid.permission.MODIFY_AUDIO_SETTINGS/
uses-permission android:nameandroid.permission.ACCESS_WIFI_STATE/
uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE/
uses-permission android:nameandroid.permission.BLUETOOTH/
!-- 对于 Android 12.0 及以上且集成 v4.1.0 以下 SDK 的设备还需要添加以下权限 --
uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT/
!-- 对于 Android 12.0 及以上设备还需要添加以下权限 --
uses-permission android:nameandroid.permission.READ_PHONE_STATE/
uses-permission android:nameandroid.permission.BLUETOOTH_SCAN/
tip这里有个需要特别注意的点。如果当前项目中目标版本不是Android 12.0且不是集成 v4.1.0 以下 SDK 的设备。不能加入最后的三个权限。否则部分机型会有闪退问题。目前验证是有一部华为手机鸿蒙4.0的会闪退。 参考链接快速开始-实现音视频互动
基本流程
创建 RtcEngineConfig 对象并进行配置。
// 先初始化相关
try {//创建 RtcEngineConfig 对象并进行配置initRtcEngineConfig()
} catch (e: Exception) {throw RuntimeException(Check the error.)
}private fun initRtcEngineConfig() {// 创建 RtcEngineConfig 对象并进行配置val config RtcEngineConfig()config.mContext baseContextconfig.mAppId config.mEventHandler mRtcEventHandler// 创建并初始化 RtcEnginemRtcEngine RtcEngine.create(config)}启用视频模块
private fun startVideo() {mRtcEngine?.apply {// 启用视频模块this.enableVideo()// 开启本地预览this.startPreview()// 创建一个 SurfaceView 对象并将其作为 FrameLayout 的子对象val container: FrameLayout findViewById(R.id.local_video_view_container)container.removeAllViews()container.addView(surfaceView)// 将 SurfaceView 对象传入声网实时互动 SDK设置本地视图this.setupLocalVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_HIDDEN, 0))}
}获取token并且处理token过期问题 private val mRtcEventHandler: IRtcEngineEventHandler object : IRtcEngineEventHandler() {//token监听override fun onTokenPrivilegeWillExpire(token: String?) {super.onTokenPrivilegeWillExpire(token)needUpdateToken truemPresenter.getLiveToken()}override fun onRequestToken() {super.onRequestToken()needUpdateToken truemPresenter.getLiveToken()}
}
//处理token过期问题
if (needUpdateToken) {mRtcEngine?.renewToken(mPresenter.roomToken?.token.orEmpty())return
}获取直播间配置参数
其中不同分辨率和帧率下适配的码率可以看这个文档
//获取直播间配置参数
mRtcEngine?.queryDeviceScore()?.let {mPresenter.getLiveConfig(it)
}
override fun getLiveConfig() {mPresenter.liveRoomConfig?.definition_high?.let {setVideoEncoderConfiguration(it)}
}
private fun setVideoEncoderConfiguration(configuration: LiveRoomDefinitionMedium) {if (renderMode ! configuration.render_mode) {mRtcEngine?.setupLocalVideo(VideoCanvas(surfaceView, configuration.render_mode, 0))renderMode configuration.render_mode}videoEncoderConfiguration.bitrate configuration.bitratevideoEncoderConfiguration.frameRate configuration.frame_rate //帧率videoEncoderConfiguration.mirrorMode configuration.getMirrorMode()videoEncoderConfiguration.dimensions configuration.getDimensions() //分辨率videoEncoderConfiguration.orientationMode configuration.getOrientationMode()//自适应模式val res mRtcEngine?.setVideoEncoderConfiguration(videoEncoderConfiguration)
}加入频道并发布音视频流
如果是要为极速直播则需要多设置将 options 参数设置为 AUDIENCE_LATENCY_LEVEL_LOW_LATENCY低延时。
//加入频道并发布音视频流
private fun initChannelMediaOptions() {mPresenter.roomToken?.let {// 创建 ChannelMediaOptions 对象并进行配置val options ChannelMediaOptions()options.clientRoleType if (isFromControl) Constants.CLIENT_ROLE_AUDIENCE else Constants.CLIENT_ROLE_BROADCASTERoptions.channelProfile Constants.CHANNEL_PROFILE_LIVE_BROADCASTINGval res mRtcEngine?.joinChannel(it.token,liveRoomBean.room_id,if (isFromControl) it.audience_uid.toIntDefault else it.live_uid.toIntDefault,options)if(res 0){isConnectionLost falseif(isFromControl.not()){mPresenter.startPushStream()}}} ?: run {toast(直播地址为空)}
}观看端
如果是设置观看端的话则可以使用setupRemoteVideo。
离开页面的时候需要留意关闭预览 mRtcEngine?.stopPreview()
mRtcEngine?.leaveChannel()一些功能使用和踩坑记录
处理截图问题
需要留意的是不能直接在takeSnapshot这里拿到路径就开始操作。而应该到回调onSnapshotTaken中处理。 处理的过程需要留意耗时的操作要放到子线程去执行。 private fun takeSnapshot(fileName: String ${System.currentTimeMillis()}.jpg) {val uid if(isFromControl) mPresenter.roomToken?.live_uid.toIntDefault else 0val filePath: String SaveUtils.mkdir(live) File.separator fileNameval ret: Int? mRtcEngine?.takeSnapshot(uid, filePath)
}tip这里遇到一个声网的bug在使用远端用户进行截图的时候能够正常返回图片资源。但是如果使用主播端则会发现有延迟问题。 通过排查定位发现这种延迟不是时间上的。而是会返回上一次截图的资源过来。目前已同步给声网声网表示已跟进这个bug。 但是我们还需要解决。所以首先这边是考虑自己实现截图。简易代码如下 fun takeScreen(view: View,path: String?,){val bitmap Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)val canvas Canvas(bitmap)view.draw(canvas)saveBitmap(bitmap, path)}private fun saveBitmap(bitmap: Bitmap,path: String? null,){try {val imageFile File(path)val fos FileOutputStream(imageFile)bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)fos.flush()fos.close()} catch (e: IOException) {e.printStackTrace()}}通过验证SurfaceView这样子截图是会失败的截取到数据是0size。于是改成传入其包裹的容器之后验证发现SurfaceView部分是黑屏。 当然也不能直接截全屏因为不符合业务需求。声网官方示例中是通过SurfaceView来作为视图组件的。后面通过验证使用TextureView替代SurfaceView也是可以。 而SurfaceView 是用于绘制图形的视图组件它通常不会保存绘制的内容而是直接将内容显示在屏幕上。具体来说SurfaceView 的绘制是由 SurfaceFlinger 系统服务管理的 它将 SurfaceView 的内容绘制到屏幕上的一个独立的 Surface 上。这个过程是在底层硬件加速的情况下进行的绘制的内容并不会保存在普通的 Bitmap 中 因此无法直接通过传统的方法获取 SurfaceView 的截图。这边不过多对SurfaceView做详解。 SurfaceView采用双缓存机制SurfaceView在更新视图时用到了两张 Canvas一张 frontCanvas 和一张 backCanvas 每次实际显示的是 frontCanvas backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候它已经提前帮你加载好后面一帧了所以播放起视频很流畅。当使用lockCanvas() 获取画布时得到的实际上是backCanvas 而不是正在显示的 frontCanvas 之后你在获取到的 backCanvas 上绘制新视图 再 unlockCanvasAndPostcanvas此视图那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas 原来的 frontCanvas 将切换到后台作为 backCanvas 。例如如果你已经先后两次绘制了视图A和B那么你再调用 lockCanvas() 获取视图获得的将是A而不是正在显示的B之后你将重绘的 A 视图上传那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上原来的B则转换为backCanvas。相当与多个线程交替解析和渲染每一帧视频数据 引用 https://www.jianshu.com/p/a2a235bee59e 普通View onDraw 内容是静态的不调invalidate() 它是不会发生变化你可以拿到里面的Bitmap但是SurfaceView不同无法拿到它back buffer里面的Bitmap。 回到解决问题本身既然没办法直接通过对SurfaceView截图。还是从声网的api入手。最后验证通过截图两次取第二次的方式可以解决该问题。 但是还需要注意的是不能简单粗暴直接调用两次api。否则api还是返回异常。而是在调用第一次之后在onSnapshotTaken再判断处理调起第二次。
设置清晰度
在设置清晰度的时候需要先得知当前设备的设备评分等级。使用的api是queryDeviceScore。然后再根据分数得到适合配置进行设置。 在高清或超高清视频场景下可以先调用该方法查询设备的等级评分。如果返回的评分较低比如低于 60则需要适当调低视频分辨率以避免影响视频体验。 private fun setVideoEncoderConfiguration(configuration: LiveRoomDefinitionMedium) {videoEncoderConfiguration.bitrate configuration.bitratevideoEncoderConfiguration.frameRate configuration.frame_rate //帧率videoEncoderConfiguration.mirrorMode configuration.getMirrorMode()videoEncoderConfiguration.dimensions configuration.getDimensions() //分辨率videoEncoderConfiguration.orientationMode configuration.getOrientationMode()//自适应模式val res mRtcEngine?.setVideoEncoderConfiguration(videoEncoderConfiguration)
}token失效问题
不仅在加入频道的时候需要token直播过程也会一直监测token的时效。因此还需要监听。声网提供了两个api
onTokenPrivilegeWillExpireonRequestToken。 需要在监听到token过期或即将过期的时候重新拿到新的token并通过renewToken重新赋值。
对焦问题
声网提供了相关对焦的功能包括人脸自动对焦和手动对焦功能。
isCameraFocusSupported 检测设备是否支持手动对焦功能isCameraAutoFocusFaceModeSupported 检测设备是否支持人脸对焦功能setCameraFocusPositionInPreview 设置手动对焦位置并触发对焦setCameraAutoFocusFaceModeEnabled 设置是否开启人脸对焦功能 经过验证发现人脸对焦在前置摄像的时候会检测不支持。因此联系声网得到反馈是就算开启了人脸对焦也会比较损耗性能。 再加上经过多次验证发现再切换镜头的时候立即调用也偶现失败。结合业务大多是开启前置因此只接入手动对焦功能。 而手动对焦功能发现相比腾讯的手动对焦声网的手动对焦感官体验上只会晃动一下因此最好还是像腾讯的一样加多个动效。 目前初步实现如下感兴趣可以看看也可以直接跳过这趴
surfaceView.setOnTouchListener { view, event -when (event.action) {MotionEvent.ACTION_DOWN - {val x event.xval y event.y// 在这里处理点击事件可以使用 x 和 y 坐标执行相应操作// 检测当前设备是否支持手动对焦并设置。if (isCameraFocusSupported) {// 假设在屏幕(50100)的位置对焦。val res1 setCameraFocusPositionInPreview(x, y)if(res1 0){val borderAnimationView BorderAnimationView(thisLivePlayerActivityV2)local_video_view_container.addView(borderAnimationView)borderAnimationView.showAnimation(x, y)Handler(Looper.getMainLooper()).postDelayed({local_video_view_container.removeView(borderAnimationView)}, 500) //850毫秒后执行}}true}else - false}}BorderAnimationView则是一个直播对焦边框动画View这边处理的方式通过绘制的方式画出对应区域内的一个白色边框。然后开启缩放动画效果最后消失。 不多说初步代码如下
class BorderAnimationView(context: Context) : View(context) {private var x 0fprivate var y 0fprivate var scale 1fprivate var paint Paint().apply {color Color.WHITEstyle Paint.Style.STROKEstrokeWidth 3f}fun showAnimation(x: Float, y: Float) {this.x xthis.y yscale 1finvalidate() // 请求重绘startScaleAnimation()}private fun startScaleAnimation() {val scaleTo 1.25fval scaleBack 1fval scaleAnimation ValueAnimator.ofFloat(scale, scaleTo)scaleAnimation.addUpdateListener { valueAnimator -scale valueAnimator.animatedValue as Floatinvalidate()}scaleAnimation.duration 250 // 0.25秒scaleAnimation.interpolator AccelerateDecelerateInterpolator()val scaleBackAnimation ValueAnimator.ofFloat(scaleTo, scaleBack)scaleBackAnimation.addUpdateListener { valueAnimator -scale valueAnimator.animatedValue as Floatinvalidate()}scaleBackAnimation.duration 350 // 0.35秒scaleBackAnimation.interpolator AccelerateDecelerateInterpolator()scaleAnimation.addListener(object : AnimatorListenerAdapter() {override fun onAnimationEnd(animation: Animator?) {scaleBackAnimation.start()}})scaleAnimation.start()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val scaledSize 80.dp * scaleval halfScaledSize scaledSize / 2canvas.drawRect(x - halfScaledSize, y - halfScaledSize, x halfScaledSize, y halfScaledSize, paint) // 绘制带有白色边框的矩形}
}网络监听
直播过程避免网络波动因此需要监听网络做出必要的交互。声网提供的api是onNetworkQuality。 需要注意的是要判断是主播端还是观看端。 如果是主播端则应该拿上行网络质量txQuality进行判断如果是观看端则应该用下行网络质量rxQuality。 不足的地方是只能拿到网络质量状态的枚举而没能拿到具体网络速率等数值。
其他
美颜美白这个主要是业务逻辑比较多实际核心代码就是调用setBeautyEffectOptions进行设置。声网还支持了更多的美颜面板设置。
mRtcEngine?.setBeautyEffectOptions(true, options)告警通知服务比如视频卡顿率在一定周期内连续大于某个阈值。告警通知…