Fix orientation issues
This commit is contained in:
@@ -42,12 +42,14 @@ class FragmentedRecordingManager(
|
||||
val cameraOrientationDegrees = cameraOrientation.toDegrees()
|
||||
val recordingOrientationDegrees = (options.orientation ?: Orientation.PORTRAIT).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 camera orientation, same as ChunkedRecordingManager
|
||||
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}, " +
|
||||
Log.d(TAG, "Input size: ${size.width}x${size.height}, encoder size: ${width}x${height}, " +
|
||||
"cameraOrientation: $cameraOrientation ($cameraOrientationDegrees°), " +
|
||||
"recordingOrientation: $recordingOrientationDegrees°")
|
||||
|
||||
|
||||
@@ -478,31 +478,80 @@ 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
|
||||
writeRotationMatrix(dos)
|
||||
// Rotation matrix based on orientationDegrees
|
||||
writeRotationMatrix(dos, width, height)
|
||||
|
||||
// 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)
|
||||
// For 90° and 270° rotations, the display dimensions are swapped
|
||||
// The tkhd width/height represent the final display size after rotation
|
||||
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())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
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
|
||||
|
||||
// Identity matrix - no transformation
|
||||
// Most HLS players handle rotation via the dimensions themselves
|
||||
// or we can add rotation metadata separately if needed
|
||||
when (orientationDegrees) {
|
||||
90 -> {
|
||||
// 90° rotation: x' = y, y' = -x + width
|
||||
dos.writeInt(0) // a = 0
|
||||
dos.writeInt(negOne) // b = -1
|
||||
dos.writeInt(0) // u = 0
|
||||
dos.writeInt(one) // c = 1
|
||||
dos.writeInt(0) // d = 0
|
||||
dos.writeInt(0) // v = 0
|
||||
dos.writeInt(0) // x = 0
|
||||
dos.writeInt(width shl 16) // y = width (translation)
|
||||
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
|
||||
@@ -513,6 +562,8 @@ class HlsMuxer(
|
||||
dos.writeInt(0) // y = 0
|
||||
dos.writeInt(w) // w = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildMdiaBox(width: Int, height: Int, sps: ByteArray, pps: ByteArray): ByteArray {
|
||||
val content = ByteArrayOutputStream()
|
||||
|
||||
Reference in New Issue
Block a user