Pārlūkot izejas kodu

Refactor export movie and add progress dialog

Wayne 6 gadi atpakaļ
vecāks
revīzija
8166bf525d

+ 32 - 108
src/main/java/com/bomostory/sceneeditmodule/MovieEditActivity.kt

@@ -1,13 +1,13 @@
 package com.bomostory.sceneeditmodule
 
+import android.app.AlertDialog
+import android.app.ProgressDialog
 import android.content.DialogInterface
-import android.graphics.*
-import android.support.v7.app.AppCompatActivity
 import android.os.Bundle
 import android.os.Environment
 import android.os.Handler
 import android.support.v4.app.DialogFragment
-import android.util.Log
+import android.support.v7.app.AppCompatActivity
 import android.view.View
 import android.widget.CompoundButton
 import android.widget.SeekBar
@@ -16,17 +16,13 @@ import com.bomostory.sceneeditmodule.utils.MoviePlayer
 import com.bomostory.sceneeditmodule.utils.MusicPlayer
 import com.bomostory.sceneeditmodule.view.AudioTrackGroupView
 import com.bomostory.sceneeditmodule.view.MovieSelectView
+import com.example.exportmedia.MediaHelper
 import com.example.tfat.myapplication.R
 import com.google.gson.Gson
-import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.schedulers.Schedulers
 import kotlinx.android.synthetic.main.activity_movie_edit.*
-import com.example.exportmedia.MediaHelper
-import com.example.exportmedia.data.AudioSource
-import com.example.exportmedia.vedio.MovieMaker
 import java.io.File
