Fix orientation issues
This commit is contained in:
@@ -42,12 +42,14 @@ class FragmentedRecordingManager(
|
|||||||
val cameraOrientationDegrees = cameraOrientation.toDegrees()
|
val cameraOrientationDegrees = cameraOrientation.toDegrees()
|
||||||
val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).toDegrees()
|
val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).toDegrees()
|
||||||
|
|
||||||
// Use size dimensions directly - the encoder output format will have the actual dimensions
|
// Swap dimensions based on camera orientation, same as ChunkedRecordingManager
|
||||||
// Don't swap based on orientation here; the camera pipeline handles that
|
val (width, height) = if (cameraOrientation.isLandscape()) {
|
||||||
val width = size.width
|
size.height to size.width
|
||||||
val height = size.height
|
} else {
|
||||||
|
size.width to size.height
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Input size: ${size.width}x${size.height}, " +
|
Log.d(TAG, "Input size: ${size.width}x${size.height}, encoder size: ${width}x${height}, " +
|
||||||
"cameraOrientation: $cameraOrientation ($cameraOrientationDegrees°), " +
|
"cameraOrientation: $cameraOrientation ($cameraOrientationDegrees°), " +
|
||||||
"recordingOrientation: $recordingOrientationDegrees°")
|
"recordingOrientation: $recordingOrientationDegrees°")
|
||||||
|
|
||||||
|
|||||||
@@ -478,40 +478,91 @@ class HlsMuxer(
|
|||||||
dos.writeShort(0) // volume (0 for video)
|
dos.writeShort(0) // volume (0 for video)
|
||||||
dos.writeShort(0) // reserved
|
dos.writeShort(0) // reserved
|
||||||
|
|
||||||
// Rotation matrix - use identity and rely on correct dimensions from encoder
|
// Rotation matrix based on orientationDegrees
|
||||||
// The encoder output format already has the correct dimensions for the content
|
writeRotationMatrix(dos, width, height)
|
||||||
writeRotationMatrix(dos)
|
|
||||||
|
|
||||||
// Use dimensions as-is from encoder output format
|
// For 90° and 270° rotations, the display dimensions are swapped
|
||||||
dos.writeInt(width shl 16) // width (16.16 fixed point)
|
// The tkhd width/height represent the final display size after rotation
|
||||||
dos.writeInt(height shl 16) // height (16.16 fixed point)
|
val (displayWidth, displayHeight) = when (orientationDegrees) {
|
||||||
|
90, 270 -> Pair(height, width)
|
||||||
|
else -> Pair(width, 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: encoder=${width}x${height}, display=${displayWidth}x${displayHeight}, rotation=$orientationDegrees")
|
||||||
|
|
||||||
return wrapBox("tkhd", output.toByteArray())
|
return wrapBox("tkhd", output.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the 3x3 transformation matrix for video rotation.
|
* Writes the 3x3 transformation matrix for video rotation.
|
||||||
* Uses simple rotation values - the encoder already outputs correctly oriented frames.
|
* The matrix is applied to rotate the video content for correct display.
|
||||||
|
*
|
||||||
|
* Matrix format in tkhd box (all values in fixed-point):
|
||||||
|
* | a b u | where a,b,c,d are 16.16 fixed-point
|
||||||
|
* | c d v | and u,v are 2.30 fixed-point (always 0)
|
||||||
|
* | x y w | x,y are 16.16, w is 2.30 (always 1.0)
|
||||||
|
*
|
||||||
|
* For rotation by θ: a=cos(θ), b=sin(θ), c=-sin(θ), d=cos(θ)
|
||||||
|
* Translation (x,y) keeps the rotated video in the visible area.
|
||||||
*/
|
*/
|
||||||
private fun writeRotationMatrix(dos: DataOutputStream) {
|
private fun writeRotationMatrix(dos: DataOutputStream, width: Int, height: Int) {
|
||||||
// Fixed-point constants
|
// Fixed-point constants
|
||||||
val one = 0x00010000 // 1.0 in 16.16
|
val one = 0x00010000 // 1.0 in 16.16
|
||||||
|
val negOne = -0x00010000 // -1.0 in 16.16 (will be written as unsigned)
|
||||||
val w = 0x40000000 // 1.0 in 2.30
|
val w = 0x40000000 // 1.0 in 2.30
|
||||||
|
|
||||||
// Identity matrix - no transformation
|
when (orientationDegrees) {
|
||||||
// Most HLS players handle rotation via the dimensions themselves
|
90 -> {
|
||||||
// or we can add rotation metadata separately if needed
|
// 90° rotation: x' = y, y' = -x + width
|
||||||
dos.writeInt(one) // a = 1
|
dos.writeInt(0) // a = 0
|
||||||
dos.writeInt(0) // b = 0
|
dos.writeInt(negOne) // b = -1
|
||||||
dos.writeInt(0) // u = 0
|
dos.writeInt(0) // u = 0
|
||||||
dos.writeInt(0) // c = 0
|
dos.writeInt(one) // c = 1
|
||||||
dos.writeInt(one) // d = 1
|
dos.writeInt(0) // d = 0
|
||||||
dos.writeInt(0) // v = 0
|
dos.writeInt(0) // v = 0
|
||||||
dos.writeInt(0) // x = 0
|
dos.writeInt(0) // x = 0
|
||||||
dos.writeInt(0) // y = 0
|
dos.writeInt(width shl 16) // y = width (translation)
|
||||||
dos.writeInt(w) // w = 1
|
dos.writeInt(w) // w = 1
|
||||||
|
}
|
||||||
|
180 -> {
|
||||||
|
// 180° rotation
|
||||||
|
dos.writeInt(negOne) // a = -1
|
||||||
|
dos.writeInt(0) // b = 0
|
||||||
|
dos.writeInt(0) // u = 0
|
||||||
|
dos.writeInt(0) // c = 0
|
||||||
|
dos.writeInt(negOne) // d = -1
|
||||||
|
dos.writeInt(0) // v = 0
|
||||||
|
dos.writeInt(width shl 16) // x = width (translation)
|
||||||
|
dos.writeInt(height shl 16) // y = height (translation)
|
||||||
|
dos.writeInt(w) // w = 1
|
||||||
|
}
|
||||||
|
270 -> {
|
||||||
|
// 270° rotation: x' = -y + height, y' = x
|
||||||
|
dos.writeInt(0) // a = 0
|
||||||
|
dos.writeInt(one) // b = 1
|
||||||
|
dos.writeInt(0) // u = 0
|
||||||
|
dos.writeInt(negOne) // c = -1
|
||||||
|
dos.writeInt(0) // d = 0
|
||||||
|
dos.writeInt(0) // v = 0
|
||||||
|
dos.writeInt(height shl 16) // x = height (translation)
|
||||||
|
dos.writeInt(0) // y = 0
|
||||||
|
dos.writeInt(w) // w = 1
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// 0° or unknown: identity matrix
|
||||||
|
dos.writeInt(one) // a = 1
|
||||||
|
dos.writeInt(0) // b = 0
|
||||||
|
dos.writeInt(0) // u = 0
|
||||||
|
dos.writeInt(0) // c = 0
|
||||||
|
dos.writeInt(one) // d = 1
|
||||||
|
dos.writeInt(0) // v = 0
|
||||||
|
dos.writeInt(0) // x = 0
|
||||||
|
dos.writeInt(0) // y = 0
|
||||||
|
dos.writeInt(w) // w = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildMdiaBox(width: Int, height: Int, sps: ByteArray, pps: ByteArray): ByteArray {
|
private fun buildMdiaBox(width: Int, height: Int, sps: ByteArray, pps: ByteArray): ByteArray {
|
||||||
|
|||||||
Reference in New Issue
Block a user