diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt new file mode 100644 index 0000000..0bb0050 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt @@ -0,0 +1,81 @@ +package com.casic.br.operationsite.utils + +import android.view.View +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.model.VideoOptionModel +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import tv.danmaku.ijk.media.player.IjkMediaPlayer + +object VideoPlayerManager { + fun setGSYVideoPlayerOptions(videoPlayer: StandardGSYVideoPlayer, url: String) { + val list = ArrayList() + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt new file mode 100644 index 0000000..0bb0050 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt @@ -0,0 +1,81 @@ +package com.casic.br.operationsite.utils + +import android.view.View +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.model.VideoOptionModel +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import tv.danmaku.ijk.media.player.IjkMediaPlayer + +object VideoPlayerManager { + fun setGSYVideoPlayerOptions(videoPlayer: StandardGSYVideoPlayer, url: String) { + val list = ArrayList() + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt index c63c6c3..0bef03b 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt @@ -1,12 +1,11 @@ package com.casic.br.operationsite.view.check import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 -import android.util.Log +import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.casic.br.operationsite.R @@ -14,6 +13,7 @@ import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.RuntimeCache +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -21,12 +21,15 @@ import com.casic.br.operationsite.vm.WorkSiteViewModel import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.saveImage import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.ActivityStackManager import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -41,6 +44,7 @@ import java.util.TimerTask import java.util.concurrent.TimeUnit + class EnvironmentActivity : KotlinBaseActivity(), Handler.Callback { private val kTag = "EnvironmentActivity" @@ -86,8 +90,6 @@ } SocketManager.get.send(LocaleConstant.START_ENV_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_environment") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) } binding.blowerImageView.setOnClickListener { @@ -152,10 +154,14 @@ weakReferenceHandler = WeakReferenceHandler(this) constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() workSiteViewModel = ViewModelProvider(this)[WorkSiteViewModel::class.java] @@ -174,7 +180,6 @@ if (worker.lat.isNotBlank() && worker.lng.isNotBlank()) { val value = "CO:${worker.co}ppm, CH4:${worker.gas}ppm, H2S:${worker.co}ppm, O2:${worker.o2}%VOL" - Log.d(kTag, "value: $value") if (!isSecondConfirm && !isThirdConfirm) { binding.firstValueView.text = value @@ -257,8 +262,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt new file mode 100644 index 0000000..0bb0050 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt @@ -0,0 +1,81 @@ +package com.casic.br.operationsite.utils + +import android.view.View +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.model.VideoOptionModel +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import tv.danmaku.ijk.media.player.IjkMediaPlayer + +object VideoPlayerManager { + fun setGSYVideoPlayerOptions(videoPlayer: StandardGSYVideoPlayer, url: String) { + val list = ArrayList() + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt index c63c6c3..0bef03b 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt @@ -1,12 +1,11 @@ package com.casic.br.operationsite.view.check import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 -import android.util.Log +import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.casic.br.operationsite.R @@ -14,6 +13,7 @@ import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.RuntimeCache +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -21,12 +21,15 @@ import com.casic.br.operationsite.vm.WorkSiteViewModel import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.saveImage import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.ActivityStackManager import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -41,6 +44,7 @@ import java.util.TimerTask import java.util.concurrent.TimeUnit + class EnvironmentActivity : KotlinBaseActivity(), Handler.Callback { private val kTag = "EnvironmentActivity" @@ -86,8 +90,6 @@ } SocketManager.get.send(LocaleConstant.START_ENV_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_environment") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) } binding.blowerImageView.setOnClickListener { @@ -152,10 +154,14 @@ weakReferenceHandler = WeakReferenceHandler(this) constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() workSiteViewModel = ViewModelProvider(this)[WorkSiteViewModel::class.java] @@ -174,7 +180,6 @@ if (worker.lat.isNotBlank() && worker.lng.isNotBlank()) { val value = "CO:${worker.co}ppm, CH4:${worker.gas}ppm, H2S:${worker.co}ppm, O2:${worker.o2}%VOL" - Log.d(kTag, "value: $value") if (!isSecondConfirm && !isThirdConfirm) { binding.firstValueView.text = value @@ -257,8 +262,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt index 93e94c7..46954bc 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt @@ -130,7 +130,7 @@ weakReferenceHandler = WeakReferenceHandler(this) //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) + binding.videoView.setVideoURI(Uri.parse(LocaleConstant.RTSP_URL)) binding.videoView.setOnPreparedListener { binding.videoView.requestFocus() binding.videoView.start() diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt new file mode 100644 index 0000000..0bb0050 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt @@ -0,0 +1,81 @@ +package com.casic.br.operationsite.utils + +import android.view.View +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.model.VideoOptionModel +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import tv.danmaku.ijk.media.player.IjkMediaPlayer + +object VideoPlayerManager { + fun setGSYVideoPlayerOptions(videoPlayer: StandardGSYVideoPlayer, url: String) { + val list = ArrayList() + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt index c63c6c3..0bef03b 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt @@ -1,12 +1,11 @@ package com.casic.br.operationsite.view.check import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 -import android.util.Log +import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.casic.br.operationsite.R @@ -14,6 +13,7 @@ import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.RuntimeCache +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -21,12 +21,15 @@ import com.casic.br.operationsite.vm.WorkSiteViewModel import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.saveImage import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.ActivityStackManager import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -41,6 +44,7 @@ import java.util.TimerTask import java.util.concurrent.TimeUnit + class EnvironmentActivity : KotlinBaseActivity(), Handler.Callback { private val kTag = "EnvironmentActivity" @@ -86,8 +90,6 @@ } SocketManager.get.send(LocaleConstant.START_ENV_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_environment") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) } binding.blowerImageView.setOnClickListener { @@ -152,10 +154,14 @@ weakReferenceHandler = WeakReferenceHandler(this) constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() workSiteViewModel = ViewModelProvider(this)[WorkSiteViewModel::class.java] @@ -174,7 +180,6 @@ if (worker.lat.isNotBlank() && worker.lng.isNotBlank()) { val value = "CO:${worker.co}ppm, CH4:${worker.gas}ppm, H2S:${worker.co}ppm, O2:${worker.o2}%VOL" - Log.d(kTag, "value: $value") if (!isSecondConfirm && !isThirdConfirm) { binding.firstValueView.text = value @@ -257,8 +262,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt index 93e94c7..46954bc 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt @@ -130,7 +130,7 @@ weakReferenceHandler = WeakReferenceHandler(this) //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) + binding.videoView.setVideoURI(Uri.parse(LocaleConstant.RTSP_URL)) binding.videoView.setOnPreparedListener { binding.videoView.requestFocus() binding.videoView.start() diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt index 9688a09..8949056 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt @@ -2,13 +2,13 @@ import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 import android.util.Log import android.view.View +import android.widget.LinearLayout import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -16,6 +16,7 @@ import com.casic.br.operationsite.databinding.ActivitySuppliesBinding import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -34,6 +35,7 @@ import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -101,8 +103,6 @@ Log.d(kTag, "startVideoLauncher: ") SocketManager.get.send(LocaleConstant.START_VIDEO_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_protection") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) binding.stepView.text = "稍后开始检查第一项:四合一,请准备" } @@ -154,10 +154,14 @@ constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() timer.schedule(object : TimerTask() { @@ -169,8 +173,7 @@ }, 0, 5000) //左右边距 - val viewWidth = getScreenWidth() - (15 + 15).dp2px(this) - imageAdapter = EditableImageAdapter(this, recyclerViewImages, viewWidth, 6, 3) + imageAdapter = EditableImageAdapter(this, recyclerViewImages, videoWidth, 6, 3) binding.recyclerView.addItemDecoration( RecyclerViewItemOffsets(marginOffset, marginOffset, marginOffset, marginOffset) ) @@ -248,8 +251,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 45330a8..645873b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,8 +113,12 @@ implementation "androidx.camera:camera-view:$camerax_version" //TCP implementation 'io.netty:netty-all:4.1.23.Final' - //播放RTSP流 - implementation 'com.github.NodeMedia:NodeMediaClient-Android:2.8.4' //WebView implementation 'com.just.agentweb:agentweb-androidx:4.1.4' + //视频播放器,RTSP流 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:v8.4.0-release-jitpack' + //是否需要ExoPlayer模式 + implementation 'com.github.CarGuo.GSYVideoPlayer:GSYVideoPlayer-exo2:v8.4.0-release-jitpack' + //更多ijk的编码支持 + implementation 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.4.0-release-jitpack' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index be24acf..c3ead82 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -54,6 +54,7 @@ //一体机算法程序IP地址 const val AI_BASE_IP = "192.168.10.141" const val AI_BASE_URL = "http://${AI_BASE_IP}:5000" + const val RTSP_URL = "rtsp://192.168.10.137:554" const val DEVICE_CONTROLLER_URL = "https://gbs.ntvgbs.cn/login/" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" const val AI_SERVER_CONFIG = "aiServerConfig" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt new file mode 100644 index 0000000..0bb0050 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/VideoPlayerManager.kt @@ -0,0 +1,81 @@ +package com.casic.br.operationsite.utils + +import android.view.View +import com.shuyu.gsyvideoplayer.GSYVideoManager +import com.shuyu.gsyvideoplayer.model.VideoOptionModel +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import tv.danmaku.ijk.media.player.IjkMediaPlayer + +object VideoPlayerManager { + fun setGSYVideoPlayerOptions(videoPlayer: StandardGSYVideoPlayer, url: String) { + val list = ArrayList() + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt index c63c6c3..0bef03b 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt @@ -1,12 +1,11 @@ package com.casic.br.operationsite.view.check import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 -import android.util.Log +import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.casic.br.operationsite.R @@ -14,6 +13,7 @@ import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.RuntimeCache +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -21,12 +21,15 @@ import com.casic.br.operationsite.vm.WorkSiteViewModel import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.saveImage import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.ActivityStackManager import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -41,6 +44,7 @@ import java.util.TimerTask import java.util.concurrent.TimeUnit + class EnvironmentActivity : KotlinBaseActivity(), Handler.Callback { private val kTag = "EnvironmentActivity" @@ -86,8 +90,6 @@ } SocketManager.get.send(LocaleConstant.START_ENV_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_environment") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) } binding.blowerImageView.setOnClickListener { @@ -152,10 +154,14 @@ weakReferenceHandler = WeakReferenceHandler(this) constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() workSiteViewModel = ViewModelProvider(this)[WorkSiteViewModel::class.java] @@ -174,7 +180,6 @@ if (worker.lat.isNotBlank() && worker.lng.isNotBlank()) { val value = "CO:${worker.co}ppm, CH4:${worker.gas}ppm, H2S:${worker.co}ppm, O2:${worker.o2}%VOL" - Log.d(kTag, "value: $value") if (!isSecondConfirm && !isThirdConfirm) { binding.firstValueView.text = value @@ -257,8 +262,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt index 93e94c7..46954bc 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt @@ -130,7 +130,7 @@ weakReferenceHandler = WeakReferenceHandler(this) //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) + binding.videoView.setVideoURI(Uri.parse(LocaleConstant.RTSP_URL)) binding.videoView.setOnPreparedListener { binding.videoView.requestFocus() binding.videoView.start() diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt index 9688a09..8949056 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt @@ -2,13 +2,13 @@ import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 import android.util.Log import android.view.View +import android.widget.LinearLayout import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -16,6 +16,7 @@ import com.casic.br.operationsite.databinding.ActivitySuppliesBinding import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -34,6 +35,7 @@ import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -101,8 +103,6 @@ Log.d(kTag, "startVideoLauncher: ") SocketManager.get.send(LocaleConstant.START_VIDEO_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_protection") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) binding.stepView.text = "稍后开始检查第一项:四合一,请准备" } @@ -154,10 +154,14 @@ constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() timer.schedule(object : TimerTask() { @@ -169,8 +173,7 @@ }, 0, 5000) //左右边距 - val viewWidth = getScreenWidth() - (15 + 15).dp2px(this) - imageAdapter = EditableImageAdapter(this, recyclerViewImages, viewWidth, 6, 3) + imageAdapter = EditableImageAdapter(this, recyclerViewImages, videoWidth, 6, 3) binding.recyclerView.addItemDecoration( RecyclerViewItemOffsets(marginOffset, marginOffset, marginOffset, marginOffset) ) @@ -248,8 +251,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_environment.xml b/app/src/main/res/layout/activity_environment.xml index f313f2c..1b6d504 100644 --- a/app/src/main/res/layout/activity_environment.xml +++ b/app/src/main/res/layout/activity_environment.xml @@ -37,11 +37,11 @@ android:text="开始环境检测" android:textColor="@color/black" /> - + android:layout_height="220dp" + android:layout_marginVertical="@dimen/dp_5" /> () + + //开启软解码,硬解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0)) + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mmediacodec-handle-resolution-change", 0 + ) + ) + //软解码:1、打开,0、关闭 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "videotoolbox", 1)) + + // 每处理一个packet之后刷新io上下文 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1)) + //为什么拖动视屏会弹回来,因为ijk的FFMPEG对关键帧问题。 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1)) + + // 设置播放前的最大探测时间(单位毫秒),尝试减小这个值 + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_setup_timeout", "100" + ) + ) // 示例值,根据实际情况调整 + + // 视频帧处理不过来的时候丢弃一些帧达到同步的效果(如果视频帧数太高导致卡画面不同步) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 30)) + /***************rtsp 配置 */ + list.add( + VideoOptionModel( + IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video" + ) + ) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 2000)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", -1)) + // 或者尝试设置较小的缓冲大小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", 1316)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp")) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_flags", "prefer_tcp")) + //是否开启缓冲 设置无packet缓存 尝试减小或关闭预缓冲(1开启,0关闭)(谨慎设置,可能会增加卡顿风险)(一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1)) //是否限制输入缓存数,无限读 + // 设置播放前的探测时间 1,达到首屏秒开效果 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1)) + // 设置播放前的最大探测时间 (100未测试是否是最佳值) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", 100)) + //设置无packet缓存 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer")) + //分析码流时长:默认1024*1000 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100)) + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 10240)) + //设置是否开启变调 isModifyTone?0:1 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1)) + //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48)) + //播放重连次数 + list.add(VideoOptionModel(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", 5)) + /***************rtsp 配置 */ + // 应用这些配置到GsyVideoPlayer + GSYVideoManager.instance().optionModelList = list + + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_16_9) + + videoPlayer.titleTextView.visibility = View.GONE + videoPlayer.backButton.visibility = View.GONE + videoPlayer.fullscreenButton.visibility = View.GONE + videoPlayer.isReleaseWhenLossAudio = false + videoPlayer.setUp(url, false, "") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt index c63c6c3..0bef03b 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/EnvironmentActivity.kt @@ -1,12 +1,11 @@ package com.casic.br.operationsite.view.check import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 -import android.util.Log +import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.casic.br.operationsite.R @@ -14,6 +13,7 @@ import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.RuntimeCache +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -21,12 +21,15 @@ import com.casic.br.operationsite.vm.WorkSiteViewModel import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.saveImage import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.ActivityStackManager import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -41,6 +44,7 @@ import java.util.TimerTask import java.util.concurrent.TimeUnit + class EnvironmentActivity : KotlinBaseActivity(), Handler.Callback { private val kTag = "EnvironmentActivity" @@ -86,8 +90,6 @@ } SocketManager.get.send(LocaleConstant.START_ENV_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_environment") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) } binding.blowerImageView.setOnClickListener { @@ -152,10 +154,14 @@ weakReferenceHandler = WeakReferenceHandler(this) constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() workSiteViewModel = ViewModelProvider(this)[WorkSiteViewModel::class.java] @@ -174,7 +180,6 @@ if (worker.lat.isNotBlank() && worker.lng.isNotBlank()) { val value = "CO:${worker.co}ppm, CH4:${worker.gas}ppm, H2S:${worker.co}ppm, O2:${worker.o2}%VOL" - Log.d(kTag, "value: $value") if (!isSecondConfirm && !isThirdConfirm) { binding.firstValueView.text = value @@ -257,8 +262,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt index 93e94c7..46954bc 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/GuardiansActivity.kt @@ -130,7 +130,7 @@ weakReferenceHandler = WeakReferenceHandler(this) //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) + binding.videoView.setVideoURI(Uri.parse(LocaleConstant.RTSP_URL)) binding.videoView.setOnPreparedListener { binding.videoView.requestFocus() binding.videoView.start() diff --git a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt index 9688a09..8949056 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/check/SuppliesActivity.kt @@ -2,13 +2,13 @@ import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Base64 import android.util.Log import android.view.View +import android.widget.LinearLayout import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -16,6 +16,7 @@ import com.casic.br.operationsite.databinding.ActivitySuppliesBinding import com.casic.br.operationsite.extensions.initImmersionBar import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.VideoPlayerManager import com.casic.br.operationsite.utils.tcp.ISocketListener import com.casic.br.operationsite.utils.tcp.SocketManager import com.casic.br.operationsite.view.BigImageActivity @@ -34,6 +35,7 @@ import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.TitleBarView import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.shuyu.gsyvideoplayer.GSYVideoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient @@ -101,8 +103,6 @@ Log.d(kTag, "startVideoLauncher: ") SocketManager.get.send(LocaleConstant.START_VIDEO_COMMAND) constructionCheckViewModel.setCurrentPhase("before_operation_protection") - //播放RTSP流 - binding.videoView.setVideoURI(Uri.parse("rtsp://192.168.10.137:554")) binding.stepView.text = "稍后开始检查第一项:四合一,请准备" } @@ -154,10 +154,14 @@ constructionCheckViewModel = ViewModelProvider(this)[ConstructionCheckViewModel::class.java] - binding.videoView.setOnPreparedListener { - binding.videoView.requestFocus() - binding.videoView.start() - } + //动态设置rtspPlayerView宽高 + val params = binding.rtspPlayerView.layoutParams as LinearLayout.LayoutParams + val videoWidth = getScreenWidth() - 30.dp2px(this) + val videoHeight = videoWidth * (9f / 16) + params.width = videoWidth + params.height = videoHeight.toInt() + binding.rtspPlayerView.layoutParams = params + VideoPlayerManager.setGSYVideoPlayerOptions(binding.rtspPlayerView, LocaleConstant.RTSP_URL) timer = Timer() timer.schedule(object : TimerTask() { @@ -169,8 +173,7 @@ }, 0, 5000) //左右边距 - val viewWidth = getScreenWidth() - (15 + 15).dp2px(this) - imageAdapter = EditableImageAdapter(this, recyclerViewImages, viewWidth, 6, 3) + imageAdapter = EditableImageAdapter(this, recyclerViewImages, videoWidth, 6, 3) binding.recyclerView.addItemDecoration( RecyclerViewItemOffsets(marginOffset, marginOffset, marginOffset, marginOffset) ) @@ -248,8 +251,18 @@ override fun onDestroy() { super.onDestroy() - binding.videoView.suspend() + GSYVideoManager.releaseAllVideos() timer.cancel() webSocket?.close(1000, null) } + + override fun onPause() { + super.onPause() + GSYVideoManager.onPause() + } + + override fun onResume() { + super.onResume() + GSYVideoManager.onResume() + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_environment.xml b/app/src/main/res/layout/activity_environment.xml index f313f2c..1b6d504 100644 --- a/app/src/main/res/layout/activity_environment.xml +++ b/app/src/main/res/layout/activity_environment.xml @@ -37,11 +37,11 @@ android:text="开始环境检测" android:textColor="@color/black" /> - + android:layout_height="220dp" + android:layout_marginVertical="@dimen/dp_5" /> - + android:layout_height="220dp" + android:layout_marginVertical="@dimen/dp_15" />