Video export code, crashing due to memory allocation

hh
Laurent 5 years ago
parent a18e180f07
commit f502374967
  1. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  2. 6
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt
  3. 47
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryFragment.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt
  5. 40
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt
  7. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerModel.kt
  8. 17
      app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt

@ -21,7 +21,6 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.TimeFilterable import net.pokeranalytics.android.model.interfaces.TimeFilterable
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.modules.handhistory.evaluator.EvaluatorBridge 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.ActionReadRow
import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder
import net.pokeranalytics.android.ui.view.RowRepresentable 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.formatted
import net.pokeranalytics.android.util.extensions.fullDate import net.pokeranalytics.android.util.extensions.fullDate
import java.util.* import java.util.*
import kotlin.Comparator
import kotlin.math.max import kotlin.math.max
data class PositionAmount(var position: Int, var amount: Double, var isAllin: Boolean) data class PositionAmount(var position: Int, var amount: Double, var isAllin: Boolean)

@ -466,9 +466,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
*/ */
private fun createNewHandHistory() { private fun createNewHandHistory() {
val intent = Intent(requireContext(), TestActivity::class.java) // val intent = Intent(requireContext(), TestActivity::class.java)
startActivity(intent) // startActivity(intent)
return // return
AppGuard.endOfUse?.let { endDate -> AppGuard.endOfUse?.let { endDate ->

@ -1,10 +1,12 @@
package net.pokeranalytics.android.ui.modules.handhistory package net.pokeranalytics.android.ui.modules.handhistory
import android.Manifest
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.app.Activity import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.view.* import android.view.*
@ -701,8 +703,8 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDelegate, KeyboardL
builder.setTitle(R.string.export) builder.setTitle(R.string.export)
builder.setItems( builder.setItems(
arrayOf<CharSequence>( arrayOf<CharSequence>(
getString(R.string.text) getString(R.string.text),
// getString(R.string.video), getString(R.string.video)
// "GIF" // "GIF"
) )
) { _, index -> ) { _, index ->
@ -710,7 +712,7 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDelegate, KeyboardL
// of the selected item // of the selected item
when (index) { when (index) {
0 -> this.textExport() 0 -> this.textExport()
1 -> this.videoExport() 1 -> this.videoExportAskForPermission()
2 -> this.gifExport() 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() { private fun videoExport() {
val config = ReplayerAnimator(this.model.handHistory) val animator = ReplayerAnimator(this.model.handHistory, true)
val square = 1024f
val width = 480 val width = square
val height = 480 val height = square
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val tc = TableDrawer(bitmap)
// draw initial table animator.setDimension(width, height)
// TableDrawer.initializeTable(config, tc, requireContext()) TableDrawer.configurePaints(requireContext(), animator)
// TODO
val muxer = MMediaMuxer() 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 {
try { val byteArray = bitmap.toByteArray()
val byteArray = bitmap.toByteArray() muxer.AddFrame(byteArray)
muxer.AddFrame(byteArray) } catch (e: Exception) {
} catch (e: Exception) { Timber.e("error = ${e.message}")
Timber.e("error = ${e.message}")
}
} }
} }

@ -66,7 +66,7 @@ class ActionList(var listener: ActionListListener? = null) : ArrayList<ComputedA
position position
) )
this.add(ca) this.add(ca)
Timber.d(">> 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 // Adds action

@ -1,5 +1,8 @@
package net.pokeranalytics.android.ui.modules.handhistory.replayer 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 android.graphics.RectF
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.Street 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 timber.log.Timber
import kotlin.math.max import kotlin.math.max
class ReplayerAnimator(var handHistory: HandHistory) { class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
// Steps & Frames // Steps & Frames
enum class FrameType { enum class FrameType(val visualOccurences: Int) {
STATE, STATE(20),
GATHER_ANIMATION, GATHER_ANIMATION(1),
DISTRIBUTION_ANIMATION DISTRIBUTION_ANIMATION(1)
} }
/*** /***
@ -422,13 +425,13 @@ class ReplayerAnimator(var handHistory: HandHistory) {
return computedAction return computedAction
} }
fun next() { fun nextStep() {
if (this.steps.size > this.currentStepIndex + 1) { if (this.steps.size > this.currentStepIndex + 1) {
this.currentStepIndex += 1 this.currentStepIndex += 1
} }
} }
fun previous() { fun previousStep() {
if (this.currentStepIndex > this.lastBlindIndex + 1) { if (this.currentStepIndex > this.lastBlindIndex + 1) {
this.currentStepIndex -= 1 this.currentStepIndex -= 1
} }
@ -496,6 +499,29 @@ class ReplayerAnimator(var handHistory: HandHistory) {
return false 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 * Returns whether the replayer has finished showing steps & frames or not
*/ */

@ -79,7 +79,7 @@ class ReplayerFragment : RealmFragment() {
} }
private fun loadHand(handHistory: HandHistory) { private fun loadHand(handHistory: HandHistory) {
val config = ReplayerAnimator(handHistory) val config = ReplayerAnimator(handHistory, false)
this.replayer.animator = config this.replayer.animator = config
this.model.animator = config this.model.animator = config
} }

@ -20,11 +20,11 @@ class ReplayerModel : ViewModel() {
private set private set
fun previousStep() { fun previousStep() {
this.animator?.previous() this.animator?.previousStep()
} }
fun nextStep() { fun nextStep() {
this.animator?.next() this.animator?.nextStep()
} }
val actionDelay: Long val actionDelay: Long

@ -9,10 +9,10 @@ import android.media.MediaCodecInfo.CodecCapabilities
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.text.DateFormat
import java.util.* import java.util.*
@ -200,15 +200,14 @@ class MMediaMuxer {
it.start() it.start()
} }
try { try {
val currentDateTimeString = val formattedDate = Date().dateTimeFileFormatted
DateFormat.getDateTimeInstance().format(Date())
outputPath = File( outputPath = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"pixel.mp4" "video_${formattedDate}.mp4"
).toString() ).toString()
mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
} catch (ioe: IOException) { } catch (ioe: IOException) {
Loge("MediaMuxer creation failed") Loge("MediaMuxer creation failed: ${ioe.message}")
} }
} }
@ -279,7 +278,7 @@ class MMediaMuxer {
mediaMuxer!!.stop() mediaMuxer!!.stop()
mediaMuxer!!.release() mediaMuxer!!.release()
mediaMuxer = null mediaMuxer = null
Logd("RELEASE MUXER, path = ${outputPath}") Logd("RELEASE MUXER, path = $outputPath")
} }
} }
@ -354,8 +353,8 @@ class MMediaMuxer {
private var _height = 512 private var _height = 512
private const val BIT_RATE = 800000 private const val BIT_RATE = 800000
private const val INFLAME_INTERVAL = 1 private const val INFLAME_INTERVAL = 1
private const val FRAME_RATE = 10 private const val FRAME_RATE = 50
private const val DEBUG = false // private const val DEBUG = false
private const val TAG = "CODEC" private const val TAG = "CODEC"
/** /**
* Returns the first codec capable of encoding the specified MIME type, or * Returns the first codec capable of encoding the specified MIME type, or
@ -417,7 +416,7 @@ class MMediaMuxer {
} }
private fun Logd(Mess: String) { private fun Logd(Mess: String) {
Timber.d("$Mess") Timber.d(Mess)
// if (DEBUG) { // if (DEBUG) {
// Log.d(TAG, Mess) // Log.d(TAG, Mess)
// } // }

Loading…
Cancel
Save