Skip to content

Commit

Permalink
new: 4K and 720p input standardization (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkordic authored Jun 10, 2024
1 parent 3fc2f8f commit 93a9b39
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 14 deletions.
15 changes: 13 additions & 2 deletions library/src/main/java/com/mux/video/upload/api/MuxUpload.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.net.Uri
import androidx.annotation.MainThread
import com.mux.video.upload.MuxUploadSdk
import com.mux.video.upload.api.MuxUpload.Builder
import com.mux.video.upload.internal.InputStandardization
import com.mux.video.upload.internal.MaximumResolution
import com.mux.video.upload.internal.UploadInfo
import com.mux.video.upload.internal.update
import kotlinx.coroutines.*
Expand Down Expand Up @@ -324,7 +326,6 @@ class MuxUpload private constructor(
inputFile = videoFile,
chunkSize = 8 * 1024 * 1024, // GCP recommends at least 8M chunk size
retriesPerChunk = 3,
standardizationRequested = true,
optOut = false,
uploadJob = null,
statusFlow = null,
Expand All @@ -338,13 +339,23 @@ class MuxUpload private constructor(
return this
}

/**
* If requested, the Upload SDK will try to standardize the input file in order to optimize it
* for use with Mux Video
*/
@Suppress("unused")
fun standardizationRequested(enabled: Boolean, maxResolution: MaximumResolution): Builder {
uploadInfo.update(InputStandardization(enabled, maxResolution))
return this
}

/**
* If requested, the Upload SDK will try to standardize the input file in order to optimize it
* for use with Mux Video
*/
@Suppress("unused")
fun standardizationRequested(enabled: Boolean): Builder {
uploadInfo.update(standardizationRequested = enabled)
uploadInfo.update(InputStandardization(enabled))
return this
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ internal class TranscoderContext private constructor(
) {
private val logger get() = MuxUploadSdk.logger

val MAX_ALLOWED_BITRATE = 8000000
val MAX_ALLOWED_FRAMERATE = 120;
val MAX_ALLOWED_WIDTH = 1920
val MAX_ALLOWED_HEIGTH = 1080
val MAX_ALLOWED_BITRATE: Int
val MAX_ALLOWED_FRAMERATE = 120
val MIN_ALLOWED_FRAMERATE = 5
val MAX_ALLOWED_WIDTH: Int
val MAX_ALLOWED_HEIGTH: Int
val OPTIMAL_FRAMERATE = 30
val I_FRAME_INTERVAL = 5 // in seconds
val MAX_ALLOWED_I_FRAME_INTERVAL: Int
val OUTPUT_SAMPLERATE = 48000
val OUTPUT_NUMBER_OF_CHANNELS = 2
val OUTPUT_AUDIO_BITRATE = 96000
Expand All @@ -60,6 +62,7 @@ internal class TranscoderContext private constructor(
private var targetedWidth = -1
private var targetedHeight = -1
private var targetedFramerate = -1
private var targetedIFrameInterval = -1
private var targetedBitrate = -1
private var scaledSizeYuv: Nv12Buffer? = null
private var resampleCreated = false
Expand Down Expand Up @@ -100,6 +103,20 @@ internal class TranscoderContext private constructor(
private var inputFileDurationMs:Long = 0 // Ms
private var errorDescription = ""

init {
if (uploadInfo.inputStandardization.maximumResolution == MaximumResolution.Preset3840x2160) {
this.MAX_ALLOWED_BITRATE = 20000000
this.MAX_ALLOWED_I_FRAME_INTERVAL = 10
this.MAX_ALLOWED_WIDTH = 4096
this.MAX_ALLOWED_HEIGTH = 4096
} else {
this.MAX_ALLOWED_BITRATE = 8000000
this.MAX_ALLOWED_I_FRAME_INTERVAL = 5
this.MAX_ALLOWED_WIDTH = uploadInfo.inputStandardization.maximumResolution.width
this.MAX_ALLOWED_HEIGTH = uploadInfo.inputStandardization.maximumResolution.height
}
}

companion object {
const val LOG_TAG = "TranscoderContext"

Expand Down Expand Up @@ -212,7 +229,7 @@ internal class TranscoderContext private constructor(
}
inputFramerate = format.getIntegerCompat(MediaFormat.KEY_FRAME_RATE, -1)
targetedFramerate = OPTIMAL_FRAMERATE
if (inputFramerate > MAX_ALLOWED_FRAMERATE) {
if (inputFramerate > MAX_ALLOWED_FRAMERATE || inputFramerate < MIN_ALLOWED_FRAMERATE) {
logger.v(
LOG_TAG,
"Should standardize because the input frame rate is too high"
Expand All @@ -223,6 +240,13 @@ internal class TranscoderContext private constructor(
} else {
targetedFramerate = inputFramerate
}
val iFrameInterval = format.getInteger(MediaFormat.KEY_I_FRAME_INTERVAL)
if (iFrameInterval > MAX_ALLOWED_I_FRAME_INTERVAL) {
shouldStandardize = true
targetedIFrameInterval = I_FRAME_INTERVAL
} else {
targetedIFrameInterval = iFrameInterval
}
videoTrackIndex = i;
inputVideoFormat = format;
extractor.selectTrack(i)
Expand Down Expand Up @@ -316,7 +340,7 @@ internal class TranscoderContext private constructor(
)
outputVideoFormat!!.setInteger("slice-height", targetedHeight + targetedHeight/2);
outputVideoFormat!!.setInteger("stride", targetedWidth);
outputVideoFormat!!.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL)
outputVideoFormat!!.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, targetedIFrameInterval)
outputVideoFormat!!.setInteger(
MediaFormat.KEY_BITRATE_MODE,
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR
Expand Down
47 changes: 44 additions & 3 deletions library/src/main/java/com/mux/video/upload/internal/UploadInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,46 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.flow.StateFlow
import java.io.File

@Suppress("unused")
enum class MaximumResolution(val width: Int, val height: Int) {
/**
* By default the standardized input will be
* scaled down to 1920x1080 (1080p) from a larger
* size. Inputs with smaller dimensions won't be
* scaled up.
*/
Default(1920, 1080),

/**
* The standardized input will be scaled down
* to 1280x720 (720p) from a larger size. Inputs
* with smaller dimensions won't be scaled up.
*/
Preset1280x720(1280, 720), // 720p

/**
* The standardized input will be scaled down
* to 1920x1080 (1080p) from a larger size. Inputs
* with smaller dimensions won't be scaled up.
*/
Preset1920x1080(1920, 1080), // 1080p

/**
* The standardized input will be scaled down
* to 3840x2160 (2160p/4K) from a larger size.
* Inputs with smaller dimensions won't be scaled
* up.
*/
Preset3840x2160(3840, 2160) // 2160p
}

data class InputStandardization(
@JvmSynthetic internal val standardizationRequested: Boolean = true,
@JvmSynthetic internal val maximumResolution: MaximumResolution = MaximumResolution.Default,
) {

}

/**
* This object is the SDK's internal representation of an upload that is in-progress. The public
* object is [MuxUpload], which is backed by an instance of this object.
Expand All @@ -18,7 +58,7 @@ import java.io.File
* Job and Flows populated
*/
internal data class UploadInfo(
@JvmSynthetic internal val standardizationRequested: Boolean = true,
@JvmSynthetic internal val inputStandardization: InputStandardization = InputStandardization(),
@JvmSynthetic internal val remoteUri: Uri,
@JvmSynthetic internal val inputFile: File,
@JvmSynthetic internal val standardizedFile: File? = null,
Expand All @@ -29,6 +69,7 @@ internal data class UploadInfo(
@JvmSynthetic internal val statusFlow: StateFlow<UploadStatus>?,
) {
fun isRunning(): Boolean = uploadJob?.isActive ?: false
fun isStandardizationRequested(): Boolean = inputStandardization.standardizationRequested
}

/**
Expand All @@ -37,7 +78,7 @@ internal data class UploadInfo(
*/
@JvmSynthetic
internal fun UploadInfo.update(
standardizationRequested: Boolean = this.standardizationRequested,
inputStandardization: InputStandardization = InputStandardization(),
remoteUri: Uri = this.remoteUri,
file: File = this.inputFile,
standardizedFile: File? = this.standardizedFile,
Expand All @@ -47,7 +88,7 @@ internal fun UploadInfo.update(
uploadJob: Deferred<Result<UploadStatus>>? = this.uploadJob,
statusFlow: StateFlow<UploadStatus>? = this.statusFlow,
) = UploadInfo(
standardizationRequested,
inputStandardization,
remoteUri,
file,
standardizedFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ internal class UploadJobFactory private constructor(
val startTime = System.currentTimeMillis()
try {
// See if the file need to be converted to a standard input.
if (uploadInfo.standardizationRequested
if (uploadInfo.isStandardizationRequested()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
) {
statusFlow.value = UploadStatus.Preparing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ internal class UploadMetrics private constructor() {
body.put("version", "1")
val data = getEventInfo(startTimeMillis, "upload_start_time", endTimeMillis,
"upload_end_time", inputFileDurationMs, uploadInfo)
data.put("input_standardization_requested", uploadInfo.standardizationRequested
data.put("input_standardization_requested", uploadInfo.isStandardizationRequested()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
body.put("data", data)
sendPost(body)
Expand All @@ -190,7 +190,7 @@ internal class UploadMetrics private constructor() {
body.put("version", "1")
val data = getEventInfo(startTimeMillis, "upload_start_time", endTimeMillis,
"upload_end_time", inputFileDurationMs, uploadInfo)
data.put("input_standardization_requested", uploadInfo.standardizationRequested
data.put("input_standardization_requested", uploadInfo.isStandardizationRequested()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
data.put("error_description", errorDescription)
body.put("data", data)
Expand Down

0 comments on commit 93a9b39

Please sign in to comment.