Explorar o código

1. Video add audio 2. remove temporary files

cooperku_kdanmobile %!s(int64=6) %!d(string=hai) anos
pai
achega
2e9859ab2a

+ 1 - 0
src/main/java/com/bomostory/sceneeditmodule/Config.kt

@@ -7,6 +7,7 @@ object Config {
     private val ROOT = File(Environment.getExternalStorageDirectory(), "Bomo")
     private const val ASSETS_FOLDER_PATH = "/Bomo/Assets"
     private const val PROJECTS_FOLDER_PATH = "/Bomo/Projects"
+    const val MOVIE_TEMPORARY_FILE_FOLDER_NAME = "MovieTemporary"
     const val RECORD_FOLDER_NAME = "Record"
     const val IMAGE_FOLDER_NAME = "Image"
     const val MUSIC_FOLDER_NAME = "Music"

+ 106 - 30
src/main/java/com/bomostory/sceneeditmodule/SuperMovieMaker.kt

@@ -10,10 +10,13 @@ import com.bomostory.sceneeditmodule.basicdata.Project
 import com.bomostory.sceneeditmodule.screen.movie.MediaCodecMovieEncoder
 import com.bomostory.sceneeditmodule.screen.movie.MovieEncoder
 import com.bomostory.sceneeditmodule.utils.FileUtils
+import com.example.exportmedia.MediaCallBack
 import com.example.exportmedia.MediaHelper
 import com.example.exportmedia.audio.AudioConcat
 import com.example.exportmedia.audio.AudioLooper
+import com.example.exportmedia.audio.AudioMixer
 import com.example.exportmedia.data.AudioSource
+import com.example.exportmedia.video.MovieMakerV2
 import io.reactivex.*
 import io.reactivex.Observable
 import io.reactivex.schedulers.Schedulers
@@ -37,28 +40,35 @@ class SuperMovieMaker {
         return Observable.create<String> { emitter ->
 
             try {
-//                movieEncoder = MediaCodecMovieEncoder()
-//                movieEncoder = JcodecMovieEncoder()
+                val beginTime = System.nanoTime()
+                var timeAudioMixer = 0L
+                var timeMovieMakerV2 = 0L
+                var timeVideoDraw = 0L
+                var timeVideoEncode = 0L
+
+                val tmpMovieDirectory = File("${FileUtils.getProjectPath(project)}/${Config.MOVIE_TEMPORARY_FILE_FOLDER_NAME}/")
+                if (!tmpMovieDirectory.exists() || !tmpMovieDirectory.isDirectory)
+                    tmpMovieDirectory.mkdirs()
+                val tmpVideoFile = File("${tmpMovieDirectory.absolutePath}/tmpVideo.mp4")
+                val tmpAudioFile = File("${tmpMovieDirectory.absolutePath}/tmpAudio.mp4")
+
+                if (tmpVideoFile.exists())
+                    tmpVideoFile.delete()
+                if (tmpAudioFile.exists())
+                    tmpAudioFile.delete()
+                if (outputFile.exists())
+                    outputFile.delete()
+
+                emitter.onNext("Ready")
                 movieEncoder?.setVideoDimension(scaleWidth, scaleHeight)
                 movieEncoder?.prepare(outputFile, FPS)
 
-                val audioSources = ArrayList<AudioSource>()
-                project.story?.let {
-                    audioSources.add(generateRecordSource(project, mediaHelper))
-                }
-                audioSources.addAll(generateAudioSource(project, musics, mediaHelper))
-
+                var time: Long
                 val frameDataList = generateFrameDataList(project)
                 Log.d(this::class.java.simpleName, "frameDataList.size = ${frameDataList.size}")
                 val semaphore = Semaphore(5)
                 var generateCount = 0
                 var completeCount = 0
-//                emitter.onNext("$completeCount/${frameDataList.size}")
-                emitter.onNext("Ready")
-
-                val beginTime = System.nanoTime()
-                var timeDraw = 0L
-                var timeEncode = 0L
 
                 SceneDrawer.reset()
                 movieEncoder?.start()
@@ -66,9 +76,9 @@ class SuperMovieMaker {
                     frameDataList.forEach { frameData ->
                         semaphore.acquire()
                         val scene = project.story!!.scenes[frameData.sceneIndex]
-                        val time = System.nanoTime()
+                        time = System.nanoTime()
                         val bitmap = SceneDrawer.drawScene(context, scene, frameData.x, scaleWidth, scaleHeight, frameData.millisecond)
-                        timeDraw += System.nanoTime() - time
+                        timeVideoDraw += System.nanoTime() - time
                         generateCount++
                         it.onNext(Pair(bitmap!!, frameData))
                     }
@@ -80,26 +90,92 @@ class SuperMovieMaker {
                         .subscribe ({
                             val bitmap = it.first
                             val frameData = it.second
-                            val time = System.nanoTime()
+                            time = System.nanoTime()
                             movieEncoder?.addFrame(bitmap!!, frameData.repeat)
-                            timeEncode += System.nanoTime() - time
+                            timeVideoEncode += System.nanoTime() - time
                             bitmap.recycle()
 
                             completeCount++
-//                            emitter.onNext(String.format("%3.2f%% (%03d / %03d)", completeCount * 100.0 / frameDataList.size, completeCount, frameDataList.size))
                             emitter.onNext(String.format("%3.2f%%", completeCount * 100.0 / frameDataList.size))
-//                            emitter.onNext(String.format("%3.2f%% (%03d / %03d)", completeCount * 100.0 / frameDataList.size, completeCount, generateCount))
                             semaphore.release()
                         }, {
                             it.printStackTrace()
+                        }, {
+                            emitter.onNext("Audio mixing")
                         })
                 SceneDrawer.reset()
                 movieEncoder?.finish()
 
+                if (!emitter.isDisposed) {
+                    val audioSources = ArrayList<AudioSource>()
+                    project.story?.let {
+                        audioSources.add(generateRecordSource(project, mediaHelper))
+                    }
+                    audioSources.addAll(generateAudioSource(project, musics, mediaHelper))
+
+                    time = System.nanoTime()
+                    val audioMixerBuilder = AudioMixer.Builder(mediaHelper)
+                    audioMixerBuilder.audioSourceList = audioSources
+                    val audioMixer = audioMixerBuilder.build()
+                    Observable.create<String> {
+                        audioMixer.mediaCallBack = object: MediaCallBack {
+                            override fun onExecuteSuccess(message: String?) {
+                                message?.let { msg ->
+                                    it.onNext(msg)
+                                }
+                            }
+
+                            override fun onExecuteFailed(message: String?) {
+                                it.onError(Throwable(message))
+                            }
+
+                            override fun onExecuteFinish() {
+                                it.onComplete()
+                            }
+                        }
+                        audioMixer.output(tmpAudioFile)
+                    }.blockingSubscribe()
+                    timeAudioMixer = System.nanoTime() - time
+                    emitter.onNext("Encoding video")
+
+                    val outputPath = outputFile.absolutePath
+                    outputFile.renameTo(tmpVideoFile)
+
+                    time = System.nanoTime()
+                    val movieMakerBuilder = MovieMakerV2.Builder(mediaHelper)
+                    movieMakerBuilder.videoPath = tmpVideoFile.absolutePath
+                    movieMakerBuilder.audioPath = tmpAudioFile.absolutePath
+                    val movieMaker = movieMakerBuilder.build()
+                    Observable.create<String> {
+                        movieMaker.mediaCallBack = object: MediaCallBack {
+                            override fun onExecuteSuccess(message: String?) {
+                                message?.let { msg ->
+                                    it.onNext(msg)
+                                }
+                            }
+
+                            override fun onExecuteFailed(message: String?) {
+                                it.onError(Throwable(message))
+                            }
+
+                            override fun onExecuteFinish() {
+                                it.onComplete()
+                            }
+                        }
+                        movieMaker.output(File(outputPath))
+                    }.blockingSubscribe()
+                    timeMovieMakerV2 = System.nanoTime() - time
+                }
+
                 val endTime = System.nanoTime()
                 Log.d(this::class.java.simpleName, "duration = ${(endTime - beginTime) / 1000000000.0}")
-                Log.d(this::class.java.simpleName, "timeDraw = ${timeDraw / 1000000000.0}")
-                Log.d(this::class.java.simpleName, "timeEncode = ${timeEncode / 1000000000.0}")
+                Log.d(this::class.java.simpleName, "timeVideoDraw = ${timeVideoDraw / 1000000000.0}")
+                Log.d(this::class.java.simpleName, "timeVideoEncode = ${timeVideoEncode / 1000000000.0}")
+                Log.d(this::class.java.simpleName, "timeAudioMixer = ${timeAudioMixer / 1000000000.0}")
+                Log.d(this::class.java.simpleName, "timeMovieMakerV2 = ${timeMovieMakerV2 / 1000000000.0}")
+
+                tmpMovieDirectory.deleteRecursively()
+
                 emitter.onComplete()
             } catch (e: Exception) {
                 e.printStackTrace()
@@ -114,9 +190,9 @@ class SuperMovieMaker {
     private fun generateRecordSource(project: Project, mediaHelper: MediaHelper): AudioSource {
         val audioSource = AudioSource()
 
-        val recordPath = "${FileUtils.getProjectPath(project)}/${Config.RECORD_FOLDER_NAME}"
-        val fileName = "${System.currentTimeMillis()}.mp3"
-        val outputFile = File(recordPath, fileName)
+        val tmpMovieDirectory = File("${FileUtils.getProjectPath(project)}/${Config.MOVIE_TEMPORARY_FILE_FOLDER_NAME}/")
+        val fileName = "tmpRecord${System.currentTimeMillis()}.mp3"
+        val outputFile = File(tmpMovieDirectory, fileName)
 
         val audioConcatBuilder = AudioConcat.Builder(mediaHelper)
         project.story?.let {
@@ -141,13 +217,13 @@ class SuperMovieMaker {
     private fun generateAudioSource(project: Project, musics: List<Music>, mediaHelper: MediaHelper): ArrayList<AudioSource> {
         val audioSources = ArrayList<AudioSource>()
 
-        val musicPath = "${FileUtils.getProjectPath(project)}/${Config.MUSIC_FOLDER_NAME}"
+        val tmpMovieDirectory = File("${FileUtils.getProjectPath(project)}/${Config.MOVIE_TEMPORARY_FILE_FOLDER_NAME}/")
         for (music in musics) {
             val audioSource = AudioSource()
 
             if (music.isLoop) {
-                val fileName = "${System.currentTimeMillis()}.mp3"
-                val outputFile = File(musicPath, fileName)
+                val fileName = "tmpMusic${System.currentTimeMillis()}.mp3"
+                val outputFile = File(tmpMovieDirectory, fileName)
 
                 val audioLooperBuilder = AudioLooper.Builder(mediaHelper)
                 audioLooperBuilder.loopAudioFile = File(music.path)
@@ -195,8 +271,8 @@ class SuperMovieMaker {
                             val durationCount = Math.ceil(period / gifDrawable.duration.toDouble()).toInt()
                             var duration = 0L
                             for (times in 1..Math.min(loopCount, durationCount)) {
-                                for (index in 0 until numberOfFrames) {
-                                    duration += gifDrawable.getFrameDuration(index)
+                                for (frameIndex in 0 until numberOfFrames) {
+                                    duration += gifDrawable.getFrameDuration(frameIndex)
                                     if (duration > period)
                                         break
                                     timeSet.add(duration)