feat: Respect format's aspect ratio in Preview and add resizeMode
prop (#1817)
* feat(preview): respect format's aspect ratio * fix: code guidelines and previewSize in PreviewView * feat: add resizeMode 'cover' and 'contain' on Android
This commit is contained in:
parent
9add0eb571
commit
c0b80b342b
@ -7,9 +7,9 @@ import android.content.pm.PackageManager
|
|||||||
import android.hardware.camera2.CameraManager
|
import android.hardware.camera2.CameraManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
|
import android.view.Gravity
|
||||||
import android.view.ScaleGestureDetector
|
import android.view.ScaleGestureDetector
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.View
|
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
@ -23,6 +23,10 @@ import com.mrousavy.camera.parsers.Orientation
|
|||||||
import com.mrousavy.camera.parsers.PixelFormat
|
import com.mrousavy.camera.parsers.PixelFormat
|
||||||
import com.mrousavy.camera.parsers.Torch
|
import com.mrousavy.camera.parsers.Torch
|
||||||
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
||||||
|
import com.mrousavy.camera.extensions.bigger
|
||||||
|
import com.mrousavy.camera.extensions.getPreviewTargetSize
|
||||||
|
import com.mrousavy.camera.extensions.smaller
|
||||||
|
import com.mrousavy.camera.parsers.ResizeMode
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -41,7 +45,7 @@ class CameraView(context: Context) : FrameLayout(context) {
|
|||||||
companion object {
|
companion object {
|
||||||
const val TAG = "CameraView"
|
const val TAG = "CameraView"
|
||||||
|
|
||||||
private val propsThatRequirePreviewReconfiguration = arrayListOf("cameraId")
|
private val propsThatRequirePreviewReconfiguration = arrayListOf("cameraId", "format", "resizeMode")
|
||||||
private val propsThatRequireSessionReconfiguration =
|
private val propsThatRequireSessionReconfiguration =
|
||||||
arrayListOf("cameraId", "format", "photo", "video", "enableFrameProcessor", "pixelFormat")
|
arrayListOf("cameraId", "format", "photo", "video", "enableFrameProcessor", "pixelFormat")
|
||||||
private val propsThatRequireFormatReconfiguration = arrayListOf("fps", "hdr", "videoStabilizationMode", "lowLightBoost")
|
private val propsThatRequireFormatReconfiguration = arrayListOf("fps", "hdr", "videoStabilizationMode", "lowLightBoost")
|
||||||
@ -63,6 +67,7 @@ class CameraView(context: Context) : FrameLayout(context) {
|
|||||||
|
|
||||||
// props that require format reconfiguring
|
// props that require format reconfiguring
|
||||||
var format: ReadableMap? = null
|
var format: ReadableMap? = null
|
||||||
|
var resizeMode: ResizeMode = ResizeMode.COVER
|
||||||
var fps: Int? = null
|
var fps: Int? = null
|
||||||
var videoStabilizationMode: VideoStabilizationMode? = null
|
var videoStabilizationMode: VideoStabilizationMode? = null
|
||||||
var hdr: Boolean? = null // nullable bool
|
var hdr: Boolean? = null // nullable bool
|
||||||
@ -81,7 +86,7 @@ class CameraView(context: Context) : FrameLayout(context) {
|
|||||||
|
|
||||||
// session
|
// session
|
||||||
internal val cameraSession: CameraSession
|
internal val cameraSession: CameraSession
|
||||||
private var previewView: View? = null
|
private var previewView: PreviewView? = null
|
||||||
private var previewSurface: Surface? = null
|
private var previewSurface: Surface? = null
|
||||||
|
|
||||||
internal var frameProcessor: FrameProcessor? = null
|
internal var frameProcessor: FrameProcessor? = null
|
||||||
@ -115,16 +120,30 @@ class CameraView(context: Context) : FrameLayout(context) {
|
|||||||
updateLifecycle()
|
updateLifecycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPreviewTargetSize(): Size {
|
||||||
|
val cameraId = cameraId ?: throw NoCameraDeviceError()
|
||||||
|
|
||||||
|
val format = format
|
||||||
|
val targetPreviewSize = if (format != null) Size(format.getInt("videoWidth"), format.getInt("videoHeight")) else null
|
||||||
|
val formatAspectRatio = if (targetPreviewSize != null) targetPreviewSize.bigger.toDouble() / targetPreviewSize.smaller else null
|
||||||
|
|
||||||
|
return this.cameraManager.getCameraCharacteristics(cameraId).getPreviewTargetSize(formatAspectRatio)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupPreviewView() {
|
private fun setupPreviewView() {
|
||||||
removeView(previewView)
|
removeView(previewView)
|
||||||
this.previewSurface = null
|
this.previewSurface = null
|
||||||
|
|
||||||
val cameraId = cameraId ?: return
|
if (cameraId == null) return
|
||||||
val previewView = PreviewView(context, cameraManager, cameraId) { surface ->
|
|
||||||
|
val previewView = PreviewView(context, this.getPreviewTargetSize(), resizeMode) { surface ->
|
||||||
previewSurface = surface
|
previewSurface = surface
|
||||||
configureSession()
|
configureSession()
|
||||||
}
|
}
|
||||||
previewView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
previewView.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
Gravity.CENTER)
|
||||||
addView(previewView)
|
addView(previewView)
|
||||||
this.previewView = previewView
|
this.previewView = previewView
|
||||||
}
|
}
|
||||||
@ -183,7 +202,7 @@ class CameraView(context: Context) : FrameLayout(context) {
|
|||||||
// TODO: Allow previewSurface to be null/none
|
// TODO: Allow previewSurface to be null/none
|
||||||
val previewSurface = previewSurface ?: return
|
val previewSurface = previewSurface ?: return
|
||||||
|
|
||||||
val previewOutput = CameraOutputs.PreviewOutput(previewSurface)
|
val previewOutput = CameraOutputs.PreviewOutput(previewSurface, previewView?.targetSize)
|
||||||
val photoOutput = if (photo == true) {
|
val photoOutput = if (photo == true) {
|
||||||
CameraOutputs.PhotoOutput(targetPhotoSize)
|
CameraOutputs.PhotoOutput(targetPhotoSize)
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@ import com.facebook.react.uimanager.ViewGroupManager
|
|||||||
import com.facebook.react.uimanager.annotations.ReactProp
|
import com.facebook.react.uimanager.annotations.ReactProp
|
||||||
import com.mrousavy.camera.parsers.Orientation
|
import com.mrousavy.camera.parsers.Orientation
|
||||||
import com.mrousavy.camera.parsers.PixelFormat
|
import com.mrousavy.camera.parsers.PixelFormat
|
||||||
|
import com.mrousavy.camera.parsers.ResizeMode
|
||||||
import com.mrousavy.camera.parsers.Torch
|
import com.mrousavy.camera.parsers.Torch
|
||||||
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
||||||
|
|
||||||
@ -128,6 +129,14 @@ class CameraViewManager : ViewGroupManager<CameraView>() {
|
|||||||
view.format = format
|
view.format = format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "resizeMode")
|
||||||
|
fun setResizeMode(view: CameraView, resizeMode: String) {
|
||||||
|
val newMode = ResizeMode.fromUnionValue(resizeMode)
|
||||||
|
if (view.resizeMode != newMode)
|
||||||
|
addChangedPropToTransaction(view, "resizeMode")
|
||||||
|
view.resizeMode = newMode
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Change when TurboModules release.
|
// TODO: Change when TurboModules release.
|
||||||
// We're treating -1 as "null" here, because when I make the fps parameter
|
// We're treating -1 as "null" here, because when I make the fps parameter
|
||||||
// of type "Int?" the react bridge throws an error.
|
// of type "Int?" the react bridge throws an error.
|
||||||
|
@ -2,30 +2,24 @@ package com.mrousavy.camera.core
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.camera2.CameraManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import com.mrousavy.camera.extensions.getPreviewSize
|
import com.mrousavy.camera.extensions.bigger
|
||||||
|
import com.mrousavy.camera.extensions.smaller
|
||||||
|
import com.mrousavy.camera.parsers.ResizeMode
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
class PreviewView(
|
class PreviewView(
|
||||||
context: Context,
|
context: Context,
|
||||||
cameraManager: CameraManager,
|
val targetSize: Size,
|
||||||
cameraId: String,
|
private val resizeMode: ResizeMode,
|
||||||
private val onSurfaceChanged: (surface: Surface?) -> Unit
|
private val onSurfaceChanged: (surface: Surface?) -> Unit): SurfaceView(context) {
|
||||||
) : SurfaceView(context) {
|
|
||||||
private val targetSize: Size
|
|
||||||
private val aspectRatio: Float
|
|
||||||
get() = targetSize.width.toFloat() / targetSize.height.toFloat()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
|
||||||
targetSize = characteristics.getPreviewSize()
|
|
||||||
|
|
||||||
Log.i(TAG, "Using Preview Size ${targetSize.width} x ${targetSize.height}.")
|
Log.i(TAG, "Using Preview Size ${targetSize.width} x ${targetSize.height}.")
|
||||||
holder.setFixedSize(targetSize.width, targetSize.height)
|
holder.setFixedSize(targetSize.width, targetSize.height)
|
||||||
holder.addCallback(object : SurfaceHolder.Callback {
|
holder.addCallback(object : SurfaceHolder.Callback {
|
||||||
@ -45,26 +39,54 @@ class PreviewView(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
private fun coverSize(contentSize: Size, containerWidth: Int, containerHeight: Int): Size {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
val contentAspectRatio = contentSize.height.toDouble() / contentSize.width
|
||||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
val containerAspectRatio = containerWidth.toDouble() / containerHeight
|
||||||
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
||||||
Log.d(TAG, "onMeasure($width, $height)")
|
|
||||||
|
|
||||||
// Performs center-crop transformation of the camera frames
|
Log.d(TAG, "coverSize :: $contentSize ($contentAspectRatio), ${containerWidth}x${containerHeight} ($containerAspectRatio)")
|
||||||
val newWidth: Int
|
|
||||||
val newHeight: Int
|
return if (contentAspectRatio > containerAspectRatio) {
|
||||||
val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
|
// Scale by width to cover height
|
||||||
if (width < height * actualRatio) {
|
val scaledWidth = containerHeight * contentAspectRatio
|
||||||
newHeight = height
|
Size(scaledWidth.roundToInt(), containerHeight)
|
||||||
newWidth = (height * actualRatio).roundToInt()
|
|
||||||
} else {
|
} else {
|
||||||
newWidth = width
|
// Scale by height to cover width
|
||||||
newHeight = (width / actualRatio).roundToInt()
|
val scaledHeight = containerWidth / contentAspectRatio
|
||||||
|
Size(containerWidth, scaledHeight.roundToInt())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
|
private fun containSize(contentSize: Size, containerWidth: Int, containerHeight: Int): Size {
|
||||||
setMeasuredDimension(newWidth, newHeight)
|
val contentAspectRatio = contentSize.height.toDouble() / contentSize.width
|
||||||
|
val containerAspectRatio = containerWidth.toDouble() / containerHeight
|
||||||
|
|
||||||
|
Log.d(TAG, "containSize :: $contentSize ($contentAspectRatio), ${containerWidth}x${containerHeight} ($containerAspectRatio)")
|
||||||
|
|
||||||
|
return if (contentAspectRatio > containerAspectRatio) {
|
||||||
|
// Scale by height to fit within width
|
||||||
|
val scaledHeight = containerWidth / contentAspectRatio
|
||||||
|
return Size(containerWidth, scaledHeight.roundToInt())
|
||||||
|
} else {
|
||||||
|
// Scale by width to fit within height
|
||||||
|
val scaledWidth = containerHeight * contentAspectRatio
|
||||||
|
return Size(scaledWidth.roundToInt(), containerHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
val viewWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val viewHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
Log.d(TAG, "onMeasure($viewWidth, $viewHeight)")
|
||||||
|
|
||||||
|
val fittedSize = when (resizeMode) {
|
||||||
|
ResizeMode.COVER -> this.coverSize(targetSize, viewWidth, viewHeight)
|
||||||
|
ResizeMode.CONTAIN -> this.containSize(targetSize, viewWidth, viewHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Fitted dimensions set: $fittedSize")
|
||||||
|
setMeasuredDimension(fittedSize.width, fittedSize.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -12,8 +12,10 @@ import com.mrousavy.camera.CameraQueues
|
|||||||
import com.mrousavy.camera.core.VideoPipeline
|
import com.mrousavy.camera.core.VideoPipeline
|
||||||
import com.mrousavy.camera.extensions.closestToOrMax
|
import com.mrousavy.camera.extensions.closestToOrMax
|
||||||
import com.mrousavy.camera.extensions.getPhotoSizes
|
import com.mrousavy.camera.extensions.getPhotoSizes
|
||||||
import com.mrousavy.camera.extensions.getPreviewSize
|
import com.mrousavy.camera.extensions.getPreviewTargetSize
|
||||||
import com.mrousavy.camera.extensions.getVideoSizes
|
import com.mrousavy.camera.extensions.getVideoSizes
|
||||||
|
import com.mrousavy.camera.extensions.bigger
|
||||||
|
import com.mrousavy.camera.extensions.smaller
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
class CameraOutputs(
|
class CameraOutputs(
|
||||||
@ -30,7 +32,7 @@ class CameraOutputs(
|
|||||||
const val PHOTO_OUTPUT_BUFFER_SIZE = 3
|
const val PHOTO_OUTPUT_BUFFER_SIZE = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PreviewOutput(val surface: Surface)
|
data class PreviewOutput(val surface: Surface, val targetSize: Size? = null)
|
||||||
data class PhotoOutput(val targetSize: Size? = null, val format: Int = ImageFormat.JPEG)
|
data class PhotoOutput(val targetSize: Size? = null, val format: Int = ImageFormat.JPEG)
|
||||||
data class VideoOutput(
|
data class VideoOutput(
|
||||||
val targetSize: Size? = null,
|
val targetSize: Size? = null,
|
||||||
@ -63,6 +65,7 @@ class CameraOutputs(
|
|||||||
if (other !is CameraOutputs) return false
|
if (other !is CameraOutputs) return false
|
||||||
return this.cameraId == other.cameraId &&
|
return this.cameraId == other.cameraId &&
|
||||||
this.preview?.surface == other.preview?.surface &&
|
this.preview?.surface == other.preview?.surface &&
|
||||||
|
this.preview?.targetSize == other.preview?.targetSize &&
|
||||||
this.photo?.targetSize == other.photo?.targetSize &&
|
this.photo?.targetSize == other.photo?.targetSize &&
|
||||||
this.photo?.format == other.photo?.format &&
|
this.photo?.format == other.photo?.format &&
|
||||||
this.video?.enableRecording == other.video?.enableRecording &&
|
this.video?.enableRecording == other.video?.enableRecording &&
|
||||||
@ -101,7 +104,11 @@ class CameraOutputs(
|
|||||||
// Preview output: Low resolution repeating images (SurfaceView)
|
// Preview output: Low resolution repeating images (SurfaceView)
|
||||||
if (preview != null) {
|
if (preview != null) {
|
||||||
Log.i(TAG, "Adding native preview view output.")
|
Log.i(TAG, "Adding native preview view output.")
|
||||||
previewOutput = SurfaceOutput(preview.surface, characteristics.getPreviewSize(), SurfaceOutput.OutputType.PREVIEW)
|
val previewSizeAspectRatio = if (preview.targetSize != null) preview.targetSize.bigger.toDouble() / preview.targetSize.smaller else null
|
||||||
|
previewOutput = SurfaceOutput(
|
||||||
|
preview.surface,
|
||||||
|
characteristics.getPreviewTargetSize(previewSizeAspectRatio),
|
||||||
|
SurfaceOutput.OutputType.PREVIEW)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Photo output: High quality still images (takePhoto())
|
// Photo output: High quality still images (takePhoto())
|
||||||
|
@ -1,34 +1,9 @@
|
|||||||
package com.mrousavy.camera.extensions
|
package com.mrousavy.camera.extensions
|
||||||
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.hardware.camera2.CameraCharacteristics
|
import android.hardware.camera2.CameraCharacteristics
|
||||||
import android.media.CamcorderProfile
|
import android.media.CamcorderProfile
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import android.view.SurfaceHolder
|
|
||||||
import android.view.SurfaceView
|
|
||||||
|
|
||||||
private fun getMaximumPreviewSize(): Size {
|
|
||||||
// See https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
|
|
||||||
// According to the Android Developer documentation, PREVIEW streams can have a resolution
|
|
||||||
// of up to the phone's display's resolution, with a maximum of 1920x1080.
|
|
||||||
val display1080p = Size(1920, 1080)
|
|
||||||
val displaySize = Size(Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
|
||||||
val isHighResScreen = displaySize.bigger >= display1080p.bigger || displaySize.smaller >= display1080p.smaller
|
|
||||||
Log.i("PreviewSize", "Phone has a ${displaySize.width} x ${displaySize.height} screen.")
|
|
||||||
return if (isHighResScreen) display1080p else displaySize
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum Preview Resolution this device is capable of streaming at. (For [SurfaceView])
|
|
||||||
*/
|
|
||||||
fun CameraCharacteristics.getPreviewSize(): Size {
|
|
||||||
val config = this.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
|
||||||
val previewSize = getMaximumPreviewSize()
|
|
||||||
val outputSizes = config.getOutputSizes(SurfaceHolder::class.java).sortedByDescending { it.width * it.height }
|
|
||||||
return outputSizes.first { it.bigger <= previewSize.bigger && it.smaller <= previewSize.smaller }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getMaximumVideoSize(cameraId: String): Size? {
|
private fun getMaximumVideoSize(cameraId: String): Size? {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.mrousavy.camera.extensions
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.hardware.camera2.CameraCharacteristics
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.Size
|
||||||
|
import android.view.SurfaceHolder
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
private fun getMaximumPreviewSize(): Size {
|
||||||
|
// See https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
|
||||||
|
// According to the Android Developer documentation, PREVIEW streams can have a resolution
|
||||||
|
// of up to the phone's display's resolution, with a maximum of 1920x1080.
|
||||||
|
val display1080p = Size(1920, 1080)
|
||||||
|
val displaySize = Size(Resources.getSystem().displayMetrics.widthPixels,
|
||||||
|
Resources.getSystem().displayMetrics.heightPixels)
|
||||||
|
val isHighResScreen = displaySize.bigger >= display1080p.bigger || displaySize.smaller >= display1080p.smaller
|
||||||
|
|
||||||
|
return if (isHighResScreen) display1080p else displaySize
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CameraCharacteristics.getPreviewSizeFromAspectRatio(aspectRatio: Double): Size {
|
||||||
|
val config = this.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
||||||
|
val maximumPreviewSize = getMaximumPreviewSize()
|
||||||
|
val outputSizes = config.getOutputSizes(SurfaceHolder::class.java)
|
||||||
|
.sortedByDescending { it.width * it.height }
|
||||||
|
.sortedBy { abs(aspectRatio - (it.bigger.toDouble() / it.smaller)) }
|
||||||
|
|
||||||
|
return outputSizes.first { it.bigger <= maximumPreviewSize.bigger && it.smaller <= maximumPreviewSize.smaller }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CameraCharacteristics.getAutomaticPreviewSize(): Size {
|
||||||
|
val config = this.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
||||||
|
val maximumPreviewSize = getMaximumPreviewSize()
|
||||||
|
val outputSizes = config.getOutputSizes(SurfaceHolder::class.java)
|
||||||
|
.sortedByDescending { it.width * it.height }
|
||||||
|
|
||||||
|
return outputSizes.first { it.bigger <= maximumPreviewSize.bigger && it.smaller <= maximumPreviewSize.smaller }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CameraCharacteristics.getPreviewTargetSize(aspectRatio: Double?): Size {
|
||||||
|
return if (aspectRatio != null) {
|
||||||
|
getPreviewSizeFromAspectRatio(aspectRatio)
|
||||||
|
} else {
|
||||||
|
getAutomaticPreviewSize()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.mrousavy.camera.parsers
|
||||||
|
|
||||||
|
enum class ResizeMode(override val unionValue: String): JSUnionValue {
|
||||||
|
COVER("cover"),
|
||||||
|
CONTAIN("contain");
|
||||||
|
|
||||||
|
companion object: JSUnionValue.Companion<ResizeMode> {
|
||||||
|
override fun fromUnionValue(unionValue: String?): ResizeMode {
|
||||||
|
return when (unionValue) {
|
||||||
|
"cover" -> COVER
|
||||||
|
"contain" -> CONTAIN
|
||||||
|
else -> COVER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -111,6 +111,10 @@ export interface CameraProps extends ViewProps {
|
|||||||
* Selects a given format. By default, the best matching format is chosen.
|
* Selects a given format. By default, the best matching format is chosen.
|
||||||
*/
|
*/
|
||||||
format?: CameraDeviceFormat;
|
format?: CameraDeviceFormat;
|
||||||
|
/**
|
||||||
|
* Specify how you want the preview to fit the container it's in
|
||||||
|
*/
|
||||||
|
resizeMode?: 'cover' | 'contain';
|
||||||
/**
|
/**
|
||||||
* Specify the frames per second this camera should use. Make sure the given `format` includes a frame rate range with the given `fps`.
|
* Specify the frames per second this camera should use. Make sure the given `format` includes a frame rate range with the given `fps`.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user