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 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt index 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt index 6e4e62a..f54c2a2 100644 --- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt +++ b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt @@ -4,18 +4,15 @@ import android.content.Intent import android.os.IBinder import android.util.Log -import com.casic.endoscope.utils.FFmpegCommandHub +import com.casic.endoscope.extensions.transcodeVideo import com.casic.endoscope.utils.ProjectConstant -import com.pengxh.kt.lite.extensions.createLogFile -import com.pengxh.kt.lite.extensions.writeToFile -import io.microshow.rxffmpeg.RxFFmpegInvoke -import io.microshow.rxffmpeg.RxFFmpegSubscriber 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 @@ -23,6 +20,7 @@ override fun onCreate() { super.onCreate() + timer = Timer() Log.d(kTag, "onCreate => $kTag") } @@ -33,50 +31,23 @@ * */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //定时遍历缓存,看是否有新的视频被录制 - Timer().schedule(object : TimerTask() { + timer?.schedule(object : TimerTask() { override fun run() { - if (ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) { - transcodeVideo(ProjectConstant.VIDEO_PATH_STACK.pop()) + //开始后台转码 + 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 } - //开始后台转码 - private fun transcodeVideo(inputPath: String) { - /** - * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4 - * */ - val lastIndex = inputPath.lastIndexOf("/") - val fileName = inputPath.drop(lastIndex + 1) - //文件名前面+t - val newFileName = fileName.reversed().plus("t").reversed() - val outputPath = inputPath.replace(fileName, newFileName) - - RxFFmpegInvoke.getInstance() - .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(inputPath, outputPath)) - .subscribe(object : RxFFmpegSubscriber() { - override fun onError(message: String?) { - message?.writeToFile(createLogFile()) - } - - override fun onFinish() { - Log.d(kTag, "onFinish => $outputPath 转码完成") - } - - override fun onProgress(progress: Int, progressTime: Long) { - Log.d(kTag, "onProgress => $progress") - } - - override fun onCancel() { - - } - }) - } - 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/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt index 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt index 6e4e62a..f54c2a2 100644 --- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt +++ b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt @@ -4,18 +4,15 @@ import android.content.Intent import android.os.IBinder import android.util.Log -import com.casic.endoscope.utils.FFmpegCommandHub +import com.casic.endoscope.extensions.transcodeVideo import com.casic.endoscope.utils.ProjectConstant -import com.pengxh.kt.lite.extensions.createLogFile -import com.pengxh.kt.lite.extensions.writeToFile -import io.microshow.rxffmpeg.RxFFmpegInvoke -import io.microshow.rxffmpeg.RxFFmpegSubscriber 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 @@ -23,6 +20,7 @@ override fun onCreate() { super.onCreate() + timer = Timer() Log.d(kTag, "onCreate => $kTag") } @@ -33,50 +31,23 @@ * */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //定时遍历缓存,看是否有新的视频被录制 - Timer().schedule(object : TimerTask() { + timer?.schedule(object : TimerTask() { override fun run() { - if (ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) { - transcodeVideo(ProjectConstant.VIDEO_PATH_STACK.pop()) + //开始后台转码 + 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 } - //开始后台转码 - private fun transcodeVideo(inputPath: String) { - /** - * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4 - * */ - val lastIndex = inputPath.lastIndexOf("/") - val fileName = inputPath.drop(lastIndex + 1) - //文件名前面+t - val newFileName = fileName.reversed().plus("t").reversed() - val outputPath = inputPath.replace(fileName, newFileName) - - RxFFmpegInvoke.getInstance() - .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(inputPath, outputPath)) - .subscribe(object : RxFFmpegSubscriber() { - override fun onError(message: String?) { - message?.writeToFile(createLogFile()) - } - - override fun onFinish() { - Log.d(kTag, "onFinish => $outputPath 转码完成") - } - - override fun onProgress(progress: Int, progressTime: Long) { - Log.d(kTag, "onProgress => $progress") - } - - override fun onCancel() { - - } - }) - } - 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/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt index 2c214b1..02e57c2 100644 --- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt +++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt @@ -53,4 +53,10 @@ 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/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt index 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt index 6e4e62a..f54c2a2 100644 --- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt +++ b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt @@ -4,18 +4,15 @@ import android.content.Intent import android.os.IBinder import android.util.Log -import com.casic.endoscope.utils.FFmpegCommandHub +import com.casic.endoscope.extensions.transcodeVideo import com.casic.endoscope.utils.ProjectConstant -import com.pengxh.kt.lite.extensions.createLogFile -import com.pengxh.kt.lite.extensions.writeToFile -import io.microshow.rxffmpeg.RxFFmpegInvoke -import io.microshow.rxffmpeg.RxFFmpegSubscriber 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 @@ -23,6 +20,7 @@ override fun onCreate() { super.onCreate() + timer = Timer() Log.d(kTag, "onCreate => $kTag") } @@ -33,50 +31,23 @@ * */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //定时遍历缓存,看是否有新的视频被录制 - Timer().schedule(object : TimerTask() { + timer?.schedule(object : TimerTask() { override fun run() { - if (ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) { - transcodeVideo(ProjectConstant.VIDEO_PATH_STACK.pop()) + //开始后台转码 + 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 } - //开始后台转码 - private fun transcodeVideo(inputPath: String) { - /** - * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4 - * */ - val lastIndex = inputPath.lastIndexOf("/") - val fileName = inputPath.drop(lastIndex + 1) - //文件名前面+t - val newFileName = fileName.reversed().plus("t").reversed() - val outputPath = inputPath.replace(fileName, newFileName) - - RxFFmpegInvoke.getInstance() - .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(inputPath, outputPath)) - .subscribe(object : RxFFmpegSubscriber() { - override fun onError(message: String?) { - message?.writeToFile(createLogFile()) - } - - override fun onFinish() { - Log.d(kTag, "onFinish => $outputPath 转码完成") - } - - override fun onProgress(progress: Int, progressTime: Long) { - Log.d(kTag, "onProgress => $progress") - } - - override fun onCancel() { - - } - }) - } - 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/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt index 2c214b1..02e57c2 100644 --- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt +++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt @@ -53,4 +53,10 @@ 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 1bf1e99..b4e6474 100644 --- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt +++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt @@ -10,6 +10,8 @@ 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 @@ -17,25 +19,94 @@ 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() { private val kTag = "AlbumActivity" - private val context = this@AlbumActivity + private val context = this //子文件夹集合 private lateinit var tempSet: HashSet 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 -> @@ -54,6 +125,10 @@ } 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/extensions/String.kt b/app/src/main/java/com/casic/endoscope/extensions/String.kt index 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt index 6e4e62a..f54c2a2 100644 --- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt +++ b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt @@ -4,18 +4,15 @@ import android.content.Intent import android.os.IBinder import android.util.Log -import com.casic.endoscope.utils.FFmpegCommandHub +import com.casic.endoscope.extensions.transcodeVideo import com.casic.endoscope.utils.ProjectConstant -import com.pengxh.kt.lite.extensions.createLogFile -import com.pengxh.kt.lite.extensions.writeToFile -import io.microshow.rxffmpeg.RxFFmpegInvoke -import io.microshow.rxffmpeg.RxFFmpegSubscriber 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 @@ -23,6 +20,7 @@ override fun onCreate() { super.onCreate() + timer = Timer() Log.d(kTag, "onCreate => $kTag") } @@ -33,50 +31,23 @@ * */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //定时遍历缓存,看是否有新的视频被录制 - Timer().schedule(object : TimerTask() { + timer?.schedule(object : TimerTask() { override fun run() { - if (ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) { - transcodeVideo(ProjectConstant.VIDEO_PATH_STACK.pop()) + //开始后台转码 + 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 } - //开始后台转码 - private fun transcodeVideo(inputPath: String) { - /** - * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4 - * */ - val lastIndex = inputPath.lastIndexOf("/") - val fileName = inputPath.drop(lastIndex + 1) - //文件名前面+t - val newFileName = fileName.reversed().plus("t").reversed() - val outputPath = inputPath.replace(fileName, newFileName) - - RxFFmpegInvoke.getInstance() - .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(inputPath, outputPath)) - .subscribe(object : RxFFmpegSubscriber() { - override fun onError(message: String?) { - message?.writeToFile(createLogFile()) - } - - override fun onFinish() { - Log.d(kTag, "onFinish => $outputPath 转码完成") - } - - override fun onProgress(progress: Int, progressTime: Long) { - Log.d(kTag, "onProgress => $progress") - } - - override fun onCancel() { - - } - }) - } - 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/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt index 2c214b1..02e57c2 100644 --- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt +++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt @@ -53,4 +53,10 @@ 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 1bf1e99..b4e6474 100644 --- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt +++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt @@ -10,6 +10,8 @@ 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 @@ -17,25 +19,94 @@ 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() { private val kTag = "AlbumActivity" - private val context = this@AlbumActivity + private val context = this //子文件夹集合 private lateinit var tempSet: HashSet 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 -> @@ -54,6 +125,10 @@ } 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/res/drawable/bg_video_decode_progress.xml b/app/src/main/res/drawable/bg_video_decode_progress.xml new file mode 100644 index 0000000..8c2ee4e --- /dev/null +++ b/app/src/main/res/drawable/bg_video_decode_progress.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + 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 7e5bdc6..2762fe7 100644 --- a/app/src/main/java/com/casic/endoscope/extensions/String.kt +++ b/app/src/main/java/com/casic/endoscope/extensions/String.kt @@ -1,5 +1,10 @@ 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 /** @@ -10,4 +15,36 @@ 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/VideoTranscodeService.kt b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt index 6e4e62a..f54c2a2 100644 --- a/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt +++ b/app/src/main/java/com/casic/endoscope/service/VideoTranscodeService.kt @@ -4,18 +4,15 @@ import android.content.Intent import android.os.IBinder import android.util.Log -import com.casic.endoscope.utils.FFmpegCommandHub +import com.casic.endoscope.extensions.transcodeVideo import com.casic.endoscope.utils.ProjectConstant -import com.pengxh.kt.lite.extensions.createLogFile -import com.pengxh.kt.lite.extensions.writeToFile -import io.microshow.rxffmpeg.RxFFmpegInvoke -import io.microshow.rxffmpeg.RxFFmpegSubscriber 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 @@ -23,6 +20,7 @@ override fun onCreate() { super.onCreate() + timer = Timer() Log.d(kTag, "onCreate => $kTag") } @@ -33,50 +31,23 @@ * */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { //定时遍历缓存,看是否有新的视频被录制 - Timer().schedule(object : TimerTask() { + timer?.schedule(object : TimerTask() { override fun run() { - if (ProjectConstant.VIDEO_PATH_STACK.isNotEmpty()) { - transcodeVideo(ProjectConstant.VIDEO_PATH_STACK.pop()) + //开始后台转码 + 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 } - //开始后台转码 - private fun transcodeVideo(inputPath: String) { - /** - * //storage/emulated/0/Android/data/com.casic.endoscope/files/Movies/2024-02-21/20240221161555.mp4 - * */ - val lastIndex = inputPath.lastIndexOf("/") - val fileName = inputPath.drop(lastIndex + 1) - //文件名前面+t - val newFileName = fileName.reversed().plus("t").reversed() - val outputPath = inputPath.replace(fileName, newFileName) - - RxFFmpegInvoke.getInstance() - .runCommandRxJava(FFmpegCommandHub.createVideoTranscodeCommand(inputPath, outputPath)) - .subscribe(object : RxFFmpegSubscriber() { - override fun onError(message: String?) { - message?.writeToFile(createLogFile()) - } - - override fun onFinish() { - Log.d(kTag, "onFinish => $outputPath 转码完成") - } - - override fun onProgress(progress: Int, progressTime: Long) { - Log.d(kTag, "onProgress => $progress") - } - - override fun onCancel() { - - } - }) - } - 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/ProjectConstant.kt b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt index 2c214b1..02e57c2 100644 --- a/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt +++ b/app/src/main/java/com/casic/endoscope/utils/ProjectConstant.kt @@ -53,4 +53,10 @@ 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 1bf1e99..b4e6474 100644 --- a/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt +++ b/app/src/main/java/com/casic/endoscope/view/AlbumActivity.kt @@ -10,6 +10,8 @@ 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 @@ -17,25 +19,94 @@ 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() { private val kTag = "AlbumActivity" - private val context = this@AlbumActivity + private val context = this //子文件夹集合 private lateinit var tempSet: HashSet 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 -> @@ -54,6 +125,10 @@ } 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/res/drawable/bg_video_decode_progress.xml b/app/src/main/res/drawable/bg_video_decode_progress.xml new file mode 100644 index 0000000..8c2ee4e --- /dev/null +++ b/app/src/main/res/drawable/bg_video_decode_progress.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_album.xml b/app/src/main/res/layout/activity_album.xml index 3474fb0..707e032 100644 --- a/app/src/main/res/layout/activity_album.xml +++ b/app/src/main/res/layout/activity_album.xml @@ -21,14 +21,47 @@ android:src="@mipmap/ic_launcher" /> + + + + + + + +