diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt b/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
deleted file mode 100644
index 128fbd8..0000000
--- a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.casic.endoscope.utils
-
-import io.microshow.rxffmpeg.RxFFmpegCommandList
-
-object FFmpegCommandHub {
-
- /**
- * 海康视频转码指令
- * */
- fun createVideoTranscodeCommand(inputFilePath: String, outputFilePath: String): Array {
- val commandParams = RxFFmpegCommandList()
- commandParams.append("-i")
- commandParams.append(inputFilePath)
- commandParams.append("-c:v")
- commandParams.append("libx264")
- commandParams.append(outputFilePath)
- return commandParams.build()
- }
-}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt b/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
deleted file mode 100644
index 128fbd8..0000000
--- a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.casic.endoscope.utils
-
-import io.microshow.rxffmpeg.RxFFmpegCommandList
-
-object FFmpegCommandHub {
-
- /**
- * 海康视频转码指令
- * */
- fun createVideoTranscodeCommand(inputFilePath: String, outputFilePath: String): Array {
- val commandParams = RxFFmpegCommandList()
- commandParams.append("-i")
- commandParams.append(inputFilePath)
- commandParams.append("-c:v")
- commandParams.append("libx264")
- commandParams.append(outputFilePath)
- return commandParams.build()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
index 02e57c2..78c2ac0 100644
--- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
+++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
@@ -2,7 +2,6 @@
import android.Manifest
import android.os.Build
-import java.util.Stack
object ProjectConstant {
val USER_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -14,7 +13,8 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
@@ -23,21 +23,24 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
}
@@ -49,14 +52,5 @@
const val HK_NET_USERNAME = "admin"
const val HK_NET_PASSWORD = "casic203"
- //待转码视频路径
- val VIDEO_PATH_STACK = Stack()
-
const val SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb" //连接设备的UUID
-
- //视频正在转码
- var isUnderDecodingVideo = false
-
- //已转码得视频数量
- var decodedViewCount = 0
}
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt b/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
deleted file mode 100644
index 128fbd8..0000000
--- a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.casic.endoscope.utils
-
-import io.microshow.rxffmpeg.RxFFmpegCommandList
-
-object FFmpegCommandHub {
-
- /**
- * 海康视频转码指令
- * */
- fun createVideoTranscodeCommand(inputFilePath: String, outputFilePath: String): Array {
- val commandParams = RxFFmpegCommandList()
- commandParams.append("-i")
- commandParams.append(inputFilePath)
- commandParams.append("-c:v")
- commandParams.append("libx264")
- commandParams.append(outputFilePath)
- return commandParams.build()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
index 02e57c2..78c2ac0 100644
--- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
+++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
@@ -2,7 +2,6 @@
import android.Manifest
import android.os.Build
-import java.util.Stack
object ProjectConstant {
val USER_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -14,7 +13,8 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
@@ -23,21 +23,24 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
}
@@ -49,14 +52,5 @@
const val HK_NET_USERNAME = "admin"
const val HK_NET_PASSWORD = "casic203"
- //待转码视频路径
- val VIDEO_PATH_STACK = Stack()
-
const val SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb" //连接设备的UUID
-
- //视频正在转码
- var isUnderDecodingVideo = false
-
- //已转码得视频数量
- var decodedViewCount = 0
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
index b4e6474..692f453 100644
--- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
+++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
@@ -1,6 +1,5 @@
package com.casic.endoscope.view
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.ImageView
@@ -10,8 +9,6 @@
import com.casic.endoscope.adapter.MediaDirAdapter
import com.casic.endoscope.databinding.ActivityAlbumBinding
import com.casic.endoscope.utils.FileManager
-import com.casic.endoscope.utils.ProjectConstant
-import com.casic.endoscope.widgets.AlertControlDialog
import com.gyf.immersionbar.ImmersionBar
import com.pengxh.kt.lite.adapter.ViewHolder
import com.pengxh.kt.lite.base.KotlinBaseActivity
@@ -19,15 +16,12 @@
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
import com.pengxh.kt.lite.extensions.navigatePageTo
-import com.pengxh.kt.lite.extensions.show
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-import java.util.Timer
-import java.util.TimerTask
class AlbumActivity : KotlinBaseActivity() {
@@ -39,74 +33,7 @@
private lateinit var dirBeans: MutableList
private lateinit var directoryAdapter: MediaDirAdapter
- private var timer: Timer? = null
-
- @SuppressLint("NotifyDataSetChanged")
override fun initEvent() {
- binding.videoDecodeButton.setOnClickListener {
- val originVideoCollection = ArrayList()
- val convertedVideoCollection = ArrayList()
- val videoDir = File(FileManager.getRootDirectory(), "Video")
- videoDir.listFiles()?.forEach { dir ->
- val childDir = File(dir.absolutePath)
- childDir.listFiles()?.forEach { file ->
- if (file.name.startsWith("t")) {
- convertedVideoCollection.add(file.absolutePath)
- } else {
- originVideoCollection.add(file.absolutePath)
- }
- }
- }
-
- //将已转换的全部去掉t,得到一个临时数组,在和原数组比较,得到差集
- val temp = ArrayList()
- convertedVideoCollection.forEach {
- temp.add(it.replace("/t", "/"))
- }
-
- //将temp和originVideoCollection求差集
- val stringSet = originVideoCollection.subtract(temp.toSet())
- if (stringSet.isEmpty()) {
- "没有需要手动转码的视频".show(context)
- return@setOnClickListener
- }
- AlertControlDialog.Builder()
- .setContext(context)
- .setTitle("温馨提示")
- .setMessage("共有${stringSet.size}个视频需要转码,是否继续?")
- .setNegativeButton("取消")
- .setPositiveButton("确定")
- .setOnDialogButtonClickListener(object :
- AlertControlDialog.OnDialogButtonClickListener {
- override fun onConfirmClick() {
- binding.progressBarLayout.visibility = View.VISIBLE
- binding.progressBar.max = stringSet.size
- stringSet.forEach {
- ProjectConstant.VIDEO_PATH_STACK.push(it)
- }
-
- timer = Timer()
- timer?.schedule(object : TimerTask() {
- override fun run() {
- runOnUiThread {
- binding.progressBar.progress = ProjectConstant.decodedViewCount
- if (ProjectConstant.decodedViewCount == stringSet.size) {
- binding.progressBarLayout.visibility = View.INVISIBLE
- timer?.cancel()
- }
- //刷新列表
- directoryAdapter.notifyDataSetChanged()
- }
- }
- }, 0, 2000)
- }
-
- override fun onCancelClick() {
-
- }
- }).build().show()
- }
-
binding.ascButton.setOnClickListener {
//按时间排序,降序
dirBeans = tempSet.sortedWith { d1, d2 ->
@@ -125,10 +52,6 @@
}
override fun initOnCreate(savedInstanceState: Bundle?) {
- //初始化已转码得视频数量和状态
- ProjectConstant.isUnderDecodingVideo = false
- ProjectConstant.decodedViewCount = 0
-
val temp = ArrayList()
val videoDir = File(FileManager.getRootDirectory(), "Video")
videoDir.list()?.forEach {
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt b/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
deleted file mode 100644
index 128fbd8..0000000
--- a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.casic.endoscope.utils
-
-import io.microshow.rxffmpeg.RxFFmpegCommandList
-
-object FFmpegCommandHub {
-
- /**
- * 海康视频转码指令
- * */
- fun createVideoTranscodeCommand(inputFilePath: String, outputFilePath: String): Array {
- val commandParams = RxFFmpegCommandList()
- commandParams.append("-i")
- commandParams.append(inputFilePath)
- commandParams.append("-c:v")
- commandParams.append("libx264")
- commandParams.append(outputFilePath)
- return commandParams.build()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
index 02e57c2..78c2ac0 100644
--- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
+++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
@@ -2,7 +2,6 @@
import android.Manifest
import android.os.Build
-import java.util.Stack
object ProjectConstant {
val USER_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -14,7 +13,8 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
@@ -23,21 +23,24 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
}
@@ -49,14 +52,5 @@
const val HK_NET_USERNAME = "admin"
const val HK_NET_PASSWORD = "casic203"
- //待转码视频路径
- val VIDEO_PATH_STACK = Stack()
-
const val SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb" //连接设备的UUID
-
- //视频正在转码
- var isUnderDecodingVideo = false
-
- //已转码得视频数量
- var decodedViewCount = 0
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
index b4e6474..692f453 100644
--- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
+++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
@@ -1,6 +1,5 @@
package com.casic.endoscope.view
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.ImageView
@@ -10,8 +9,6 @@
import com.casic.endoscope.adapter.MediaDirAdapter
import com.casic.endoscope.databinding.ActivityAlbumBinding
import com.casic.endoscope.utils.FileManager
-import com.casic.endoscope.utils.ProjectConstant
-import com.casic.endoscope.widgets.AlertControlDialog
import com.gyf.immersionbar.ImmersionBar
import com.pengxh.kt.lite.adapter.ViewHolder
import com.pengxh.kt.lite.base.KotlinBaseActivity
@@ -19,15 +16,12 @@
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
import com.pengxh.kt.lite.extensions.navigatePageTo
-import com.pengxh.kt.lite.extensions.show
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-import java.util.Timer
-import java.util.TimerTask
class AlbumActivity : KotlinBaseActivity() {
@@ -39,74 +33,7 @@
private lateinit var dirBeans: MutableList
private lateinit var directoryAdapter: MediaDirAdapter
- private var timer: Timer? = null
-
- @SuppressLint("NotifyDataSetChanged")
override fun initEvent() {
- binding.videoDecodeButton.setOnClickListener {
- val originVideoCollection = ArrayList()
- val convertedVideoCollection = ArrayList()
- val videoDir = File(FileManager.getRootDirectory(), "Video")
- videoDir.listFiles()?.forEach { dir ->
- val childDir = File(dir.absolutePath)
- childDir.listFiles()?.forEach { file ->
- if (file.name.startsWith("t")) {
- convertedVideoCollection.add(file.absolutePath)
- } else {
- originVideoCollection.add(file.absolutePath)
- }
- }
- }
-
- //将已转换的全部去掉t,得到一个临时数组,在和原数组比较,得到差集
- val temp = ArrayList()
- convertedVideoCollection.forEach {
- temp.add(it.replace("/t", "/"))
- }
-
- //将temp和originVideoCollection求差集
- val stringSet = originVideoCollection.subtract(temp.toSet())
- if (stringSet.isEmpty()) {
- "没有需要手动转码的视频".show(context)
- return@setOnClickListener
- }
- AlertControlDialog.Builder()
- .setContext(context)
- .setTitle("温馨提示")
- .setMessage("共有${stringSet.size}个视频需要转码,是否继续?")
- .setNegativeButton("取消")
- .setPositiveButton("确定")
- .setOnDialogButtonClickListener(object :
- AlertControlDialog.OnDialogButtonClickListener {
- override fun onConfirmClick() {
- binding.progressBarLayout.visibility = View.VISIBLE
- binding.progressBar.max = stringSet.size
- stringSet.forEach {
- ProjectConstant.VIDEO_PATH_STACK.push(it)
- }
-
- timer = Timer()
- timer?.schedule(object : TimerTask() {
- override fun run() {
- runOnUiThread {
- binding.progressBar.progress = ProjectConstant.decodedViewCount
- if (ProjectConstant.decodedViewCount == stringSet.size) {
- binding.progressBarLayout.visibility = View.INVISIBLE
- timer?.cancel()
- }
- //刷新列表
- directoryAdapter.notifyDataSetChanged()
- }
- }
- }, 0, 2000)
- }
-
- override fun onCancelClick() {
-
- }
- }).build().show()
- }
-
binding.ascButton.setOnClickListener {
//按时间排序,降序
dirBeans = tempSet.sortedWith { d1, d2 ->
@@ -125,10 +52,6 @@
}
override fun initOnCreate(savedInstanceState: Bundle?) {
- //初始化已转码得视频数量和状态
- ProjectConstant.isUnderDecodingVideo = false
- ProjectConstant.decodedViewCount = 0
-
val temp = ArrayList()
val videoDir = File(FileManager.getRootDirectory(), "Video")
videoDir.list()?.forEach {
diff --git a/app/src/main/java/com/casic/endoscope/view/MainActivity.kt b/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
index cddc6fb..768bffb 100644
--- a/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
+++ b/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
@@ -1,16 +1,22 @@
package com.casic.endoscope.view
import android.annotation.SuppressLint
+import android.app.Activity
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
+import android.content.ComponentName
+import android.content.Context
import android.content.Intent
+import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.PixelFormat
+import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
+import android.os.IBinder
import android.os.Message
import android.provider.Settings
import android.util.Log
@@ -28,7 +34,7 @@
import com.casic.endoscope.extensions.getChannel
import com.casic.endoscope.extensions.init
import com.casic.endoscope.extensions.toTime
-import com.casic.endoscope.service.VideoTranscodeService
+import com.casic.endoscope.service.ScreenShortRecordService
import com.casic.endoscope.utils.DataBaseManager
import com.casic.endoscope.utils.FileManager
import com.casic.endoscope.utils.ProjectConstant
@@ -57,6 +63,7 @@
import com.pengxh.kt.lite.divider.RecyclerViewItemOffsets
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
+import com.pengxh.kt.lite.extensions.getSystemService
import com.pengxh.kt.lite.extensions.navigatePageTo
import com.pengxh.kt.lite.extensions.show
import com.pengxh.kt.lite.extensions.timestampToTime
@@ -119,12 +126,12 @@
private var timer: Timer? = null
private var timerTask: TimerTask? = null
private var seconds = 0L
- private var videoPath = ""
+ private var screenShortService: ScreenShortRecordService? = null
private lateinit var weakReferenceHandler: WeakReferenceHandler
- private lateinit var serviceIntent: Intent
private lateinit var dataSet: LineDataSet
private lateinit var lineData: LineData
private lateinit var imageAdapter: NormalRecyclerAdapter
+ private lateinit var mediaProjectionManager: MediaProjectionManager
override fun initViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
@@ -137,6 +144,13 @@
FileManager.getRootDirectory()
}
}
+
+ if (requestCode == 2 && resultCode == Activity.RESULT_OK) {
+ val videoPath = "/${FileManager.getVideoFileDir()}/${timeFormat.format(Date())}.mp4"
+ data?.let {
+ screenShortService?.startRecorder(videoPath, it)
+ }
+ }
}
override fun initOnCreate(savedInstanceState: Bundle?) {
@@ -154,8 +168,6 @@
weakReferenceHandler = WeakReferenceHandler(this)
weakReferenceHandler.sendEmptyMessage(messageCode)
- serviceIntent = Intent(this, VideoTranscodeService::class.java)
-
//初始化浓度趋势折线图和Marker
binding.lineChart.init(this)
val markerView = LineChartMarkerView(this)
@@ -164,12 +176,6 @@
binding.lineChart.marker = markerView
}
- override fun onResume() {
- super.onResume()
- //启动视频转码服务
- startService(serviceIntent)
- }
-
override fun initEvent() {
binding.openAlbumButton.setOnClickListener {
if (isPreviewSuccess) {
@@ -315,8 +321,7 @@
val deviceInfo = SDKGuider.sdkGuider.devManageGuider.devList[0]
returnUserId = deviceInfo.szUserId
- aChannelNum =
- deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt()
+ aChannelNum = deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt()
startAChannel =
deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt()
@@ -442,20 +447,20 @@
//视频录制
binding.videoButton.setOnClickListener {
- if (isPreviewSuccess) {
- videoPath = "/${FileManager.getVideoFileDir()}/${timeFormat.format(Date())}.mp4"
- hkSDK.NET_DVR_SaveRealData(returnUserId, videoPath)
- binding.videoStateView.visibility = View.VISIBLE
+ mediaProjectionManager =
+ getSystemService() ?: return@setOnClickListener
+ val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
+ if (captureIntent.resolveActivity(packageManager) != null) {
+ startActivityForResult(captureIntent, 2)
} else {
- "摄像头预览未打开,无法录制视频".show(this)
+ "该设备不支持录屏".show(this)
}
}
+ //视频保存
binding.videoButton.setOnLongClickListener {
- //停止视频抓取
- hkSDK.NET_DVR_StopSaveRealData(returnUserId)
+ screenShortService?.stopRecorder()
binding.videoStateView.visibility = View.INVISIBLE
- ProjectConstant.VIDEO_PATH_STACK.push(videoPath)
"视频录制成功".show(this)
true
}
@@ -467,8 +472,8 @@
serialStart.dwSerialPort = 2
//串口编号
serialStart.wPort = 0
- val serialHandle = hkSDK.NET_DVR_SerialStart_V40(returnUserId, serialStart)
- { _, _, _, _ -> }
+ val serialHandle =
+ hkSDK.NET_DVR_SerialStart_V40(returnUserId, serialStart) { _, _, _, _ -> }
//链式执行
lifecycleScope.launch(Dispatchers.IO) {
flow {
@@ -505,6 +510,31 @@
}
}
+ override fun onStart() {
+ super.onStart()
+ Intent(this, ScreenShortRecordService::class.java).also { intent ->
+ bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
+ }
+ }
+
+ private val serviceConnection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) {
+ if (iBinder is ScreenShortRecordService.ScreenShortRecordServiceBinder) {
+ //截屏
+ screenShortService = iBinder.getScreenShortRecordService()
+ }
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) {
+ "录屏服务已断开".show(context)
+ }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ unbindService(serviceConnection)
+ }
+
override fun handleMessage(msg: Message): Boolean {
if (msg.what == messageCode) {
//绑定数据
@@ -514,13 +544,9 @@
override fun convertView(viewHolder: ViewHolder, position: Int, item: String) {
viewHolder.setImageResource(R.id.imageView, item)
viewHolder.setOnLongClickListener(R.id.imageView) {
- AlertControlDialog.Builder()
- .setContext(context)
- .setTitle("温馨提示")
- .setMessage("是否保存此图片?")
- .setNegativeButton("取消")
- .setPositiveButton("确定")
- .setOnDialogButtonClickListener(object :
+ AlertControlDialog.Builder().setContext(context).setTitle("温馨提示")
+ .setMessage("是否保存此图片?").setNegativeButton("取消")
+ .setPositiveButton("确定").setOnDialogButtonClickListener(object :
AlertControlDialog.OnDialogButtonClickListener {
override fun onConfirmClick() {
@@ -538,10 +564,7 @@
viewHolder.setOnClickListener(R.id.imageView) {
//查看大图Dialog
- PreviewImageDialog.Builder()
- .setContext(context)
- .setImagePath(item)
- .build()
+ PreviewImageDialog.Builder().setContext(context).setImagePath(item).build()
.show()
}
}
@@ -630,7 +653,6 @@
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (System.currentTimeMillis() - clickTime > 2000) {
- stopService(serviceIntent)
"再按一次退出应用".show(this)
clickTime = System.currentTimeMillis()
return true
diff --git a/app/build.gradle b/app/build.gradle
index d518570..9b25206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -81,7 +81,7 @@
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
//基础依赖库
- implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.9'
+ implementation 'com.github.AndroidCoderPeng:Kotlin-lite-lib:1.0.10'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
@@ -111,6 +111,4 @@
implementation 'org.greenrobot:greendao:3.3.0'
//数据库升级
implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
- //ffmpeg
- implementation 'com.github.microshow:RxFFmpeg:4.9.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85791a0..d9e496a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
+
+
+
+
+
+ android:name=".service.ScreenShortRecordService"
+ android:enabled="true"
+ android:exported="true"
+ android:foregroundServiceType="mediaProjection" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
index 23a553d..fa7ec43 100644
--- a/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
+++ b/app/src/main/java/com/casic/endoscope/adapter/MediaDirAdapter.kt
@@ -42,9 +42,7 @@
val fileBeans = ArrayList()
val videoDir = File(File(FileManager.getRootDirectory(), "Video"), date)
videoDir.listFiles()?.forEach {
- if (it.name.startsWith("t")) {
- fileBeans.add(it)
- }
+ fileBeans.add(it)
}
val imageDir = File(File(FileManager.getRootDirectory(), "Picture"), date)
diff --git a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
index 21cc4f5..6439217 100644
--- a/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
+++ b/app/src/main/java/com/casic/endoscope/base/BaseApplication.kt
@@ -5,7 +5,6 @@
import com.casic.endoscope.greendao.DaoSession
import com.casic.endoscope.utils.EndoscopeDevOpenHelper
import com.pengxh.kt.lite.utils.SaveKeyValues
-import io.microshow.rxffmpeg.RxFFmpegInvoke
import kotlin.properties.Delegates
class BaseApplication : Application() {
@@ -25,7 +24,6 @@
application = this
SaveKeyValues.initSharedPreferences(this)
initDataBase()
- RxFFmpegInvoke.getInstance().setDebug(false)
}
private fun initDataBase() {
diff --git a/app/src/main/java/com/casic/endoscope/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt
index 2762fe7..7e5bdc6 100644
--- a/app/src/main/java/com/casic/endoscope/extensions/String.kt
+++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt
@@ -1,10 +1,5 @@
package com.casic.endoscope.extensions
-import android.util.Log
-import com.casic.endoscope.utils.FFmpegCommandHub
-import com.casic.endoscope.utils.ProjectConstant
-import io.microshow.rxffmpeg.RxFFmpegInvoke
-import io.microshow.rxffmpeg.RxFFmpegSubscriber
import java.util.regex.Pattern
/**
@@ -15,36 +10,4 @@
val p = Pattern.compile(regEx)
val m = p.matcher(this)
return m.replaceAll("").trim { it <= ' ' }
-}
-
-fun String.transcodeVideo(kTag: String) {
- /**
- * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4
- * */
- val lastIndex = this.lastIndexOf("/")
- val fileName = this.drop(lastIndex + 1)
- //文件名前面+t
- val newFileName = fileName.reversed().plus("t").reversed()
- val outputPath = this.replace(fileName, newFileName)
- RxFFmpegInvoke.getInstance()
- .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(this, outputPath))
- .subscribe(object : RxFFmpegSubscriber() {
- override fun onError(message: String?) {
-
- }
-
- override fun onFinish() {
- Log.d(kTag, "onFinish => $outputPath 转码完成")
- ProjectConstant.decodedViewCount += 1
- ProjectConstant.isUnderDecodingVideo = false
- }
-
- override fun onProgress(progress: Int, progressTime: Long) {
- Log.d(kTag, "onProgress => $progress")
- }
-
- override fun onCancel() {
-
- }
- })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
new file mode 100644
index 0000000..765db08
--- /dev/null
+++ b/app/src/main/java/com/casic/endoscope/service/ScreenShortRecordService.kt
@@ -0,0 +1,136 @@
+package com.casic.endoscope.service
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.media.MediaRecorder
+import android.media.projection.MediaProjection
+import android.media.projection.MediaProjectionManager
+import android.os.Binder
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import com.casic.endoscope.R
+import com.pengxh.kt.lite.extensions.getSystemService
+import kotlinx.coroutines.Runnable
+
+class ScreenShortRecordService : Service() {
+
+ private val kTag = "ScreenShortRecordService"
+ private var mediaProjection: MediaProjection? = null
+ private var recorder: MediaRecorder? = null
+ private var virtualDisplay: VirtualDisplay? = null
+
+ inner class ScreenShortRecordServiceBinder : Binder() {
+ fun getScreenShortRecordService(): ScreenShortRecordService {
+ return this@ScreenShortRecordService
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ return ScreenShortRecordServiceBinder()
+ }
+
+ fun startRecorder(videoPath: String, intent: Intent) {
+ Log.d(kTag, "startRecorder: 开始录屏 $videoPath")
+ //开启通知,并申请成为前台服务
+ createForegroundNotification()
+
+ val mediaProjectionManager = getSystemService()
+ //获得令牌
+ mediaProjection = mediaProjectionManager?.getMediaProjection(Activity.RESULT_OK, intent)
+ Handler(Looper.myLooper()!!).postDelayed(Runnable {
+ //配置MediaRecorder
+ if (configMediaRecorder(videoPath)) {
+ try {
+ //开始录屏
+ recorder?.start()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }, 400)
+ }
+
+ private fun createForegroundNotification() {
+ val notificationManager = getSystemService()
+ val builder: Notification.Builder
+ val name = resources.getString(R.string.app_name)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //创建渠道
+ val id = "${kTag}Channel"
+ val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.setShowBadge(true)
+ channel.enableVibration(false)
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC //设置锁屏可见
+ notificationManager?.createNotificationChannel(channel)
+ builder = Notification.Builder(this, id)
+ } else {
+ builder = Notification.Builder(this)
+ }
+ val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
+ builder.setContentTitle(name)
+ .setContentText("${name}录屏中")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(bitmap)
+ val notification = builder.build()
+ notification.flags = Notification.FLAG_NO_CLEAR
+ startForeground(Int.MAX_VALUE, notification)
+ }
+
+ private fun configMediaRecorder(videoPath: String): Boolean {
+ val dm = resources.displayMetrics
+ recorder = MediaRecorder()
+ recorder?.apply {
+ setAudioSource(MediaRecorder.AudioSource.MIC) //音频载体
+ setVideoSource(MediaRecorder.VideoSource.SURFACE) //视频载体
+ setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) //输出格式
+ setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) //音频格式
+ setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频格式
+ setVideoSize(dm.widthPixels, dm.heightPixels) //size
+ setVideoFrameRate(30) //帧率
+ setVideoEncodingBitRate(3 * 1024 * 1024) //比特率
+ //设置文件位置
+ setOutputFile(videoPath)
+ try {
+ prepare()
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ kTag,
+ dm.widthPixels,
+ dm.heightPixels,
+ dm.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ surface,
+ null,
+ null
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+ return true
+ }
+
+ //停止录制
+ fun stopRecorder() {
+ recorder?.stop()
+ recorder?.release()
+ recorder = null
+
+ mediaProjection?.stop()
+
+ //退出前台服务
+ stopForeground(true)
+ Log.d(kTag, "stopRecorder: 结束录屏")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
deleted file mode 100644
index f54c2a2..0000000
--- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.casic.endoscope.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import com.casic.endoscope.extensions.transcodeVideo
-import com.casic.endoscope.utils.ProjectConstant
-import java.util.Timer
-import java.util.TimerTask
-
-class VideoTranscodeService : Service() {
-
- private val kTag = "VideoTranscodeService"
- private var timer: Timer? = null
-
- override fun onBind(intent: Intent?): IBinder? {
- return null
- }
-
- override fun onCreate() {
- super.onCreate()
- timer = Timer()
- Log.d(kTag, "onCreate => $kTag")
- }
-
-
- /**
- * 执行该方法后,Service 会启动并在后台无限期执行
- * 需要调用 stopSelf() 或 stopService() 来结束Service
- * */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- //定时遍历缓存,看是否有新的视频被录制
- timer?.schedule(object : TimerTask() {
- override fun run() {
- //开始后台转码
- if (!ProjectConstant.isUnderDecodingVideo && ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) {
- ProjectConstant.isUnderDecodingVideo = true
- ProjectConstant.VIDEO_PATH_STACK.pop().transcodeVideo(kTag)
- } else {
- Log.d(kTag, "run: 转码中......")
- }
- }
- }, 0, 1000)
- return START_NOT_STICKY
- }
-
- override fun onDestroy() {
- super.onDestroy()
- Log.d(kTag, "onDestroy => $kTag")
- timer?.cancel()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt b/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
deleted file mode 100644
index 128fbd8..0000000
--- a/app/src/main/java/com/casic/endoscope/utils/FFmpegCommandHub.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.casic.endoscope.utils
-
-import io.microshow.rxffmpeg.RxFFmpegCommandList
-
-object FFmpegCommandHub {
-
- /**
- * 海康视频转码指令
- * */
- fun createVideoTranscodeCommand(inputFilePath: String, outputFilePath: String): Array {
- val commandParams = RxFFmpegCommandList()
- commandParams.append("-i")
- commandParams.append(inputFilePath)
- commandParams.append("-c:v")
- commandParams.append("libx264")
- commandParams.append(outputFilePath)
- return commandParams.build()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
index 02e57c2..78c2ac0 100644
--- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
+++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt
@@ -2,7 +2,6 @@
import android.Manifest
import android.os.Build
-import java.util.Stack
object ProjectConstant {
val USER_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -14,7 +13,8 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
@@ -23,21 +23,24 @@
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.RECORD_AUDIO
)
}
@@ -49,14 +52,5 @@
const val HK_NET_USERNAME = "admin"
const val HK_NET_PASSWORD = "casic203"
- //待转码视频路径
- val VIDEO_PATH_STACK = Stack()
-
const val SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb" //连接设备的UUID
-
- //视频正在转码
- var isUnderDecodingVideo = false
-
- //已转码得视频数量
- var decodedViewCount = 0
}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
index b4e6474..692f453 100644
--- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
+++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt
@@ -1,6 +1,5 @@
package com.casic.endoscope.view
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.ImageView
@@ -10,8 +9,6 @@
import com.casic.endoscope.adapter.MediaDirAdapter
import com.casic.endoscope.databinding.ActivityAlbumBinding
import com.casic.endoscope.utils.FileManager
-import com.casic.endoscope.utils.ProjectConstant
-import com.casic.endoscope.widgets.AlertControlDialog
import com.gyf.immersionbar.ImmersionBar
import com.pengxh.kt.lite.adapter.ViewHolder
import com.pengxh.kt.lite.base.KotlinBaseActivity
@@ -19,15 +16,12 @@
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
import com.pengxh.kt.lite.extensions.navigatePageTo
-import com.pengxh.kt.lite.extensions.show
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-import java.util.Timer
-import java.util.TimerTask
class AlbumActivity : KotlinBaseActivity() {
@@ -39,74 +33,7 @@
private lateinit var dirBeans: MutableList
private lateinit var directoryAdapter: MediaDirAdapter
- private var timer: Timer? = null
-
- @SuppressLint("NotifyDataSetChanged")
override fun initEvent() {
- binding.videoDecodeButton.setOnClickListener {
- val originVideoCollection = ArrayList()
- val convertedVideoCollection = ArrayList()
- val videoDir = File(FileManager.getRootDirectory(), "Video")
- videoDir.listFiles()?.forEach { dir ->
- val childDir = File(dir.absolutePath)
- childDir.listFiles()?.forEach { file ->
- if (file.name.startsWith("t")) {
- convertedVideoCollection.add(file.absolutePath)
- } else {
- originVideoCollection.add(file.absolutePath)
- }
- }
- }
-
- //将已转换的全部去掉t,得到一个临时数组,在和原数组比较,得到差集
- val temp = ArrayList()
- convertedVideoCollection.forEach {
- temp.add(it.replace("/t", "/"))
- }
-
- //将temp和originVideoCollection求差集
- val stringSet = originVideoCollection.subtract(temp.toSet())
- if (stringSet.isEmpty()) {
- "没有需要手动转码的视频".show(context)
- return@setOnClickListener
- }
- AlertControlDialog.Builder()
- .setContext(context)
- .setTitle("温馨提示")
- .setMessage("共有${stringSet.size}个视频需要转码,是否继续?")
- .setNegativeButton("取消")
- .setPositiveButton("确定")
- .setOnDialogButtonClickListener(object :
- AlertControlDialog.OnDialogButtonClickListener {
- override fun onConfirmClick() {
- binding.progressBarLayout.visibility = View.VISIBLE
- binding.progressBar.max = stringSet.size
- stringSet.forEach {
- ProjectConstant.VIDEO_PATH_STACK.push(it)
- }
-
- timer = Timer()
- timer?.schedule(object : TimerTask() {
- override fun run() {
- runOnUiThread {
- binding.progressBar.progress = ProjectConstant.decodedViewCount
- if (ProjectConstant.decodedViewCount == stringSet.size) {
- binding.progressBarLayout.visibility = View.INVISIBLE
- timer?.cancel()
- }
- //刷新列表
- directoryAdapter.notifyDataSetChanged()
- }
- }
- }, 0, 2000)
- }
-
- override fun onCancelClick() {
-
- }
- }).build().show()
- }
-
binding.ascButton.setOnClickListener {
//按时间排序,降序
dirBeans = tempSet.sortedWith { d1, d2 ->
@@ -125,10 +52,6 @@
}
override fun initOnCreate(savedInstanceState: Bundle?) {
- //初始化已转码得视频数量和状态
- ProjectConstant.isUnderDecodingVideo = false
- ProjectConstant.decodedViewCount = 0
-
val temp = ArrayList()
val videoDir = File(FileManager.getRootDirectory(), "Video")
videoDir.list()?.forEach {
diff --git a/app/src/main/java/com/casic/endoscope/view/MainActivity.kt b/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
index cddc6fb..768bffb 100644
--- a/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
+++ b/app/src/main/java/com/casic/endoscope/view/MainActivity.kt
@@ -1,16 +1,22 @@
package com.casic.endoscope.view
import android.annotation.SuppressLint
+import android.app.Activity
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
+import android.content.ComponentName
+import android.content.Context
import android.content.Intent
+import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.PixelFormat
+import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
+import android.os.IBinder
import android.os.Message
import android.provider.Settings
import android.util.Log
@@ -28,7 +34,7 @@
import com.casic.endoscope.extensions.getChannel
import com.casic.endoscope.extensions.init
import com.casic.endoscope.extensions.toTime
-import com.casic.endoscope.service.VideoTranscodeService
+import com.casic.endoscope.service.ScreenShortRecordService
import com.casic.endoscope.utils.DataBaseManager
import com.casic.endoscope.utils.FileManager
import com.casic.endoscope.utils.ProjectConstant
@@ -57,6 +63,7 @@
import com.pengxh.kt.lite.divider.RecyclerViewItemOffsets
import com.pengxh.kt.lite.extensions.dp2px
import com.pengxh.kt.lite.extensions.getStatusBarHeight
+import com.pengxh.kt.lite.extensions.getSystemService
import com.pengxh.kt.lite.extensions.navigatePageTo
import com.pengxh.kt.lite.extensions.show
import com.pengxh.kt.lite.extensions.timestampToTime
@@ -119,12 +126,12 @@
private var timer: Timer? = null
private var timerTask: TimerTask? = null
private var seconds = 0L
- private var videoPath = ""
+ private var screenShortService: ScreenShortRecordService? = null
private lateinit var weakReferenceHandler: WeakReferenceHandler
- private lateinit var serviceIntent: Intent
private lateinit var dataSet: LineDataSet
private lateinit var lineData: LineData
private lateinit var imageAdapter: NormalRecyclerAdapter
+ private lateinit var mediaProjectionManager: MediaProjectionManager
override fun initViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
@@ -137,6 +144,13 @@
FileManager.getRootDirectory()
}
}
+
+ if (requestCode == 2 && resultCode == Activity.RESULT_OK) {
+ val videoPath = "/${FileManager.getVideoFileDir()}/${timeFormat.format(Date())}.mp4"
+ data?.let {
+ screenShortService?.startRecorder(videoPath, it)
+ }
+ }
}
override fun initOnCreate(savedInstanceState: Bundle?) {
@@ -154,8 +168,6 @@
weakReferenceHandler = WeakReferenceHandler(this)
weakReferenceHandler.sendEmptyMessage(messageCode)
- serviceIntent = Intent(this, VideoTranscodeService::class.java)
-
//初始化浓度趋势折线图和Marker
binding.lineChart.init(this)
val markerView = LineChartMarkerView(this)
@@ -164,12 +176,6 @@
binding.lineChart.marker = markerView
}
- override fun onResume() {
- super.onResume()
- //启动视频转码服务
- startService(serviceIntent)
- }
-
override fun initEvent() {
binding.openAlbumButton.setOnClickListener {
if (isPreviewSuccess) {
@@ -315,8 +321,7 @@
val deviceInfo = SDKGuider.sdkGuider.devManageGuider.devList[0]
returnUserId = deviceInfo.szUserId
- aChannelNum =
- deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt()
+ aChannelNum = deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt()
startAChannel =
deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt()
@@ -442,20 +447,20 @@
//视频录制
binding.videoButton.setOnClickListener {
- if (isPreviewSuccess) {
- videoPath = "/${FileManager.getVideoFileDir()}/${timeFormat.format(Date())}.mp4"
- hkSDK.NET_DVR_SaveRealData(returnUserId, videoPath)
- binding.videoStateView.visibility = View.VISIBLE
+ mediaProjectionManager =
+ getSystemService() ?: return@setOnClickListener
+ val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
+ if (captureIntent.resolveActivity(packageManager) != null) {
+ startActivityForResult(captureIntent, 2)
} else {
- "摄像头预览未打开,无法录制视频".show(this)
+ "该设备不支持录屏".show(this)
}
}
+ //视频保存
binding.videoButton.setOnLongClickListener {
- //停止视频抓取
- hkSDK.NET_DVR_StopSaveRealData(returnUserId)
+ screenShortService?.stopRecorder()
binding.videoStateView.visibility = View.INVISIBLE
- ProjectConstant.VIDEO_PATH_STACK.push(videoPath)
"视频录制成功".show(this)
true
}
@@ -467,8 +472,8 @@
serialStart.dwSerialPort = 2
//串口编号
serialStart.wPort = 0
- val serialHandle = hkSDK.NET_DVR_SerialStart_V40(returnUserId, serialStart)
- { _, _, _, _ -> }
+ val serialHandle =
+ hkSDK.NET_DVR_SerialStart_V40(returnUserId, serialStart) { _, _, _, _ -> }
//链式执行
lifecycleScope.launch(Dispatchers.IO) {
flow {
@@ -505,6 +510,31 @@
}
}
+ override fun onStart() {
+ super.onStart()
+ Intent(this, ScreenShortRecordService::class.java).also { intent ->
+ bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
+ }
+ }
+
+ private val serviceConnection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) {
+ if (iBinder is ScreenShortRecordService.ScreenShortRecordServiceBinder) {
+ //截屏
+ screenShortService = iBinder.getScreenShortRecordService()
+ }
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) {
+ "录屏服务已断开".show(context)
+ }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ unbindService(serviceConnection)
+ }
+
override fun handleMessage(msg: Message): Boolean {
if (msg.what == messageCode) {
//绑定数据
@@ -514,13 +544,9 @@
override fun convertView(viewHolder: ViewHolder, position: Int, item: String) {
viewHolder.setImageResource(R.id.imageView, item)
viewHolder.setOnLongClickListener(R.id.imageView) {
- AlertControlDialog.Builder()
- .setContext(context)
- .setTitle("温馨提示")
- .setMessage("是否保存此图片?")
- .setNegativeButton("取消")
- .setPositiveButton("确定")
- .setOnDialogButtonClickListener(object :
+ AlertControlDialog.Builder().setContext(context).setTitle("温馨提示")
+ .setMessage("是否保存此图片?").setNegativeButton("取消")
+ .setPositiveButton("确定").setOnDialogButtonClickListener(object :
AlertControlDialog.OnDialogButtonClickListener {
override fun onConfirmClick() {
@@ -538,10 +564,7 @@
viewHolder.setOnClickListener(R.id.imageView) {
//查看大图Dialog
- PreviewImageDialog.Builder()
- .setContext(context)
- .setImagePath(item)
- .build()
+ PreviewImageDialog.Builder().setContext(context).setImagePath(item).build()
.show()
}
}
@@ -630,7 +653,6 @@
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (System.currentTimeMillis() - clickTime > 2000) {
- stopService(serviceIntent)
"再按一次退出应用".show(this)
clickTime = System.currentTimeMillis()
return true
diff --git a/app/src/main/res/layout/activity_album.xml b/app/src/main/res/layout/activity_album.xml
index 707e032..5c91bfb 100644
--- a/app/src/main/res/layout/activity_album.xml
+++ b/app/src/main/res/layout/activity_album.xml
@@ -55,14 +55,6 @@
-
-