Skip to content

Commit efff2ba

Browse files
committed
Optimisations:
* save last frame as photo is the input size is the same as video output * cache save as photo alignment
1 parent 7829db3 commit efff2ba

2 files changed

Lines changed: 61 additions & 35 deletions

File tree

app/src/main/java/com/dan/timelapse/screens/MainFragment.kt

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.dan.timelapse.screens
22

33
import android.content.Intent
44
import android.media.AudioManager
5+
import android.media.MediaMetadataRetriever
56
import android.media.MediaScannerConnection
67
import android.net.Uri
78
import android.os.Bundle
@@ -22,6 +23,7 @@ import com.dan.timelapse.utils.UriFile
2223
import com.dan.timelapse.video.VideoTools
2324
import com.dan.timelapse.video.VideoWriter
2425
import kotlinx.coroutines.*
26+
import org.opencv.android.Utils
2527
import org.opencv.core.Mat
2628
import java.io.File
2729
import java.io.FileNotFoundException
@@ -78,6 +80,7 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
7880
private var firstFrame = Mat()
7981
private val firstFrameMask = Mat()
8082
private val alignCache = AlignCache()
83+
private val alignCacheForPhoto = AlignCache()
8184
private var outputParams: OutputParams? = null
8285
private var alignMaskId = 0
8386

@@ -118,12 +121,11 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
118121
binding.video.start()
119122
}
120123

121-
private fun getCurrentOutputParams(framesInput: FramesInput): OutputParams {
124+
private fun getCurrentOutputParams(framesInput: FramesInput, fullSize: Boolean): OutputParams {
122125
val outputParams = OutputParams()
123126

124127
outputParams.set(OutputParams.KEY_H265, if(settings.h265) 1 else 0)
125128
outputParams.set(OutputParams.KEY_CROP, if(settings.crop) 1 else 0)
126-
outputParams.set(OutputParams.KEY_4K, if(settings.encode4K) 1 else 0)
127129

128130
outputParams.set(OutputParams.KEY_SPEED, binding.seekBarSpeed.progress + 1)
129131
outputParams.set(OutputParams.KEY_ALIGN, if (binding.switchAlign.isChecked) alignMaskId else -1)
@@ -148,7 +150,28 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
148150
}
149151
}
150152

151-
outputParams.set(OutputParams.KEY_ORIENTATION, orientation)
153+
var width: Int
154+
var height: Int
155+
156+
if (fullSize) {
157+
width = framesInput.width
158+
height = framesInput.height
159+
} else if(settings.encode4K) {
160+
width = 1920 * 2
161+
height = 1080 * 2
162+
} else {
163+
width = 1920
164+
height = 1080
165+
}
166+
167+
if ((orientation == ORIENTATION_LANDSCAPE && width < height) ||(orientation == ORIENTATION_PORTRAIT && width > height)) {
168+
val tmp = width
169+
width = height
170+
height = tmp
171+
}
172+
173+
outputParams.set(OutputParams.KEY_WIDTH, width)
174+
outputParams.set(OutputParams.KEY_HEIGHT, height)
152175

153176
return outputParams
154177
}
@@ -170,7 +193,7 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
170193

171194
videoStop()
172195

173-
val outputParams = getCurrentOutputParams(framesInput)
196+
val outputParams = getCurrentOutputParams(framesInput, false)
174197
val changes = outputParams.compareWith(this.outputParams)
175198

176199
if (!tmpOutputVideo.exists() || OutputParams.COMPARE_NOT_CHANGED != changes) {
@@ -230,6 +253,7 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
230253
MaskEditFragment.show(activity, firstFrame, firstFrameMask) {
231254
alignMaskId++
232255
alignCache.reset()
256+
alignCacheForPhoto.reset()
233257
}
234258
}
235259

@@ -416,6 +440,7 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
416440
private fun setFramesInput(framesInput: FramesInput) {
417441
outputParams = null
418442
alignCache.resetAll()
443+
alignCacheForPhoto.resetAll()
419444
this.framesInput = framesInput
420445
binding.seekBarFPS.progress = Settings.getClosestFpsIndex(framesInput.fps)
421446
firstFrame.release()
@@ -441,7 +466,7 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
441466
val framesInput = this.framesInput ?: return
442467
videoStop()
443468
runAsync(TITLE_SAVE_PHOTO) {
444-
generateAsync(getCurrentOutputParams(framesInput), true)
469+
generateAsync(getCurrentOutputParams(framesInput, true), true)
445470
}
446471
}
447472

