From 3558f053e6f694c228f2013f9fbb097573d215d7 Mon Sep 17 00:00:00 2001 From: maxinyu Date: Wed, 6 May 2026 17:05:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetSelector.xml | 7 + app/build.gradle.kts | 7 + app/src/main/AndroidManifest.xml | 16 +- .../liveVideo/LiveVideoActivity.kt | 262 ++++++++++++++---- app/src/main/res/drawable/copy_open.png | Bin 0 -> 1172 bytes app/src/main/res/drawable/full_screen.png | Bin 0 -> 3160 bytes app/src/main/res/drawable/light_on_open.png | Bin 0 -> 669 bytes app/src/main/res/drawable/voice_open.png | Bin 0 -> 796 bytes .../main/res/layout/live_video_activity.xml | 194 +++++++------ gradle/libs.versions.toml | 2 + 10 files changed, 353 insertions(+), 135 deletions(-) create mode 100644 app/src/main/res/drawable/copy_open.png create mode 100644 app/src/main/res/drawable/full_screen.png create mode 100644 app/src/main/res/drawable/light_on_open.png create mode 100644 app/src/main/res/drawable/voice_open.png diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index ca16a99..ea2981d 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,13 @@ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 751c58f..978e094 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { implementation(libs.androidx.constraintlayout) implementation("androidx.recyclerview:recyclerview:1.3.2") implementation(libs.firebase.crashlytics.buildtools) + implementation(libs.androidx.ui.test) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -76,4 +77,10 @@ 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") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a0a16f1..a119749 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,7 +25,13 @@ android:theme="@style/Theme.DefenseApplication"> + android:exported="true"> + + + + + + @@ -46,13 +52,7 @@ android:exported="false" /> - - - - - - + android:exported="false"/> 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 958b1b7..f1aa315 100644 --- a/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt +++ b/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt @@ -1,80 +1,251 @@ package com.example.defenseapplication.liveVideo -import android.content.Context +import android.content.pm.ActivityInfo +import android.media.AudioManager +import android.media.MediaPlayer +import android.net.Uri import android.os.Bundle -import android.os.Handler -import android.os.Looper +import android.view.SurfaceHolder import android.view.View +import android.view.ViewGroup +import android.view.WindowManager import android.widget.Toast +import androidx.activity.OnBackPressedDispatcher import androidx.appcompat.app.AppCompatActivity +import androidx.compose.ui.test.isEnabled import com.example.defenseapplication.databinding.LiveVideoActivityBinding +import java.io.IOException -class LiveVideoActivity : AppCompatActivity() { +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 recordTime = 0 - private var recordHandler: Handler? = null - private var recordRunnable: Runnable? = null + + private var videoWidth = 0 + private var videoHeight = 0 + + private val testVideoUrl = "https://vjs.zencdn.net/v/oceans.mp4" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = LiveVideoActivityBinding.inflate(layoutInflater) setContentView(binding.root) - + + + 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 { Toast.makeText(this, "菜单", Toast.LENGTH_SHORT).show() } - // 全屏按钮 binding.ivFullscreen.setOnClickListener { - Toast.makeText(this, "全屏", Toast.LENGTH_SHORT).show() + 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() } } + 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) { - binding.vVoiceActive.visibility = View.VISIBLE - binding.ivVoice.setColorFilter(resources.getColor(com.example.defenseapplication.R.color.green)) - Toast.makeText(this, "语音已开启", Toast.LENGTH_SHORT).show() + mediaPlayer?.setVolume(1.0f, 1.0f) + binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice_open) + Toast.makeText(this, "声音已开启", Toast.LENGTH_SHORT).show() } else { - binding.vVoiceActive.visibility = View.INVISIBLE - binding.ivVoice.clearColorFilter() - Toast.makeText(this, "语音已关闭", Toast.LENGTH_SHORT).show() + mediaPlayer?.setVolume(0.0f, 0.0f) + binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice) + Toast.makeText(this, "声音已关闭", Toast.LENGTH_SHORT).show() } } @@ -82,12 +253,10 @@ class LiveVideoActivity : AppCompatActivity() { isLightOn = !isLightOn if (isLightOn) { - binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on) - binding.ivLight.setColorFilter(resources.getColor(com.example.defenseapplication.R.color.green)) + 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.low_light) - binding.ivLight.clearColorFilter() + binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on) Toast.makeText(this, "灯光已关闭", Toast.LENGTH_SHORT).show() } } @@ -96,38 +265,37 @@ class LiveVideoActivity : AppCompatActivity() { isRecording = !isRecording if (isRecording) { - recordTime = 0 - binding.tvRecordTime.text = "00" - startRecordTimer() - binding.ivRecord.setColorFilter(resources.getColor(com.example.defenseapplication.R.color.red)) + binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy_open) Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show() } else { - stopRecordTimer() - binding.ivRecord.clearColorFilter() - Toast.makeText(this, "停止录像,共录制 ${recordTime} 秒", Toast.LENGTH_SHORT).show() + binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy) + Toast.makeText(this, "停止录像", Toast.LENGTH_SHORT).show() } } - private fun startRecordTimer() { - recordHandler = Handler(Looper.getMainLooper()) - recordRunnable = object : Runnable { - override fun run() { - recordTime++ - binding.tvRecordTime.text = String.format("%02d", recordTime) - recordHandler?.postDelayed(this, 1000) + private fun releaseMediaPlayer() { + mediaPlayer?.let { + if (it.isPlaying) { + it.stop() } + it.release() + mediaPlayer = null } - recordHandler?.post(recordRunnable!!) } - private fun stopRecordTimer() { - recordRunnable?.let { recordHandler?.removeCallbacks(it) } - recordHandler = null - recordRunnable = null + override fun onPause() { + super.onPause() + mediaPlayer?.pause() + } + + override fun onResume() { + super.onResume() + mediaPlayer?.start() } override fun onDestroy() { + releaseMediaPlayer() super.onDestroy() - stopRecordTimer() } -} \ No newline at end of file +} + diff --git a/app/src/main/res/drawable/copy_open.png b/app/src/main/res/drawable/copy_open.png new file mode 100644 index 0000000000000000000000000000000000000000..17a346ba4778c7372d73c928b595b8d66ee261f6 GIT binary patch literal 1172 zcmV;F1Z(?=P)Px(PDw;TR9Hvtm%&q8RTRd*@4k@tP&aizsUuVuZfYG|IF2on6p~Tv6jz0+e*$2& zj&5wVt+sA-tXAMpPzkQ2W7SCrNklu23z=d!F0|m-Xgj*m@?Mg6k8=}nLPD7YQpb+T zDwCYt^UHVcIp=%CP4ePR!igqmqbt=^w5e#L6=?K%%{%(vQ#6y=X2W|%3HFJg8^oO} zkXZ`nHRN+o*&m>{_m8c>Z@%{44=HS}$yUpvk1;DE}Lh4?vJDC_a(z?Vm3P z45s4;pvm0c$W-~~EpuNy7${ihKy-*;!M)3S2=_hd)v9b=piU}rlEe{#T=Om6lka)# zyETcD$+lKYUo=HtMsOEGp(|@(Gc(_9xZm~F@G5XHH8W&UCPie)4M)2g4eAjLLd%}zz0t3Ovxz}V$r!*pggCwtC zT=5~pxlr`uz-)YXi{e)T$+@AZeM3O=TJ{tSXgYqt0?qwuuMJdw^EF4!*lL}P@5T)S zO~n^UcKX7Dxp051sDf!AOHY9Ke#6ph>~z^Ox_Xa;srW&Q{74{+ZYa8=p^CPq9Gsalz)v6vrT zGoA`+yy_1Wx10Xa?0+pX>V>1@4FyGU;O7>NQ_|pzUa0?c)xb|BCP@qx2R<<6h9brH zYe!S!B}Ket9F?o=&V~Bb9sJjH_1U765YDS34`!H2dE2)toUW zA~@xSBS&jd)5O#~@QVi;5BiJ!ZPDHtQxfp^RqlH;wxC4J7%GQo$;@;Iw0u@FC33;l z*2DF9)A~S-s^vBha!rKCJgvsAn=}>++8u2p6nWE_k$=bLtw>Zd&N5xJdnE&s6=F(6 z<}ATt9)P0iByg$t zpILY5tANbIo%Wtp;NwE+21<_|5d2DFUK8g$*dNx~-PilWTL-PRZMPWL1l^*5RX=Yk m+EldB3N-q><{katD*89oJ^4DWl3+Ig0000A@w literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/full_screen.png b/app/src/main/res/drawable/full_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..cb91da8f8b1d4556a33e6e5751d95dfb73b9169a GIT binary patch literal 3160 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 N2nNl;y2Z+y_kT!TUV{Jt literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/light_on_open.png b/app/src/main/res/drawable/light_on_open.png new file mode 100644 index 0000000000000000000000000000000000000000..6f1bacba98f9eddbec831ee48c673ca5014097a4 GIT binary patch literal 669 zcmV;O0%HA%P)Px%S4l)cR9Hvtl|5?|Q5c2KnZ@j^f?p{jh=P`aT|hAGPQpwY3l$OU)F}P|Q;3LQ zk-|d#1;G#ORun~S%nW*WHK3)9gE%T@@{mxRN$phy`8sucp=a>#RXt6`m)# zXc{WO1zq!veRiO4!g05aj|8_`fUfGY?8Bkpn~B#7w4$O*Ag-GP^=n0NS$n>b-wM=K z(Q6WiGk`8@&z~~!TY*+o^!=Y-`49pTSe??`Y58Wa764 z{YrAeBxnE>@uE*ggN|Pvz|6IdiPsJkK&A0~N~9E$aMK2gKojs=p1+*dv!=5K z?F|@fEFE)%57XQLBGQeClfP@hV^heUJ`-qfKv{+J4tZl(cubjZSA$!_bV=_sgQ8w9 zRVKzC2_^BrPvL4SZk{mR%YNwWptcnm`tku)YEQY;-JF!&?v{SR^FAB04ajo=bsOOe zg40PL0(q%x!OZAERd|==nM6>Ld)f=mjvlnRw3jg-2r)^}SpyPC48dDCa!OZcKaCtT z3qV?LE<2hwEQ_uH&C=d_-lEZ>(YzbTdtR}lc{ln83TCz|fj|j500000NkvXXu0mjf Dt|&Hs literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/voice_open.png b/app/src/main/res/drawable/voice_open.png new file mode 100644 index 0000000000000000000000000000000000000000..ea45370cfb7910c32c74d88fe20c5721c741c06a GIT binary patch literal 796 zcmV+%1LOROP)Px%)=5M`R9Hvt)lEngVHgMS|1}J?Sio8Tb&?SNh z>Lh{N7afAYLmd-3bl5<-DI|7(e0?dWdA9$|-GtK=VrD?S?Lb$Pfb1k% z9h2p+{=ZJN^rZ`yW^+n7?n~-7CBnmcZ6XBU$aY5_m5wq=;DD*9bx(;}Yc-UpoYdw) zG;apgRynDMiJ;d%o{AAOcv6>B`W%>>O(oL2n*LuorZH$rdrhEIMVHi;OC)Ffp{{8V zb<`@(%v0vXWT!_Bwz-))+Kf1)_Grd_Ai~hLBGCeTBu#1SwELFocn95>HG2 zc;7qink3N>GrpR_=s+k|SRW_=Z%S*I7%33#AmDLko4AFe(S<6&bfU?>B;8}+D!_jQ z5Xc21U)G8&jc=$&D}_9hiB5M>8U42+rOKnVxI+)G4FVEin&L|?uu`k@h| zW$1Fzbh^ctUm7If;xcN*#o#~87|zPLi(`@BjUw%VR;o`7%Wi4!VGr=C{OEK>eE*2h zCDRJ9GS|}9*8=#H`j67{jH2$b1Gx2|vXSV5peF!%%5T5Fe4j1f3y{PWGZ3A$lCb5i z_olQy2^1%w0~MAuTx%2)?#CB0g6okX2P?M}e%^_(8H aJJEm5a~C=dE(THn0000 -