diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 2e0ff56..775586e 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -3,11 +3,13 @@ package app.myzel394.alibi.db import android.content.Context import android.media.MediaRecorder import android.os.Build +import androidx.camera.video.FileOutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import app.myzel394.alibi.R import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.services.VideoRecorderService import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.time.LocalDateTime @@ -260,6 +262,19 @@ data class AudioRecorderSettings( return supportedFormats.contains(outputFormat) } + val fileExtension: String + get() = when (outputFormat) { + MediaRecorder.OutputFormat.AAC_ADTS -> "aac" + MediaRecorder.OutputFormat.THREE_GPP -> "3gp" + MediaRecorder.OutputFormat.MPEG_4 -> "mp4" + MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" + MediaRecorder.OutputFormat.WEBM -> "webm" + MediaRecorder.OutputFormat.AMR_NB -> "amr" + MediaRecorder.OutputFormat.AMR_WB -> "awb" + MediaRecorder.OutputFormat.OGG -> "ogg" + else -> "raw" + } + companion object { fun getDefaultInstance(): AudioRecorderSettings = AudioRecorderSettings() val EXAMPLE_MAX_DURATIONS = listOf( @@ -403,6 +418,9 @@ data class VideoRecorderSettings( fun getMimeType() = "video/mp4" + val fileExtension + get() = "mp4" + companion object { fun getDefaultInstance() = VideoRecorderSettings() diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 4de2300..ebcd08f 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -13,7 +13,6 @@ import android.os.Looper import androidx.core.app.ServiceCompat import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.db.AudioRecorderSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder @@ -22,7 +21,7 @@ import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException class AudioRecorderService : - IntervalRecorderService() { + IntervalRecorderService() { override var batchesFolder: BatchesFolder = AudioBatchesFolder.viaInternalFolder(this) private val handler = Handler(Looper.getMainLooper()) @@ -169,6 +168,8 @@ class AudioRecorderService : } else { MediaRecorder() }.apply { + val audioSettings = settings.audioRecorderSettings + // Audio Source is kinda strange, here are my experimental findings using a Pixel 7 Pro // and Redmi Buds 3 Pro: // - MIC: Uses the bottom microphone of the phone (17) @@ -180,22 +181,25 @@ class AudioRecorderService : when (batchesFolder.type) { BatchesFolder.BatchType.INTERNAL -> { setOutputFile( - batchesFolder.asInternalGetOutputPath(counter, settings.fileExtension) + batchesFolder.asInternalGetOutputPath(counter, audioSettings.fileExtension) ) } BatchesFolder.BatchType.CUSTOM -> { setOutputFile( - batchesFolder.asCustomGetFileDescriptor(counter, settings.fileExtension) + batchesFolder.asCustomGetFileDescriptor( + counter, + audioSettings.fileExtension + ) ) } } - setOutputFormat(settings.outputFormat) + setOutputFormat(audioSettings.getOutputFormat()) - setAudioEncoder(settings.encoder) - setAudioEncodingBitRate(settings.bitRate) - setAudioSamplingRate(settings.samplingRate) + setAudioEncoder(audioSettings.getEncoder()) + setAudioEncodingBitRate(audioSettings.bitRate) + setAudioSamplingRate(audioSettings.getSamplingRate()) setOnErrorListener(OnErrorListener { _, _, _ -> onError() }) @@ -281,47 +285,8 @@ class AudioRecorderService : folderPath = batchesFolder.exportFolderForSettings(), recordingStart = recordingStart, maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, + fileExtension = settings.audioRecorderSettings.fileExtension, intervalDuration = settings.intervalDuration, type = RecordingInformation.Type.AUDIO, ) - - data class Settings( - override val maxDuration: Long, - override val intervalDuration: Long, - val bitRate: Int, - val samplingRate: Int, - val outputFormat: Int, - val encoder: Int, - val folder: String? = null, - ) : IntervalRecorderService.Settings( - maxDuration = maxDuration, - intervalDuration = intervalDuration - ) { - val fileExtension: String - get() = when (outputFormat) { - MediaRecorder.OutputFormat.AAC_ADTS -> "aac" - MediaRecorder.OutputFormat.THREE_GPP -> "3gp" - MediaRecorder.OutputFormat.MPEG_4 -> "mp4" - MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" - MediaRecorder.OutputFormat.WEBM -> "webm" - MediaRecorder.OutputFormat.AMR_NB -> "amr" - MediaRecorder.OutputFormat.AMR_WB -> "awb" - MediaRecorder.OutputFormat.OGG -> "ogg" - else -> "raw" - } - - companion object { - fun from(appSettings: AppSettings): Settings { - return Settings( - intervalDuration = appSettings.intervalDuration, - maxDuration = appSettings.maxDuration, - bitRate = appSettings.audioRecorderSettings.bitRate, - samplingRate = appSettings.audioRecorderSettings.getSamplingRate(), - outputFormat = appSettings.audioRecorderSettings.getOutputFormat(), - encoder = appSettings.audioRecorderSettings.getEncoder(), - ) - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 35139b2..f5e600a 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -1,16 +1,17 @@ package app.myzel394.alibi.services +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.BatchesFolder import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -abstract class IntervalRecorderService : +abstract class IntervalRecorderService : RecorderService() { protected var counter = 0L private set - lateinit var settings: S + lateinit var settings: AppSettings private lateinit var cycleTimer: ScheduledExecutorService diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 2607720..1449724 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -19,7 +19,6 @@ import androidx.camera.video.VideoRecordEvent import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import app.myzel394.alibi.NotificationHelper -import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder @@ -34,7 +33,7 @@ import kotlinx.coroutines.withTimeoutOrNull import kotlin.properties.Delegates class VideoRecorderService : - IntervalRecorderService() { + IntervalRecorderService() { override var batchesFolder: BatchesFolder = VideoBatchesFolder.viaInternalFolder(this) private val job = SupervisorJob() @@ -149,18 +148,22 @@ class VideoRecorderService : } private fun buildRecorder() = Recorder.Builder() - .setQualitySelector(settings.quality) + .setQualitySelector( + settings.videoRecorderSettings.getQualitySelector() + ?: QualitySelector.from(Quality.HIGHEST) + ) .apply { - if (settings.targetVideoBitRate != null) { - setTargetVideoEncodingBitRate(settings.targetVideoBitRate!!) + if (settings.videoRecorderSettings.targetedVideoBitRate != null) { + setTargetVideoEncodingBitRate(settings.videoRecorderSettings.targetedVideoBitRate!!) } } .build() private fun buildVideoCapture(recorder: Recorder) = VideoCapture.Builder(recorder) .apply { - if (settings.targetFrameRate != null) { - setTargetFrameRate(Range(settings.targetFrameRate!!, settings.targetFrameRate!!)) + val frameRate = settings.videoRecorderSettings.targetFrameRate + if (frameRate != null) { + setTargetFrameRate(Range(frameRate, frameRate)) } } .build() @@ -218,7 +221,7 @@ class VideoRecorderService : @SuppressLint("MissingPermission") private fun prepareVideoRecording() = videoCapture!!.output - .prepareRecording(this, settings.getOutputOptions(this)) + .prepareRecording(this, getOutputOptions()) .run { if (enableAudio) { return@run withAudioEnabled() @@ -231,49 +234,22 @@ class VideoRecorderService : folderPath = batchesFolder.exportFolderForSettings(), recordingStart = recordingStart, maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, + fileExtension = settings.videoRecorderSettings.fileExtension, intervalDuration = settings.intervalDuration, type = RecordingInformation.Type.VIDEO, ) - companion object { - const val CAMERA_CLOSE_TIMEOUT = 20000L + fun getOutputOptions(): FileOutputOptions { + val fileName = "${counter}.${settings.videoRecorderSettings.fileExtension}" + val file = batchesFolder.getInternalFolder().resolve(fileName).apply { + createNewFile() + } + + return FileOutputOptions.Builder(file).build() } - data class Settings( - override val maxDuration: Long, - override val intervalDuration: Long, - val folder: String? = null, - val targetVideoBitRate: Int? = null, - val targetFrameRate: Int? = null, - val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), - ) : IntervalRecorderService.Settings( - maxDuration = maxDuration, - intervalDuration = intervalDuration - ) { - val fileExtension - get() = "mp4" - - fun getOutputOptions(video: VideoRecorderService): FileOutputOptions { - val fileName = "${video.counter}.$fileExtension" - val file = video.batchesFolder.getInternalFolder().resolve(fileName).apply { - createNewFile() - } - - return FileOutputOptions.Builder(file).build() - } - - companion object { - fun from(appSettings: AppSettings) = Settings( - maxDuration = appSettings.maxDuration, - intervalDuration = appSettings.intervalDuration, - folder = appSettings.saveFolder, - targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, - targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate, - quality = appSettings.videoRecorderSettings.getQualitySelector() - ?: QualitySelector.from(Quality.HIGHEST), - ) - } + companion object { + const val CAMERA_CLOSE_TIMEOUT = 20000L } class CameraControl( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index 877d063..8e37950 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -31,9 +31,8 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch typealias RecorderModel = BaseRecorderModel< - IntervalRecorderService.Settings, RecordingInformation, - IntervalRecorderService, + IntervalRecorderService, BatchesFolder? > diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 874dd61..83f6cdb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -56,7 +56,7 @@ fun AudioRecordingStatus( recordingTime = audioRecorder.recordingTime, progress = audioRecorder.progress, recordingStart = audioRecorder.recordingStart, - maxDuration = audioRecorder.settings.maxDuration, + maxDuration = audioRecorder.settings!!.maxDuration, ) MicrophoneStatus(audioRecorder) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 61d0f68..d34ad62 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -110,7 +110,7 @@ fun VideoRecordingStatus( recordingTime = videoRecorder.recordingTime, progress = videoRecorder.progress, recordingStart = videoRecorder.recordingStart, - maxDuration = videoRecorder.settings.maxDuration, + maxDuration = videoRecorder.settings!!.maxDuration, ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 6b9bebf..4d679c7 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -13,7 +13,7 @@ import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.ui.utils.MicrophoneInfo class AudioRecorderModel : - BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: AudioBatchesFolder? = null override val intentClass = AudioRecorderService::class.java @@ -46,8 +46,6 @@ class AudioRecorderModel : amplitudes = amps onAmplitudeChange() } - service.settings = - AudioRecorderService.Settings.from(settings) service.clearAllRecordings() service.startRecording() diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 28de54d..ad9f195 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -19,7 +19,7 @@ import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderService import kotlinx.serialization.json.Json -abstract class BaseRecorderModel, B : BatchesFolder?> : +abstract class BaseRecorderModel, B : BatchesFolder?> : ViewModel() { protected abstract val intentClass: Class @@ -51,7 +51,7 @@ abstract class BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: VideoBatchesFolder? = null override val intentClass = VideoRecorderService::class.java @@ -39,8 +39,6 @@ class VideoRecorderModel : } override fun onServiceConnected(service: VideoRecorderService) { - service.settings = VideoRecorderService.Settings.from(settings) - service.clearAllRecordings() service.startRecording()