|
@@ -10,10 +10,13 @@ import com.bomostory.sceneeditmodule.basicdata.Project
|
|
import com.bomostory.sceneeditmodule.screen.movie.MediaCodecMovieEncoder
|
|
import com.bomostory.sceneeditmodule.screen.movie.MediaCodecMovieEncoder
|
|
import com.bomostory.sceneeditmodule.screen.movie.MovieEncoder
|
|
import com.bomostory.sceneeditmodule.screen.movie.MovieEncoder
|
|
import com.bomostory.sceneeditmodule.utils.FileUtils
|
|
import com.bomostory.sceneeditmodule.utils.FileUtils
|
|
|
|
+import com.example.exportmedia.MediaCallBack
|
|
import com.example.exportmedia.MediaHelper
|
|
import com.example.exportmedia.MediaHelper
|
|
import com.example.exportmedia.audio.AudioConcat
|
|
import com.example.exportmedia.audio.AudioConcat
|
|
import com.example.exportmedia.audio.AudioLooper
|
|
import com.example.exportmedia.audio.AudioLooper
|
|
|
|
+import com.example.exportmedia.audio.AudioMixer
|
|
import com.example.exportmedia.data.AudioSource
|
|
import com.example.exportmedia.data.AudioSource
|
|
|
|
+import com.example.exportmedia.video.MovieMakerV2
|
|
import io.reactivex.*
|
|
import io.reactivex.*
|
|
import io.reactivex.Observable
|
|
import io.reactivex.Observable
|
|
import io.reactivex.schedulers.Schedulers
|
|
import io.reactivex.schedulers.Schedulers
|
|
@@ -37,28 +40,35 @@ class SuperMovieMaker {
|
|
return Observable.create<String> { emitter ->
|
|
return Observable.create<String> { emitter ->
|
|
|
|
|
|
try {
|
|
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?.setVideoDimension(scaleWidth, scaleHeight)
|
|
movieEncoder?.prepare(outputFile, FPS)
|
|
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)
|
|
val frameDataList = generateFrameDataList(project)
|
|
Log.d(this::class.java.simpleName, "frameDataList.size = ${frameDataList.size}")
|
|
Log.d(this::class.java.simpleName, "frameDataList.size = ${frameDataList.size}")
|
|
val semaphore = Semaphore(5)
|
|
val semaphore = Semaphore(5)
|
|
var generateCount = 0
|
|
var generateCount = 0
|
|
var completeCount = 0
|
|
var completeCount = 0
|
|
-// emitter.onNext("$completeCount/${frameDataList.size}")
|
|
|
|
- emitter.onNext("Ready")
|
|
|
|
-
|
|
|
|
- val beginTime = System.nanoTime()
|
|
|
|
- var timeDraw = 0L
|
|
|
|
- var timeEncode = 0L
|
|
|
|
|
|
|
|
SceneDrawer.reset()
|
|
SceneDrawer.reset()
|
|
movieEncoder?.start()
|
|
movieEncoder?.start()
|
|
@@ -66,9 +76,9 @@ class SuperMovieMaker {
|
|
frameDataList.forEach { frameData ->
|
|
frameDataList.forEach { frameData ->
|
|
semaphore.acquire()
|
|
semaphore.acquire()
|
|
val scene = project.story!!.scenes[frameData.sceneIndex]
|
|
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)
|
|
val bitmap = SceneDrawer.drawScene(context, scene, frameData.x, scaleWidth, scaleHeight, frameData.millisecond)
|
|
- timeDraw += System.nanoTime() - time
|
|
|
|
|
|
+ timeVideoDraw += System.nanoTime() - time
|
|
generateCount++
|
|
generateCount++
|
|
it.onNext(Pair(bitmap!!, frameData))
|
|
it.onNext(Pair(bitmap!!, frameData))
|
|
}
|
|
}
|
|
@@ -80,26 +90,92 @@ class SuperMovieMaker {
|
|
.subscribe ({
|
|
.subscribe ({
|
|
val bitmap = it.first
|
|
val bitmap = it.first
|
|
val frameData = it.second
|
|
val frameData = it.second
|
|
- val time = System.nanoTime()
|
|
|
|
|
|
+ time = System.nanoTime()
|
|
movieEncoder?.addFrame(bitmap!!, frameData.repeat)
|
|
movieEncoder?.addFrame(bitmap!!, frameData.repeat)
|
|
- timeEncode += System.nanoTime() - time
|
|
|
|
|
|
+ timeVideoEncode += System.nanoTime() - time
|
|
bitmap.recycle()
|
|
bitmap.recycle()
|
|
|
|
|
|
completeCount++
|
|
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%%", completeCount * 100.0 / frameDataList.size))
|
|
-// emitter.onNext(String.format("%3.2f%% (%03d / %03d)", completeCount * 100.0 / frameDataList.size, completeCount, generateCount))
|
|
|
|
semaphore.release()
|
|
semaphore.release()
|
|
}, {
|
|
}, {
|
|
it.printStackTrace()
|
|
it.printStackTrace()
|
|
|
|
+ }, {
|
|
|
|
+ emitter.onNext("Audio mixing")
|
|
})
|
|
})
|
|
SceneDrawer.reset()
|
|
SceneDrawer.reset()
|
|
movieEncoder?.finish()
|
|
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()
|
|
val endTime = System.nanoTime()
|
|
Log.d(this::class.java.simpleName, "duration = ${(endTime - beginTime) / 1000000000.0}")
|
|
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()
|
|
emitter.onComplete()
|
|
} catch (e: Exception) {
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
e.printStackTrace()
|
|
@@ -114,9 +190,9 @@ class SuperMovieMaker {
|
|
private fun generateRecordSource(project: Project, mediaHelper: MediaHelper): AudioSource {
|
|
private fun generateRecordSource(project: Project, mediaHelper: MediaHelper): AudioSource {
|
|
val audioSource = 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)
|
|
val audioConcatBuilder = AudioConcat.Builder(mediaHelper)
|
|
project.story?.let {
|
|
project.story?.let {
|
|
@@ -141,13 +217,13 @@ class SuperMovieMaker {
|
|
private fun generateAudioSource(project: Project, musics: List<Music>, mediaHelper: MediaHelper): ArrayList<AudioSource> {
|
|
private fun generateAudioSource(project: Project, musics: List<Music>, mediaHelper: MediaHelper): ArrayList<AudioSource> {
|
|
val audioSources = 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) {
|
|
for (music in musics) {
|
|
val audioSource = AudioSource()
|
|
val audioSource = AudioSource()
|
|
|
|
|
|
if (music.isLoop) {
|
|
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)
|
|
val audioLooperBuilder = AudioLooper.Builder(mediaHelper)
|
|
audioLooperBuilder.loopAudioFile = File(music.path)
|
|
audioLooperBuilder.loopAudioFile = File(music.path)
|
|
@@ -195,8 +271,8 @@ class SuperMovieMaker {
|
|
val durationCount = Math.ceil(period / gifDrawable.duration.toDouble()).toInt()
|
|
val durationCount = Math.ceil(period / gifDrawable.duration.toDouble()).toInt()
|
|
var duration = 0L
|
|
var duration = 0L
|
|
for (times in 1..Math.min(loopCount, durationCount)) {
|
|
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)
|
|
if (duration > period)
|
|
break
|
|
break
|
|
timeSet.add(duration)
|
|
timeSet.add(duration)
|