fix: Clean up codebase
This commit is contained in:
parent
07ba0e1a41
commit
e1b04088c6
@ -221,13 +221,14 @@ class CameraSession(private val context: Context,
|
|||||||
suspend fun startRecording(enableAudio: Boolean,
|
suspend fun startRecording(enableAudio: Boolean,
|
||||||
codec: VideoCodec,
|
codec: VideoCodec,
|
||||||
fileType: VideoFileType,
|
fileType: VideoFileType,
|
||||||
callback: (video: RecordingSession.Video) -> Unit) {
|
callback: (video: RecordingSession.Video) -> Unit,
|
||||||
|
onError: (error: RecorderError) -> Unit) {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
if (recording != null) throw RecordingInProgressError()
|
if (recording != null) throw RecordingInProgressError()
|
||||||
val outputs = outputs ?: throw CameraNotReadyError()
|
val outputs = outputs ?: throw CameraNotReadyError()
|
||||||
val videoOutput = outputs.videoOutput ?: throw VideoNotEnabledError()
|
val videoOutput = outputs.videoOutput ?: throw VideoNotEnabledError()
|
||||||
|
|
||||||
val recording = RecordingSession(context, enableAudio, videoOutput.size, fps, codec, orientation, fileType, callback)
|
val recording = RecordingSession(context, enableAudio, videoOutput.size, fps, codec, orientation, fileType, callback, onError)
|
||||||
recording.start()
|
recording.start()
|
||||||
this.recording = recording
|
this.recording = recording
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import com.mrousavy.camera.parsers.Torch
|
|||||||
import com.mrousavy.camera.parsers.VideoCodec
|
import com.mrousavy.camera.parsers.VideoCodec
|
||||||
import com.mrousavy.camera.parsers.VideoFileType
|
import com.mrousavy.camera.parsers.VideoFileType
|
||||||
import com.mrousavy.camera.utils.RecordingSession
|
import com.mrousavy.camera.utils.RecordingSession
|
||||||
|
import com.mrousavy.camera.utils.makeErrorMap
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Callback) {
|
suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Callback) {
|
||||||
@ -39,7 +40,11 @@ suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Ca
|
|||||||
map.putDouble("duration", video.durationMs.toDouble() / 1000.0)
|
map.putDouble("duration", video.durationMs.toDouble() / 1000.0)
|
||||||
onRecordCallback(map, null)
|
onRecordCallback(map, null)
|
||||||
}
|
}
|
||||||
cameraSession.startRecording(audio == true, codec, fileType, callback)
|
val onError = { error: RecorderError ->
|
||||||
|
val errorMap = makeErrorMap(error.code, error.message)
|
||||||
|
onRecordCallback(null, errorMap)
|
||||||
|
}
|
||||||
|
cameraSession.startRecording(audio == true, codec, fileType, callback, onError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
|
@ -30,7 +30,6 @@ suspend fun CameraView.takePhoto(optionsMap: ReadableMap): WritableMap {
|
|||||||
val flash = options["flash"] as? String ?: "off"
|
val flash = options["flash"] as? String ?: "off"
|
||||||
val enableAutoRedEyeReduction = options["enableAutoRedEyeReduction"] == true
|
val enableAutoRedEyeReduction = options["enableAutoRedEyeReduction"] == true
|
||||||
val enableAutoStabilization = options["enableAutoStabilization"] == true
|
val enableAutoStabilization = options["enableAutoStabilization"] == true
|
||||||
val skipMetadata = options["skipMetadata"] == true
|
|
||||||
|
|
||||||
val flashMode = Flash.fromUnionValue(flash)
|
val flashMode = Flash.fromUnionValue(flash)
|
||||||
val qualityPrioritizationMode = QualityPrioritization.fromUnionValue(qualityPrioritization)
|
val qualityPrioritizationMode = QualityPrioritization.fromUnionValue(qualityPrioritization)
|
||||||
@ -58,8 +57,6 @@ suspend fun CameraView.takePhoto(optionsMap: ReadableMap): WritableMap {
|
|||||||
map.putBoolean("isRawPhoto", photo.format == ImageFormat.RAW_SENSOR)
|
map.putBoolean("isRawPhoto", photo.format == ImageFormat.RAW_SENSOR)
|
||||||
map.putBoolean("isMirrored", photo.isMirrored)
|
map.putBoolean("isMirrored", photo.isMirrored)
|
||||||
|
|
||||||
// TODO: Add metadata prop to resulting photo
|
|
||||||
|
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.mrousavy.camera
|
package com.mrousavy.camera
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext
|
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
import com.facebook.react.common.MapBuilder
|
import com.facebook.react.common.MapBuilder
|
||||||
import com.facebook.react.uimanager.ThemedReactContext
|
import com.facebook.react.uimanager.ThemedReactContext
|
||||||
@ -13,7 +12,7 @@ import com.mrousavy.camera.parsers.Torch
|
|||||||
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
import com.mrousavy.camera.parsers.VideoStabilizationMode
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class CameraViewManager(reactContext: ReactApplicationContext) : ViewGroupManager<CameraView>() {
|
class CameraViewManager() : ViewGroupManager<CameraView>() {
|
||||||
|
|
||||||
public override fun createViewInstance(context: ThemedReactContext): CameraView {
|
public override fun createViewInstance(context: ThemedReactContext): CameraView {
|
||||||
return CameraView(context)
|
return CameraView(context)
|
||||||
|
@ -40,17 +40,11 @@ class NoCameraDeviceError : CameraError("device", "no-device", "No device was se
|
|||||||
class NoFlashAvailableError : CameraError("device", "flash-unavailable", "The Camera Device does not have a flash unit! Make sure you select a device where `hasFlash`/`hasTorch` is true!")
|
class NoFlashAvailableError : CameraError("device", "flash-unavailable", "The Camera Device does not have a flash unit! Make sure you select a device where `hasFlash`/`hasTorch` is true!")
|
||||||
class PixelFormatNotSupportedError(format: String) : CameraError("device", "pixel-format-not-supported", "The pixelFormat $format is not supported on the given Camera Device!")
|
class PixelFormatNotSupportedError(format: String) : CameraError("device", "pixel-format-not-supported", "The pixelFormat $format is not supported on the given Camera Device!")
|
||||||
|
|
||||||
class FpsNotContainedInFormatError(fps: Int) : CameraError("format", "invalid-fps", "The given format cannot run at $fps FPS! Make sure your FPS is lower than `format.maxFps` but higher than `format.minFps`.")
|
|
||||||
class HdrNotContainedInFormatError : CameraError(
|
class HdrNotContainedInFormatError : CameraError(
|
||||||
"format", "invalid-hdr",
|
"format", "invalid-hdr",
|
||||||
"The currently selected format does not support HDR capture! " +
|
"The currently selected format does not support HDR capture! " +
|
||||||
"Make sure you select a format which includes `supportsPhotoHDR`!"
|
"Make sure you select a format which includes `supportsPhotoHDR`!"
|
||||||
)
|
)
|
||||||
class LowLightBoostNotContainedInFormatError : CameraError(
|
|
||||||
"format", "invalid-low-light-boost",
|
|
||||||
"The currently selected format does not support low-light boost (night mode)! " +
|
|
||||||
"Make sure you select a format which includes `supportsLowLightBoost`."
|
|
||||||
)
|
|
||||||
|
|
||||||
class CameraNotReadyError : CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
class CameraNotReadyError : CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
||||||
class CameraCannotBeOpenedError(cameraId: String, error: CameraDeviceError) : CameraError("session", "camera-cannot-be-opened", "The given Camera device (id: $cameraId) could not be opened! Error: $error")
|
class CameraCannotBeOpenedError(cameraId: String, error: CameraDeviceError) : CameraError("session", "camera-cannot-be-opened", "The given Camera device (id: $cameraId) could not be opened! Error: $error")
|
||||||
@ -62,48 +56,7 @@ class PhotoNotEnabledError : CameraError("capture", "photo-not-enabled", "Photo
|
|||||||
class CaptureAbortedError(wasImageCaptured: Boolean) : CameraError("capture", "aborted", "The image capture was aborted! Was Image captured: $wasImageCaptured")
|
class CaptureAbortedError(wasImageCaptured: Boolean) : CameraError("capture", "aborted", "The image capture was aborted! Was Image captured: $wasImageCaptured")
|
||||||
class UnknownCaptureError(wasImageCaptured: Boolean) : CameraError("capture", "unknown", "An unknown error occurred while trying to capture an Image! Was Image captured: $wasImageCaptured")
|
class UnknownCaptureError(wasImageCaptured: Boolean) : CameraError("capture", "unknown", "An unknown error occurred while trying to capture an Image! Was Image captured: $wasImageCaptured")
|
||||||
|
|
||||||
class VideoEncoderError(cause: Throwable?) : CameraError("capture", "encoder-error", "The recording failed while encoding.\n" +
|
class RecorderError(name: String, extra: Int) : CameraError("capture", "recorder-error", "An error occured while recording a video! $name $extra")
|
||||||
"This error may be generated when the video or audio codec encounters an error during encoding. " +
|
|
||||||
"When this happens and the output file is generated, the output file is not properly constructed. " +
|
|
||||||
"The application will need to clean up the output file, such as deleting the file.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class InvalidVideoOutputOptionsError(cause: Throwable?) : CameraError("capture", "invalid-video-options",
|
|
||||||
"The recording failed due to invalid output options.\n" +
|
|
||||||
"This error is generated when invalid output options have been used while preparing a recording",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class RecorderError(cause: Throwable?) : CameraError("capture", "recorder-error",
|
|
||||||
"The recording failed because the Recorder is in an unrecoverable error state.\n" +
|
|
||||||
"When this happens and the output file is generated, the output file is not properly constructed. " +
|
|
||||||
"The application will need to clean up the output file, such as deleting the file. " +
|
|
||||||
"Such an error will usually require creating a new Recorder object to start a new recording.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class NoValidDataError(cause: Throwable?) : CameraError("capture", "no-valid-data",
|
|
||||||
"The recording failed because no valid data was produced to be recorded.\n" +
|
|
||||||
"This error is generated when the essential data for a recording to be played correctly is missing, for example, " +
|
|
||||||
"a recording must contain at least one key frame. The application will need to clean up the output file, such as deleting the file.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class InactiveSourceError(cause: Throwable?) : CameraError("capture", "inactive-source",
|
|
||||||
"The recording failed because the source becomes inactive and stops sending frames.\n" +
|
|
||||||
"One case is that if camera is closed due to lifecycle stopped, the active recording will be finalized with this error, " +
|
|
||||||
"and the output will be generated, containing the frames produced before camera closing. " +
|
|
||||||
"Attempting to start a new recording will be finalized immediately if the source remains inactive and no output will be generated.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class InsufficientStorageError(cause: Throwable?) : CameraError("capture", "insufficient-storage",
|
|
||||||
"The recording failed due to insufficient storage space.\n" +
|
|
||||||
"There are two possible cases that will cause this error.\n" +
|
|
||||||
"1. The storage is already full before the recording starts, so no output file will be generated.\n" +
|
|
||||||
"2. The storage becomes full during recording, so the output file will be generated.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class FileSizeLimitReachedError(cause: Throwable?) : CameraError("capture", "file-size-limit-reached",
|
|
||||||
"The recording failed due to file size limitation.\n" +
|
|
||||||
"The file size limitation will refer to OutputOptions.getFileSizeLimit(). The output file will still be generated with this error.",
|
|
||||||
cause)
|
|
||||||
|
|
||||||
class NoRecordingInProgressError : CameraError("capture", "no-recording-in-progress", "There was no active video recording in progress! Did you call stopRecording() twice?")
|
class NoRecordingInProgressError : CameraError("capture", "no-recording-in-progress", "There was no active video recording in progress! Did you call stopRecording() twice?")
|
||||||
class RecordingInProgressError : CameraError("capture", "recording-in-progress", "There is already an active video recording in progress! Did you call startRecording() twice?")
|
class RecordingInProgressError : CameraError("capture", "recording-in-progress", "There is already an active video recording in progress! Did you call startRecording() twice?")
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package com.mrousavy.camera.extensions
|
|
||||||
|
|
||||||
import android.media.CamcorderProfile
|
|
||||||
import android.util.Size
|
|
||||||
|
|
||||||
private val qualitiesMap = mapOf(
|
|
||||||
Size(176 - 1, 144 - 1) to CamcorderProfile.QUALITY_LOW,
|
|
||||||
Size(176, 144) to CamcorderProfile.QUALITY_QCIF,
|
|
||||||
Size(320, 240) to CamcorderProfile.QUALITY_QVGA,
|
|
||||||
Size(352, 288) to CamcorderProfile.QUALITY_CIF,
|
|
||||||
Size(640, 480) to CamcorderProfile.QUALITY_VGA,
|
|
||||||
Size(720, 480) to CamcorderProfile.QUALITY_480P,
|
|
||||||
Size(1280, 720) to CamcorderProfile.QUALITY_720P,
|
|
||||||
Size(1920, 1080) to CamcorderProfile.QUALITY_1080P,
|
|
||||||
Size(2048, 1080) to CamcorderProfile.QUALITY_2K,
|
|
||||||
Size(2560, 1440) to CamcorderProfile.QUALITY_QHD,
|
|
||||||
Size(3840, 2160) to CamcorderProfile.QUALITY_2160P,
|
|
||||||
Size(4096, 2160) to CamcorderProfile.QUALITY_4KDCI,
|
|
||||||
Size(7680, 4320) to CamcorderProfile.QUALITY_8KUHD,
|
|
||||||
Size(7680 + 1, 4320 + 1) to CamcorderProfile.QUALITY_HIGH,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getCamcorderQualityForSize(size: Size): Int {
|
|
||||||
// Find closest match
|
|
||||||
val closestMatch = qualitiesMap.keys.closestTo(size)
|
|
||||||
return qualitiesMap[closestMatch] ?: CamcorderProfile.QUALITY_HIGH
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package com.mrousavy.camera.extensions
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.view.Surface
|
|
||||||
import android.view.WindowManager
|
|
||||||
import com.facebook.react.bridge.ReactContext
|
|
||||||
|
|
||||||
val Context.displayRotation: Int
|
|
||||||
get() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
// Context.display
|
|
||||||
this.display?.let { display ->
|
|
||||||
return display.rotation
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReactContext.currentActivity.display
|
|
||||||
if (this is ReactContext) {
|
|
||||||
currentActivity?.display?.let { display ->
|
|
||||||
return display.rotation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowManager.defaultDisplay
|
|
||||||
val windowManager = getSystemService(Context.WINDOW_SERVICE) as? WindowManager
|
|
||||||
if (windowManager != null) {
|
|
||||||
@Suppress("DEPRECATION") // deprecated since SDK 30
|
|
||||||
windowManager.defaultDisplay?.let { display ->
|
|
||||||
return display.rotation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0
|
|
||||||
return Surface.ROTATION_0
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package com.mrousavy.camera.extensions
|
|
||||||
|
|
||||||
import android.hardware.camera2.params.DynamicRangeProfiles
|
|
||||||
import android.media.MediaCodecInfo
|
|
||||||
import android.media.MediaFormat
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.N)
|
|
||||||
private fun getTransferFunction(codecProfile: Int) = when (codecProfile) {
|
|
||||||
MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10 -> MediaFormat.COLOR_TRANSFER_HLG
|
|
||||||
MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10 -> MediaFormat.COLOR_TRANSFER_ST2084
|
|
||||||
MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus -> MediaFormat.COLOR_TRANSFER_ST2084
|
|
||||||
else -> MediaFormat.COLOR_TRANSFER_SDR_VIDEO
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MediaFormat.setDynamicRangeProfile(dynamicRangeProfile: Long) {
|
|
||||||
val profile = when (dynamicRangeProfile) {
|
|
||||||
DynamicRangeProfiles.HLG10 -> MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10
|
|
||||||
DynamicRangeProfiles.HDR10 -> MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10
|
|
||||||
DynamicRangeProfiles.HDR10_PLUS -> MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profile != null) {
|
|
||||||
Log.i("MediaFormat", "Using HDR Profile $profile")
|
|
||||||
this.setInteger(MediaFormat.KEY_PROFILE, profile)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
this.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020)
|
|
||||||
this.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_FULL)
|
|
||||||
this.setInteger(MediaFormat.KEY_COLOR_TRANSFER, getTransferFunction(profile))
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
this.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,10 +15,6 @@ fun List<Size>.closestToOrMax(size: Size?): Size {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Collection<Size>.closestTo(size: Size): Size {
|
|
||||||
return this.minBy { abs(it.width - size.width) + abs(it.height - size.height) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate by a given Surface Rotation
|
* Rotate by a given Surface Rotation
|
||||||
*/
|
*/
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package com.mrousavy.camera.extensions
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.WritableArray
|
|
||||||
|
|
||||||
fun WritableArray.pushInt(value: Int?) {
|
|
||||||
if (value == null)
|
|
||||||
this.pushNull()
|
|
||||||
else
|
|
||||||
this.pushInt(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun WritableArray.pushDouble(value: Double?) {
|
|
||||||
if (value == null)
|
|
||||||
this.pushNull()
|
|
||||||
else
|
|
||||||
this.pushDouble(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun WritableArray.pushBoolean(value: Boolean?) {
|
|
||||||
if (value == null)
|
|
||||||
this.pushNull()
|
|
||||||
else
|
|
||||||
this.pushBoolean(value)
|
|
||||||
}
|
|
@ -15,10 +15,3 @@ fun WritableMap.putDouble(key: String, value: Double?) {
|
|||||||
else
|
else
|
||||||
this.putDouble(key, value)
|
this.putDouble(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun WritableMap.putBoolean(key: String, value: Boolean?) {
|
|
||||||
if (value == null)
|
|
||||||
this.putNull(key)
|
|
||||||
else
|
|
||||||
this.putBoolean(key, value)
|
|
||||||
}
|
|
||||||
|
@ -11,13 +11,6 @@ enum class PixelFormat(override val unionValue: String): JSUnionValue {
|
|||||||
NATIVE("native"),
|
NATIVE("native"),
|
||||||
UNKNOWN("unknown");
|
UNKNOWN("unknown");
|
||||||
|
|
||||||
private fun bestMatch(formats: IntArray, targetFormats: Array<Int>): Int? {
|
|
||||||
targetFormats.forEach { format ->
|
|
||||||
if (formats.contains(format)) return format
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toImageFormat(): Int {
|
fun toImageFormat(): Int {
|
||||||
val result = when (this) {
|
val result = when (this) {
|
||||||
YUV -> ImageFormat.YUV_420_888
|
YUV -> ImageFormat.YUV_420_888
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package com.mrousavy.camera.utils
|
|
||||||
|
|
||||||
import androidx.exifinterface.media.ExifInterface
|
|
||||||
|
|
||||||
class ExifUtils {
|
|
||||||
companion object {
|
|
||||||
fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
|
|
||||||
rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
|
|
||||||
rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
|
|
||||||
rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
|
|
||||||
rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
|
|
||||||
rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
|
|
||||||
rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
|
|
||||||
rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
|
|
||||||
rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
|
|
||||||
rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
|
|
||||||
else -> ExifInterface.ORIENTATION_UNDEFINED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,6 +9,7 @@ import android.os.Build
|
|||||||
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 com.mrousavy.camera.RecorderError
|
||||||
import com.mrousavy.camera.parsers.Orientation
|
import com.mrousavy.camera.parsers.Orientation
|
||||||
import com.mrousavy.camera.parsers.VideoCodec
|
import com.mrousavy.camera.parsers.VideoCodec
|
||||||
import com.mrousavy.camera.parsers.VideoFileType
|
import com.mrousavy.camera.parsers.VideoFileType
|
||||||
@ -22,7 +23,8 @@ class RecordingSession(context: Context,
|
|||||||
private val codec: VideoCodec = VideoCodec.H264,
|
private val codec: VideoCodec = VideoCodec.H264,
|
||||||
private val orientation: Orientation,
|
private val orientation: Orientation,
|
||||||
private val fileType: VideoFileType = VideoFileType.MP4,
|
private val fileType: VideoFileType = VideoFileType.MP4,
|
||||||
private val callback: (video: Video) -> Unit) {
|
private val callback: (video: Video) -> Unit,
|
||||||
|
private val onError: (error: RecorderError) -> Unit) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "RecordingSession"
|
private const val TAG = "RecordingSession"
|
||||||
// bits per second
|
// bits per second
|
||||||
@ -77,6 +79,12 @@ class RecordingSession(context: Context,
|
|||||||
recorder.setOnErrorListener { _, what, extra ->
|
recorder.setOnErrorListener { _, what, extra ->
|
||||||
Log.e(TAG, "MediaRecorder Error: $what ($extra)")
|
Log.e(TAG, "MediaRecorder Error: $what ($extra)")
|
||||||
stop()
|
stop()
|
||||||
|
val name = when (what) {
|
||||||
|
MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN -> "unknown"
|
||||||
|
MediaRecorder.MEDIA_ERROR_SERVER_DIED -> "server-died"
|
||||||
|
else -> "unknown"
|
||||||
|
}
|
||||||
|
onError(RecorderError(name, extra))
|
||||||
}
|
}
|
||||||
recorder.setOnInfoListener { _, what, extra ->
|
recorder.setOnInfoListener { _, what, extra ->
|
||||||
Log.i(TAG, "MediaRecorder Info: $what ($extra)")
|
Log.i(TAG, "MediaRecorder Info: $what ($extra)")
|
||||||
|
@ -2,10 +2,10 @@ package com.mrousavy.camera.utils.outputs
|
|||||||
|
|
||||||
import android.graphics.ImageFormat
|
import android.graphics.ImageFormat
|
||||||
import android.hardware.HardwareBuffer
|
import android.hardware.HardwareBuffer
|
||||||
import android.hardware.camera2.CameraCharacteristics
|
|
||||||
import android.hardware.camera2.CameraManager
|
import android.hardware.camera2.CameraManager
|
||||||
import android.media.Image
|
import android.media.Image
|
||||||
import android.media.ImageReader
|
import android.media.ImageReader
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
@ -92,7 +92,6 @@ class CameraOutputs(val cameraId: String,
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
|
||||||
val config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
|
|
||||||
|
|
||||||
Log.i(TAG, "Preparing Outputs for Camera $cameraId...")
|
Log.i(TAG, "Preparing Outputs for Camera $cameraId...")
|
||||||
|
|
||||||
@ -118,6 +117,8 @@ class CameraOutputs(val cameraId: String,
|
|||||||
|
|
||||||
// Video output: High resolution repeating images (startRecording() or useFrameProcessor())
|
// Video output: High resolution repeating images (startRecording() or useFrameProcessor())
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) throw Error("Video Recordings and/or Frame Processors are only available on API 29 and above!")
|
||||||
|
|
||||||
val size = characteristics.getVideoSizes(cameraId, video.format).closestToOrMax(video.targetSize)
|
val size = characteristics.getVideoSizes(cameraId, video.format).closestToOrMax(video.targetSize)
|
||||||
|
|
||||||
val flags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_VIDEO_ENCODE
|
val flags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_VIDEO_ENCODE
|
||||||
|
@ -64,7 +64,6 @@ const _CaptureButton: React.FC<Props> = ({
|
|||||||
qualityPrioritization: 'speed',
|
qualityPrioritization: 'speed',
|
||||||
flash: flash,
|
flash: flash,
|
||||||
quality: 90,
|
quality: 90,
|
||||||
skipMetadata: true,
|
|
||||||
}),
|
}),
|
||||||
[flash],
|
[flash],
|
||||||
);
|
);
|
||||||
|
@ -38,16 +38,6 @@ export interface TakePhotoOptions {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
enableAutoDistortionCorrection?: boolean;
|
enableAutoDistortionCorrection?: boolean;
|
||||||
/**
|
|
||||||
* When set to `true`, metadata reading and mapping will be skipped. ({@linkcode PhotoFile.metadata} will be null)
|
|
||||||
*
|
|
||||||
* This might result in a faster capture, as metadata reading and mapping requires File IO.
|
|
||||||
*
|
|
||||||
* @default false
|
|
||||||
*
|
|
||||||
* @platform Android
|
|
||||||
*/
|
|
||||||
skipMetadata?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,10 +70,11 @@ export interface PhotoFile extends TemporaryFile {
|
|||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
thumbnail?: Record<string, unknown>;
|
thumbnail?: Record<string, unknown>;
|
||||||
/**
|
/**
|
||||||
* Metadata information describing the captured image.
|
* Metadata information describing the captured image. (iOS only)
|
||||||
*
|
*
|
||||||
* @see [AVCapturePhoto.metadata](https://developer.apple.com/documentation/avfoundation/avcapturephoto/2873982-metadata)
|
* @see [AVCapturePhoto.metadata](https://developer.apple.com/documentation/avfoundation/avcapturephoto/2873982-metadata)
|
||||||
* @see [AndroidX ExifInterface](https://developer.android.com/reference/androidx/exifinterface/media/ExifInterface)
|
*
|
||||||
|
* @platform iOS
|
||||||
*/
|
*/
|
||||||
metadata?: {
|
metadata?: {
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user