debug: current stand trying to make ffmpeg to work

This commit is contained in:
Myzel394 2023-11-28 18:59:01 +01:00
parent 1eb7a7dd9a
commit fe9d9d7298
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
12 changed files with 80 additions and 69 deletions

View File

@ -56,15 +56,12 @@ class AudioBatchesFolder(
} }
companion object { companion object {
fun viaInternalFolder(context: Context): BatchesFolder { fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL)
return AudioBatchesFolder(context, BatchType.INTERNAL)
}
fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { fun viaCustomFolder(context: Context, folder: DocumentFile) =
return AudioBatchesFolder(context, BatchType.CUSTOM, folder) AudioBatchesFolder(context, BatchType.CUSTOM, folder)
}
fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { fun importFromFolder(folder: String, context: Context) = when (folder) {
"_'internal" -> viaInternalFolder(context) "_'internal" -> viaInternalFolder(context)
else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!)
} }

View File

@ -21,7 +21,7 @@ abstract class BatchesFolder(
fun initFolders() { fun initFolders() {
when (type) { when (type) {
BatchType.INTERNAL -> getFolder(context).mkdirs() BatchType.INTERNAL -> getInternalFolder().mkdirs()
BatchType.CUSTOM -> { BatchType.CUSTOM -> {
if (customFolder!!.findFile(subfolderName) == null) { if (customFolder!!.findFile(subfolderName) == null) {
customFolder!!.createDirectory(subfolderName) customFolder!!.createDirectory(subfolderName)
@ -35,7 +35,7 @@ abstract class BatchesFolder(
} }
fun getInternalFolder(): File { fun getInternalFolder(): File {
return getFolder(context) return File(context.filesDir, subfolderName)
} }
fun getCustomDefinedFolder(): DocumentFile { fun getCustomDefinedFolder(): DocumentFile {
@ -189,9 +189,5 @@ abstract class BatchesFolder(
INTERNAL, INTERNAL,
CUSTOM, CUSTOM,
} }
companion object {
fun getFolder(context: Context) = File(context.filesDir, RECORDER_SUBFOLDER_NAME)
}
} }

View File

@ -67,9 +67,9 @@ class MediaConverter {
val listFile = createTempFile(inputFiles.joinToString("\n", prefix = "file ")) val listFile = createTempFile(inputFiles.joinToString("\n", prefix = "file "))
val command = val command =
"-protocol_whitelist saf,concat,content,file,subfile" + " -f concat" +
" -f concat" +
" -y" + " -y" +
" -safe 0" +
" -i ${listFile.absolutePath}" + " -i ${listFile.absolutePath}" +
" -c copy" + " -c copy" +
extraCommand + extraCommand +

View File

@ -53,17 +53,14 @@ class VideoBatchesFolder(
} }
companion object { companion object {
fun viaInternalFolder(context: Context): BatchesFolder { fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL)
return VideoBatchesFolder(context, BatchType.INTERNAL)
}
fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { fun viaCustomFolder(context: Context, folder: DocumentFile) =
return VideoBatchesFolder(context, BatchType.CUSTOM, folder) VideoBatchesFolder(context, BatchType.CUSTOM, folder)
}
fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { fun importFromFolder(folder: String, context: Context) = when (folder) {
"_'internal" -> AudioBatchesFolder.viaInternalFolder(context) "_'internal" -> viaInternalFolder(context)
else -> AudioBatchesFolder.viaCustomFolder( else -> viaCustomFolder(
context, context,
DocumentFile.fromTreeUri(context, Uri.parse(folder))!! DocumentFile.fromTreeUri(context, Uri.parse(folder))!!
) )

View File

@ -162,12 +162,14 @@ abstract class RecorderService : LifecycleService() {
changeState(RecorderState.RECORDING) changeState(RecorderState.RECORDING)
} }
fun stopRecording() {
changeState(RecorderState.IDLE)
stop()
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
stop()
changeState(RecorderState.IDLE)
stopForeground(STOP_FOREGROUND_REMOVE) stopForeground(STOP_FOREGROUND_REMOVE)
NotificationManagerCompat.from(this) NotificationManagerCompat.from(this)
.cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID)

View File

@ -1,6 +1,7 @@
package app.myzel394.alibi.services package app.myzel394.alibi.services
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.util.Range
import androidx.camera.core.Camera import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
@ -38,6 +39,8 @@ class VideoRecorderService :
// Used to listen and check if the camera is available // Used to listen and check if the camera is available
private var _cameraAvailableListener = CompletableDeferred<Unit>() private var _cameraAvailableListener = CompletableDeferred<Unit>()
private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// Runs a function in the main thread // Runs a function in the main thread
private fun runInMain(callback: () -> Unit) { private fun runInMain(callback: () -> Unit) {
val mainHandler = ContextCompat.getMainExecutor(this) val mainHandler = ContextCompat.getMainExecutor(this)
@ -45,6 +48,23 @@ class VideoRecorderService :
mainHandler.execute(callback) mainHandler.execute(callback)
} }
private fun buildRecorder() = Recorder.Builder()
.setQualitySelector(settings.quality)
.apply {
if (settings.targetVideoBitRate != null) {
setTargetVideoEncodingBitRate(settings.targetVideoBitRate!!)
}
}
.build()
private fun buildVideoCapture(recorder: Recorder) = VideoCapture.Builder(recorder)
.apply {
if (settings.targetFrameRate != null) {
setTargetFrameRate(Range(settings.targetFrameRate!!, settings.targetFrameRate!!))
}
}
.build()
// Open the camera. // Open the camera.
// Used to open it for a longer time, shouldn't be called when pausing / resuming. // Used to open it for a longer time, shouldn't be called when pausing / resuming.
// This should only be called when starting a recording. // This should only be called when starting a recording.
@ -53,16 +73,13 @@ class VideoRecorderService :
ProcessCameraProvider.getInstance(this@VideoRecorderService).get() ProcessCameraProvider.getInstance(this@VideoRecorderService).get()
} }
val recorder = Recorder.Builder() val recorder = buildRecorder()
.setQualitySelector(settings.quality) videoCapture = buildVideoCapture(recorder)
.build()
videoCapture = VideoCapture.Builder(recorder)
.build()
runInMain { runInMain {
camera = cameraProvider!!.bindToLifecycle( camera = cameraProvider!!.bindToLifecycle(
this, this,
CameraSelector.DEFAULT_BACK_CAMERA, selectedCamera,
videoCapture videoCapture
) )
@ -74,7 +91,7 @@ class VideoRecorderService :
// Used to close it finally, shouldn't be called when pausing / resuming. // Used to close it finally, shouldn't be called when pausing / resuming.
// This should only be called after recording has finished. // This should only be called after recording has finished.
private fun closeCamera() { private fun closeCamera() {
clearOldVideoRecording() stopActiveRecording()
runCatching { runCatching {
cameraProvider?.unbindAll() cameraProvider?.unbindAll()
@ -99,10 +116,8 @@ class VideoRecorderService :
closeCamera() closeCamera()
} }
private fun clearOldVideoRecording() { private fun stopActiveRecording() {
runCatching { activeRecording?.stop()
activeRecording?.stop()
}
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
@ -143,11 +158,13 @@ class VideoRecorderService :
type = RecordingInformation.Type.VIDEO, type = RecordingInformation.Type.VIDEO,
) )
// TODO: Save camera selector as it doesn't make sense to change the camera midway
data class Settings( data class Settings(
override val maxDuration: Long, override val maxDuration: Long,
override val intervalDuration: Long, override val intervalDuration: Long,
val folder: String? = null, val folder: String? = null,
val targetVideoBitRate: Int? = null, val targetVideoBitRate: Int? = null,
val targetFrameRate: Int? = null,
val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST),
) : IntervalRecorderService.Settings( ) : IntervalRecorderService.Settings(
maxDuration = maxDuration, maxDuration = maxDuration,
@ -172,6 +189,7 @@ class VideoRecorderService :
intervalDuration = appSettings.audioRecorderSettings.intervalDuration, intervalDuration = appSettings.audioRecorderSettings.intervalDuration,
folder = appSettings.audioRecorderSettings.saveFolder, folder = appSettings.audioRecorderSettings.saveFolder,
targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate,
targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate,
quality = appSettings.videoRecorderSettings.getQualitySelector() quality = appSettings.videoRecorderSettings.getQualitySelector()
?: QualitySelector.from( ?: QualitySelector.from(
Quality.HIGHEST Quality.HIGHEST

View File

@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.alibi.dataStore import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.helpers.AudioRecorderExporter
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.DeleteButton import app.myzel394.alibi.ui.components.AudioRecorder.atoms.DeleteButton
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog
import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneReconnectedDialog import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneReconnectedDialog
@ -106,7 +105,7 @@ fun RecordingStatus(
) { ) {
DeleteButton( DeleteButton(
onDelete = { onDelete = {
audioRecorder.stopRecording(context) //audioRecorder.stopRecording(context)
audioRecorder.batchesFolder!!.deleteRecordings(); audioRecorder.batchesFolder!!.deleteRecordings();
} }
) )
@ -136,7 +135,7 @@ fun RecordingStatus(
SaveButton( SaveButton(
onSave = { onSave = {
runCatching { runCatching {
audioRecorder.stopRecording(context) //audioRecorder.stopRecording(context)
} }
audioRecorder.onRecordingSave() audioRecorder.onRecordingSave()
} }

View File

@ -1,32 +1,20 @@
package app.myzel394.alibi.ui.models package app.myzel394.alibi.ui.models
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri import android.net.Uri
import android.os.IBinder
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.content.ContextCompat
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModel
import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.db.RecordingInformation
import app.myzel394.alibi.enums.RecorderState
import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.AudioBatchesFolder
import app.myzel394.alibi.helpers.AudioRecorderExporter
import app.myzel394.alibi.helpers.BatchesFolder
import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.services.AudioRecorderService
import app.myzel394.alibi.services.IntervalRecorderService
import app.myzel394.alibi.services.RecorderNotificationHelper
import app.myzel394.alibi.services.RecorderService
import kotlinx.serialization.json.Json
import app.myzel394.alibi.ui.utils.MicrophoneInfo import app.myzel394.alibi.ui.utils.MicrophoneInfo
class AudioRecorderModel : class AudioRecorderModel :
BaseRecorderModel<AudioRecorderService.Settings, RecordingInformation, AudioRecorderService>() { BaseRecorderModel<AudioRecorderService.Settings, RecordingInformation, AudioRecorderService, AudioBatchesFolder?>() {
override var batchesFolder: AudioBatchesFolder? = null
override val intentClass = AudioRecorderService::class.java override val intentClass = AudioRecorderService::class.java
var amplitudes by mutableStateOf<List<Int>>(emptyList()) var amplitudes by mutableStateOf<List<Int>>(emptyList())

View File

@ -19,9 +19,10 @@ import app.myzel394.alibi.services.AudioRecorderService
import app.myzel394.alibi.services.IntervalRecorderService import app.myzel394.alibi.services.IntervalRecorderService
import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderNotificationHelper
import app.myzel394.alibi.services.RecorderService import app.myzel394.alibi.services.RecorderService
import kotlinx.coroutines.delay
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
abstract class BaseRecorderModel<S : IntervalRecorderService.Settings, I, T : IntervalRecorderService<S, I>> : abstract class BaseRecorderModel<S : IntervalRecorderService.Settings, I, T : IntervalRecorderService<S, I>, B : BatchesFolder?> :
ViewModel() { ViewModel() {
protected abstract val intentClass: Class<T> protected abstract val intentClass: Class<T>
@ -44,7 +45,7 @@ abstract class BaseRecorderModel<S : IntervalRecorderService.Settings, I, T : In
var onRecordingSave: () -> Unit = {} var onRecordingSave: () -> Unit = {}
var onError: () -> Unit = {} var onError: () -> Unit = {}
var batchesFolder: BatchesFolder? = null abstract var batchesFolder: B
private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null
@ -69,6 +70,8 @@ abstract class BaseRecorderModel<S : IntervalRecorderService.Settings, I, T : In
if (batchesFolder != null) { if (batchesFolder != null) {
recorder.batchesFolder = batchesFolder!! recorder.batchesFolder = batchesFolder!!
} else {
batchesFolder = recorder.batchesFolder as B
} }
// Rest should be initialized from the child class // Rest should be initialized from the child class
@ -123,15 +126,9 @@ abstract class BaseRecorderModel<S : IntervalRecorderService.Settings, I, T : In
context.bindService(intent, connection, Context.BIND_AUTO_CREATE) context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
} }
fun stopRecording(context: Context) { suspend fun stopRecording(context: Context) {
runCatching { recorderService!!.stopRecording()
context.unbindService(connection)
}
val intent = Intent(context, intentClass)
context.stopService(intent)
reset()
} }
fun pauseRecording() { fun pauseRecording() {

View File

@ -1,10 +1,12 @@
package app.myzel394.alibi.ui.models package app.myzel394.alibi.ui.models
import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.db.RecordingInformation
import app.myzel394.alibi.helpers.VideoBatchesFolder
import app.myzel394.alibi.services.VideoRecorderService import app.myzel394.alibi.services.VideoRecorderService
class VideoRecorderModel : class VideoRecorderModel :
BaseRecorderModel<VideoRecorderService.Settings, RecordingInformation, VideoRecorderService>() { BaseRecorderModel<VideoRecorderService.Settings, RecordingInformation, VideoRecorderService, VideoBatchesFolder?>() {
override var batchesFolder: VideoBatchesFolder? = null
override val intentClass = VideoRecorderService::class.java override val intentClass = VideoRecorderService::class.java
override fun onServiceConnected(service: VideoRecorderService) { override fun onServiceConnected(service: VideoRecorderService) {

View File

@ -178,7 +178,7 @@ fun AudioRecorderScreen(
audioRecorder.onError = { audioRecorder.onError = {
saveAsLastRecording() saveAsLastRecording()
audioRecorder.stopRecording(context) //audioRecorder.stopRecording(context)
showRecorderError = true showRecorderError = true
} }

View File

@ -9,6 +9,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -16,6 +17,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
@ -29,6 +32,8 @@ fun POCVideo(
mutableStateOf(false) mutableStateOf(false)
} }
val scope = rememberCoroutineScope()
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -38,9 +43,19 @@ fun POCVideo(
if (!started) { if (!started) {
videoRecorder.startRecording(context, settings) videoRecorder.startRecording(context, settings)
} else { } else {
videoRecorder.stopRecording(context) scope.launch {
val information = videoRecorder.recorderService!!.getRecordingInformation()
val batchesFolder = videoRecorder.batchesFolder!!
videoRecorder.stopRecording(context)
val folder = "content://media/external/video/media/DCIM/Recordings" delay(5000)
batchesFolder.concatenate(
recordingStart = information.recordingStart,
extension = information.fileExtension,
disableCache = true,
)
}
} }
started = !started started = !started