From f5023749674aed1009ba6733c30258e52c588d27 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 15 Jun 2020 17:23:37 +0200 Subject: [PATCH] Video export code, crashing due to memory allocation --- .../model/realm/handhistory/HandHistory.kt | 2 - .../android/ui/modules/feed/FeedFragment.kt | 6 +-- .../handhistory/HandHistoryFragment.kt | 47 +++++++++++-------- .../modules/handhistory/model/ActionList.kt | 2 +- .../handhistory/replayer/ReplayerAnimator.kt | 40 +++++++++++++--- .../handhistory/replayer/ReplayerFragment.kt | 2 +- .../handhistory/replayer/ReplayerModel.kt | 4 +- .../android/util/video/MMediaMuxer.kt | 17 ++++--- 8 files changed, 75 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index 575b3b4f..7eb1ab04 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -21,7 +21,6 @@ import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.TimeFilterable import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.modules.handhistory.evaluator.EvaluatorBridge -import net.pokeranalytics.android.ui.modules.handhistory.model.ActionList import net.pokeranalytics.android.ui.modules.handhistory.model.ActionReadRow import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder import net.pokeranalytics.android.ui.view.RowRepresentable @@ -30,7 +29,6 @@ import net.pokeranalytics.android.util.extensions.addLineReturn import net.pokeranalytics.android.util.extensions.formatted import net.pokeranalytics.android.util.extensions.fullDate import java.util.* -import kotlin.Comparator import kotlin.math.max data class PositionAmount(var position: Int, var amount: Double, var isAllin: Boolean) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt index 6402a52e..5afab74a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt @@ -466,9 +466,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { */ private fun createNewHandHistory() { - val intent = Intent(requireContext(), TestActivity::class.java) - startActivity(intent) - return +// val intent = Intent(requireContext(), TestActivity::class.java) +// startActivity(intent) +// return AppGuard.endOfUse?.let { endDate -> diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt index f7a418d7..f9974344 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt @@ -1,10 +1,12 @@ package net.pokeranalytics.android.ui.modules.handhistory +import android.Manifest import android.animation.ValueAnimator import android.app.Activity import android.app.AlertDialog import android.content.Intent import android.graphics.Bitmap +import android.graphics.Canvas import android.os.Bundle import android.os.Handler import android.view.* @@ -701,8 +703,8 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDelegate, KeyboardL builder.setTitle(R.string.export) builder.setItems( arrayOf( - getString(R.string.text) -// getString(R.string.video), + getString(R.string.text), + getString(R.string.video) // "GIF" ) ) { _, index -> @@ -710,7 +712,7 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDelegate, KeyboardL // of the selected item when (index) { 0 -> this.textExport() - 1 -> this.videoExport() + 1 -> this.videoExportAskForPermission() 2 -> this.gifExport() } } @@ -718,31 +720,36 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDelegate, KeyboardL } + private fun videoExportAskForPermission() { + askForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value) { granted -> + if (granted) { + videoExport() + } + } + } + private fun videoExport() { - val config = ReplayerAnimator(this.model.handHistory) + val animator = ReplayerAnimator(this.model.handHistory, true) + + val square = 1024f - val width = 480 - val height = 480 - val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val tc = TableDrawer(bitmap) + val width = square + val height = square - // draw initial table -// TableDrawer.initializeTable(config, tc, requireContext()) - // TODO + animator.setDimension(width, height) + TableDrawer.configurePaints(requireContext(), animator) val muxer = MMediaMuxer() - muxer.Init(requireActivity(), bitmap.width, bitmap.height, "hhVideo", "YES!") + muxer.Init(requireActivity(), width.toInt(), height.toInt(), "hhVideo", "YES!") - HandStep.createSteps(this.model.handHistory).forEach { step -> + animator.frames(requireContext()) { bitmap -> - step.frames(config, tc, requireContext()) { - try { - val byteArray = bitmap.toByteArray() - muxer.AddFrame(byteArray) - } catch (e: Exception) { - Timber.e("error = ${e.message}") - } + try { + val byteArray = bitmap.toByteArray() + muxer.AddFrame(byteArray) + } catch (e: Exception) { + Timber.e("error = ${e.message}") } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt index 7bf76b4e..cf860749 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt @@ -66,7 +66,7 @@ class ActionList(var listener: ActionListListener? = null) : ArrayList> CA: position:${ca.position}, ${ca.action.amount}, ${ca.stackBeforeActing}, ${ca.stackAfterActing}") +// Timber.d(">> CA: position:${ca.position}, ${ca.action.amount}, ${ca.stackBeforeActing}, ${ca.stackAfterActing}") } // Adds action diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt index de6e02ce..5b91813d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt @@ -1,5 +1,8 @@ package net.pokeranalytics.android.ui.modules.handhistory.replayer +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas import android.graphics.RectF import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.handhistory.Street @@ -9,14 +12,14 @@ import net.pokeranalytics.android.ui.modules.handhistory.model.ComputedAction import timber.log.Timber import kotlin.math.max -class ReplayerAnimator(var handHistory: HandHistory) { +class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) { // Steps & Frames - enum class FrameType { - STATE, - GATHER_ANIMATION, - DISTRIBUTION_ANIMATION + enum class FrameType(val visualOccurences: Int) { + STATE(20), + GATHER_ANIMATION(1), + DISTRIBUTION_ANIMATION(1) } /*** @@ -422,13 +425,13 @@ class ReplayerAnimator(var handHistory: HandHistory) { return computedAction } - fun next() { + fun nextStep() { if (this.steps.size > this.currentStepIndex + 1) { this.currentStepIndex += 1 } } - fun previous() { + fun previousStep() { if (this.currentStepIndex > this.lastBlindIndex + 1) { this.currentStepIndex -= 1 } @@ -496,6 +499,29 @@ class ReplayerAnimator(var handHistory: HandHistory) { return false } + fun frames(context: Context, frameHandler: (Bitmap) -> Unit) { + + this.steps.forEach { step -> + + while (this.shouldShowAdditionalFrame) { + + val bitmap = Bitmap.createBitmap(this.width.toInt(), this.height.toInt(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + TableDrawer.drawTable(this, canvas, context) + (0 until this.frameType.visualOccurences).forEach { i -> + Timber.d(">>> step: $currentStepIndex, currentFrame: $currentFrame, vo = $i") + frameHandler(bitmap) + } +// bitmap.recycle() + + frameDrawn() + } + + nextStep() + } + + } + /*** * Returns whether the replayer has finished showing steps & frames or not */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt index 4b1bfe5c..e37e1503 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt @@ -79,7 +79,7 @@ class ReplayerFragment : RealmFragment() { } private fun loadHand(handHistory: HandHistory) { - val config = ReplayerAnimator(handHistory) + val config = ReplayerAnimator(handHistory, false) this.replayer.animator = config this.model.animator = config } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt index 3108d11b..3daf06b0 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt @@ -20,11 +20,11 @@ class ReplayerModel : ViewModel() { private set fun previousStep() { - this.animator?.previous() + this.animator?.previousStep() } fun nextStep() { - this.animator?.next() + this.animator?.nextStep() } val actionDelay: Long diff --git a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt index 772c029c..3e33c609 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt @@ -9,10 +9,10 @@ import android.media.MediaCodecInfo.CodecCapabilities import android.os.Environment import android.os.Handler import android.util.Log +import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted import timber.log.Timber import java.io.File import java.io.IOException -import java.text.DateFormat import java.util.* @@ -200,15 +200,14 @@ class MMediaMuxer { it.start() } try { - val currentDateTimeString = - DateFormat.getDateTimeInstance().format(Date()) + val formattedDate = Date().dateTimeFileFormatted outputPath = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), - "pixel.mp4" + "video_${formattedDate}.mp4" ).toString() mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) } catch (ioe: IOException) { - Loge("MediaMuxer creation failed") + Loge("MediaMuxer creation failed: ${ioe.message}") } } @@ -279,7 +278,7 @@ class MMediaMuxer { mediaMuxer!!.stop() mediaMuxer!!.release() mediaMuxer = null - Logd("RELEASE MUXER, path = ${outputPath}") + Logd("RELEASE MUXER, path = $outputPath") } } @@ -354,8 +353,8 @@ class MMediaMuxer { private var _height = 512 private const val BIT_RATE = 800000 private const val INFLAME_INTERVAL = 1 - private const val FRAME_RATE = 10 - private const val DEBUG = false + private const val FRAME_RATE = 50 +// private const val DEBUG = false private const val TAG = "CODEC" /** * Returns the first codec capable of encoding the specified MIME type, or @@ -417,7 +416,7 @@ class MMediaMuxer { } private fun Logd(Mess: String) { - Timber.d("$Mess") + Timber.d(Mess) // if (DEBUG) { // Log.d(TAG, Mess) // }