mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
refactor: Improve BatchesFolder concatenation behavior
This commit is contained in:
parent
fe9d9d7298
commit
1be3912812
@ -3,6 +3,7 @@ package app.myzel394.alibi.helpers
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@ -17,6 +18,9 @@ class AudioBatchesFolder(
|
|||||||
customFolder,
|
customFolder,
|
||||||
subfolderName,
|
subfolderName,
|
||||||
) {
|
) {
|
||||||
|
override val concatenateFunction = ::concatenateAudioFiles
|
||||||
|
override val ffmpegParameters = FFMPEG_PARAMETERS
|
||||||
|
|
||||||
override fun getOutputFileForFFmpeg(
|
override fun getOutputFileForFFmpeg(
|
||||||
date: LocalDateTime,
|
date: LocalDateTime,
|
||||||
extension: String,
|
extension: String,
|
||||||
@ -33,28 +37,6 @@ class AudioBatchesFolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun concatenate(
|
|
||||||
recordingStart: LocalDateTime,
|
|
||||||
extension: String,
|
|
||||||
disableCache: Boolean,
|
|
||||||
): String {
|
|
||||||
val outputFile = getOutputFileForFFmpeg(
|
|
||||||
date = recordingStart,
|
|
||||||
extension = extension,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (disableCache || checkIfOutputAlreadyExists(recordingStart, extension)) {
|
|
||||||
val filePaths = getBatchesForFFmpeg()
|
|
||||||
|
|
||||||
MediaConverter.concatenateAudioFiles(
|
|
||||||
inputFiles = filePaths,
|
|
||||||
outputFile = outputFile,
|
|
||||||
).await()
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL)
|
fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL)
|
||||||
|
|
||||||
@ -65,5 +47,18 @@ class AudioBatchesFolder(
|
|||||||
"_'internal" -> viaInternalFolder(context)
|
"_'internal" -> viaInternalFolder(context)
|
||||||
else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!)
|
else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parameters to be passed in descending order
|
||||||
|
// Those parameters first try to concatenate without re-encoding
|
||||||
|
// if that fails, it'll try several fallback methods
|
||||||
|
// this is audio only
|
||||||
|
val FFMPEG_PARAMETERS = arrayOf(
|
||||||
|
" -c copy",
|
||||||
|
" -acodec copy",
|
||||||
|
" -c:a aac",
|
||||||
|
" -c:a libmp3lame",
|
||||||
|
" -c:a libopus",
|
||||||
|
" -c:a libvorbis",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,14 +2,14 @@ package app.myzel394.alibi.helpers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
import android.net.Uri
|
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
|
import kotlin.reflect.KFunction3
|
||||||
|
|
||||||
abstract class BatchesFolder(
|
abstract class BatchesFolder(
|
||||||
open val context: Context,
|
open val context: Context,
|
||||||
@ -19,6 +19,9 @@ abstract class BatchesFolder(
|
|||||||
) {
|
) {
|
||||||
private var customFileFileDescriptor: ParcelFileDescriptor? = null
|
private var customFileFileDescriptor: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
abstract val concatenateFunction: KFunction3<Iterable<String>, String, String, CompletableDeferred<Unit>>
|
||||||
|
abstract val ffmpegParameters: Array<String>
|
||||||
|
|
||||||
fun initFolders() {
|
fun initFolders() {
|
||||||
when (type) {
|
when (type) {
|
||||||
BatchType.INTERNAL -> getInternalFolder().mkdirs()
|
BatchType.INTERNAL -> getInternalFolder().mkdirs()
|
||||||
@ -111,11 +114,37 @@ abstract class BatchesFolder(
|
|||||||
extension: String,
|
extension: String,
|
||||||
): String
|
): String
|
||||||
|
|
||||||
abstract suspend fun concatenate(
|
open suspend fun concatenate(
|
||||||
recordingStart: LocalDateTime,
|
recordingStart: LocalDateTime,
|
||||||
extension: String,
|
extension: String,
|
||||||
disableCache: Boolean = false,
|
disableCache: Boolean = false,
|
||||||
): String
|
onNextParameterTry: (String) -> Unit = {},
|
||||||
|
): String {
|
||||||
|
val outputFile = getOutputFileForFFmpeg(
|
||||||
|
date = recordingStart,
|
||||||
|
extension = extension,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) {
|
||||||
|
val filePaths = getBatchesForFFmpeg()
|
||||||
|
|
||||||
|
for (parameter in ffmpegParameters) {
|
||||||
|
onNextParameterTry(parameter)
|
||||||
|
|
||||||
|
try {
|
||||||
|
concatenateFunction(
|
||||||
|
filePaths,
|
||||||
|
outputFile,
|
||||||
|
parameter,
|
||||||
|
)
|
||||||
|
} catch (e: MediaConverter.FFmpegException) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputFile
|
||||||
|
}
|
||||||
|
|
||||||
fun exportFolderForSettings(): String {
|
fun exportFolderForSettings(): String {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
|
@ -22,7 +22,6 @@ class MediaConverter {
|
|||||||
"-protocol_whitelist saf,concat,content,file,subfile" +
|
"-protocol_whitelist saf,concat,content,file,subfile" +
|
||||||
" -i 'concat:$filePathsConcatenated'" +
|
" -i 'concat:$filePathsConcatenated'" +
|
||||||
" -y" +
|
" -y" +
|
||||||
" -acodec copy" +
|
|
||||||
extraCommand +
|
extraCommand +
|
||||||
" $outputFile"
|
" $outputFile"
|
||||||
|
|
||||||
@ -68,16 +67,17 @@ class MediaConverter {
|
|||||||
|
|
||||||
val command =
|
val command =
|
||||||
" -f concat" +
|
" -f concat" +
|
||||||
" -y" +
|
|
||||||
" -safe 0" +
|
" -safe 0" +
|
||||||
" -i ${listFile.absolutePath}" +
|
" -i ${listFile.absolutePath}" +
|
||||||
" -c copy" +
|
|
||||||
extraCommand +
|
extraCommand +
|
||||||
|
" -y" +
|
||||||
" $outputFile"
|
" $outputFile"
|
||||||
|
|
||||||
FFmpegKit.executeAsync(
|
FFmpegKit.executeAsync(
|
||||||
command
|
command
|
||||||
) { session ->
|
) { session ->
|
||||||
|
listFile.delete()
|
||||||
|
|
||||||
if (!ReturnCode.isSuccess(session!!.returnCode)) {
|
if (!ReturnCode.isSuccess(session!!.returnCode)) {
|
||||||
Log.d(
|
Log.d(
|
||||||
"Video Concatenation",
|
"Video Concatenation",
|
||||||
@ -89,7 +89,7 @@ class MediaConverter {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
completer.completeExceptionally(Exception("Failed to concatenate videos"))
|
completer.completeExceptionally(FFmpegException("Failed to concatenate videos"))
|
||||||
} else {
|
} else {
|
||||||
completer.complete(Unit)
|
completer.complete(Unit)
|
||||||
}
|
}
|
||||||
@ -98,4 +98,6 @@ class MediaConverter {
|
|||||||
return completer
|
return completer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FFmpegException(message: String) : Exception(message)
|
||||||
}
|
}
|
@ -3,7 +3,9 @@ package app.myzel394.alibi.helpers
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
|
import com.arthenica.ffmpegkit.ReturnCode
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class VideoBatchesFolder(
|
class VideoBatchesFolder(
|
||||||
@ -17,6 +19,9 @@ class VideoBatchesFolder(
|
|||||||
customFolder,
|
customFolder,
|
||||||
subfolderName,
|
subfolderName,
|
||||||
) {
|
) {
|
||||||
|
override val concatenateFunction = ::concatenateVideoFiles
|
||||||
|
override val ffmpegParameters = FFMPEG_PARAMETERS
|
||||||
|
|
||||||
override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String {
|
override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath
|
BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath
|
||||||
@ -30,28 +35,6 @@ class VideoBatchesFolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun concatenate(
|
|
||||||
recordingStart: LocalDateTime,
|
|
||||||
extension: String,
|
|
||||||
disableCache: Boolean
|
|
||||||
): String {
|
|
||||||
val outputFile = getOutputFileForFFmpeg(
|
|
||||||
date = recordingStart,
|
|
||||||
extension = extension,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) {
|
|
||||||
val filePaths = getBatchesForFFmpeg()
|
|
||||||
|
|
||||||
MediaConverter.concatenateVideoFiles(
|
|
||||||
inputFiles = filePaths,
|
|
||||||
outputFile = outputFile,
|
|
||||||
).await()
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputFile
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL)
|
fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL)
|
||||||
|
|
||||||
@ -65,5 +48,31 @@ class VideoBatchesFolder(
|
|||||||
DocumentFile.fromTreeUri(context, Uri.parse(folder))!!
|
DocumentFile.fromTreeUri(context, Uri.parse(folder))!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parameters to be passed in descending order
|
||||||
|
// Those parameters first try to concatenate without re-encoding
|
||||||
|
// if that fails, it'll try several fallback methods
|
||||||
|
val FFMPEG_PARAMETERS = arrayOf(
|
||||||
|
" -c copy",
|
||||||
|
" -c:v copy",
|
||||||
|
" -c:v copy -c:a aac",
|
||||||
|
" -c:v copy -c:a libmp3lame",
|
||||||
|
" -c:v copy -c:a libopus",
|
||||||
|
" -c:v copy -c:a libvorbis",
|
||||||
|
" -c:a copy",
|
||||||
|
// There's nothing else we can do to avoid re-encoding,
|
||||||
|
// so we'll just have to re-encode the whole thing
|
||||||
|
" -c:v libx264 -c:a copy",
|
||||||
|
" -c:v libx264 -c:a aac",
|
||||||
|
" -c:v libx265 -c:a aac",
|
||||||
|
" -c:v libx264 -c:a libmp3lame",
|
||||||
|
" -c:v libx264 -c:a libopus",
|
||||||
|
" -c:v libx264 -c:a libvorbis",
|
||||||
|
" -c:v libx265 -c:a copy",
|
||||||
|
" -c:v libx265 -c:a aac",
|
||||||
|
" -c:v libx265 -c:a libmp3lame",
|
||||||
|
" -c:v libx265 -c:a libopus",
|
||||||
|
" -c:v libx265 -c:a libvorbis",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user