try to handle changing video quality and image resolution
- CameraX video allowed predefined buckets of qualities UHD, FHD, HD, SD (defined in VideoQuality enum), the CameraXPreview is configured to use the highest quality and CameraX will select the closest corresponding resolution supported by the device. - tentatively add ChangeResolutionDialogX (which would be renamed back to ChangeResolutionDialog) to give user option to select photo resolution and video qualities - add ImageQualityManager which performs the same operation for getting all resolutions supported by a device using the Camera2 API, as defined in the legacy CameraPreview - add VideoQualityManager to manage saving/ getting user selected quality.
This commit is contained in:
parent
889a384f21
commit
74e2656831
|
@ -0,0 +1,104 @@
|
|||
package com.simplemobiletools.camera.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.camera.R
|
||||
import com.simplemobiletools.camera.extensions.config
|
||||
import com.simplemobiletools.camera.models.MySize
|
||||
import com.simplemobiletools.camera.models.VideoQuality
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo
|
||||
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_photo_holder
|
||||
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_video
|
||||
import kotlinx.android.synthetic.main.dialog_change_resolution.view.change_resolution_video_holder
|
||||
|
||||
class ChangeResolutionDialogX(
|
||||
private val activity: Activity,
|
||||
private val isFrontCamera: Boolean,
|
||||
private val photoResolutions: List<MySize> = listOf(),
|
||||
private val videoResolutions: List<VideoQuality>,
|
||||
private val callback: () -> Unit
|
||||
) {
|
||||
private var dialog: AlertDialog
|
||||
private val config = activity.config
|
||||
|
||||
private val TAG = "ChangeResolutionDialogX"
|
||||
init {
|
||||
val view = LayoutInflater.from(activity).inflate(R.layout.dialog_change_resolution, null).apply {
|
||||
setupPhotoResolutionPicker(this)
|
||||
setupVideoResolutionPicker(this)
|
||||
}
|
||||
|
||||
dialog = AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, if (isFrontCamera) R.string.front_camera else R.string.back_camera)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPhotoResolutionPicker(view: View) {
|
||||
val items = getFormattedResolutions(photoResolutions)
|
||||
var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex
|
||||
selectionIndex = Math.max(selectionIndex, 0)
|
||||
|
||||
view.change_resolution_photo_holder.setOnClickListener {
|
||||
RadioGroupDialog(activity, items, selectionIndex) {
|
||||
selectionIndex = it as Int
|
||||
Log.w(TAG, "setupPhotoResolutionPicker: selectionIndex=$it")
|
||||
view.change_resolution_photo.text = items[selectionIndex].title
|
||||
if (isFrontCamera) {
|
||||
config.frontPhotoResIndex = it
|
||||
} else {
|
||||
config.backPhotoResIndex = it
|
||||
}
|
||||
dialog.dismiss()
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title
|
||||
}
|
||||
|
||||
private fun setupVideoResolutionPicker(view: View) {
|
||||
val items = videoResolutions.mapIndexed { index, videoQuality ->
|
||||
val megapixels = videoQuality.megaPixels
|
||||
val aspectRatio = videoQuality.getAspectRatio(activity)
|
||||
RadioItem(index, "${videoQuality.width} x ${videoQuality.height} ($megapixels MP, $aspectRatio)")
|
||||
}
|
||||
|
||||
val videoQuality = if (isFrontCamera) config.frontVideoQuality else config.backVideoQuality
|
||||
var selectionIndex = videoResolutions.indexOf(videoQuality)
|
||||
|
||||
view.change_resolution_video_holder.setOnClickListener {
|
||||
RadioGroupDialog(activity, ArrayList(items), selectionIndex) {
|
||||
selectionIndex = it as Int
|
||||
val selectedItem = items[selectionIndex]
|
||||
val selectedQuality = videoResolutions[selectionIndex]
|
||||
view.change_resolution_video.text = selectedItem.title
|
||||
if (isFrontCamera) {
|
||||
config.frontVideoQuality = selectedQuality
|
||||
} else {
|
||||
config.backVideoQuality = selectedQuality
|
||||
}
|
||||
dialog.dismiss()
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title
|
||||
}
|
||||
|
||||
private fun getFormattedResolutions(resolutions: List<MySize>): ArrayList<RadioItem> {
|
||||
val items = ArrayList<RadioItem>(resolutions.size)
|
||||
val sorted = resolutions.sortedByDescending { it.width * it.height }
|
||||
sorted.forEachIndexed { index, size ->
|
||||
val megapixels = String.format("%.1f", (size.width * size.height.toFloat()) / 1000000)
|
||||
val aspectRatio = size.getAspectRatio(activity)
|
||||
items.add(RadioItem(index, "${size.width} x ${size.height} ($megapixels MP, $aspectRatio)"))
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.simplemobiletools.camera.extensions
|
||||
|
||||
import androidx.camera.core.AspectRatio
|
||||
import androidx.camera.video.Quality
|
||||
import com.simplemobiletools.camera.models.VideoQuality
|
||||
|
||||
fun Quality.toVideoQuality(): VideoQuality {
|
||||
return when (this) {
|
||||
Quality.UHD -> VideoQuality.UHD
|
||||
Quality.FHD -> VideoQuality.FHD
|
||||
Quality.HD -> VideoQuality.HD
|
||||
Quality.SD -> VideoQuality.SD
|
||||
else -> throw IllegalArgumentException("Unsupported quality: $this")
|
||||
}
|
||||
}
|
||||
|
||||
fun VideoQuality.toCameraXQuality(): Quality {
|
||||
return when (this) {
|
||||
VideoQuality.UHD -> Quality.UHD
|
||||
VideoQuality.FHD -> Quality.FHD
|
||||
VideoQuality.HD -> Quality.HD
|
||||
VideoQuality.SD -> Quality.SD
|
||||
}
|
||||
}
|
||||
|
||||
fun Quality.getAspectRatio(): Int {
|
||||
return when(this) {
|
||||
Quality.UHD, Quality.FHD, Quality.HD -> AspectRatio.RATIO_16_9
|
||||
Quality.SD -> AspectRatio.RATIO_4_3
|
||||
else -> throw IllegalArgumentException("Unsupported quality: $this")
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.simplemobiletools.camera.helpers
|
|||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import androidx.camera.core.CameraSelector
|
||||
import com.simplemobiletools.camera.models.VideoQuality
|
||||
import com.simplemobiletools.commons.helpers.BaseConfig
|
||||
import java.io.File
|
||||
|
||||
|
@ -62,6 +63,20 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
get() = prefs.getInt(FRONT_PHOTO_RESOLUTION_INDEX, 0)
|
||||
set(frontPhotoResIndex) = prefs.edit().putInt(FRONT_PHOTO_RESOLUTION_INDEX, frontPhotoResIndex).apply()
|
||||
|
||||
var backVideoQuality: VideoQuality
|
||||
get() {
|
||||
val backQuality = prefs.getString(BACK_VIDEO_QUALITY, VideoQuality.UHD.name)
|
||||
return VideoQuality.values().first { it.name == backQuality }
|
||||
}
|
||||
set(backVideoQuality) = prefs.edit().putString(BACK_VIDEO_QUALITY, backVideoQuality.name).apply()
|
||||
|
||||
var frontVideoQuality: VideoQuality
|
||||
get() {
|
||||
val frontQuality = prefs.getString(FRONT_VIDEO_QUALITY, VideoQuality.UHD.name)
|
||||
return VideoQuality.values().first { it.name == frontQuality }
|
||||
}
|
||||
set(frontVideoQuality) = prefs.edit().putString(FRONT_VIDEO_QUALITY, frontVideoQuality.name).apply()
|
||||
|
||||
var frontVideoResIndex: Int
|
||||
get() = prefs.getInt(FRONT_VIDEO_RESOLUTION_INDEX, 0)
|
||||
set(frontVideoResIndex) = prefs.edit().putInt(FRONT_VIDEO_RESOLUTION_INDEX, frontVideoResIndex).apply()
|
||||
|
|
|
@ -15,6 +15,8 @@ const val FLASHLIGHT_STATE = "flashlight_state"
|
|||
const val INIT_PHOTO_MODE = "init_photo_mode"
|
||||
const val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index_2"
|
||||
const val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index_2"
|
||||
const val BACK_VIDEO_QUALITY = "back_video_quality_2"
|
||||
const val FRONT_VIDEO_QUALITY = "front_video_quality_2"
|
||||
const val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index_2"
|
||||
const val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index_2"
|
||||
const val KEEP_SETTINGS_VISIBLE = "keep_settings_visible"
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package com.simplemobiletools.camera.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.ImageFormat
|
||||
import android.hardware.camera2.CameraCharacteristics
|
||||
import android.hardware.camera2.CameraManager
|
||||
import android.hardware.camera2.params.StreamConfigurationMap
|
||||
import android.media.MediaRecorder
|
||||
import android.util.Log
|
||||
import android.util.Size
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.video.Quality
|
||||
import com.simplemobiletools.camera.extensions.config
|
||||
import com.simplemobiletools.camera.extensions.toCameraXQuality
|
||||
import com.simplemobiletools.camera.models.CameraSelectorImageQualities
|
||||
import com.simplemobiletools.camera.models.CameraSelectorVideoQualities
|
||||
import com.simplemobiletools.camera.models.MySize
|
||||
|
||||
class ImageQualityManager(
|
||||
activity: AppCompatActivity,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ImageQualityManager"
|
||||
private const val MAX_VIDEO_WIDTH = 4096
|
||||
private const val MAX_VIDEO_HEIGHT = 2160
|
||||
private val CAMERA_LENS = arrayOf(CameraCharacteristics.LENS_FACING_FRONT, CameraCharacteristics.LENS_FACING_BACK)
|
||||
}
|
||||
|
||||
private val cameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||
private val config = activity.config
|
||||
private val imageQualities = mutableListOf<CameraSelectorImageQualities>()
|
||||
|
||||
fun initSupportedQualities() {
|
||||
for (cameraId in cameraManager.cameraIdList) {
|
||||
try {
|
||||
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
||||
for (lens in CAMERA_LENS) {
|
||||
if (characteristics.get(CameraCharacteristics.LENS_FACING) == lens) {
|
||||
val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
|
||||
val imageSizes = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) }
|
||||
val cameraSelector = lens.toCameraSelector()
|
||||
imageQualities.add(CameraSelectorImageQualities(cameraSelector, imageSizes))
|
||||
Log.i(TAG, "initQualities: imageSizes=$imageSizes")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Camera ID=$cameraId is not supported", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvailableVideoSizes(configMap: StreamConfigurationMap): List<Size> {
|
||||
return configMap.getOutputSizes(MediaRecorder::class.java).filter {
|
||||
it.width <= MAX_VIDEO_WIDTH && it.height <= MAX_VIDEO_HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.toCameraSelector(): CameraSelector {
|
||||
return if (this == CameraCharacteristics.LENS_FACING_FRONT) {
|
||||
CameraSelector.DEFAULT_FRONT_CAMERA
|
||||
} else {
|
||||
CameraSelector.DEFAULT_BACK_CAMERA
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserSelectedResolution(cameraSelector: CameraSelector): Size? {
|
||||
val index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex
|
||||
return imageQualities.filter { it.camSelector == cameraSelector }
|
||||
.flatMap { it.qualities }
|
||||
.sortedByDescending { it.pixels}
|
||||
.distinctBy { it.pixels }
|
||||
.map { Size(it.width, it.height) }
|
||||
.also {
|
||||
Log.i(TAG, "Resolutions: $it, index=$index")
|
||||
}
|
||||
.getOrNull(index).also {
|
||||
Log.i(TAG, "getUserSelectedResolution: $it, index=$index")
|
||||
}
|
||||
}
|
||||
|
||||
fun getSupportedResolutions(cameraSelector: CameraSelector): List<MySize> {
|
||||
return imageQualities.filter { it.camSelector == cameraSelector }
|
||||
.flatMap { it.qualities }
|
||||
.sortedByDescending { it.pixels }
|
||||
.distinctBy { it.pixels }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.simplemobiletools.camera.helpers
|
||||
|
||||
import android.util.Log
|
||||
import androidx.camera.core.Camera
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.video.Quality
|
||||
import androidx.camera.video.QualitySelector
|
||||
import com.simplemobiletools.camera.extensions.toCameraXQuality
|
||||
import com.simplemobiletools.camera.extensions.toVideoQuality
|
||||
import com.simplemobiletools.camera.models.CameraSelectorVideoQualities
|
||||
import com.simplemobiletools.camera.models.VideoQuality
|
||||
|
||||
class VideoQualityManager(private val config: Config) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "VideoQualityHelper"
|
||||
private val QUALITIES = listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
|
||||
private val CAMERA_SELECTORS = arrayOf(CameraSelector.DEFAULT_BACK_CAMERA, CameraSelector.DEFAULT_FRONT_CAMERA)
|
||||
}
|
||||
|
||||
private val videoQualities = mutableListOf<CameraSelectorVideoQualities>()
|
||||
|
||||
fun initSupportedQualities(
|
||||
cameraProvider: ProcessCameraProvider,
|
||||
camera: Camera,
|
||||
) {
|
||||
if (videoQualities.isEmpty()) {
|
||||
for (camSelector in CAMERA_SELECTORS) {
|
||||
try {
|
||||
if (cameraProvider.hasCamera(camSelector)) {
|
||||
QualitySelector.getSupportedQualities(camera.cameraInfo)
|
||||
.filter(QUALITIES::contains)
|
||||
.also { allQualities ->
|
||||
val qualities = allQualities.map { it.toVideoQuality() }
|
||||
videoQualities.add(CameraSelectorVideoQualities(camSelector, qualities))
|
||||
}
|
||||
Log.i(TAG, "bindCameraUseCases: videoQualities=$videoQualities")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Camera Face $camSelector is not supported", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserSelectedQuality(cameraSelector: CameraSelector): Quality {
|
||||
return if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) {
|
||||
config.frontVideoQuality.toCameraXQuality()
|
||||
} else {
|
||||
config.backVideoQuality.toCameraXQuality()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSupportedQualities(cameraSelector: CameraSelector): List<VideoQuality> {
|
||||
return videoQualities.filter { it.camSelector == cameraSelector }
|
||||
.flatMap { it.qualities }
|
||||
.sortedByDescending { it.pixels }
|
||||
}
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
package com.simplemobiletools.camera.implementations
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.hardware.SensorManager
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.Display
|
||||
import android.view.GestureDetector
|
||||
|
@ -36,14 +32,7 @@ import androidx.camera.core.ImageCaptureException
|
|||
import androidx.camera.core.Preview
|
||||
import androidx.camera.core.UseCase
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.video.FileDescriptorOutputOptions
|
||||
import androidx.camera.video.MediaStoreOutputOptions
|
||||
import androidx.camera.video.Quality
|
||||
import androidx.camera.video.QualitySelector
|
||||
import androidx.camera.video.Recorder
|
||||
import androidx.camera.video.Recording
|
||||
import androidx.camera.video.VideoCapture
|
||||
import androidx.camera.video.VideoRecordEvent
|
||||
import androidx.camera.video.*
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
|
@ -52,23 +41,12 @@ import androidx.lifecycle.LifecycleOwner
|
|||
import androidx.window.layout.WindowMetricsCalculator
|
||||
import com.bumptech.glide.load.ImageHeaderParser.UNKNOWN_ORIENTATION
|
||||
import com.simplemobiletools.camera.R
|
||||
import com.simplemobiletools.camera.extensions.config
|
||||
import com.simplemobiletools.camera.extensions.getRandomMediaName
|
||||
import com.simplemobiletools.camera.extensions.toAppFlashMode
|
||||
import com.simplemobiletools.camera.extensions.toCameraSelector
|
||||
import com.simplemobiletools.camera.extensions.toLensFacing
|
||||
import com.simplemobiletools.camera.helpers.CameraErrorHandler
|
||||
import com.simplemobiletools.camera.helpers.MediaOutputHelper
|
||||
import com.simplemobiletools.camera.helpers.MediaSoundHelper
|
||||
import com.simplemobiletools.camera.helpers.PinchToZoomOnScaleGestureListener
|
||||
import com.simplemobiletools.camera.dialogs.ChangeResolutionDialogX
|
||||
import com.simplemobiletools.camera.extensions.*
|
||||
import com.simplemobiletools.camera.helpers.*
|
||||
import com.simplemobiletools.camera.interfaces.MyPreview
|
||||
import com.simplemobiletools.camera.models.MediaOutput
|
||||
import com.simplemobiletools.commons.extensions.hasPermission
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_RECORD_AUDIO
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
@ -98,6 +76,8 @@ class CameraXPreview(
|
|||
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
private val mediaSoundHelper = MediaSoundHelper()
|
||||
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
|
||||
private val videoQualityManager = VideoQualityManager(config)
|
||||
private val imageQualityManager = ImageQualityManager(activity)
|
||||
|
||||
private val orientationEventListener = object : OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
|
||||
@SuppressLint("RestrictedApi")
|
||||
|
@ -146,6 +126,7 @@ class CameraXPreview(
|
|||
|
||||
private fun startCamera(switching: Boolean = false) {
|
||||
Log.i(TAG, "startCamera: ")
|
||||
imageQualityManager.initSupportedQualities()
|
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(activity)
|
||||
cameraProviderFuture.addListener({
|
||||
try {
|
||||
|
@ -163,7 +144,12 @@ class CameraXPreview(
|
|||
private fun bindCameraUseCases() {
|
||||
val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.")
|
||||
val metrics = windowMetricsCalculator.computeCurrentWindowMetrics(activity).bounds
|
||||
val aspectRatio = aspectRatio(metrics.width(), metrics.height())
|
||||
val aspectRatio = if (isPhotoCapture) {
|
||||
aspectRatio(metrics.width(), metrics.height())
|
||||
} else {
|
||||
val selectedQuality = videoQualityManager.getUserSelectedQuality(cameraSelector)
|
||||
selectedQuality.getAspectRatio()
|
||||
}
|
||||
val rotation = previewView.display.rotation
|
||||
|
||||
preview = buildPreview(aspectRatio, rotation)
|
||||
|
@ -174,7 +160,10 @@ class CameraXPreview(
|
|||
cameraSelector,
|
||||
preview,
|
||||
captureUseCase,
|
||||
)
|
||||
).also {
|
||||
videoQualityManager.initSupportedQualities(cameraProvider, it)
|
||||
}
|
||||
|
||||
preview?.setSurfaceProvider(previewView.surfaceProvider)
|
||||
setupZoomAndFocus()
|
||||
}
|
||||
|
@ -220,22 +209,30 @@ class CameraXPreview(
|
|||
.setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
|
||||
.setFlashMode(flashMode)
|
||||
.setJpegQuality(config.photoQuality)
|
||||
.setTargetAspectRatio(aspectRatio)
|
||||
.setTargetRotation(rotation)
|
||||
.apply {
|
||||
imageQualityManager.getUserSelectedResolution(cameraSelector)?.let { resolution ->
|
||||
Log.i(TAG, "buildImageCapture: resolution=$resolution")
|
||||
setTargetResolution(resolution)
|
||||
} ?: setTargetAspectRatio(aspectRatio)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildPreview(aspectRatio: Int, rotation: Int): Preview {
|
||||
return Preview.Builder()
|
||||
.setTargetAspectRatio(aspectRatio)
|
||||
.setTargetRotation(rotation)
|
||||
.setTargetAspectRatio(aspectRatio)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildVideoCapture(): VideoCapture<Recorder> {
|
||||
val qualitySelector = QualitySelector.from(
|
||||
videoQualityManager.getUserSelectedQuality(cameraSelector),
|
||||
FallbackStrategy.lowerQualityOrHigherThan(Quality.SD),
|
||||
)
|
||||
val recorder = Recorder.Builder()
|
||||
//TODO: user control for quality
|
||||
.setQualitySelector(QualitySelector.from(Quality.FHD))
|
||||
.setQualitySelector(qualitySelector)
|
||||
.build()
|
||||
return VideoCapture.withOutput(recorder)
|
||||
}
|
||||
|
@ -305,7 +302,18 @@ class CameraXPreview(
|
|||
}
|
||||
|
||||
override fun showChangeResolutionDialog() {
|
||||
|
||||
val oldQuality = videoQualityManager.getUserSelectedQuality(cameraSelector)
|
||||
ChangeResolutionDialogX(
|
||||
activity,
|
||||
isFrontCameraInUse(),
|
||||
imageQualityManager.getSupportedResolutions(cameraSelector),
|
||||
videoQualityManager.getSupportedQualities(cameraSelector)
|
||||
) {
|
||||
if (oldQuality != videoQualityManager.getUserSelectedQuality(cameraSelector)) {
|
||||
currentRecording?.stop()
|
||||
}
|
||||
startCamera()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toggleFrontBackCamera() {
|
||||
|
@ -344,7 +352,6 @@ class CameraXPreview(
|
|||
}
|
||||
|
||||
override fun tryTakePicture() {
|
||||
Log.i(TAG, "captureImage: ")
|
||||
val imageCapture = imageCapture ?: throw IllegalStateException("Camera initialization failed.")
|
||||
|
||||
val metadata = Metadata().apply {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.simplemobiletools.camera.models
|
||||
|
||||
import androidx.camera.core.CameraSelector
|
||||
|
||||
data class CameraSelectorImageQualities(
|
||||
val camSelector: CameraSelector,
|
||||
val qualities: List<MySize>,
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
package com.simplemobiletools.camera.models
|
||||
|
||||
import androidx.camera.core.CameraSelector
|
||||
|
||||
data class CameraSelectorVideoQualities(
|
||||
val camSelector: CameraSelector,
|
||||
val qualities: List<VideoQuality>,
|
||||
)
|
|
@ -6,6 +6,7 @@ import com.simplemobiletools.camera.R
|
|||
|
||||
data class MySize(val width: Int, val height: Int) {
|
||||
val ratio = width / height.toFloat()
|
||||
val pixels: Int = width * height
|
||||
fun isSixteenToNine() = ratio == 16 / 9f
|
||||
|
||||
private fun isFiveToThree() = ratio == 5 / 3f
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package com.simplemobiletools.camera.models
|
||||
|
||||
import android.content.Context
|
||||
import com.simplemobiletools.camera.R
|
||||
|
||||
enum class VideoQuality(val width: Int, val height: Int) {
|
||||
UHD(3840, 2160),
|
||||
FHD(1920, 1080),
|
||||
HD(1280, 720),
|
||||
SD(720, 480);
|
||||
|
||||
|
||||
val pixels: Int = width * height
|
||||
|
||||
val megaPixels: String = String.format("%.1f", (width * height.toFloat()) / VideoQuality.ONE_MEGA_PIXELS)
|
||||
|
||||
val ratio = width / height.toFloat()
|
||||
|
||||
private fun isSixteenToNine() = ratio == 16 / 9f
|
||||
|
||||
private fun isFiveToThree() = ratio == 5 / 3f
|
||||
|
||||
private fun isFourToThree() = ratio == 4 / 3f
|
||||
|
||||
private fun isTwoToOne() = ratio == 2f
|
||||
|
||||
private fun isThreeToFour() = ratio == 3 / 4f
|
||||
|
||||
private fun isThreeToTwo() = ratio == 3 / 2f
|
||||
|
||||
private fun isSixToFive() = ratio == 6 / 5f
|
||||
|
||||
private fun isNineteenToNine() = ratio == 19 / 9f
|
||||
|
||||
private fun isNineteenToEight() = ratio == 19 / 8f
|
||||
|
||||
private fun isOneNineToOne() = ratio == 1.9f
|
||||
|
||||
private fun isSquare() = width == height
|
||||
|
||||
fun getAspectRatio(context: Context) = when {
|
||||
isSixteenToNine() -> "16:9"
|
||||
isFiveToThree() -> "5:3"
|
||||
isFourToThree() -> "4:3"
|
||||
isThreeToFour() -> "3:4"
|
||||
isThreeToTwo() -> "3:2"
|
||||
isSixToFive() -> "6:5"
|
||||
isOneNineToOne() -> "1.9:1"
|
||||
isNineteenToNine() -> "19:9"
|
||||
isNineteenToEight() -> "19:8"
|
||||
isSquare() -> "1:1"
|
||||
isTwoToOne() -> "2:1"
|
||||
else -> context.resources.getString(R.string.other)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ONE_MEGA_PIXELS = 1000000
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue