视频播放1
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 视频流信息 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/bitRateBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp"
|
||||
android:background="#FFFFFF"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBitRate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="120 B/S"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 视频播放区域 -->
|
||||
<FrameLayout
|
||||
android:id="@+id/videoContainer"
|
||||
<chuangyuan.ycj.videolibrary.widget.VideoPlayerView
|
||||
android:id="@+id/exoPlayContextId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="220dp"
|
||||
android:background="#000000">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<!-- 全屏按钮 -->
|
||||
<ImageView
|
||||
android:id="@+id/ivFullscreen"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/full_screen"/>
|
||||
|
||||
<!-- 播放控制按钮 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/playControl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivPlay"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:src="@drawable/full_screen" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
android:layout_height="250dp"
|
||||
android:background="@android:color/transparent"/>
|
||||
|
||||
<!-- 功能按钮区域 -->
|
||||
<LinearLayout
|
||||
|
||||
Reference in New Issue
Block a user