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.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.appcompat.app.AppCompatActivity import androidx.compose.ui.test.isEnabled import androidx.core.content.ContextCompat import com.example.defenseapplication.R import com.example.defenseapplication.databinding.LiveVideoActivityBinding import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ktx.destroyImmersionBar import java.io.File import java.io.IOException private const val TAG = "LiveVideoActivity" class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { private lateinit var binding: LiveVideoActivityBinding private var mediaPlayer: MediaPlayer? = null private var isFullscreen = false private var isPlaying = false 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 -> if (isGranted) { startRecordingMode() } else { Toast.makeText(this, "需要录音权限才能使用控制功能", Toast.LENGTH_SHORT).show() isRecording = false } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = LiveVideoActivityBinding.inflate(layoutInflater) setContentView(binding.root) // ImmersionBar核心调用,沉浸式 ImmersionBar.with(this) .statusBarDarkFont(true) .titleBar(binding.topBar) .init() initSurfaceView() 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, "视频准备完成", Toast.LENGTH_SHORT).show() } setOnCompletionListener { this@LiveVideoActivity.isPlaying = false Toast.makeText(this@LiveVideoActivity, "播放完成", Toast.LENGTH_SHORT).show() } setOnErrorListener { _, what, extra -> Toast.makeText(this@LiveVideoActivity, "播放错误: $what, $extra", Toast.LENGTH_SHORT).show() releaseMediaPlayer() true } setOnBufferingUpdateListener { _, percent -> if (percent < 100) { Toast.makeText(this@LiveVideoActivity, "缓冲中: $percent%", Toast.LENGTH_SHORT).show() } } } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@LiveVideoActivity, "无法加载视频", Toast.LENGTH_SHORT).show() } } } 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 } private fun initListener() { binding.btnBack.setOnClickListener { finish() } binding.ivMenu.setOnClickListener { val intent = Intent(this, SettingsActivity::class.java) startActivity(intent) } binding.ivFullscreen.setOnClickListener { toggleFullscreen() } binding.surfaceView.setOnClickListener { togglePlayPause() } binding.llVoice.setOnClickListener { toggleVoice() } binding.llLight.setOnClickListener { toggleLight() } binding.llRecord.setOnClickListener { toggleRecording() } binding.llAlarmLog.setOnClickListener { Toast.makeText(this, "查看告警日志", Toast.LENGTH_SHORT).show() } // 点击xq箭头跳转到AlarmLogActivity binding.xq.setOnClickListener { val intent = Intent(this, AlarmLogActivity::class.java) startActivity(intent) } } private fun togglePlayPause() { mediaPlayer?.let { mp -> if (mp.isPlaying) { mp.pause() isPlaying = false Toast.makeText(this, "已暂停", Toast.LENGTH_SHORT).show() } else { mp.start() isPlaying = true Toast.makeText(this, "继续播放", 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, "已进入全屏", 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, "已退出全屏", 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, "声音已开启", Toast.LENGTH_SHORT).show() } else { mediaPlayer?.setVolume(0.0f, 0.0f) binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice) Toast.makeText(this, "声音已关闭", Toast.LENGTH_SHORT).show() } } private fun toggleLight() { isLightOn = !isLightOn if (isLightOn) { binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on_open) Toast.makeText(this, "灯光已开启", Toast.LENGTH_SHORT).show() } else { binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on) Toast.makeText(this, "灯光已关闭", Toast.LENGTH_SHORT).show() } } private fun toggleRecording() { if (!isRecording) { if (checkRecordAudioPermission()) { startRecordingMode() } else { requestRecordAudioPermission() } } else { stopRecordingMode() } } private fun checkRecordAudioPermission(): Boolean { return ContextCompat.checkSelfPermission( this, Manifest.permission.RECORD_AUDIO ) == PackageManager.PERMISSION_GRANTED } private fun requestRecordAudioPermission() { requestPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO) } private fun startRecordingMode() { isRecording = true binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy_open) // 隐藏告警日志区域,显示控制台区域 binding.llAlarmLog.visibility = View.GONE binding.consoleBar.visibility = View.VISIBLE // 麦克风切换成开启状态 binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice_on_open) isVoiceEnabled = true // 启动语音录制 startVoiceRecording() // 启动麦克风脉冲动画 //startMicPulseAnimation() Toast.makeText(this, "开始控制模式", Toast.LENGTH_SHORT).show() } private fun stopRecordingMode() { isRecording = false binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy) // 显示告警日志区域,隐藏控制台区域 binding.llAlarmLog.visibility = View.VISIBLE binding.consoleBar.visibility = View.GONE // 麦克风切换成关闭状态 binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice) isVoiceEnabled = false // 停止语音录制 stopVoiceRecording() // 停止麦克风脉冲动画 stopMicPulseAnimation() Toast.makeText(this, "停止控制模式", 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 { binding.ivVoice.clearAnimation() micPulseAnimation = null } } private fun startVoiceRecording() { mediaRecorder = MediaRecorder().apply { setAudioSource(MediaRecorder.AudioSource.MIC) setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) val outputDir = getExternalFilesDir(null) ?: filesDir val audioDir = File(outputDir, "audio") if (!audioDir.exists()) { audioDir.mkdirs() } val outputFile = File(audioDir, "voice_record_${System.currentTimeMillis()}.3gp") setOutputFile(outputFile.absolutePath) setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) try { prepare() start() } catch (e: IOException) { e.printStackTrace() Toast.makeText(this@LiveVideoActivity, "录音启动失败", Toast.LENGTH_SHORT).show() } } } private fun stopVoiceRecording() { mediaRecorder?.let { try { it.stop() it.release() } catch (e: Exception) { e.printStackTrace() } mediaRecorder = null } } private fun releaseMediaPlayer() { mediaPlayer?.let { if (it.isPlaying) { it.stop() } it.release() mediaPlayer = null } } override fun onPause() { super.onPause() mediaPlayer?.pause() } override fun onResume() { super.onResume() mediaPlayer?.start() } override fun onDestroy() { releaseMediaPlayer() super.onDestroy() } }