add orientation and aspect ratio handling for landscape recording
This commit is contained in:
@@ -429,15 +429,15 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
||||
// Get actual device rotation from WindowManager since the React Native orientation hook
|
||||
// doesn't update when rotating between landscape-left and landscape-right on Android.
|
||||
// Map device rotation to the correct orientationHint for video recording:
|
||||
// - Counter-clockwise (ROTATION_90) → 270° hint
|
||||
// - Clockwise (ROTATION_270) → 90° hint
|
||||
// - Counter-clockwise (ROTATION_90) → 90° hint
|
||||
// - Clockwise (ROTATION_270) → 270° hint
|
||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
val deviceRotation = windowManager.defaultDisplay.rotation
|
||||
val recordingOrientation = when (deviceRotation) {
|
||||
Surface.ROTATION_0 -> Orientation.PORTRAIT
|
||||
Surface.ROTATION_90 -> Orientation.LANDSCAPE_RIGHT
|
||||
Surface.ROTATION_90 -> Orientation.LANDSCAPE_LEFT
|
||||
Surface.ROTATION_180 -> Orientation.PORTRAIT_UPSIDE_DOWN
|
||||
Surface.ROTATION_270 -> Orientation.LANDSCAPE_LEFT
|
||||
Surface.ROTATION_270 -> Orientation.LANDSCAPE_RIGHT
|
||||
else -> Orientation.PORTRAIT
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ class CameraSession(private val context: Context, private val cameraManager: Cam
|
||||
enableAudio,
|
||||
fps,
|
||||
videoOutput.enableHdr,
|
||||
orientation,
|
||||
recordingOrientation,
|
||||
options,
|
||||
filePath,
|
||||
callback,
|
||||
|
||||
@@ -39,17 +39,21 @@ class FragmentedRecordingManager(
|
||||
segmentDurationSeconds: Int = DEFAULT_SEGMENT_DURATION_SECONDS
|
||||
): FragmentedRecordingManager {
|
||||
val mimeType = options.videoCodec.toMimeType()
|
||||
val cameraOrientationDegrees = cameraOrientation.toDegrees()
|
||||
val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).toDegrees()
|
||||
// Use cameraOrientation from Android (computed from device rotation)
|
||||
// instead of options.orientation from JS which may be stale
|
||||
val recordingOrientationDegrees = cameraOrientation.toDegrees()
|
||||
|
||||
// Use size dimensions directly - the encoder output format will have the actual dimensions
|
||||
// Don't swap based on orientation here; the camera pipeline handles that
|
||||
val width = size.width
|
||||
val height = size.height
|
||||
// Swap dimensions based on orientation - same logic as ChunkedRecordingManager
|
||||
// When camera is in landscape orientation, we need to swap width/height for the encoder
|
||||
val (width, height) = if (cameraOrientation.isLandscape()) {
|
||||
size.height to size.width
|
||||
} else {
|
||||
size.width to size.height
|
||||
}
|
||||
|
||||
Log.d(TAG, "Input size: ${size.width}x${size.height}, " +
|
||||
"cameraOrientation: $cameraOrientation ($cameraOrientationDegrees°), " +
|
||||
"recordingOrientation: $recordingOrientationDegrees°")
|
||||
"encoder size: ${width}x${height}, " +
|
||||
"orientation: $cameraOrientation ($recordingOrientationDegrees°)")
|
||||
|
||||
val format = MediaFormat.createVideoFormat(mimeType, width, height)
|
||||
val codec = MediaCodec.createEncoderByType(mimeType)
|
||||
|
||||
@@ -478,40 +478,56 @@ class HlsMuxer(
|
||||
dos.writeShort(0) // volume (0 for video)
|
||||
dos.writeShort(0) // reserved
|
||||
|
||||
// Rotation matrix - use identity and rely on correct dimensions from encoder
|
||||
// The encoder output format already has the correct dimensions for the content
|
||||
// Rotation matrix
|
||||
writeRotationMatrix(dos)
|
||||
|
||||
// Use dimensions as-is from encoder output format
|
||||
dos.writeInt(width shl 16) // width (16.16 fixed point)
|
||||
dos.writeInt(height shl 16) // height (16.16 fixed point)
|
||||
// Display dimensions should be post-rotation dimensions
|
||||
// For 90° or 270° rotation, swap width and height
|
||||
val (displayWidth, displayHeight) = when (orientationDegrees) {
|
||||
90, 270 -> height to width
|
||||
else -> width to height
|
||||
}
|
||||
dos.writeInt(displayWidth shl 16) // width (16.16 fixed point)
|
||||
dos.writeInt(displayHeight shl 16) // height (16.16 fixed point)
|
||||
|
||||
Log.d(TAG, "tkhd: ${width}x${height}, rotation=$orientationDegrees")
|
||||
Log.d(TAG, "tkhd: encoded=${width}x${height}, display=${displayWidth}x${displayHeight}, rotation=$orientationDegrees")
|
||||
|
||||
return wrapBox("tkhd", output.toByteArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the 3x3 transformation matrix for video rotation.
|
||||
* Uses simple rotation values - the encoder already outputs correctly oriented frames.
|
||||
*/
|
||||
private fun writeRotationMatrix(dos: DataOutputStream) {
|
||||
// Fixed-point constants
|
||||
val one = 0x00010000 // 1.0 in 16.16
|
||||
val w = 0x40000000 // 1.0 in 2.30
|
||||
val one = 0x00010000 // 1.0 in 16.16
|
||||
val negOne = 0xFFFF0000.toInt() // -1.0 in 16.16
|
||||
val w = 0x40000000 // 1.0 in 2.30
|
||||
|
||||
// Identity matrix - no transformation
|
||||
// Most HLS players handle rotation via the dimensions themselves
|
||||
// or we can add rotation metadata separately if needed
|
||||
dos.writeInt(one) // a = 1
|
||||
dos.writeInt(0) // b = 0
|
||||
// For 270° device orientation (landscape-right), apply 90° CW rotation
|
||||
// For 90° device orientation (landscape-left), apply 270° CW rotation
|
||||
val a: Int
|
||||
val b: Int
|
||||
val c: Int
|
||||
val d: Int
|
||||
|
||||
when (orientationDegrees) {
|
||||
90 -> { a = 0; b = negOne; c = one; d = 0 }
|
||||
180 -> { a = negOne; b = 0; c = 0; d = negOne }
|
||||
270 -> { a = 0; b = one; c = negOne; d = 0 }
|
||||
else -> { a = one; b = 0; c = 0; d = one }
|
||||
}
|
||||
|
||||
dos.writeInt(a)
|
||||
dos.writeInt(b)
|
||||
dos.writeInt(0) // u = 0
|
||||
dos.writeInt(0) // c = 0
|
||||
dos.writeInt(one) // d = 1
|
||||
dos.writeInt(c)
|
||||
dos.writeInt(d)
|
||||
dos.writeInt(0) // v = 0
|
||||
dos.writeInt(0) // x = 0
|
||||
dos.writeInt(0) // y = 0
|
||||
dos.writeInt(w) // w = 1
|
||||
dos.writeInt(0) // tx = 0
|
||||
dos.writeInt(0) // ty = 0
|
||||
dos.writeInt(w) // w = 1.0
|
||||
|
||||
Log.d(TAG, "Rotation matrix for $orientationDegrees°")
|
||||
}
|
||||
|
||||
private fun buildMdiaBox(width: Int, height: Int, sps: ByteArray, pps: ByteArray): ByteArray {
|
||||
|
||||
Reference in New Issue
Block a user