Prechádzať zdrojové kódy

Seek gif frame with seekbar in MovieEditActivity

cooperku_kdanmobile 6 rokov pred
rodič
commit
4d14eca641

+ 32 - 0
src/main/java/com/bomostory/sceneeditmodule/screen/movie/MovieEditActivity.kt

@@ -288,6 +288,38 @@ class MovieEditActivity : AppCompatActivity(),
             }
         }
 
+        moviePlayer.gifPlayListener = object : MoviePlayerV2.OnGifPlayListener {
+            override fun onGifStart() {
+                runOnUiThread {
+                    for (gifView in movieView.gifViews) {
+                        gifView.startGifAnimation()
+                    }
+                }
+            }
+
+            override fun onGifSeek(time: Int) {
+                onGifUpdate(time)
+                onGifPause()
+                onGifStart()
+            }
+
+            override fun onGifPause() {
+                runOnUiThread {
+                    for (gifView in movieView.gifViews) {
+                        gifView.stopGifAnimation()
+                    }
+                }
+            }
+
+            override fun onGifUpdate(time: Int) {
+                runOnUiThread {
+                    for (gifView in movieView.gifViews) {
+                        gifView.setImageBitmap(gifView.getBitmapAt(time))
+                    }
+                }
+            }
+        }
+
         val monitorSize = DisplayMetrics()
         windowManager.defaultDisplay.getMetrics(monitorSize)
         var layoutPrams = movieView.layoutParams

+ 14 - 15
src/main/java/com/bomostory/sceneeditmodule/screen/view/ActorView.kt

