2022-06-10 23:41:29 +02:00
|
|
|
package org.pixeldroid.app.postCreation.photoEdit
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
import android.app.Activity
|
|
|
|
import android.app.AlertDialog
|
|
|
|
import android.content.Intent
|
2022-10-21 00:03:08 +02:00
|
|
|
import android.graphics.Color
|
2022-10-19 00:19:42 +02:00
|
|
|
import android.graphics.Rect
|
2022-06-18 22:21:19 +02:00
|
|
|
import android.media.AudioManager
|
2022-06-10 23:41:29 +02:00
|
|
|
import android.net.Uri
|
|
|
|
import android.os.Bundle
|
2022-06-18 22:21:19 +02:00
|
|
|
import android.os.Handler
|
|
|
|
import android.os.Looper
|
|
|
|
import android.text.format.DateUtils
|
2022-06-10 23:41:29 +02:00
|
|
|
import android.util.Log
|
2022-10-21 00:03:08 +02:00
|
|
|
import android.util.TypedValue
|
2022-06-18 22:21:19 +02:00
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuItem
|
|
|
|
import android.view.View
|
|
|
|
import android.widget.FrameLayout
|
|
|
|
import android.widget.ImageView
|
2022-06-10 23:41:29 +02:00
|
|
|
import androidx.core.net.toUri
|
2022-06-18 22:21:19 +02:00
|
|
|
import androidx.core.os.HandlerCompat
|
2022-10-21 00:03:08 +02:00
|
|
|
import androidx.core.view.isVisible
|
2022-06-18 22:21:19 +02:00
|
|
|
import androidx.media.AudioAttributesCompat
|
|
|
|
import androidx.media2.common.MediaMetadata
|
|
|
|
import androidx.media2.common.UriMediaItem
|
|
|
|
import androidx.media2.player.MediaPlayer
|
2022-10-19 00:19:42 +02:00
|
|
|
import com.arthenica.ffmpegkit.FFmpegKit
|
|
|
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
|
|
|
import com.arthenica.ffmpegkit.FFprobeKit
|
|
|
|
import com.arthenica.ffmpegkit.MediaInformation
|
|
|
|
import com.arthenica.ffmpegkit.ReturnCode
|
2022-06-10 23:41:29 +02:00
|
|
|
import com.bumptech.glide.Glide
|
2022-06-18 22:21:19 +02:00
|
|
|
import com.google.android.material.slider.RangeSlider
|
2022-10-22 22:07:03 +02:00
|
|
|
import com.google.android.material.slider.Slider
|
2022-06-18 22:21:19 +02:00
|
|
|
import org.pixeldroid.app.R
|
2022-06-10 23:41:29 +02:00
|
|
|
import org.pixeldroid.app.databinding.ActivityVideoEditBinding
|
2022-06-18 22:21:19 +02:00
|
|
|
import org.pixeldroid.app.postCreation.PostCreationActivity
|
|
|
|
import org.pixeldroid.app.postCreation.carousel.dpToPx
|
2022-07-10 13:42:19 +02:00
|
|
|
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
2022-08-21 00:31:36 +02:00
|
|
|
import org.pixeldroid.app.utils.ffmpegCompliantUri
|
2022-06-10 23:41:29 +02:00
|
|
|
import java.io.File
|
2022-10-19 00:19:42 +02:00
|
|
|
import java.io.Serializable
|
|
|
|
import kotlin.math.absoluteValue
|
2022-06-10 23:41:29 +02:00
|
|
|
|
2022-07-10 13:42:19 +02:00
|
|
|
class VideoEditActivity : BaseThemedWithBarActivity() {
|
2022-06-18 22:21:19 +02:00
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
data class RelativeCropPosition(
|
2022-10-21 00:03:08 +02:00
|
|
|
// Width of the selected part of the video, relative to the width of the video
|
|
|
|
val relativeWidth: Float = 1f,
|
|
|
|
// Height of the selected part of the video, relative to the height of the video
|
|
|
|
val relativeHeight: Float = 1f,
|
|
|
|
// Distance of left corner of selected part, relative to the width of the video
|
|
|
|
val relativeX: Float = 0f,
|
|
|
|
// Distance of top of selected part, relative to the height of the video
|
|
|
|
val relativeY: Float = 0f,
|
2022-10-19 00:19:42 +02:00
|
|
|
): Serializable {
|
|
|
|
fun notCropped(): Boolean =
|
2022-10-21 00:03:08 +02:00
|
|
|
(relativeWidth - 1f).absoluteValue < 0.001f
|
|
|
|
&& (relativeHeight - 1f).absoluteValue < 0.001f
|
2022-10-19 00:19:42 +02:00
|
|
|
&& relativeX.absoluteValue < 0.001f
|
2022-10-21 00:03:08 +02:00
|
|
|
&& relativeY.absoluteValue < 0.001f
|
2022-10-19 00:19:42 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
private lateinit var mediaPlayer: MediaPlayer
|
|
|
|
private var videoPosition: Int = -1
|
2022-10-15 20:13:29 +02:00
|
|
|
|
2022-10-21 00:03:08 +02:00
|
|
|
private var cropRelativeDimensions: RelativeCropPosition = RelativeCropPosition()
|
2022-10-19 00:19:42 +02:00
|
|
|
|
2022-10-22 22:07:03 +02:00
|
|
|
private var stabilization: Float = 0f
|
|
|
|
set(value){
|
|
|
|
field = value
|
|
|
|
if(value > 0.01f && value <= 100f){
|
|
|
|
// Stabilization requested, show UI
|
|
|
|
binding.stabilisationSaved.isVisible = true
|
|
|
|
val typedValue = TypedValue()
|
|
|
|
val color: Int = if (binding.stabilizer.context.theme
|
2022-10-23 16:05:02 +02:00
|
|
|
.resolveAttribute(R.attr.colorSecondary, typedValue, true)
|
2022-10-22 22:07:03 +02:00
|
|
|
) typedValue.data else Color.TRANSPARENT
|
|
|
|
|
|
|
|
binding.stabilizer.drawable.setTint(color)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
binding.stabilisationSaved.isVisible = false
|
|
|
|
binding.stabilizer.drawable.setTintList(null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 20:13:29 +02:00
|
|
|
private var speed: Int = 1
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
|
|
|
|
mediaPlayer.playbackSpeed = speedChoices[value].toFloat()
|
|
|
|
|
|
|
|
if(speed != 1) binding.muter.callOnClick()
|
|
|
|
}
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
private lateinit var binding: ActivityVideoEditBinding
|
|
|
|
// Map photoData indexes to FFmpeg Session IDs
|
|
|
|
private val sessionList: ArrayList<Long> = arrayListOf()
|
|
|
|
private val tempFiles: ArrayList<File> = ArrayList()
|
|
|
|
|
2022-06-10 23:41:29 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2022-06-18 22:21:19 +02:00
|
|
|
binding = ActivityVideoEditBinding.inflate(layoutInflater)
|
2022-06-10 23:41:29 +02:00
|
|
|
setContentView(binding.root)
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
supportActionBar?.setTitle(R.string.toolbar_title_edit)
|
|
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
|
|
supportActionBar?.setHomeButtonEnabled(true)
|
|
|
|
|
|
|
|
|
|
|
|
binding.videoRangeSeekBar.setCustomThumbDrawablesForValues(R.drawable.thumb_left,R.drawable.double_circle,R.drawable.thumb_right)
|
|
|
|
binding.videoRangeSeekBar.thumbRadius = 20.dpToPx(this)
|
|
|
|
|
|
|
|
|
|
|
|
val resultHandler: Handler = HandlerCompat.createAsync(Looper.getMainLooper())
|
|
|
|
|
|
|
|
val uri = intent.getParcelableExtra<Uri>(PhotoEditActivity.PICTURE_URI)!!
|
2022-10-17 13:23:02 +02:00
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
videoPosition = intent.getIntExtra(PhotoEditActivity.PICTURE_POSITION, -1)
|
|
|
|
|
2022-08-21 00:31:36 +02:00
|
|
|
val inputVideoPath = ffmpegCompliantUri(uri)
|
2022-06-10 23:41:29 +02:00
|
|
|
val mediaInformation: MediaInformation? = FFprobeKit.getMediaInformation(inputVideoPath).mediaInformation
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
//Duration in seconds, or null
|
|
|
|
val duration: Float? = mediaInformation?.duration?.toFloatOrNull()
|
|
|
|
|
|
|
|
binding.videoRangeSeekBar.valueFrom = 0f
|
|
|
|
binding.videoRangeSeekBar.valueTo = duration ?: 100f
|
|
|
|
binding.videoRangeSeekBar.values = listOf(0f,(duration?: 100f) / 2, duration ?: 100f)
|
2022-06-10 23:41:29 +02:00
|
|
|
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
val mediaItem: UriMediaItem = UriMediaItem.Builder(uri).build()
|
|
|
|
mediaItem.metadata = MediaMetadata.Builder()
|
|
|
|
.putString(MediaMetadata.METADATA_KEY_TITLE, "")
|
|
|
|
.build()
|
2022-06-10 23:41:29 +02:00
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
mediaPlayer = MediaPlayer(this)
|
|
|
|
mediaPlayer.setMediaItem(mediaItem)
|
|
|
|
|
|
|
|
//binding.videoView.mediaControlView?.setMediaController()
|
|
|
|
|
|
|
|
// Configure audio
|
|
|
|
mediaPlayer.setAudioAttributes(AudioAttributesCompat.Builder()
|
|
|
|
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
|
|
|
|
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
|
|
|
|
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MOVIE)
|
|
|
|
.build()
|
2022-06-10 23:41:29 +02:00
|
|
|
)
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
findViewById<FrameLayout?>(R.id.progress_bar)?.visibility = View.GONE
|
|
|
|
|
|
|
|
mediaPlayer.prepare()
|
2022-06-10 23:41:29 +02:00
|
|
|
|
2022-10-15 20:13:29 +02:00
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
binding.muter.setOnClickListener {
|
|
|
|
if(!binding.muter.isSelected) mediaPlayer.playerVolume = 0f
|
2022-10-15 20:13:29 +02:00
|
|
|
else {
|
|
|
|
mediaPlayer.playerVolume = 1f
|
|
|
|
speed = 1
|
|
|
|
}
|
2022-06-18 22:21:19 +02:00
|
|
|
binding.muter.isSelected = !binding.muter.isSelected
|
2022-06-10 23:41:29 +02:00
|
|
|
}
|
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
binding.cropper.setOnClickListener {
|
|
|
|
showCropInterface(show = true, uri = uri)
|
|
|
|
}
|
|
|
|
|
|
|
|
binding.saveCropButton.setOnClickListener {
|
|
|
|
// This is the rectangle selected by the crop
|
2022-10-21 17:11:37 +02:00
|
|
|
val cropRect = binding.cropImageView.cropWindowRect
|
2022-10-19 00:19:42 +02:00
|
|
|
|
|
|
|
// This is the rectangle of the whole image
|
|
|
|
val fullImageRect: Rect = binding.cropImageView.getInitialCropWindowRect()
|
|
|
|
|
|
|
|
// x, y are coordinates of top left, in the ImageView
|
|
|
|
val x = cropRect.left - fullImageRect.left
|
|
|
|
val y = cropRect.top - fullImageRect.top
|
|
|
|
|
|
|
|
// width and height selected by the crop
|
|
|
|
val width = cropRect.width()
|
|
|
|
val height = cropRect.height()
|
|
|
|
|
|
|
|
// To avoid having to calculate the dimensions of the video here, we pass
|
|
|
|
// relative width, height and x, y back to be treated in FFmpeg
|
|
|
|
cropRelativeDimensions = RelativeCropPosition(
|
|
|
|
relativeWidth = width/fullImageRect.width(),
|
|
|
|
relativeHeight = height/fullImageRect.height(),
|
|
|
|
relativeX = x/fullImageRect.width(),
|
|
|
|
relativeY = y/fullImageRect.height()
|
|
|
|
)
|
|
|
|
|
2022-10-21 00:03:08 +02:00
|
|
|
// If a crop was saved, change the color of the crop button to give a visual indication
|
|
|
|
if(!cropRelativeDimensions.notCropped()){
|
|
|
|
val typedValue = TypedValue()
|
|
|
|
val color: Int = if (binding.checkMarkCropped.context.theme
|
2022-10-23 16:05:02 +02:00
|
|
|
.resolveAttribute(R.attr.colorSecondary, typedValue, true)
|
2022-10-21 00:03:08 +02:00
|
|
|
) typedValue.data else Color.TRANSPARENT
|
|
|
|
|
|
|
|
binding.cropper.drawable.setTint(color)
|
|
|
|
} else {
|
|
|
|
// Else reset the tint
|
|
|
|
binding.cropper.drawable.setTintList(null)
|
|
|
|
}
|
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
showCropInterface(show = false)
|
|
|
|
}
|
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
binding.videoView.setPlayer(mediaPlayer)
|
|
|
|
|
|
|
|
mediaPlayer.seekTo((binding.videoRangeSeekBar.values[1]*1000).toLong())
|
|
|
|
|
|
|
|
object : Runnable {
|
|
|
|
override fun run() {
|
|
|
|
val getCurrent = mediaPlayer.currentPosition / 1000f
|
|
|
|
if(getCurrent >= binding.videoRangeSeekBar.values[0] && getCurrent <= binding.videoRangeSeekBar.values[2] ) {
|
|
|
|
binding.videoRangeSeekBar.values = listOf(binding.videoRangeSeekBar.values[0],getCurrent, binding.videoRangeSeekBar.values[2])
|
|
|
|
}
|
|
|
|
Handler(Looper.getMainLooper()).postDelayed(this, 1000)
|
|
|
|
}
|
|
|
|
}.run()
|
|
|
|
|
|
|
|
binding.videoRangeSeekBar.addOnChangeListener { rangeSlider: RangeSlider, value, fromUser ->
|
|
|
|
// Responds to when the middle slider's value is changed
|
|
|
|
if(fromUser && value != rangeSlider.values[0] && value != rangeSlider.values[2]) {
|
|
|
|
mediaPlayer.seekTo((rangeSlider.values[1]*1000).toLong())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
binding.videoRangeSeekBar.setLabelFormatter { value: Float ->
|
|
|
|
DateUtils.formatElapsedTime(value.toLong())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-10-15 20:13:29 +02:00
|
|
|
|
|
|
|
binding.speeder.setOnClickListener {
|
|
|
|
AlertDialog.Builder(this).apply {
|
|
|
|
setIcon(R.drawable.speed)
|
|
|
|
setTitle(R.string.video_speed)
|
|
|
|
setSingleChoiceItems(speedChoices.map { it.toString() + "x" }.toTypedArray(), speed) { dialog, which ->
|
|
|
|
// update the selected item which is selected by the user so that it should be selected
|
|
|
|
// when user opens the dialog next time and pass the instance to setSingleChoiceItems method
|
|
|
|
speed = which
|
|
|
|
|
|
|
|
// when selected an item the dialog should be closed with the dismiss method
|
|
|
|
dialog.dismiss()
|
|
|
|
}
|
|
|
|
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
|
|
}.show()
|
|
|
|
}
|
|
|
|
|
2022-10-22 22:07:03 +02:00
|
|
|
binding.stabilizer.setOnClickListener {
|
|
|
|
AlertDialog.Builder(this).apply {
|
|
|
|
setIcon(R.drawable.video_stable)
|
|
|
|
setTitle(R.string.stabilize_video_intensity)
|
|
|
|
val slider = Slider(context).apply {
|
|
|
|
valueFrom = 0f
|
|
|
|
valueTo = 100f
|
|
|
|
value = stabilization
|
|
|
|
}
|
|
|
|
setView(slider)
|
|
|
|
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
|
|
setPositiveButton(android.R.string.ok) { _, _ -> stabilization = slider.value}
|
|
|
|
}.show()
|
|
|
|
}
|
|
|
|
|
2022-10-15 20:13:29 +02:00
|
|
|
|
2022-06-18 22:21:19 +02:00
|
|
|
val thumbInterval: Float? = duration?.div(7)
|
|
|
|
|
|
|
|
thumbInterval?.let {
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail1, it)
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail2, it.times(2))
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail3, it.times(3))
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail4, it.times(4))
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail5, it.times(5))
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail6, it.times(6))
|
|
|
|
thumbnail(uri, resultHandler, binding.thumbnail7, it.times(7))
|
|
|
|
}
|
|
|
|
|
2022-10-23 16:05:02 +02:00
|
|
|
resetControls()
|
2022-06-18 22:21:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
|
|
menuInflater.inflate(R.menu.edit_menu, menu)
|
|
|
|
return true
|
2022-06-10 23:41:29 +02:00
|
|
|
}
|
2022-06-18 22:21:19 +02:00
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
|
|
|
|
when(item.itemId) {
|
|
|
|
R.id.action_save -> {
|
|
|
|
returnWithValues()
|
|
|
|
}
|
|
|
|
R.id.action_reset -> {
|
|
|
|
resetControls()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBackPressed() {
|
2022-10-21 17:08:57 +02:00
|
|
|
if(binding.cropImageView.isVisible) {
|
|
|
|
showCropInterface(false)
|
|
|
|
} else if (noEdits()) super.onBackPressed()
|
2022-06-18 22:21:19 +02:00
|
|
|
else {
|
|
|
|
val builder = AlertDialog.Builder(this)
|
|
|
|
builder.apply {
|
|
|
|
setMessage(R.string.save_before_returning)
|
|
|
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
|
|
returnWithValues()
|
|
|
|
}
|
|
|
|
setNegativeButton(R.string.no_cancel_edit) { _, _ ->
|
|
|
|
super.onBackPressed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create the AlertDialog
|
|
|
|
builder.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun noEdits(): Boolean {
|
|
|
|
val videoPositions = binding.videoRangeSeekBar.values.let {
|
|
|
|
it[0] == 0f && it[2] == binding.videoRangeSeekBar.valueTo
|
|
|
|
}
|
|
|
|
val muted = binding.muter.isSelected
|
2022-10-15 20:13:29 +02:00
|
|
|
val speedUnchanged = speed == 1
|
|
|
|
|
2022-10-23 16:05:02 +02:00
|
|
|
val stabilizationUnchanged = stabilization <= 0.01f || stabilization > 100.5f
|
|
|
|
|
|
|
|
return !muted && videoPositions && speedUnchanged && cropRelativeDimensions.notCropped() && stabilizationUnchanged
|
2022-06-18 22:21:19 +02:00
|
|
|
}
|
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
private fun showCropInterface(show: Boolean, uri: Uri? = null){
|
|
|
|
val visibilityOfOthers = if(show) View.GONE else View.VISIBLE
|
|
|
|
val visibilityOfCrop = if(show) View.VISIBLE else View.GONE
|
|
|
|
|
|
|
|
if(show) mediaPlayer.pause()
|
|
|
|
|
2022-10-21 00:03:08 +02:00
|
|
|
if(show) binding.cropSavedCard.visibility = View.GONE
|
2022-10-21 17:08:57 +02:00
|
|
|
else if(!cropRelativeDimensions.notCropped()) binding.cropSavedCard.visibility = View.VISIBLE
|
2022-10-21 00:03:08 +02:00
|
|
|
|
2022-10-22 22:07:03 +02:00
|
|
|
binding.stabilisationSaved.visibility =
|
|
|
|
if(!show && stabilization > 0.01f && stabilization <= 100f) View.VISIBLE
|
|
|
|
else View.GONE
|
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
binding.muter.visibility = visibilityOfOthers
|
|
|
|
binding.speeder.visibility = visibilityOfOthers
|
|
|
|
binding.cropper.visibility = visibilityOfOthers
|
2022-10-22 22:07:03 +02:00
|
|
|
binding.stabilizer.visibility = visibilityOfOthers
|
2022-10-19 00:19:42 +02:00
|
|
|
binding.videoRangeSeekBar.visibility = visibilityOfOthers
|
|
|
|
binding.videoView.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail1.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail2.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail3.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail4.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail5.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail6.visibility = visibilityOfOthers
|
|
|
|
binding.thumbnail7.visibility = visibilityOfOthers
|
|
|
|
|
2022-10-21 00:03:08 +02:00
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
binding.cropImageView.visibility = visibilityOfCrop
|
|
|
|
binding.saveCropButton.visibility = visibilityOfCrop
|
|
|
|
|
|
|
|
if(show && uri != null) binding.cropImageView.setImageUriAsync(uri, cropRelativeDimensions)
|
|
|
|
}
|
2022-06-18 22:21:19 +02:00
|
|
|
|
|
|
|
private fun returnWithValues() {
|
2022-10-22 22:07:03 +02:00
|
|
|
//TODO Check if some of these should be null to indicate no changes in that category? Ex start/end
|
2022-06-18 22:21:19 +02:00
|
|
|
val intent = Intent(this, PostCreationActivity::class.java)
|
|
|
|
.apply {
|
|
|
|
putExtra(PhotoEditActivity.PICTURE_POSITION, videoPosition)
|
|
|
|
putExtra(MUTED, binding.muter.isSelected)
|
2022-10-15 20:13:29 +02:00
|
|
|
putExtra(SPEED, speed)
|
2022-06-18 22:21:19 +02:00
|
|
|
putExtra(MODIFIED, !noEdits())
|
|
|
|
putExtra(VIDEO_START, binding.videoRangeSeekBar.values.first())
|
|
|
|
putExtra(VIDEO_END, binding.videoRangeSeekBar.values[2])
|
2022-10-19 00:19:42 +02:00
|
|
|
putExtra(VIDEO_CROP, cropRelativeDimensions)
|
2022-10-22 22:07:03 +02:00
|
|
|
putExtra(VIDEO_STABILIZE, stabilization)
|
2022-06-18 22:21:19 +02:00
|
|
|
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
|
|
|
}
|
|
|
|
|
|
|
|
setResult(Activity.RESULT_OK, intent)
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun resetControls() {
|
|
|
|
binding.videoRangeSeekBar.values = listOf(0f, binding.videoRangeSeekBar.valueTo/2, binding.videoRangeSeekBar.valueTo)
|
|
|
|
binding.muter.isSelected = false
|
2022-10-21 00:03:08 +02:00
|
|
|
|
2022-10-19 00:19:42 +02:00
|
|
|
binding.cropImageView.resetCropRect()
|
2022-10-21 00:03:08 +02:00
|
|
|
cropRelativeDimensions = RelativeCropPosition()
|
|
|
|
binding.cropper.drawable.setTintList(null)
|
2022-10-22 22:07:03 +02:00
|
|
|
binding.stabilizer.drawable.setTintList(null)
|
2022-10-21 00:03:08 +02:00
|
|
|
binding.cropSavedCard.visibility = View.GONE
|
2022-10-22 22:07:03 +02:00
|
|
|
stabilization = 0f
|
2022-06-18 22:21:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
|
|
|
sessionList.forEach {
|
|
|
|
FFmpegKit.cancel(it)
|
|
|
|
}
|
|
|
|
tempFiles.forEach{
|
|
|
|
it.delete()
|
|
|
|
}
|
|
|
|
mediaPlayer.close()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun thumbnail(
|
|
|
|
inputUri: Uri?,
|
|
|
|
resultHandler: Handler,
|
|
|
|
thumbnail: ImageView,
|
|
|
|
thumbTime: Float,
|
|
|
|
) {
|
2022-08-21 00:32:14 +02:00
|
|
|
val file = File.createTempFile("temp_img", ".bmp", cacheDir)
|
2022-06-18 22:21:19 +02:00
|
|
|
tempFiles.add(file)
|
|
|
|
val fileUri = file.toUri()
|
2022-08-21 00:31:36 +02:00
|
|
|
val ffmpegCompliantUri = ffmpegCompliantUri(inputUri)
|
2022-06-18 22:21:19 +02:00
|
|
|
|
|
|
|
val outputImagePath =
|
|
|
|
if(fileUri.toString().startsWith("content://"))
|
|
|
|
FFmpegKitConfig.getSafParameterForWrite(this, fileUri)
|
|
|
|
else fileUri.toString()
|
2022-08-21 00:31:36 +02:00
|
|
|
val session = FFmpegKit.executeWithArgumentsAsync(arrayOf(
|
|
|
|
"-noaccurate_seek", "-ss", "$thumbTime", "-i", ffmpegCompliantUri, "-vf",
|
|
|
|
"scale=${thumbnail.width}:${thumbnail.height}",
|
|
|
|
"-frames:v", "1", "-f", "image2", "-y", outputImagePath), { session ->
|
2022-06-18 22:21:19 +02:00
|
|
|
val state = session.state
|
|
|
|
val returnCode = session.returnCode
|
|
|
|
|
|
|
|
if (ReturnCode.isSuccess(returnCode)) {
|
|
|
|
// SUCCESS
|
|
|
|
resultHandler.post {
|
|
|
|
if(!this.isFinishing)
|
|
|
|
Glide.with(this).load(outputImagePath).centerCrop().into(thumbnail)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// CALLED WHEN SESSION IS EXECUTED
|
|
|
|
Log.d("VideoEditActivity", "FFmpeg process exited with state $state and rc $returnCode.${session.failStackTrace}")
|
|
|
|
},
|
2022-08-21 00:31:36 +02:00
|
|
|
{/* CALLED WHEN SESSION PRINTS LOGS */ }, { /*CALLED WHEN SESSION GENERATES STATISTICS*/ })
|
2022-06-18 22:21:19 +02:00
|
|
|
sessionList.add(session.sessionId)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPause() {
|
|
|
|
super.onPause()
|
|
|
|
mediaPlayer.pause()
|
|
|
|
}
|
|
|
|
|
2022-06-10 23:41:29 +02:00
|
|
|
companion object {
|
|
|
|
const val VIDEO_TAG = "VideoEditTag"
|
2022-06-18 22:21:19 +02:00
|
|
|
const val MUTED = "VideoEditMutedTag"
|
2022-10-15 20:13:29 +02:00
|
|
|
const val SPEED = "VideoEditSpeedTag"
|
|
|
|
// List of choices of speeds
|
|
|
|
val speedChoices: List<Number> = listOf(0.5, 1, 2, 4, 8)
|
2022-06-18 22:21:19 +02:00
|
|
|
const val VIDEO_START = "VideoEditVideoStartTag"
|
|
|
|
const val VIDEO_END = "VideoEditVideoEndTag"
|
2022-10-19 00:19:42 +02:00
|
|
|
const val VIDEO_CROP = "VideoEditVideoCropTag"
|
2022-10-22 22:07:03 +02:00
|
|
|
const val VIDEO_STABILIZE = "VideoEditVideoStabilizeTag"
|
2022-06-18 22:21:19 +02:00
|
|
|
const val MODIFIED = "VideoEditModifiedTag"
|
2022-06-10 23:41:29 +02:00
|
|
|
}
|
|
|
|
}
|