Skip to content

Fix issue 215 (Incorrect Cropping When Desired Aspect Ratio < 1) + tests #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import com.otaliastudios.transcoder.TranscoderListener
import com.otaliastudios.transcoder.TranscoderOptions
import com.otaliastudios.transcoder.common.TrackType
import com.otaliastudios.transcoder.internal.utils.Logger
import com.otaliastudios.transcoder.resize.AspectRatioResizer
import com.otaliastudios.transcoder.source.AssetFileDescriptorDataSource
import com.otaliastudios.transcoder.source.BlankAudioDataSource
import com.otaliastudios.transcoder.source.ClipDataSource
import com.otaliastudios.transcoder.source.FileDescriptorDataSource
import com.otaliastudios.transcoder.source.FilePathDataSource
import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy
import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy
import com.otaliastudios.transcoder.validator.WriteAlwaysValidator
Expand All @@ -22,6 +24,7 @@ import org.junit.AssumptionViolatedException
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import kotlin.math.roundToInt

@RunWith(AndroidJUnit4::class)
class IssuesTests {
Expand Down Expand Up @@ -76,6 +79,64 @@ class IssuesTests {
}
throw it
}
fun getRotation(file:File) : Int {
val outputDataSource = FilePathDataSource(file.absolutePath)
var displayRotation = 0
try{
outputDataSource.initialize()
val mediaFormat = outputDataSource.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
displayRotation = mediaFormat.getInteger(MediaFormat.KEY_ROTATION)
}catch (_:Exception){

}finally {
outputDataSource.deinitialize()
}
return displayRotation
}
fun getRotation(fd:AssetFileDescriptorDataSource) : Int {
var displayRotation = 0
try{
fd.initialize()
val mediaFormat = fd.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
displayRotation = mediaFormat.getInteger(MediaFormat.KEY_ROTATION)
}catch (_:Exception){

}finally {
fd.deinitialize()
}
return displayRotation
}
fun getFrameSize(fd:AssetFileDescriptorDataSource): Pair<Int, Int>{
var outputWidth = 0
var outputHeight = 0
try{
fd.initialize()
val mediaFormat = fd.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
outputWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
outputHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
}catch (ex:Exception){
throw Exception("Get video size\n${ex.localizedMessage}")
}finally {
fd.deinitialize()
}
return Pair(outputWidth, outputHeight)
}
fun getFrameSize(file:File) : Pair<Int, Int> {
val outputDataSource = FilePathDataSource(file.absolutePath)
var outputWidth = 0
var outputHeight = 0
try{
outputDataSource.initialize()
val mediaFormat = outputDataSource.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
outputWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
outputHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
}catch (ex:Exception){
throw Exception("Get video size\n${ex.localizedMessage}")
}finally {
outputDataSource.deinitialize()
}
return Pair(outputWidth, outputHeight)
}
}


Expand Down Expand Up @@ -151,4 +212,31 @@ class IssuesTests {
}
Unit
}
@Test
fun issue215() = with(Helper(215)){
//Display rotation 0, 90, 180, 270
val rotations = listOf(0,270,180,90)
val videoNames = listOf("bbb_720p_1sec.mp4", "bbb_720p_1sec_r90.mp4", "bbb_720p_1sec_r180.mp4", "bbb_720p_1sec_r270.mp4")
videoNames.forEachIndexed { i, videoName ->
val inputVideoSize = getFrameSize(input(videoName))
val inputRatio = inputVideoSize.first.toFloat() / inputVideoSize.second.toFloat()
check(inputRatio > 1f){"Input video ($videoName) ratio is: $inputRatio but expected: greater then 1"}
val inputRotation = getRotation(input(videoName))
check(inputRotation == rotations[i]) {"Input video ($videoName) display rotation is: $inputRotation but expected: ${rotations[i]}"}
val expectedOutputRatio = 0.5f
val outputVideo = transcode {
val vds = input(videoName)
addDataSource(vds)
val videoTrackStrategy = DefaultVideoStrategy.Builder()
videoTrackStrategy.addResizer(AspectRatioResizer(expectedOutputRatio))
setVideoTrackStrategy(videoTrackStrategy.build())
}
val outputRotation = getRotation(outputVideo)
val outputVideoSize = getFrameSize(outputVideo)
val outputRatio = ((outputVideoSize.first.toFloat() / outputVideoSize.second.toFloat()) * 10).roundToInt() / 10f
check(outputRatio == expectedOutputRatio) {"Output ratio is: $outputRatio but expects: $expectedOutputRatio (input video[$i]: $videoName)"}
check(outputRotation == 0) {"Output video display rotation is: $outputRotation but expected: 0 (input video[$i]: $videoName)"}
println("$videoName (rotation: $outputRotation expected: 0), inputRatio: $inputRatio (${inputVideoSize.first}x${inputVideoSize.second}) expectedOutputRatio: $expectedOutputRatio (${outputVideoSize.first}x${outputVideoSize.second}) OK")
}
}
}
12 changes: 12 additions & 0 deletions lib/src/main/java/com/otaliastudios/transcoder/common/Size.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class Size {

private final int mMajor;
private final int mMinor;
private final int mFirst;
private final int mSecond;

/**
* The order does not matter.
Expand All @@ -19,6 +21,8 @@ public class Size {
public Size(int firstSize, int secondSize) {
mMajor = Math.max(firstSize, secondSize);
mMinor = Math.min(firstSize, secondSize);
mFirst = firstSize;
mSecond = secondSize;
}

public int getMinor() {
Expand All @@ -28,4 +32,12 @@ public int getMinor() {
public int getMajor() {
return mMajor;
}
public int getFirst() {
return mFirst;
}

public int getSecond() {
return mSecond;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import androidx.annotation.NonNull;

import com.otaliastudios.transcoder.common.ExactSize;
import com.otaliastudios.transcoder.common.Size;
import com.otaliastudios.transcoder.resize.Resizer;

Expand All @@ -24,17 +25,15 @@ public AspectRatioResizer(float aspectRatio) {
@NonNull
@Override
public Size getOutputSize(@NonNull Size inputSize) {
float inputRatio = (float) inputSize.getMajor() / inputSize.getMinor();
float outputRatio = aspectRatio > 1 ? aspectRatio : 1F / aspectRatio;
// now both are greater than 1 (major / minor).
if (inputRatio > outputRatio) {
// input is "wider". We must reduce the input major dimension.
return new Size(inputSize.getMinor(), (int) (outputRatio * inputSize.getMinor()));
} else if (inputRatio < outputRatio) {
// input is more square. We must reduce the input minor dimension.
return new Size(inputSize.getMajor(), (int) (inputSize.getMajor() / outputRatio));
} else {
return inputSize;
float inputRatio = (float) inputSize.getFirst() / inputSize.getSecond();
if(inputRatio > aspectRatio){
int outputFirst = (int)(aspectRatio * ((float) inputSize.getSecond()));
return new ExactSize(outputFirst, inputSize.getSecond());
}else if(inputRatio < aspectRatio){
int outputSecond = (int)(((float) inputSize.getFirst()) / aspectRatio);
return new ExactSize(inputSize.getFirst(), outputSecond);
}else{
return new ExactSize(inputSize.getFirst(), inputSize.getSecond());
}
}
}