diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 6bc384b..66e4918 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -81,12 +81,6 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
// ZXing 二维码扫描库
implementation("com.google.zxing:core:3.5.2")
- // ExoPlayer 核心库
- implementation("androidx.media3:media3-exoplayer:1.9.0")
- // ExoPlayer UI组件库,提供PlayerView等
- implementation("androidx.media3:media3-ui:1.9.0")
- // ExoPlayer 通用库
- implementation("androidx.media3:media3-common:1.9.0")
// 基础依赖包,必须要依赖
implementation("com.geyifeng.immersionbar:immersionbar:3.2.2")
// kotlin扩展(可选)
@@ -107,5 +101,7 @@ dependencies {
implementation("com.yanzhenjie.recyclerview:x:1.3.2")
//Android-PickerView-时间弹窗
implementation("com.contrarywind:Android-PickerView:4.1.9")
+ //基于exoPlayer 自定义播放器 JPlayer支持功能
+ implementation("com.ycjiang:VideoPlayModule-Lite:2.3.61")
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt b/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt
index aa67c87..bbbf1d4 100644
--- a/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt
+++ b/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt
@@ -2,55 +2,40 @@ package com.example.defenseapplication.liveVideo
import android.Manifest
import android.content.Intent
-import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
-import android.media.AudioManager
-import android.media.MediaPlayer
import android.media.MediaRecorder
-import android.net.Uri
-import android.os.Build
import android.os.Bundle
-import android.os.Environment
-import android.view.SurfaceHolder
+import android.util.Log
import android.view.View
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.view.animation.Animation
-import android.view.animation.LinearInterpolator
import android.view.animation.ScaleAnimation
import android.widget.Toast
-import androidx.activity.OnBackPressedDispatcher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
+import chuangyuan.ycj.videolibrary.video.ExoUserPlayer
import com.example.defenseapplication.R
import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.databinding.LiveVideoActivityBinding
import com.example.defenseapplication.utils.ActivityManager
-import com.example.defenseapplication.utils.DeviceIdEvent
import com.gyf.immersionbar.ImmersionBar
-import com.gyf.immersionbar.ktx.destroyImmersionBar
import java.io.File
import java.io.IOException
+import android.content.res.Configuration
+import androidx.activity.OnBackPressedCallback
+import chuangyuan.ycj.videolibrary.listener.VideoInfoListener
+import chuangyuan.ycj.videolibrary.video.VideoPlayerManager
private const val TAG = "LiveVideoActivity"
//智能防御机A1
-class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
+class LiveVideoActivity : BaseActivity() {
private lateinit var binding: LiveVideoActivityBinding
-
- private var mediaPlayer: MediaPlayer? = null
- private var isFullscreen = false
- private var isPlaying = false
+
+ private lateinit var exoPlayerManager: ExoUserPlayer
private var isVoiceEnabled = false
private var isLightOn = false
private var isRecording = false
private var mediaRecorder: MediaRecorder? = null
private var micPulseAnimation: ScaleAnimation? = null
- private var videoWidth = 0
- private var videoHeight = 0
-
- private val testVideoUrl = "https://vjs.zencdn.net/v/oceans.mp4"
-
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
@@ -78,93 +63,55 @@ class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
binding.tvTitle.text = deviceName
}
- initSurfaceView()
+ // 初始化播放器
+ initVideoPlayer()
initListener()
- }
-
- private fun initSurfaceView() {
- binding.surfaceView.holder.addCallback(this)
- }
-
- override fun surfaceCreated(holder: SurfaceHolder) {
- initMediaPlayer(holder)
- }
-
- override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
- adjustSurfaceViewSize()
- }
-
- override fun surfaceDestroyed(holder: SurfaceHolder) {
- releaseMediaPlayer()
- }
-
- private fun initMediaPlayer(holder: SurfaceHolder) {
- mediaPlayer = MediaPlayer().apply {
- setDisplay(holder)
- setAudioStreamType(AudioManager.STREAM_MUSIC)
-
- try {
- setDataSource(this@LiveVideoActivity, Uri.parse(testVideoUrl))
- prepareAsync()
-
- setOnPreparedListener { mp ->
- this@LiveVideoActivity.videoWidth = mp.videoWidth
- this@LiveVideoActivity.videoHeight = mp.videoHeight
- adjustSurfaceViewSize()
- mp.isLooping = true
- mp.start()
- this@LiveVideoActivity.isPlaying = true
- Toast.makeText(this@LiveVideoActivity, R.string.video_ready, Toast.LENGTH_SHORT).show()
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (exoPlayerManager.onBackPressed()) {
+ return
}
-
- setOnCompletionListener {
- this@LiveVideoActivity.isPlaying = false
- Toast.makeText(this@LiveVideoActivity, R.string.playback_complete, Toast.LENGTH_SHORT).show()
- }
-
- setOnErrorListener { _, what, extra ->
- Toast.makeText(this@LiveVideoActivity, getString(R.string.video_error_format, what, extra), Toast.LENGTH_SHORT).show()
- releaseMediaPlayer()
- true
- }
-
- setOnBufferingUpdateListener { _, percent ->
- if (percent < 100) {
- Toast.makeText(this@LiveVideoActivity, getString(R.string.buffering_format, percent), Toast.LENGTH_SHORT).show()
- }
- }
-
- } catch (e: IOException) {
- e.printStackTrace()
- Toast.makeText(this@LiveVideoActivity, R.string.cannot_load_video, Toast.LENGTH_SHORT).show()
+ // 无全屏时执行默认返回逻辑(如 finish)
+ finish()
}
- }
+ })
}
+ private fun initVideoPlayer() {
+ val videoPlayerView = binding.exoPlayContextId
- private fun adjustSurfaceViewSize() {
- if (videoWidth == 0 || videoHeight == 0) return
-
- val screenWidth = resources.displayMetrics.widthPixels
- val screenHeight = resources.displayMetrics.heightPixels
- val aspectRatio = videoWidth.toFloat() / videoHeight.toFloat()
-
- val params = binding.surfaceView.layoutParams
-
- if (isFullscreen) {
- if (screenWidth.toFloat() / screenHeight.toFloat() > aspectRatio) {
- params.width = (screenHeight * aspectRatio).toInt()
- params.height = screenHeight
- } else {
- params.width = screenWidth
- params.height = (screenWidth / aspectRatio).toInt()
- }
- } else {
- val containerHeight = binding.videoContainer.height
- params.width = (containerHeight * aspectRatio).toInt()
- params.height = containerHeight
- }
-
- binding.surfaceView.layoutParams = params
+ exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, videoPlayerView)
+ .setPlayUri("https://media.w3.org/2010/05/sintel/trailer.mp4")//真实地址
+ .setPosition(0)
+ .addVideoInfoListener(object : VideoInfoListener {
+ // 注意:此处的 p0: Long 是视频开始播放时的位置信息,可以按需使用
+ override fun onPlayStart(p0: Long) {
+ Log.d(TAG, "视频开始播放")
+ }
+
+ override fun onLoadingChanged() {
+ // 视频加载状态变化
+ }
+
+ override fun onPlayerError(p0: com.google.android.exoplayer2.ExoPlaybackException?) {
+ Toast.makeText(this@LiveVideoActivity, "播放失败", Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onPlayEnd() {
+ Log.d(TAG, "播放结束")
+ }
+
+ // 修正点:库的接口要求必须实现 isPlaying 方法
+ override fun isPlaying(p0: Boolean) {
+ // p0 表示当前是否正在播放
+ }
+
+ fun onRepeatModeChanged(p0: Int) {
+ // 循环模式改变时的回调
+ }
+ })
+ .create()
+
+ exoPlayerManager.startPlayer()
}
private fun initListener() {
@@ -180,15 +127,6 @@ class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
-
- binding.ivFullscreen.setOnClickListener {
- toggleFullscreen()
- }
-
- binding.surfaceView.setOnClickListener {
- togglePlayPause()
- }
-
binding.llVoice.setOnClickListener {
toggleVoice()
}
@@ -212,90 +150,14 @@ class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
}
}
- private fun togglePlayPause() {
- mediaPlayer?.let { mp ->
- if (mp.isPlaying) {
- mp.pause()
- isPlaying = false
- Toast.makeText(this, R.string.paused, Toast.LENGTH_SHORT).show()
- } else {
- mp.start()
- isPlaying = true
- Toast.makeText(this, R.string.resume_playback, Toast.LENGTH_SHORT).show()
- }
- }
- }
-
- private fun toggleFullscreen() {
- isFullscreen = !isFullscreen
-
- if (isFullscreen) {
- enterFullscreen()
- } else {
- exitFullscreen()
- }
- }
-
- private fun enterFullscreen() {
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
- window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
- window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
-
- binding.topBar.visibility = View.GONE
- binding.bitRateBar.visibility = View.GONE
- binding.functionBar.visibility = View.GONE
-
- val alarmLogParent = binding.llAlarmLog.parent.parent as View
- alarmLogParent.visibility = View.GONE
-
- val containerParams = binding.videoContainer.layoutParams
- containerParams.width = ViewGroup.LayoutParams.MATCH_PARENT
- containerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
- binding.videoContainer.layoutParams = containerParams
-
- val surfaceParams = binding.surfaceView.layoutParams
- surfaceParams.width = resources.displayMetrics.widthPixels
- surfaceParams.height = resources.displayMetrics.heightPixels
- binding.surfaceView.layoutParams = surfaceParams
-
- binding.ivFullscreen.setImageResource(com.example.defenseapplication.R.drawable.back)
-
- Toast.makeText(this, R.string.entered_fullscreen, Toast.LENGTH_SHORT).show()
- }
-
- private fun exitFullscreen() {
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
- window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
-
- binding.topBar.visibility = View.VISIBLE
- binding.bitRateBar.visibility = View.VISIBLE
- binding.functionBar.visibility = View.VISIBLE
-
- val alarmLogParent = binding.llAlarmLog.parent.parent as View
- alarmLogParent.visibility = View.VISIBLE
-
- val containerParams = binding.videoContainer.layoutParams
- containerParams.width = ViewGroup.LayoutParams.MATCH_PARENT
- containerParams.height = (220 * resources.displayMetrics.density).toInt()
- binding.videoContainer.layoutParams = containerParams
-
- adjustSurfaceViewSize()
-
- binding.ivFullscreen.setImageResource(com.example.defenseapplication.R.drawable.full_screen)
-
- Toast.makeText(this, R.string.exited_fullscreen, Toast.LENGTH_SHORT).show()
- }
private fun toggleVoice() {
isVoiceEnabled = !isVoiceEnabled
if (isVoiceEnabled) {
- mediaPlayer?.setVolume(1.0f, 1.0f)
binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice_on_open)
Toast.makeText(this, R.string.sound_on, Toast.LENGTH_SHORT).show()
} else {
- mediaPlayer?.setVolume(0.0f, 0.0f)
binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice)
Toast.makeText(this, R.string.sound_off, Toast.LENGTH_SHORT).show()
}
@@ -378,20 +240,6 @@ class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
Toast.makeText(this, R.string.stop_control_mode, Toast.LENGTH_SHORT).show()
}
-// private fun startMicPulseAnimation() {
-// micPulseAnimation = ScaleAnimation(
-// 1.0f, 1.3f,
-// 1.0f, 1.3f,
-// Animation.RELATIVE_TO_SELF, 0.5f,
-// Animation.RELATIVE_TO_SELF, 0.5f
-// ).apply {
-// duration = 500
-// repeatCount = Animation.INFINITE
-// repeatMode = Animation.REVERSE
-// interpolator = LinearInterpolator()
-// }
-// binding.ivVoice.startAnimation(micPulseAnimation)
-// }
private fun stopMicPulseAnimation() {
micPulseAnimation?.let {
@@ -434,32 +282,26 @@ class LiveVideoActivity : BaseActivity(), SurfaceHolder.Callback {
mediaRecorder = null
}
}
-
- private fun releaseMediaPlayer() {
- mediaPlayer?.let {
- if (it.isPlaying) {
- it.stop()
- }
- it.release()
- mediaPlayer = null
- }
+ override fun onResume() {
+ super.onResume()
+ exoPlayerManager.onResume()
}
override fun onPause() {
super.onPause()
- mediaPlayer?.pause()
- }
-
- override fun onResume() {
- super.onResume()
- mediaPlayer?.start()
+ exoPlayerManager.onPause()
}
override fun onDestroy() {
- releaseMediaPlayer()
super.onDestroy()
+ exoPlayerManager.onDestroy()
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ // 横竖屏切换时通知播放器
+ exoPlayerManager.onConfigurationChanged(newConfig)
+ }
}
diff --git a/app/src/main/res/layout/live_video_activity.xml b/app/src/main/res/layout/live_video_activity.xml
index 0ef3109..be6562e 100644
--- a/app/src/main/res/layout/live_video_activity.xml
+++ b/app/src/main/res/layout/live_video_activity.xml
@@ -4,6 +4,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F6F6"
+ android:name=".liveVideo.LiveVideoActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:screenOrientation="portrait"
android:orientation="vertical">
@@ -41,68 +44,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="250dp"
+ android:background="@android:color/transparent"/>