Compare commits
1 Commits
bump-react
...
loewy/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d4221fdde |
@@ -15,6 +15,7 @@ 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.WindowManager
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.mlkit.vision.barcode.common.Barcode
|
import com.google.mlkit.vision.barcode.common.Barcode
|
||||||
import com.mrousavy.camera.core.capture.RepeatingCaptureRequest
|
import com.mrousavy.camera.core.capture.RepeatingCaptureRequest
|
||||||
@@ -425,6 +426,19 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
|||||||
|
|
||||||
val fps = configuration?.fps ?: 30
|
val fps = configuration?.fps ?: 30
|
||||||
|
|
||||||
|
// Get actual device rotation from WindowManager instead of relying on
|
||||||
|
// the orientation prop from React, which may not update on Android when
|
||||||
|
// rotating between landscape-left and landscape-right.
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
val deviceRotation = windowManager.defaultDisplay.rotation
|
||||||
|
val actualOrientation = when (deviceRotation) {
|
||||||
|
Surface.ROTATION_0 -> Orientation.PORTRAIT
|
||||||
|
Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT
|
||||||
|
Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN
|
||||||
|
Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT
|
||||||
|
else -> Orientation.PORTRAIT
|
||||||
|
}
|
||||||
|
|
||||||
val recording = RecordingSession(
|
val recording = RecordingSession(
|
||||||
context,
|
context,
|
||||||
cameraId,
|
cameraId,
|
||||||
|
|||||||
@@ -105,12 +105,6 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu
|
|||||||
muxerContext?.finish()
|
muxerContext?.finish()
|
||||||
chunkIndex++
|
chunkIndex++
|
||||||
|
|
||||||
val format = this.encodedFormat
|
|
||||||
if (format == null) {
|
|
||||||
Log.e(TAG, "Cannot create muxer: encodedFormat is null (onOutputFormatChanged not called yet)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val newFileName = "$chunkIndex.mp4"
|
val newFileName = "$chunkIndex.mp4"
|
||||||
val newOutputFile = File(this.outputDirectory, newFileName)
|
val newOutputFile = File(this.outputDirectory, newFileName)
|
||||||
Log.i(TAG, "Creating new muxer for file: $newFileName")
|
Log.i(TAG, "Creating new muxer for file: $newFileName")
|
||||||
@@ -120,7 +114,7 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu
|
|||||||
)
|
)
|
||||||
muxer.setOrientationHint(orientationHint)
|
muxer.setOrientationHint(orientationHint)
|
||||||
muxerContext = MuxerContext(
|
muxerContext = MuxerContext(
|
||||||
muxer, newOutputFile, chunkIndex, bufferInfo.presentationTimeUs, format, this.callbacks
|
muxer, newOutputFile, chunkIndex, bufferInfo.presentationTimeUs, this.encodedFormat!!, this.callbacks
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +123,7 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun chunkLengthUs(bufferInfo: BufferInfo): Long {
|
private fun chunkLengthUs(bufferInfo: BufferInfo): Long {
|
||||||
val context = muxerContext ?: return 0L
|
return bufferInfo.presentationTimeUs - muxerContext!!.startTimeUs
|
||||||
return bufferInfo.presentationTimeUs - context.startTimeUs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
@@ -162,13 +155,7 @@ class ChunkedRecordingManager(private val encoder: MediaCodec, private val outpu
|
|||||||
if (muxerContext == null || (atKeyframe(bufferInfo) && chunkLengthUs(bufferInfo) >= targetDurationUs)) {
|
if (muxerContext == null || (atKeyframe(bufferInfo) && chunkLengthUs(bufferInfo) >= targetDurationUs)) {
|
||||||
this.createNextMuxer(bufferInfo)
|
this.createNextMuxer(bufferInfo)
|
||||||
}
|
}
|
||||||
val context = muxerContext
|
muxerContext!!.muxer.writeSampleData(muxerContext!!.videoTrack, encodedData, bufferInfo)
|
||||||
if (context == null) {
|
|
||||||
Log.e(TAG, "Cannot write sample data: muxerContext is null")
|
|
||||||
encoder.releaseOutputBuffer(index, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
context.muxer.writeSampleData(context.videoTrack, encodedData, bufferInfo)
|
|
||||||
encoder.releaseOutputBuffer(index, false)
|
encoder.releaseOutputBuffer(index, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import android.os.Looper
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import android.view.PixelCopy
|
import android.view.PixelCopy
|
||||||
|
import android.view.Surface
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
|
import android.view.WindowManager
|
||||||
import com.facebook.react.bridge.UiThreadUtil
|
import com.facebook.react.bridge.UiThreadUtil
|
||||||
import com.mrousavy.camera.extensions.resize
|
import com.mrousavy.camera.extensions.resize
|
||||||
import com.mrousavy.camera.extensions.rotatedBy
|
import com.mrousavy.camera.extensions.rotatedBy
|
||||||
@@ -150,6 +152,8 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
|
|||||||
val width = frame.width()
|
val width = frame.width()
|
||||||
val height = frame.height()
|
val height = frame.height()
|
||||||
|
|
||||||
|
// Create bitmap matching surface frame dimensions for PixelCopy
|
||||||
|
// The original code swapped dimensions assuming landscape input - keep that for consistency
|
||||||
val bitmap = Bitmap.createBitmap(height, width, Bitmap.Config.ARGB_8888)
|
val bitmap = Bitmap.createBitmap(height, width, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
// Use a coroutine to suspend until the PixelCopy request is complete
|
// Use a coroutine to suspend until the PixelCopy request is complete
|
||||||
@@ -159,7 +163,23 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) :
|
|||||||
bitmap,
|
bitmap,
|
||||||
{ copyResult ->
|
{ copyResult ->
|
||||||
if (copyResult == PixelCopy.SUCCESS) {
|
if (copyResult == PixelCopy.SUCCESS) {
|
||||||
continuation.resume(rotateBitmap90CounterClockwise(bitmap))
|
// Get actual device rotation from WindowManager instead of relying on
|
||||||
|
// the orientation prop, which may not update on Android when rotating
|
||||||
|
// between landscape-left and landscape-right.
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
val deviceRotation = windowManager.defaultDisplay.rotation
|
||||||
|
|
||||||
|
val actualOrientation = when (deviceRotation) {
|
||||||
|
Surface.ROTATION_0 -> Orientation.PORTRAIT
|
||||||
|
Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT
|
||||||
|
Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN
|
||||||
|
Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT
|
||||||
|
else -> Orientation.PORTRAIT
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "getBitmap: orientation prop = $orientation, deviceRotation = $deviceRotation, actualOrientation = $actualOrientation")
|
||||||
|
|
||||||
|
continuation.resume(bitmap.transformBitmap(actualOrientation))
|
||||||
} else {
|
} else {
|
||||||
continuation.resumeWithException(
|
continuation.resumeWithException(
|
||||||
RuntimeException("PixelCopy failed with error code $copyResult")
|
RuntimeException("PixelCopy failed with error code $copyResult")
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ inline fun withPromise(promise: Promise, closure: () -> Any?) {
|
|||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
val error = if (e is CameraError) e else UnknownCameraError(e)
|
val error = if (e is CameraError) e else UnknownCameraError(e)
|
||||||
promise.reject("${error.domain}/${error.id}", error.message ?: "Unknown error", error.cause)
|
promise.reject("${error.domain}/${error.id}", error.message, error.cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user