|
|
@@ -5,17 +5,12 @@ import android.app.Activity
|
|
|
import android.media.MediaPlayer
|
|
|
import android.media.MediaRecorder
|
|
|
import android.os.Build
|
|
|
-import android.os.Handler
|
|
|
-import android.os.Message
|
|
|
import android.text.TextUtils
|
|
|
import android.view.View
|
|
|
import cn.i2edu.dubbing_lib.audioUtils.AudioDecoder
|
|
|
import cn.i2edu.dubbing_lib.audioUtils.AudioEncoder
|
|
|
-import cn.i2edu.dubbing_lib.audioUtils.VideoAudioMixer
|
|
|
import cn.i2edu.dubbing_lib.audioUtils.compose.AudioComposer
|
|
|
-import cn.i2edu.dubbing_lib.bean.VideoData
|
|
|
-import cn.i2edu.dubbing_lib.callback.MixinHandlerCallback
|
|
|
-import cn.i2edu.dubbing_lib.util.PausableThreadPool
|
|
|
+import cn.i2edu.dubbing_lib.util.*
|
|
|
import io.flutter.plugin.common.MethodCall
|
|
|
import io.flutter.plugin.common.MethodChannel
|
|
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
|
|
@@ -23,474 +18,301 @@ import io.flutter.plugin.common.MethodChannel.Result
|
|
|
import io.flutter.plugin.common.PluginRegistry.Registrar
|
|
|
import java.io.File
|
|
|
import java.io.IOException
|
|
|
-import java.lang.ref.WeakReference
|
|
|
import java.util.*
|
|
|
|
|
|
-class DubbingLibPlugin: MethodCallHandler, MixinHandlerCallback {
|
|
|
+class DubbingLibPlugin : MethodCallHandler {
|
|
|
|
|
|
- private val activity: Activity
|
|
|
- private var mediaRecorder: MediaRecorder? = null
|
|
|
- private var mediaPlayer: MediaPlayer? = null
|
|
|
- private var isRecording: Boolean = false
|
|
|
- private var timer: Timer? = null
|
|
|
- private val pausableThreadPool: PausableThreadPool = PausableThreadPool(1)
|
|
|
- private val mixinHandler: MixinHandler = MixinHandler(this)
|
|
|
- private var mixinResult: Result? = null
|
|
|
- private var videoData: VideoData? = null
|
|
|
- private var videoAudioMixer: VideoAudioMixer? = null
|
|
|
- private val Tag: String = "DubbingPlugin"
|
|
|
- // mix in 时用到的目录
|
|
|
- private var pathBgmDecodeDir: String? = null
|
|
|
- private var pathBgmRecordSyncDir: String? = null
|
|
|
- private var pathBgmRecordDecodeSyncDir: String? = null
|
|
|
- private var pathVideoMixinDir: String? = null
|
|
|
+ private val activity: Activity
|
|
|
+ private var mediaRecorder: MediaRecorder? = null
|
|
|
+ private var mediaPlayer: MediaPlayer? = null
|
|
|
+ private var isRecording: Boolean = false
|
|
|
+ private var timer: Timer? = null
|
|
|
+ private val pausableThreadPool: PausableThreadPool = PausableThreadPool(1)
|
|
|
+ private val Tag: String = "DubbingPlugin"
|
|
|
|
|
|
- companion object {
|
|
|
-
|
|
|
- private lateinit var channel: MethodChannel
|
|
|
- val CHANNEL_NAME = "cn.hwwwwh.flutter.plugins/DubbingPlugin"
|
|
|
- private val BGM_DOWNLOAD_FINISHED = 0X3999
|
|
|
- private val AUDIO_SYN_FINISHED = 0X4001
|
|
|
- private val AUDIO_MIX_VIDIO_FINISHED = 0X4002
|
|
|
- private val AUDIO_DECODE_FINISHED = 0x4003
|
|
|
- private val AUDIO_ENCODE_FINISHED = 0x4004
|
|
|
-
|
|
|
- @JvmStatic
|
|
|
- fun registerWith(registrar: Registrar) {
|
|
|
- if (registrar.activity() == null) return
|
|
|
- channel = MethodChannel(registrar.messenger(), "dubbing_lib")
|
|
|
- channel.setMethodCallHandler(DubbingLibPlugin(registrar.activity()))
|
|
|
- }
|
|
|
+ companion object {
|
|
|
|
|
|
- private class MixinHandler(plugin: DubbingLibPlugin): Handler() {
|
|
|
- private val plugin: WeakReference<DubbingLibPlugin> = WeakReference(plugin)
|
|
|
+ private lateinit var channel: MethodChannel
|
|
|
+ val CHANNEL_NAME = "cn.hwwwwh.flutter.plugins/DubbingPlugin"
|
|
|
|
|
|
- override fun handleMessage(msg: Message) {
|
|
|
- if (plugin.get() != null) {
|
|
|
- plugin.get()?.onHandleMessage(msg)
|
|
|
+ @JvmStatic
|
|
|
+ fun registerWith(registrar: Registrar) {
|
|
|
+ if (registrar.activity() == null) return
|
|
|
+ channel = MethodChannel(registrar.messenger(), "dubbing_lib")
|
|
|
+ channel.setMethodCallHandler(DubbingLibPlugin(registrar.activity()))
|
|
|
}
|
|
|
- }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- @SuppressLint("CheckResult")
|
|
|
- constructor(activity: Activity) {
|
|
|
- this.activity = activity
|
|
|
- }
|
|
|
|
|
|
- override fun onMethodCall(call: MethodCall, result: Result) {
|
|
|
- when (call.method) {
|
|
|
- "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
|
|
- "setExtraFullScreen" -> {
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
- activity.window.decorView.systemUiVisibility = activity.window.decorView.systemUiVisibility.or(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
|
|
- }
|
|
|
- }
|
|
|
- "startRecord" -> {
|
|
|
- val duration = call.argument<Int>("duration")!!
|
|
|
- val fileName = call.argument<String>("fileName")!!
|
|
|
- val index = call.argument<Int>("index")!!
|
|
|
- val pathAudio = call.argument<String>("pathAudio")!!
|
|
|
- val pathAudioDecode = call.argument<String>("pathAudioDecode")!!
|
|
|
- // 开启录音
|
|
|
- initMediaRecorder(pathAudio, pathAudioDecode, index, duration, fileName, result)
|
|
|
- }
|
|
|
- "playRecordAudio" -> {
|
|
|
- val fileName = call.argument<String>("fileName")!!
|
|
|
- playRecord(fileName, result)
|
|
|
- }
|
|
|
- "pauseRecordAudio" -> {
|
|
|
- mediaPlayer?.pause()
|
|
|
- result.success(true)
|
|
|
- }
|
|
|
- "getIsMediaPlayPause" -> {
|
|
|
- result.success(mediaPlayer != null && !mediaPlayer!!.isPlaying)
|
|
|
- }
|
|
|
- "cleanAudioData" -> {
|
|
|
- try {
|
|
|
- // 取得 video id, 根据前缀进行删除操作
|
|
|
- val videoId = call.argument<String>("videoId")!!
|
|
|
- val pathAudio = call.argument<String>("pathAudio")!!
|
|
|
- val pathAudioDecode = call.argument<String>("pathAudioDecode")!!
|
|
|
- deleteFileWithPrefix(videoId, pathAudio)
|
|
|
- deleteFileWithPrefix(videoId, pathAudioDecode)
|
|
|
- result.success(true)
|
|
|
- } catch (e: Exception){
|
|
|
- e.printStackTrace()
|
|
|
- result.error("1003", "clean cache file failed", null)
|
|
|
- }
|
|
|
- }
|
|
|
- "startMixinAudio" -> {
|
|
|
- try {
|
|
|
- videoData = VideoData(
|
|
|
- videoId = call.argument<String>("videoId")!!,
|
|
|
- bgmUrl = call.argument<String>("bgmUrl")!!,
|
|
|
- durationList = call.argument<List<Long>>("durationList")!!,
|
|
|
- endTimeList = call.argument<List<Long>>("endTimeList")!!,
|
|
|
- audioDecodePaths = call.argument<List<String>>("audioDecodePaths")!!,
|
|
|
- videoPath = call.argument<String>("videoPath")!!
|
|
|
- )
|
|
|
- videoData!!.bgmPath = call.argument<String>("bgmPath")!!
|
|
|
- pathBgmDecodeDir = call.argument<String>("pathBgmDecode")!!
|
|
|
- pathBgmRecordSyncDir = call.argument<String>("pathBgmRecordSync")!!
|
|
|
- pathBgmRecordDecodeSyncDir = call.argument<String>("pathBgmRecordDecodeSync")!!
|
|
|
- pathVideoMixinDir = call.argument<String>("pathVideoMixin")!!
|
|
|
+ @SuppressLint("CheckResult")
|
|
|
+ constructor(activity: Activity) {
|
|
|
+ this.activity = activity
|
|
|
+ }
|
|
|
|
|
|
- mixinResult = result
|
|
|
- startMixinAudio()
|
|
|
- } catch (e: Exception){
|
|
|
- e.printStackTrace()
|
|
|
- result.error("1005", "mixin audio failed", null)
|
|
|
+ override fun onMethodCall(call: MethodCall, result: Result) {
|
|
|
+ when (call.method) {
|
|
|
+ "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
|
|
+ "setExtraFullScreen" -> {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
|
+ activity.window.decorView.systemUiVisibility = activity.window.decorView.systemUiVisibility.or(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "startRecord" -> {
|
|
|
+ val duration = call.argument<Int>("duration")!!
|
|
|
+ val fileName = call.argument<String>("fileName")!!
|
|
|
+ val index = call.argument<Int>("index")!!
|
|
|
+ val pathAudio = call.argument<String>("pathAudio")!!
|
|
|
+ val pathAudioDecode = call.argument<String>("pathAudioDecode")!!
|
|
|
+ // 开启录音
|
|
|
+ initMediaRecorder(pathAudio, pathAudioDecode, index, duration, fileName, result)
|
|
|
+ }
|
|
|
+ "playRecordAudio" -> {
|
|
|
+ val fileName = call.argument<String>("fileName")!!
|
|
|
+ playRecord(fileName, result)
|
|
|
+ }
|
|
|
+ "pauseRecordAudio" -> {
|
|
|
+ mediaPlayer?.pause()
|
|
|
+ result.success(true)
|
|
|
+ }
|
|
|
+ "getIsMediaPlayPause" -> {
|
|
|
+ result.success(mediaPlayer != null && !mediaPlayer!!.isPlaying)
|
|
|
+ }
|
|
|
+ "cleanAudioData" -> {
|
|
|
+ try {
|
|
|
+ // 取得 video id, 根据前缀进行删除操作
|
|
|
+ val videoId = call.argument<String>("videoId")!!
|
|
|
+ val pathAudio = call.argument<String>("pathAudio")!!
|
|
|
+ val pathAudioDecode = call.argument<String>("pathAudioDecode")!!
|
|
|
+ deleteFileWithPrefix(videoId, pathAudio)
|
|
|
+ deleteFileWithPrefix(videoId, pathAudioDecode)
|
|
|
+ result.success(true)
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ result.error("1003", "clean cache file failed", null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "startMixinAudio" -> {
|
|
|
+ try {
|
|
|
+ MixinVideoUtil.getInstance(context = activity.applicationContext)
|
|
|
+ .initParams(videoId = call.argument<String>("videoId")!!, bgmPath = call.argument<String>("bgmPath")!!,
|
|
|
+ videoPath = call.argument<String>("videoPath")!!, durationList = call.argument<List<Long>>("durationList")!!,
|
|
|
+ endTimeList = call.argument<List<Long>>("endTimeList")!!, audioDecodePaths = call.argument<List<String>>("audioDecodePaths")!!,
|
|
|
+ pathBgmDecodeDir = call.argument<String>("pathBgmDecode")!!, pathBgmRecordSyncDir = call.argument<String>("pathBgmRecordSync")!!,
|
|
|
+ pathBgmRecordDecodeSyncDir = call.argument<String>("pathBgmRecordDecodeSync")!!, pathVideoMixinDir = call.argument<String>("pathVideoMixin")!!)
|
|
|
+ .setComposeCallBack(
|
|
|
+ object: MixinVideoCallBack {
|
|
|
+ override fun onResult(resultPath: String) {
|
|
|
+ activity.runOnUiThread {
|
|
|
+ result.success(resultPath)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onError(message: String) {
|
|
|
+ activity.runOnUiThread {
|
|
|
+ result.error("1005", "message", null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .startMixin()
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ result.error("1005", "mixin audio failed", null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ "startMixinPaintedAudio" -> {
|
|
|
+ try {
|
|
|
+ MixinPaintedUtil.getInstance(context = activity.applicationContext)
|
|
|
+ .initParams(audioPaths = call.argument<List<String>>("audioPaths")!!, bgmPath = call.argument<String>("bgmPath")!!,
|
|
|
+ durationList = call.argument<List<Long>>("durationList")!!, endTimeList = call.argument<List<Long>>("endTimeList")!!,
|
|
|
+ audioDecodePath = call.argument<String>("audioDecodePath")!!, mixinFilePath = call.argument<String>("mixinFilePath")!!,
|
|
|
+ encodePath = call.argument<String>("encodePath")!!)
|
|
|
+ .setComposeCallBack(
|
|
|
+ object: MixinPaintedCallBack {
|
|
|
+ override fun onResult(resultPath: String) {
|
|
|
+ activity.runOnUiThread {
|
|
|
+ result.success(resultPath)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onError(message: String) {
|
|
|
+ activity.runOnUiThread {
|
|
|
+ result.error("1005", "message", null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .startMixin()
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ result.error("1006", "mixin painted failed", null)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else -> result.notImplemented()
|
|
|
}
|
|
|
- }
|
|
|
- else -> result.notImplemented()
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private fun initMediaRecorder(pathAudio: String, pathAudioDecode: String,
|
|
|
- index: Int, duration: Int, fileName: String, result: Result) {
|
|
|
- // create file
|
|
|
- stopRecord()
|
|
|
- // config media recorder
|
|
|
- if (mediaRecorder == null) {
|
|
|
- mediaRecorder = MediaRecorder()
|
|
|
- mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
|
|
|
- mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)// 设置所录制的音视频文件的格式
|
|
|
- mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)// 设置所录制的声音的编码格式
|
|
|
- mediaRecorder?.setAudioEncodingBitRate(96000)// 比特率
|
|
|
- mediaRecorder?.setAudioChannels(2)// 通道
|
|
|
- mediaRecorder?.setAudioSamplingRate(44100)// 采样率
|
|
|
+ private fun initMediaRecorder(pathAudio: String, pathAudioDecode: String,
|
|
|
+ index: Int, duration: Int, fileName: String, result: Result) {
|
|
|
+ // create file
|
|
|
+ stopRecord()
|
|
|
+ // config media recorder
|
|
|
+ if (mediaRecorder == null) {
|
|
|
+ mediaRecorder = MediaRecorder()
|
|
|
+ mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
|
|
|
+ mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)// 设置所录制的音视频文件的格式
|
|
|
+ mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)// 设置所录制的声音的编码格式
|
|
|
+ mediaRecorder?.setAudioEncodingBitRate(96000)// 比特率
|
|
|
+ mediaRecorder?.setAudioChannels(2)// 通道
|
|
|
+ mediaRecorder?.setAudioSamplingRate(44100)// 采样率
|
|
|
+ }
|
|
|
+ // start record
|
|
|
+ startRecord(pathAudio, pathAudioDecode, index, duration, fileName, result)
|
|
|
}
|
|
|
- // start record
|
|
|
- startRecord(pathAudio, pathAudioDecode, index, duration, fileName, result)
|
|
|
- }
|
|
|
|
|
|
- private fun startRecord(pathAudio: String, pathAudioDecode: String, index: Int, duration: Int, fileName: String, result: Result) {
|
|
|
- if (mediaRecorder == null) return
|
|
|
- val filePath = pathAudio + fileName
|
|
|
- try {
|
|
|
- mediaRecorder?.setOutputFile(filePath)
|
|
|
- mediaRecorder?.prepare()
|
|
|
- isRecording = true
|
|
|
- mediaRecorder?.start()
|
|
|
- } catch (e: IOException) {
|
|
|
- e.printStackTrace()
|
|
|
- isRecording = false
|
|
|
- }
|
|
|
- if (isRecording) {
|
|
|
- var piecePosition = 0
|
|
|
- if (timer != null) {
|
|
|
- timer?.cancel()
|
|
|
- }
|
|
|
- timer = Timer()
|
|
|
- // 这里参照少儿英语秀之前的逻辑
|
|
|
- timer?.schedule(object : TimerTask() {
|
|
|
- override fun run() {
|
|
|
- activity.runOnUiThread {
|
|
|
+ private fun startRecord(pathAudio: String, pathAudioDecode: String, index: Int, duration: Int, fileName: String, result: Result) {
|
|
|
+ if (mediaRecorder == null) return
|
|
|
+ val filePath = pathAudio + fileName
|
|
|
+ try {
|
|
|
+ mediaRecorder?.setOutputFile(filePath)
|
|
|
+ mediaRecorder?.prepare()
|
|
|
isRecording = true
|
|
|
- piecePosition++
|
|
|
- // 传递进度
|
|
|
- channel.invokeMethod("recordProgress", mapOf("progress" to piecePosition * 5))
|
|
|
- while (piecePosition * 5 >= duration) {
|
|
|
- // 录制完毕
|
|
|
- try {
|
|
|
- result.success(filePath)
|
|
|
- } catch (e: Exception) {
|
|
|
- e.printStackTrace()
|
|
|
- } finally {
|
|
|
+ mediaRecorder?.start()
|
|
|
+ } catch (e: IOException) {
|
|
|
+ e.printStackTrace()
|
|
|
+ isRecording = false
|
|
|
+ }
|
|
|
+ if (isRecording) {
|
|
|
+ var piecePosition = 0
|
|
|
+ if (timer != null) {
|
|
|
timer?.cancel()
|
|
|
- stopRecord()
|
|
|
- // 解码音频
|
|
|
- Thread {
|
|
|
- decodeRecord(pathAudioDecode, index, fileName, filePath)
|
|
|
- }.start()
|
|
|
- }
|
|
|
- break
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }, 0, 5)
|
|
|
- } else {
|
|
|
- result.error("1002", "start record failed", null)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun decodeRecord(pathAudioDecode: String, index: Int, fileName: String, recordPath: String) {
|
|
|
- val decodePath = doDecode(fileName, recordPath, pathAudioDecode)
|
|
|
- if (decodePath != null && File(decodePath).exists()) {
|
|
|
- // 解码成功
|
|
|
- activity.runOnUiThread {
|
|
|
- channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to decodePath))
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 解码失败
|
|
|
- activity.runOnUiThread {
|
|
|
- channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to ""))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun stopRecord() {
|
|
|
- try {
|
|
|
- mediaRecorder?.stop()
|
|
|
- } catch (e: Exception) {
|
|
|
- // TODO from i2 dubit
|
|
|
- // 这里有可能会抛出两种异常,一种是IllegalStateException,抛出这种异常是由于在没有start的情况下使用了stop
|
|
|
- // 另外一种是RuntimeException, 这种异常的产生原因是在执行了start()过后马上执行stop(),具体可看mRecorder.stop()的源码注释
|
|
|
- e.printStackTrace()
|
|
|
- } finally {
|
|
|
- isRecording = false
|
|
|
- try {
|
|
|
- mediaRecorder?.reset()
|
|
|
- mediaRecorder?.release()
|
|
|
- mediaRecorder = null
|
|
|
- } catch (e: Exception) {
|
|
|
- e.printStackTrace()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun playRecord(fileName: String, result: Result) {
|
|
|
- resetMediaPlayer()
|
|
|
- mediaPlayer = MediaPlayer()
|
|
|
- mediaPlayer?.setDataSource(fileName)
|
|
|
- mediaPlayer?.prepare()
|
|
|
- mediaPlayer?.start()
|
|
|
-
|
|
|
- mediaPlayer?.setOnCompletionListener {
|
|
|
- resetMediaPlayer()
|
|
|
- // 播放完成
|
|
|
- result.success(true)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun resetMediaPlayer() {
|
|
|
- mediaPlayer?.stop()
|
|
|
- mediaPlayer?.release()
|
|
|
- mediaPlayer = null
|
|
|
- }
|
|
|
-
|
|
|
- @Throws(Exception::class)
|
|
|
- private fun deleteFileWithPrefix(video: String, path: String): Boolean {
|
|
|
- val file = File(path)
|
|
|
- if (!file.exists()) return false
|
|
|
- // 取得所有文件
|
|
|
- val files = file.listFiles()
|
|
|
- if (files == null || files.isEmpty()) return false
|
|
|
- for (fileExist in files) {
|
|
|
- if (fileExist.isFile) {
|
|
|
- // 验证文件名
|
|
|
- if (fileExist.name.startsWith("${video}_")) {
|
|
|
- fileExist.delete()
|
|
|
+ timer = Timer()
|
|
|
+ // 这里参照少儿英语秀之前的逻辑
|
|
|
+ timer?.schedule(object : TimerTask() {
|
|
|
+ override fun run() {
|
|
|
+ activity.runOnUiThread {
|
|
|
+ isRecording = true
|
|
|
+ piecePosition++
|
|
|
+ // 传递进度
|
|
|
+ channel.invokeMethod("recordProgress", mapOf("progress" to piecePosition * 5))
|
|
|
+ while (piecePosition * 5 >= duration) {
|
|
|
+ // 录制完毕
|
|
|
+ try {
|
|
|
+ result.success(filePath)
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ } finally {
|
|
|
+ timer?.cancel()
|
|
|
+ stopRecord()
|
|
|
+ // 解码音频
|
|
|
+ Thread {
|
|
|
+ decodeRecord(pathAudioDecode, index, fileName, filePath)
|
|
|
+ }.start()
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 0, 5)
|
|
|
+ } else {
|
|
|
+ result.error("1002", "start record failed", null)
|
|
|
}
|
|
|
- }
|
|
|
}
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
-/* @Throws(Exception::class)
|
|
|
- private fun getExistCache(video: String, pathAudio: String, pathAudioDecode: String): Map<Int, String> {
|
|
|
- val map = hashMapOf<Int, String>()
|
|
|
|
|
|
- // 取得所有文件
|
|
|
- val file = File(pathAudio)
|
|
|
- if (!file.exists()) return map
|
|
|
- val files = file.listFiles()
|
|
|
- if (files == null || files.isEmpty()) return map
|
|
|
-
|
|
|
- // 取得所有文件(这里取decode的文件)
|
|
|
- val decodeFile = File(pathAudioDecode)
|
|
|
- if (!decodeFile.exists()) return map
|
|
|
- val decodeFiles = decodeFile.listFiles()
|
|
|
- if (decodeFiles == null || decodeFiles.isEmpty()) return map
|
|
|
-
|
|
|
- for (i in 0 until files.size) {
|
|
|
- val fileExist = files[i]
|
|
|
- if (fileExist.isFile) {
|
|
|
- // 验证文件名
|
|
|
- if (fileExist.name.startsWith("${video}_")) {
|
|
|
- // 填充map
|
|
|
- try {
|
|
|
- val videoIndex = fileExist.name.substring(fileExist.name.indexOf("_") + 1, fileExist.name.length).toInt()
|
|
|
- map[videoIndex]= fileExist.absolutePath.toString()
|
|
|
- // 取decode的文件
|
|
|
- var decodePath = ""
|
|
|
- for (decodeFile in decodeFiles) {
|
|
|
- val decodeVideoIndex = decodeFile.name.substring(decodeFile.name.indexOf("_") + 1, decodeFile.name.length).toInt()
|
|
|
- if (decodeVideoIndex == videoIndex) {
|
|
|
- decodePath = decodeFile.absolutePath
|
|
|
- }
|
|
|
- }
|
|
|
- if (decodePath.isEmpty()) {
|
|
|
- map.remove(videoIndex)
|
|
|
+ private fun decodeRecord(pathAudioDecode: String, index: Int, fileName: String, recordPath: String) {
|
|
|
+ pausableThreadPool.execute {
|
|
|
+ val decodePath = doDecode(fileName, recordPath, pathAudioDecode)
|
|
|
+ if (decodePath != null && File(decodePath).exists()) {
|
|
|
+ // 解码成功
|
|
|
+ activity.runOnUiThread {
|
|
|
+ channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to decodePath))
|
|
|
+ }
|
|
|
} else {
|
|
|
- map[videoIndex] = "${map[videoIndex]}|$decodePath"
|
|
|
+ // 解码失败
|
|
|
+ activity.runOnUiThread {
|
|
|
+ channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to ""))
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (e: Exception) {
|
|
|
- e.printStackTrace()
|
|
|
- }
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- return map
|
|
|
- }*/
|
|
|
-
|
|
|
- // -------------- 合成录音相关
|
|
|
-
|
|
|
- override fun onHandleMessage(msg: Message) {
|
|
|
- when (msg.what) {
|
|
|
- BGM_DOWNLOAD_FINISHED -> {
|
|
|
- // step2 解码背景音乐
|
|
|
-// Toast.makeText(activity.applicationContext, "BGM_DOWNLOAD_FINISHED", Toast.LENGTH_SHORT).show()
|
|
|
- val i = videoData?.bgmUrl?.lastIndexOf('/')
|
|
|
- val name = videoData?.bgmUrl?.substring(i!!)
|
|
|
- decodeBgmAudio(name!!, videoData?.bgmPath!!)
|
|
|
- }
|
|
|
- AUDIO_DECODE_FINISHED -> {
|
|
|
- // step3 背景音乐与录音合成
|
|
|
-// Toast.makeText(activity.applicationContext, "AUDIO_DECODE_FINISHED", Toast.LENGTH_SHORT).show()
|
|
|
- syncAudios()
|
|
|
- }
|
|
|
- AUDIO_SYN_FINISHED -> {
|
|
|
- // step4 编码音频
|
|
|
-// Toast.makeText(activity.applicationContext, "AUDIO_SYN_FINISHED", Toast.LENGTH_SHORT).show()
|
|
|
- encodeAsynAudio()
|
|
|
- }
|
|
|
- AUDIO_ENCODE_FINISHED -> {
|
|
|
-// Toast.makeText(activity.applicationContext, "AUDIO_ENCODE_FINISHED", Toast.LENGTH_SHORT).show()
|
|
|
- mixinAudioAndVideo()
|
|
|
- }
|
|
|
- AUDIO_MIX_VIDIO_FINISHED -> {
|
|
|
-// Toast.makeText(activity.applicationContext, "AUDIO_MIX_VIDIO_FINISHED", Toast.LENGTH_SHORT).show()
|
|
|
- audioMixVideoFinish()
|
|
|
- }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private fun startMixinAudio() {
|
|
|
- activity.runOnUiThread {
|
|
|
- // step2 解码背景音乐
|
|
|
- val message = Message.obtain()
|
|
|
- message.what = BGM_DOWNLOAD_FINISHED
|
|
|
- mixinHandler.sendMessage(message)
|
|
|
+ private fun stopRecord() {
|
|
|
+ try {
|
|
|
+ mediaRecorder?.stop()
|
|
|
+ } catch (e: Exception) {
|
|
|
+ // TODO from i2 dubit
|
|
|
+ // 这里有可能会抛出两种异常,一种是IllegalStateException,抛出这种异常是由于在没有start的情况下使用了stop
|
|
|
+ // 另外一种是RuntimeException, 这种异常的产生原因是在执行了start()过后马上执行stop(),具体可看mRecorder.stop()的源码注释
|
|
|
+ e.printStackTrace()
|
|
|
+ } finally {
|
|
|
+ isRecording = false
|
|
|
+ try {
|
|
|
+ mediaRecorder?.reset()
|
|
|
+ mediaRecorder?.release()
|
|
|
+ mediaRecorder = null
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private fun decodeBgmAudio(fileName:String, localPath: String) {
|
|
|
- if (TextUtils.isEmpty(localPath)) return
|
|
|
- pausableThreadPool.execute {
|
|
|
- val decodedPath = doDecode(fileName, localPath, pathBgmDecodeDir!!)
|
|
|
- videoData?.decodeBgmPath = decodedPath
|
|
|
- // step3 背景音乐与录音合成
|
|
|
- val message = Message.obtain()
|
|
|
- message.what = AUDIO_DECODE_FINISHED
|
|
|
- mixinHandler.sendMessage(message)
|
|
|
+ private fun playRecord(fileName: String, result: Result) {
|
|
|
+ resetMediaPlayer()
|
|
|
+ mediaPlayer = MediaPlayer()
|
|
|
+ mediaPlayer?.setDataSource(fileName)
|
|
|
+ mediaPlayer?.prepare()
|
|
|
+ mediaPlayer?.start()
|
|
|
+
|
|
|
+ mediaPlayer?.setOnCompletionListener {
|
|
|
+ resetMediaPlayer()
|
|
|
+ // 播放完成
|
|
|
+ result.success(true)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private fun syncAudios() {
|
|
|
- // 合成背景音乐和录音 (从尾部开始)
|
|
|
- val fileDIR = File(pathBgmRecordSyncDir)
|
|
|
- if (!fileDIR.exists()) {
|
|
|
- fileDIR.mkdirs()
|
|
|
+ private fun resetMediaPlayer() {
|
|
|
+ mediaPlayer?.stop()
|
|
|
+ mediaPlayer?.release()
|
|
|
+ mediaPlayer = null
|
|
|
}
|
|
|
- val mixFilePath = File(fileDIR.absolutePath + "/" + "mixin.mp3")
|
|
|
-// mixFilePath.createNewFile()
|
|
|
- // 合成操作
|
|
|
- val tempPath = arrayOf<String>(videoData?.decodeBgmPath!!)
|
|
|
- pausableThreadPool.execute {
|
|
|
- AudioComposer.composeAudio(tempPath[0],
|
|
|
- videoData?.audioDecodePaths,
|
|
|
- mixFilePath.absolutePath,
|
|
|
- false,
|
|
|
- videoData?.endTimeList,
|
|
|
- videoData?.durationList,
|
|
|
- object : AudioComposer.ComposeAudioInterface {
|
|
|
- override fun composeSuccess(result: String?) {
|
|
|
- videoData?.decodeAsyncBgmPath = result!!
|
|
|
- val message = Message.obtain()
|
|
|
- message.what = AUDIO_SYN_FINISHED
|
|
|
- mixinHandler.sendMessage(message)
|
|
|
- }
|
|
|
|
|
|
- override fun composeFail() {
|
|
|
- activity.runOnUiThread {
|
|
|
- mixinResult?.error("1005", "async bgm and record failed", "")
|
|
|
- }
|
|
|
+ @Throws(Exception::class)
|
|
|
+ private fun deleteFileWithPrefix(video: String, path: String): Boolean {
|
|
|
+ val file = File(path)
|
|
|
+ if (!file.exists()) return false
|
|
|
+ // 取得所有文件
|
|
|
+ val files = file.listFiles()
|
|
|
+ if (files == null || files.isEmpty()) return false
|
|
|
+ for (fileExist in files) {
|
|
|
+ if (fileExist.isFile) {
|
|
|
+ // 验证文件名
|
|
|
+ if (fileExist.name.startsWith("${video}_")) {
|
|
|
+ fileExist.delete()
|
|
|
}
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun encodeAsynAudio() {
|
|
|
- pausableThreadPool.execute {
|
|
|
- val accEncoder = AudioEncoder
|
|
|
- .createAccEncoder(videoData?.decodeAsyncBgmPath)
|
|
|
-// String finalMixPath = FileBiz.get_temp_recorders_dir() + "/" + UUID.randomUUID() + ".mp4";
|
|
|
- val file = File(pathBgmRecordDecodeSyncDir)
|
|
|
- if (!file.exists()) file.mkdirs()
|
|
|
- val finalMixPath = pathBgmRecordDecodeSyncDir + "mixinDecode.aac"
|
|
|
- val isEncodeFinished = !TextUtils.isEmpty(accEncoder.encodeToFile(finalMixPath))
|
|
|
- if (!isEncodeFinished) {
|
|
|
- activity.runOnUiThread{
|
|
|
- mixinResult?.error("1005", "encode async audio failed", "")
|
|
|
+ }
|
|
|
}
|
|
|
- return@execute
|
|
|
- }
|
|
|
- videoData?.encodeAudioWithBgmPath = finalMixPath
|
|
|
- val message = Message.obtain()
|
|
|
- message.what = AUDIO_ENCODE_FINISHED
|
|
|
- mixinHandler.sendMessage(message)
|
|
|
+ return true
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private fun mixinAudioAndVideo() {
|
|
|
- pausableThreadPool.execute {
|
|
|
- if (videoAudioMixer == null) {
|
|
|
- videoAudioMixer = VideoAudioMixer(activity.applicationContext)
|
|
|
- }
|
|
|
- videoAudioMixer?.setListener(object: VideoAudioMixer.VideoAudioMixListener {
|
|
|
- override fun mixSuccess() {
|
|
|
- videoData?.mixVideoPath = pathVideoMixinDir + "${videoData?.videoId}_mix.mp4"
|
|
|
- val msg = Message()
|
|
|
- msg.what = AUDIO_MIX_VIDIO_FINISHED
|
|
|
- mixinHandler.sendMessage(msg)
|
|
|
- }
|
|
|
|
|
|
- override fun mixFail(reason: String?) {
|
|
|
- activity.runOnUiThread{
|
|
|
- mixinResult?.error("1005", "mix video and audio failed", "")
|
|
|
- }
|
|
|
+ private fun doDecode(fileName: String, path: String, saveDirPath: String): String? {
|
|
|
+ try {
|
|
|
+ // 解码后的路径
|
|
|
+ val decodeFile = File(saveDirPath)
|
|
|
+ if (!decodeFile.exists()) {
|
|
|
+ decodeFile.mkdirs()
|
|
|
+ }
|
|
|
+ val finalFile = File(decodeFile.absolutePath + "/" + fileName)
|
|
|
+ if (!finalFile.exists()) {
|
|
|
+ finalFile.createNewFile()
|
|
|
+ }
|
|
|
+ val audioDec = AudioDecoder
|
|
|
+ .createDefualtDecoder(path)
|
|
|
+ audioDec.decodeToFile(finalFile.absolutePath)
|
|
|
+ return finalFile.absolutePath
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
}
|
|
|
- })
|
|
|
- videoAudioMixer?.mux(videoData?.encodeAudioWithBgmPath, videoData?.videoPath,
|
|
|
- "${videoData?.videoId}_mix.mp4", pathVideoMixinDir)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun audioMixVideoFinish() {
|
|
|
- mixinResult?.success(videoData?.mixVideoPath)
|
|
|
- }
|
|
|
-
|
|
|
- private fun doDecode(fileName: String, path: String, saveDirPath: String): String? {
|
|
|
- try {
|
|
|
- // 解码后的路径
|
|
|
- val decodeFile = File(saveDirPath)
|
|
|
- if (!decodeFile.exists()) {
|
|
|
- decodeFile.mkdirs()
|
|
|
- }
|
|
|
- val finalFile = File(decodeFile.absolutePath + "/" + fileName)
|
|
|
- if (!finalFile.exists()) {
|
|
|
- finalFile.createNewFile()
|
|
|
- }
|
|
|
- val audioDec = AudioDecoder
|
|
|
- .createDefualtDecoder(path)
|
|
|
- audioDec.decodeToFile(finalFile.absolutePath)
|
|
|
- return finalFile.absolutePath
|
|
|
- } catch (e: Exception) {
|
|
|
- e.printStackTrace()
|
|
|
+ return null
|
|
|
}
|
|
|
- return null
|
|
|
- }
|
|
|
|
|
|
}
|