@@ -13,15 +13,17 @@ import kotlin.math.max
 
 class ActorView : ImageView {
 
+    var gifDrawable: GifDrawable? = null
+
     var data: Actor? = null
         set(value) {
             field = value
             value?.let {
                 if(!it.isDialogue) {
                     if (it.resourcePath.toLowerCase().endsWith(".gif")) {
-                        val gifDrawable = GifDrawable(it.resourcePath)
+                        gifDrawable = GifDrawable(it.resourcePath)
                         setImageDrawable(gifDrawable)
-                        gifDrawable.start()
+                        gifDrawable?.start()
                     } else {
                         setImageDrawable(Drawable.createFromPath(it.resourcePath))
                     }
@@ -51,35 +53,32 @@ class ActorView : ImageView {
     fun GifDrawable.getBitmapAt(milliseconds: Int): Bitmap = seekToPositionAndGet(if (loopCount == 0 || milliseconds < duration * loopCount) max(0, milliseconds % duration) else (duration))
 
     fun isGif(): Boolean {
-        return drawable is GifDrawable
+        return gifDrawable != null
     }
 
     fun isGifPlaying(): Boolean {
-        if (drawable is GifDrawable) {
-            val gifDrawable = drawable as GifDrawable
-            return gifDrawable.isPlaying
+        if (isGif()) {
+            return gifDrawable!!.isPlaying
         }
         return false
     }
 
     fun startGifAnimation() {
-        if (drawable is GifDrawable) {
-            val gifDrawable = drawable as GifDrawable
-            gifDrawable.start()
+        if (isGif()) {
+            setImageDrawable(gifDrawable)
+            gifDrawable!!.start()
         }
     }
 
     fun stopGifAnimation() {
-        if (drawable is GifDrawable) {
-            val gifDrawable = drawable as GifDrawable
-            gifDrawable.stop()
+        if (isGif()) {
+            gifDrawable!!.stop()
         }
     }
 
     fun getBitmapAt(milliseconds: Int): Bitmap? {
-        if (drawable is GifDrawable) {
-            val gifDrawable = drawable as GifDrawable
-            return gifDrawable.seekToPositionAndGet(milliseconds)
+        if (isGif()) {
+            return gifDrawable!!.getBitmapAt(milliseconds)
         } else if (drawable is BitmapDrawable) {
             val bitmapDrawable = drawable as BitmapDrawable
             return bitmapDrawable.bitmap

+ 12 - 0
src/main/java/com/bomostory/sceneeditmodule/screen/view/MovieView.kt

@@ -15,6 +15,8 @@ import kotlin.math.pow
 
 class MovieView : ConstraintLayout {
 
+    val gifViews = ArrayList<ActorView>()
+
     private var backgroundView = LayerView(context)
 
     var scene: Scene? = null
@@ -22,6 +24,7 @@ class MovieView : ConstraintLayout {
             field = value
             value?.let {
                 removeAllViews()
+                gifViews.clear()
 
                 val screenWidth = it.sceneWidth
                 val screenHeight = it.sceneWidth / 2
@@ -79,6 +82,15 @@ class MovieView : ConstraintLayout {
                         actors = actorsFix
                     }
                     addView(layerView)
+
+                    for (i in 0 until layerView.childCount) {
+                        val childView = layerView.getChildAt(i)
+                        if (childView is ActorView) {
+                            if (childView.isGif()) {
+                                gifViews.add(childView)
+                            }
+                        }
+                    }
                 }
             }
         }

+ 102 - 10
src/main/java/com/bomostory/sceneeditmodule/utils/MoviePlayerV2.kt

@@ -16,7 +16,15 @@ class MoviePlayerV2 {
         fun onMoviePlayComplete()
     }
 
+    interface OnGifPlayListener {
+        fun onGifStart()
+        fun onGifSeek(time: Int)
+        fun onGifPause()
+        fun onGifUpdate(time: Int)
+    }
+
     var moviePlayListener: OnMoviePlayListener? = null
+    var gifPlayListener: OnGifPlayListener? = null
 
     var story: Story? = null
 
@@ -44,7 +52,7 @@ class MoviePlayerV2 {
 
         currentTime = 0
 
-        updateMovie()
+        updateMovie(true)
 
         tryStopRecord()
 
@@ -54,16 +62,19 @@ class MoviePlayerV2 {
 
         initMediaPlayer()
 
+        gifPlayListener?.onGifPause()
     }
 
     fun start() {
         if (timer == null) {
+            var time = System.nanoTime()
             timer = Timer()
             timer?.scheduleAtFixedRate(object : TimerTask() {
                 override fun run() {
+                    var now = System.nanoTime()
                     if (!isPause) {
 
-                        currentTime++
+                        currentTime = Math.min(period, currentTime + (now - time) / 1000000)
 
                         updateMovie()
 
@@ -76,11 +87,14 @@ class MoviePlayerV2 {
                             init()
                         }
                     }
+                    time = now
                 }
-            }, 0, 1)
+            }, 0, 20)
         } else {
             isPause = false
+            updateMovie(true)
         }
+        gifPlayListener?.onGifStart()
     }
 
     fun pause() {
@@ -89,31 +103,109 @@ class MoviePlayerV2 {
         tryPauseRecord()
 
         tryPauseMusic()
+
+        gifPlayListener?.onGifPause()
     }
 
     fun seekTo(currentTime: Long) {
         this.currentTime = currentTime
 
-        updateMovie()
+        updateMovie(true)
 
         trySeekRecord()
 
         trySeekMusic()
     }
 
-    private fun updateMovie() {
-        val scene = getScene(currentTime)
-        scene?.apply {
+    private var startPeriodCache = 0L
+    private var endPeriodCache = 0L
+    private var targetScene: Scene? = null
+    private var previousTrackIndex = -1
+    private var currentTrackIndex = -1
+
+    private fun updateMovie(seek: Boolean = false) {
+        if (currentTime < startPeriodCache || currentTime >= endPeriodCache) {
+            targetScene = null
+            var startPeriod = 0L
+            var endPeriod = 0L
+            story?.apply {
+                for (scene in scenes) {
+                    scene.record?.apply {
+                        endPeriod += period
+                        if (currentTime in (endPeriod - period)..endPeriod) {
+                            targetScene = scene
+                            startPeriod = endPeriod - period
+                        }
+                    }
+                    if (targetScene != null)
+                        break
+                }
+            }
+            startPeriodCache = startPeriod
+            endPeriodCache = endPeriod
+            previousTrackIndex = -1
+            currentTrackIndex = -1
+        }
+
+        val currentTimeInScene = currentTime - startPeriodCache
+        targetScene?.apply {
             moviePlayListener?.onMovieSceneUpdate(this)
             record?.let {
-                for (track in it.tracks) {
-                    if (track.time == currentTime) {
-                        moviePlayListener?.onMoviePlayViewUpdate(track.positionX)
+                if (it.tracks == null || it.tracks.size == 0) {
+                    moviePlayListener?.onMoviePlayViewUpdate(0)
+                    return@let
+                }
+                if (currentTimeInScene < it.tracks[0].time) {
+                    moviePlayListener?.onMoviePlayViewUpdate(0)
+                    return@let
+                }
+                if (currentTimeInScene >= it.tracks[it.tracks.size - 1].time) {
+                    moviePlayListener?.onMoviePlayViewUpdate(it.tracks[it.tracks.size - 1].positionX)
+                    return@let
+                }
+
+                var startIndex = 1
+                var endIndex = it.tracks.size
+                var hitCache = true
+                if (currentTrackIndex > 0) {
+                    if (currentTimeInScene < it.tracks[previousTrackIndex].time) {
+                        endIndex = previousTrackIndex
+                        hitCache = false
+                    } else if (currentTimeInScene > it.tracks[currentTrackIndex].time) {
+                        startIndex = Math.max(1, currentTrackIndex - 1)
+                        hitCache = false
+                    }
+                } else {
+                    hitCache = false
+                }
+                if (!hitCache) {
+                    previousTrackIndex = Math.max(0, startIndex)
+                    currentTrackIndex = Math.min(it.tracks.size - 1, endIndex)
+                    for (i in startIndex until endIndex) {
+                        if (currentTimeInScene in (it.tracks[i - 1].time .. it.tracks[i].time)) {
+                            previousTrackIndex = i - 1
+                            currentTrackIndex = i
+                            break
+                        }
                     }
                 }
+
+                val previousTrack = it.tracks[previousTrackIndex]
+                val track = it.tracks[currentTrackIndex]
+                val positionX = previousTrack.positionX + (track.positionX - previousTrack.positionX) * (currentTimeInScene - previousTrack.time) / Math.max(1, track.time - previousTrack.time)
+                moviePlayListener?.onMoviePlayViewUpdate(positionX.toInt())
             }
         }
         moviePlayListener?.onMoviePlayTimeUpdate(currentTime)
+        if (seek) {
+            if (timer == null || isPause) {
+                gifPlayListener?.onGifUpdate(currentTimeInScene.toInt())
+                gifPlayListener?.onGifPause()
+            }
+            else {
+                gifPlayListener?.onGifSeek(currentTimeInScene.toInt())
+            }
+        }
     }
 
     private fun getScene(t: Long): Scene? {