Fix bitrate value used for video transcoding (#5183)

* Fix bitrate value used for video transcoding:

It should be 1000 times what it is now. The video size estimation was wrong since the retrieved duration value was in milliseconds, not seconds.

* Use `Duration` as the result type for `getDuration`
This commit is contained in:
Jorge Martin Espinosa 2025-08-18 23:12:11 +02:00 committed by GitHub
parent aac9642159
commit 5feb7a99a9
5 changed files with 15 additions and 8 deletions

View file

@ -101,8 +101,9 @@ class DefaultMediaOptimizationSelectorPresenter @AssistedInject constructor(
val sizeEstimations = VideoCompressionPreset.entries val sizeEstimations = VideoCompressionPreset.entries
.map { preset -> .map { preset ->
val bitRate = preset.compressorHelper().calculateOptimalBitrate(videoDimensions, 30) val bitRateAsBytes = preset.compressorHelper().calculateOptimalBitrate(videoDimensions, 30) / 8f
val calculatedSize = (bitRate * duration / 8 * 1.1).roundToLong() // Adding 10% overhead for safety val durationInSeconds = duration.inWholeSeconds.toFloat()
val calculatedSize = (bitRateAsBytes * durationInSeconds * 1.1f).roundToLong() // Adding 10% overhead for safety
VideoUploadEstimation( VideoUploadEstimation(
preset = preset, preset = preset,
sizeInBytes = calculatedSize, sizeInBytes = calculatedSize,

View file

@ -18,10 +18,12 @@ import dagger.assisted.AssistedInject
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
interface VideoMetadataExtractor : AutoCloseable { interface VideoMetadataExtractor : AutoCloseable {
fun getSize(): Result<Size> fun getSize(): Result<Size>
fun getDuration(): Result<Long> fun getDuration(): Result<Duration>
interface Factory { interface Factory {
fun create(uri: Uri): VideoMetadataExtractor fun create(uri: Uri): VideoMetadataExtractor
} }
@ -57,9 +59,10 @@ class DefaultVideoMetadataExtractor @AssistedInject constructor(
} }
} }
override fun getDuration(): Result<Long> = runCatchingExceptions { override fun getDuration(): Result<Duration> = runCatchingExceptions {
mediaMetadataRetriever.value.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() mediaMetadataRetriever.value.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong()
?.takeIf { it > 0L } ?.takeIf { it > 0L }
?.milliseconds
?: error("Could not retrieve video duration from metadata") ?: error("Could not retrieve video duration from metadata")
} }

View file

@ -34,6 +34,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import kotlin.time.Duration.Companion.minutes
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class DefaultMediaOptimizationSelectorPresenterTest { class DefaultMediaOptimizationSelectorPresenterTest {
@ -158,7 +159,7 @@ class DefaultMediaOptimizationSelectorPresenterTest {
mediaExtractorFactory = FakeVideoMetadataExtractorFactory( mediaExtractorFactory = FakeVideoMetadataExtractorFactory(
FakeVideoMetadataExtractor( FakeVideoMetadataExtractor(
sizeResult = Result.success(Size(10_000, 10_000)), sizeResult = Result.success(Size(10_000, 10_000)),
duration = Result.success(600L) duration = Result.success(10.minutes)
) )
), ),
) )

View file

@ -10,14 +10,16 @@ package io.element.android.features.messages.test.attachments.video
import android.net.Uri import android.net.Uri
import android.util.Size import android.util.Size
import io.element.android.features.messages.impl.attachments.video.VideoMetadataExtractor import io.element.android.features.messages.impl.attachments.video.VideoMetadataExtractor
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
class FakeVideoMetadataExtractor( class FakeVideoMetadataExtractor(
private val sizeResult: Result<Size> = Result.success(Size(1, 1)), private val sizeResult: Result<Size> = Result.success(Size(1, 1)),
private val duration: Result<Long> = Result.success(1L), private val duration: Result<Duration> = Result.success(1.milliseconds),
) : VideoMetadataExtractor { ) : VideoMetadataExtractor {
override fun getSize(): Result<Size> = sizeResult override fun getSize(): Result<Size> = sizeResult
override fun getDuration(): Result<Long> = duration override fun getDuration(): Result<Duration> = duration
override fun close() = Unit override fun close() = Unit
} }

View file

@ -38,7 +38,7 @@ class VideoCompressorHelper(
val pixelsPerFrame = outputSize.width * outputSize.height val pixelsPerFrame = outputSize.width * outputSize.height
// Apparently, 0.1 bits per pixel is a sweet spot for video compression // Apparently, 0.1 bits per pixel is a sweet spot for video compression
val bitsPerPixel = 0.1f val bitsPerPixel = 0.1f
return (pixelsPerFrame * bitsPerPixel * frameRate).toLong() / 1000 return (pixelsPerFrame * bitsPerPixel * frameRate).toLong()
} }
} }