@@ -492,28 +517,10 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
492517
outputParams: OutputParams,
493518
framesInput: FramesInput,
494519
finalConsumer: FramesConsumer,
495-
alignCache: AlignCache,
496-
_videoWidth: Int = -1,
497-
_videoHeight: Int = -1): Boolean {
498-
var videoWidth = _videoWidth
499-
var videoHeight = _videoHeight
500-
501-
if (videoWidth <= 0 || videoHeight <= 0) {
502-
videoWidth = 1920
503-
videoHeight = 1080
504-
505-
if (outputParams.get(OutputParams.KEY_4K) != 0) {
506-
videoWidth *= 2
507-
videoHeight *= 2
508-
}
509-
510-
if (ORIENTATION_PORTRAIT == outputParams.get(OutputParams.KEY_ORIENTATION)) {
511-
val tmp = videoWidth
512-
videoWidth = videoHeight
513-
videoHeight = tmp
514-
}
515-
}
520+
alignCache: AlignCache): Boolean {
516521

522+
val videoWidth = outputParams.get(OutputParams.KEY_WIDTH)
523+
val videoHeight = outputParams.get(OutputParams.KEY_HEIGHT)
517524
var frameConsumer: FramesConsumer = finalConsumer
518525

519526
if (binding.spinnerEffect.selectedItemPosition > 0) {
@@ -607,13 +614,32 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
607614

608615
try {
609616
val finalFrameConsumer = ImageWriter(outputFile, settings.jpegQuality)
610-
generateAsync(
611-
outputParams,
612-
framesInput,
613-
finalFrameConsumer,
614-
AlignCache(),
615-
framesInput.width,
616-
framesInput.height)
617+
618+
val changes = outputParams.compareWith(this.outputParams)
619+
if (tmpOutputVideo.exists() && (OutputParams.COMPARE_NOT_CHANGED == changes || OutputParams.COMPARE_CHANGED_ONLY_FPS == changes)) {
620+
try {
621+
val mediaMetadataRetriever = MediaMetadataRetriever()
622+
mediaMetadataRetriever.setDataSource(tmpOutputVideo.absolutePath)
623+
val strFrameCount = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT) ?: throw FileNotFoundException()
624+
val frameCount = strFrameCount.toIntOrNull() ?: throw FileNotFoundException()
625+
val bitmap = mediaMetadataRetriever.getFrameAtIndex(frameCount-1) ?: throw FileNotFoundException()
626+
val frame = Mat()
627+
Utils.bitmapToMat(bitmap, frame)
628+
finalFrameConsumer.start()
629+
finalFrameConsumer.consume(0, frame)
630+
finalFrameConsumer.stop(false)
631+
} catch (e: Exception) {
632+
e.printStackTrace()
633+
}
634+
} else {
635+
generateAsync(
636+
outputParams,
637+
framesInput,
638+
finalFrameConsumer,
639+
alignCacheForPhoto
640+
)
641+
}
642+
617643
success = finalFrameConsumer.success
618644
} catch (e: Exception) {
619645
e.printStackTrace()

app/src/main/java/com/dan/timelapse/utils/OutputParams.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ class OutputParams {
1010

1111
const val KEY_H265 = "H265"
1212
const val KEY_CROP = "CROP"
13-
const val KEY_4K = "4K"
1413
const val KEY_FPS = "FPS"
1514
const val KEY_SPEED = "SPEED"
1615
const val KEY_ALIGN = "ALIGN"
1716
const val KEY_EFFECT = "EFFECT"
1817
const val KEY_EFFECT_SIZE = "EFFECT-SIZE"
1918
const val KEY_DURATION = "DURATION"
20-
const val KEY_ORIENTATION = "ORIENTATION"
19+
const val KEY_WIDTH = "WIDTH"
20+
const val KEY_HEIGHT = "HEIGHT"
2121

2222
private fun compare(a: Map<String, Int>, b: Map<String, Int>): Int {
2323
var fpsChanged = false

0 commit comments

Comments
 (0)