-import java.util.AbstractMap
-import kotlin.math.pow
 
 
 class MovieEditActivity : AppCompatActivity(), DialogInterface.OnDismissListener, MusicSelectFragment.OnFragmentInteractionListener {
@@ -248,113 +244,41 @@ class MovieEditActivity : AppCompatActivity(), DialogInterface.OnDismissListener
     }
 
     private fun saveMovie(view: View) {
-        val inputSceneStatus = LinkedHashMap<Int, Int>()
-        var sceneIndex = -1
-        var sceneRepeatCount = 0
-        var sceneCount = 0
-
-
-        val movieBuilder = MovieMaker.Builder(mediaHelper)
-        movieBuilder.fps = FPS
-        movieBuilder.audioSources = generateAudioSource()
+        val scaledWidth = resources.getDimensionPixelSize(R.dimen.movie_width)
+        val scaleHeight = resources.getDimensionPixelSize(R.dimen.movie_height)
 
-        var totalFrame = 0
-        scene?.record?.period?.let {
-             totalFrame = (it/((1f / FPS) * 1000).toLong()).toInt()
+        val pd = ProgressDialog(this).apply {
+            val t = scene?.record?.period ?: 0
+            max = (t / 33).toInt()
+            setMessage("progressing")
         }
 
-        generateMovieFilms()
+        val fileName = "test${System.currentTimeMillis()}.mp4"
+        val outputFile = File(Environment.getExternalStorageDirectory(), fileName)
+        val s = scene ?: return
+
+        SuperMovieMaker().make(
+                outputFile,
+                s,
+                musics,
+                scaledWidth,
+                scaleHeight,
+                mediaHelper)
                 .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .doOnSubscribe { pd.show() }
+                .doFinally { pd.dismiss() }
                 .subscribe({
-                    Log.d("Debug","progress ${sceneCount++}/$totalFrame")
-                    if (it.key == sceneIndex) {
-                        sceneRepeatCount++
-                    } else {
-                        sceneIndex = it.key
-                        sceneRepeatCount = 1
-                        movieBuilder.addImage(it.key, it.value).subscribe()
-                    }
-                    inputSceneStatus[sceneIndex] = sceneRepeatCount
+                    pd.setMessage(it)
                 }, {
-
+                    val msg = """
+                    Error:
+                    ${it.message}
+                """.trimIndent()
+                    AlertDialog.Builder(this).setMessage(msg).show()
                 }, {
-                    movieBuilder.addInputFile(inputSceneStatus).subscribe({
-                        movieBuilder.inputFilePath = it
-                    }, {
-
-                    }, {
-                        val movieMaker = movieBuilder.build()
-                        movieMaker.output(File(Environment.getExternalStorageDirectory(), "test.mp4")).subscribe()
-                    })
+                    AlertDialog.Builder(this).setMessage("complete").show()
                 })
     }
 
-    private fun generateAudioSource(): ArrayList<AudioSource> {
-        val audioSources = ArrayList<AudioSource>()
-        for (music in musics) {
-            val audioSource = AudioSource()
-            audioSource.duration = music.duration
-            audioSource.offsetSec = music.offsetTime.toFloat() / 1000
-            audioSource.path = music.path
-            audioSources.add(audioSource)
-        }
-        return audioSources
-    }
-
-    private fun generateMovieFilms(): Observable<AbstractMap.SimpleEntry<Int, Bitmap>> {
-        var preBitmap: Bitmap?
-        return Observable.create<AbstractMap.SimpleEntry<Int, Bitmap>> {
-            scene?.apply {
-                record?.apply {
-                    var bitmap = drawScene(0)
-                    var bitmapIndex = 0
-                    var trackPosition = -1
-
-                    for (t in 0 .. period step ((1f / FPS) * 1000).toLong()) {
-                        for (track in tracks) {
-                            if (t >= track.time && tracks.indexOf(track) > trackPosition) {
-                                preBitmap = bitmap
-                                preBitmap?.recycle()
-
-                                bitmap = drawScene(track.positionX)
-                                trackPosition = tracks.indexOf(track)
-                            }
-                        }
-
-                        bitmap?.apply {
-                                it.onNext(AbstractMap.SimpleEntry(bitmapIndex++, this))
-                        }
-                    }
-                    it.onComplete()
-                }
-            }
-        }
-    }
-
-    private fun drawScene(trackX: Int): Bitmap? {
-        var sceneBitmap: Bitmap? = null
-        val scaledWidth = resources.getDimensionPixelSize(R.dimen.movie_width)
-        val scaleHeight = resources.getDimensionPixelSize(R.dimen.movie_height)
-
-        scene?.apply {
-            sceneBitmap = BitmapFactory.decodeFile(backgroundPath)
-            sceneBitmap = Bitmap.createScaledBitmap(sceneBitmap, scaledWidth, scaleHeight, true)
-
-            val canvas = Canvas(sceneBitmap)
-            for (layer in layers) {
-                for (actor in layer.actors) {
-                    var actorBitmap = BitmapFactory.decodeFile(actor.resourcePath)
-                    actorBitmap = Bitmap.createScaledBitmap(actorBitmap, actor.sideLength, actor.sideLength, true)
-
-                    canvas.save()
-                    canvas.translate(trackX / 2f.pow(layers.indexOf(layer)), 0f)
-                    canvas.drawBitmap(actorBitmap, actor.positionX.toFloat(), actor.positionY.toFloat(), Paint())
-                    canvas.restore()
-
-                    actorBitmap.recycle()
-                }
-            }
-        }
-        return sceneBitmap
-    }
 }

+ 143 - 0
src/main/java/com/bomostory/sceneeditmodule/SuperMovieMaker.kt

