Skip to content

Commit 9994562

Browse files
authored
Keep video rotation metadata when transcoding (#5008)
1 parent d30454d commit 9994562

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
lines changed

libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class VideoCompressor @Inject constructor(
9090
val height = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toIntOrNull() ?: -1
9191
val bitrate = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)?.toLongOrNull() ?: -1
9292
val framerate = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE)?.toIntOrNull() ?: -1
93+
val rotation = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toIntOrNull() ?: 0
9394

9495
val (actualWidth, actualHeight) = if (width == -1 || height == -1) {
9596
// Try getting the first frame instead
@@ -103,7 +104,8 @@ class VideoCompressor @Inject constructor(
103104
width = actualWidth,
104105
height = actualHeight,
105106
bitrate = bitrate,
106-
frameRate = framerate
107+
frameRate = framerate,
108+
rotation = rotation,
107109
)
108110
}
109111
}.onFailure {
@@ -113,10 +115,11 @@ class VideoCompressor @Inject constructor(
113115
}
114116

115117
internal data class VideoFileMetadata(
116-
val width: Int?,
117-
val height: Int?,
118-
val bitrate: Long?,
119-
val frameRate: Int?,
118+
val width: Int,
119+
val height: Int,
120+
val bitrate: Long,
121+
val frameRate: Int,
122+
val rotation: Int,
120123
)
121124

122125
sealed interface VideoTranscodingEvent {
@@ -136,10 +139,11 @@ internal object VideoStrategyFactory {
136139
metadata: VideoFileMetadata?,
137140
shouldBeCompressed: Boolean,
138141
): TrackStrategy {
139-
val width = metadata?.width ?: Int.MAX_VALUE
140-
val height = metadata?.height ?: Int.MAX_VALUE
141-
val bitrate = metadata?.bitrate
142-
val frameRate = metadata?.frameRate
142+
val width = metadata?.width?.takeIf { it >= 0 } ?: Int.MAX_VALUE
143+
val height = metadata?.height?.takeIf { it >= 0 } ?: Int.MAX_VALUE
144+
val bitrate = metadata?.bitrate?.takeIf { it >= 0 }
145+
val frameRate = metadata?.frameRate?.takeIf { it >= 0 }
146+
val rotation = metadata?.rotation?.takeIf { it >= 0 }
143147

144148
// We only create a resizer if needed
145149
val resizer = when {
@@ -148,8 +152,9 @@ internal object VideoStrategyFactory {
148152
else -> null
149153
}
150154

151-
return if (resizer == null && expectedExtension == MP4_EXTENSION) {
155+
return if (resizer == null && rotation == 0 && expectedExtension == MP4_EXTENSION) {
152156
// If there's no transcoding or resizing needed for the video file, just create a new file with the same contents but no metadata
157+
// Rotation is not kept by the PassThroughTrackStrategy, so we need to ensure the video is not rotated
153158
PassThroughTrackStrategy()
154159
} else {
155160
DefaultVideoStrategy.Builder()

libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/VideoStrategyFactoryTest.kt

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class VideoStrategyFactoryTest {
3939
fun `if the video should be compressed and is larger than 720p it will be transcoded`() {
4040
// Given
4141
val expectedExtension = "mp4"
42-
val metadata = VideoFileMetadata(width = 1920, height = 1080, bitrate = 1_000_000, frameRate = 50)
42+
val metadata = VideoFileMetadata(width = 1920, height = 1080, bitrate = 1_000_000, frameRate = 50, rotation = 0)
4343
val shouldBeCompressed = true
4444

4545
// When
@@ -57,7 +57,7 @@ class VideoStrategyFactoryTest {
5757
fun `if the video should be compressed, has the right format and is smaller or equal to 720p it will not be transcoded`() {
5858
// Given
5959
val expectedExtension = "mp4"
60-
val metadata = VideoFileMetadata(width = 1280, height = 720, bitrate = 1_000_000, frameRate = 50)
60+
val metadata = VideoFileMetadata(width = 1280, height = 720, bitrate = 1_000_000, frameRate = 50, rotation = 0)
6161
val shouldBeCompressed = true
6262

6363
// When
@@ -75,7 +75,7 @@ class VideoStrategyFactoryTest {
7575
fun `if the video should not be compressed and is larger than 1080p it will be transcoded`() {
7676
// Given
7777
val expectedExtension = "mp4"
78-
val metadata = VideoFileMetadata(width = 2560, height = 1440, bitrate = 1_000_000, frameRate = 50)
78+
val metadata = VideoFileMetadata(width = 2560, height = 1440, bitrate = 1_000_000, frameRate = 50, rotation = 0)
7979
val shouldBeCompressed = false
8080

8181
// When
@@ -93,7 +93,7 @@ class VideoStrategyFactoryTest {
9393
fun `if the video should not be compressed, has the right format and is smaller or equal than 1080p it will not be transcoded`() {
9494
// Given
9595
val expectedExtension = "mp4"
96-
val metadata = VideoFileMetadata(width = 1920, height = 1080, bitrate = 1_000_000, frameRate = 50)
96+
val metadata = VideoFileMetadata(width = 1920, height = 1080, bitrate = 1_000_000, frameRate = 50, rotation = 0)
9797
val shouldBeCompressed = false
9898

9999
// When
@@ -111,7 +111,7 @@ class VideoStrategyFactoryTest {
111111
fun `if the video should not be compressed but has a wrong format it will be transcoded`() {
112112
// Given
113113
val expectedExtension = "mkv"
114-
val metadata = VideoFileMetadata(width = 320, height = 240, bitrate = 1_000_000, frameRate = 50)
114+
val metadata = VideoFileMetadata(width = 320, height = 240, bitrate = 1_000_000, frameRate = 50, rotation = 0)
115115
val shouldBeCompressed = false
116116

117117
// When
@@ -129,7 +129,7 @@ class VideoStrategyFactoryTest {
129129
fun `if the video should be compressed and has a wrong format it will be transcoded`() {
130130
// Given
131131
val expectedExtension = "mkv"
132-
val metadata = VideoFileMetadata(width = 320, height = 240, bitrate = 1_000_000, frameRate = 50)
132+
val metadata = VideoFileMetadata(width = 320, height = 240, bitrate = 1_000_000, frameRate = 50, rotation = 0)
133133
val shouldBeCompressed = true
134134

135135
// When
@@ -143,6 +143,24 @@ class VideoStrategyFactoryTest {
143143
assertIsTranscoded(videoStrategy)
144144
}
145145

146+
@Test
147+
fun `if the video should not be compressed but has a rotation not zero it will be transcoded`() {
148+
// Given
149+
val expectedExtension = "mp4"
150+
val metadata = VideoFileMetadata(width = 320, height = 240, bitrate = 1_000_000, frameRate = 50, rotation = 90)
151+
val shouldBeCompressed = false
152+
153+
// When
154+
val videoStrategy = VideoStrategyFactory.create(
155+
expectedExtension = expectedExtension,
156+
metadata = metadata,
157+
shouldBeCompressed = shouldBeCompressed
158+
)
159+
160+
// Then
161+
assertIsTranscoded(videoStrategy)
162+
}
163+
146164
private inline fun assertIsTranscoded(videoStrategy: TrackStrategy) {
147165
assert(videoStrategy is DefaultVideoStrategy)
148166
}

0 commit comments

Comments
 (0)