@@ -0,0 +1,143 @@
+package com.bomostory.sceneeditmodule
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.Log
+import com.bomostory.sceneeditmodule.MovieEditActivity.Companion.FPS
+import com.bomostory.sceneeditmodule.basicdata.Music
+import com.bomostory.sceneeditmodule.basicdata.Scene
+import com.example.exportmedia.MediaHelper
+import com.example.exportmedia.data.AudioSource
+import com.example.exportmedia.vedio.MovieMaker
+import io.reactivex.Observable
+import java.io.File
+import java.util.AbstractMap
+import kotlin.collections.ArrayList
+import kotlin.collections.LinkedHashMap
+import kotlin.collections.List
+import kotlin.collections.set
+import kotlin.math.pow
+
+class SuperMovieMaker {
+
+    fun make(
+            outputFile: File,
+            scene: Scene,
+            musics: List<Music>,
+            scaleWidth: Int,
+            scaleHeight: Int,
+            mediaHelper: MediaHelper): Observable<String> {
+        return Observable.create<String> { emitter ->
+            val movieBuilder = MovieMaker.Builder(mediaHelper)
+            movieBuilder.fps = FPS
+            movieBuilder.audioSources = generateAudioSource(musics)
+
+            val inputSceneStatus = LinkedHashMap<Int, Int>()
+            var sceneIndex = -1
+            var sceneRepeatCount = 0
+            var sceneCount = 0
+
+            var totalFrame = 0
+            scene?.record?.period?.let {
+                totalFrame = (it / ((1f / FPS) * 1000).toLong()).toInt()
+            }
+
+            generateMovieFilms(
+                    scene,
+                    scaleWidth,
+                    scaleHeight
+            )
+                    .subscribe {
+                        val msg = "progress ${sceneCount++}/$totalFrame"
+                        emitter.onNext("Compute image $msg")
+                        Log.d("Debug", msg)
+                        if (it.key == sceneIndex) {
+                            sceneRepeatCount++
+                        } else {
+                            sceneIndex = it.key
+                            sceneRepeatCount = 1
+                            movieBuilder.addImage(it.key, it.value).subscribe()
+                            emitter.onNext("Save image $msg")
+                        }
+                        inputSceneStatus[sceneIndex] = sceneRepeatCount
+                    }
+
+            movieBuilder.inputFilePath = movieBuilder.addInputFile(inputSceneStatus).blockingFirst()
+            emitter.onNext("Encoding video")
+            movieBuilder.build().output(outputFile).blockingSubscribe()
+            emitter.onComplete()
+        }
+    }
+
+    private fun generateAudioSource(musics: List<Music>): ArrayList<AudioSource> {
+        val audioSources = ArrayList<AudioSource>()
+        for (music in musics) {
+            val audioSource = AudioSource()
+            audioSource.duration = music.duration
+            audioSource.offsetSec = music.offsetTime.toFloat() / 1000
+            audioSource.path = music.path
+            audioSources.add(audioSource)
+        }
+        return audioSources
+    }
+
+    private fun generateMovieFilms(
+            scene: Scene,
+            scaleWidth: Int,
+            scaleHeight: Int): Observable<AbstractMap.SimpleEntry<Int, Bitmap>> {
+        var preBitmap: Bitmap?
+        return Observable.create<AbstractMap.SimpleEntry<Int, Bitmap>> {
+            scene?.apply {
+                record?.apply {
+                    var bitmap = drawScene(scene, 0, scaleWidth, scaleHeight)
+                    var bitmapIndex = 0
+                    var trackPosition = -1
+
+                    for (t in 0..period step ((1f / FPS) * 1000).toLong()) {
+                        for (track in tracks) {
+                            if (t >= track.time && tracks.indexOf(track) > trackPosition) {
+                                preBitmap = bitmap
+                                preBitmap?.recycle()
+
+                                bitmap = drawScene(scene, track.positionX, scaleWidth, scaleHeight)
+                                trackPosition = tracks.indexOf(track)
+                            }
+                        }
+
+                        bitmap?.apply {
+                            it.onNext(AbstractMap.SimpleEntry(bitmapIndex++, this))
+                        }
+                    }
+                    it.onComplete()
+                }
+            }
+        }
+    }
+
+    private fun drawScene(scene: Scene, trackX: Int, scaleWidth: Int, scaleHeight: Int): Bitmap? {
+        var sceneBitmap: Bitmap? = null
+
+        scene?.apply {
+            sceneBitmap = BitmapFactory.decodeFile(backgroundPath)
+            sceneBitmap = Bitmap.createScaledBitmap(sceneBitmap, scaleWidth, scaleHeight, true)
+
+            val canvas = Canvas(sceneBitmap)
+            for (layer in layers) {
+                for (actor in layer.actors) {
+                    var actorBitmap = BitmapFactory.decodeFile(actor.resourcePath)
+                    actorBitmap = Bitmap.createScaledBitmap(actorBitmap, actor.sideLength, actor.sideLength, true)
+
+                    canvas.save()
+                    canvas.translate(trackX / 2f.pow(layers.indexOf(layer)), 0f)
+                    canvas.drawBitmap(actorBitmap, actor.positionX.toFloat(), actor.positionY.toFloat(), Paint())
+                    canvas.restore()
+
+                    actorBitmap.recycle()
+                }
+            }
+        }
+        return sceneBitmap
+    }
+}