From 406c51c77e0d084e218b9949bfa1ec7f3222b5a2 Mon Sep 17 00:00:00 2001 From: Christian Bernier Date: Tue, 28 Apr 2020 22:24:32 -0400 Subject: [PATCH 1/2] - Add a video strategy to change the way that the videos are scaled to be the same resolution - Add a method in VideoDecoderOutput to scale the canvas instead of the frame --- .../transcoder/demo/TranscoderActivity.java | 3 ++- .../transcoder/TranscoderOptions.java | 13 +++++++------ .../transcoder/engine/Engine.java | 1 + .../strategy/DefaultVideoStrategy.java | 8 +++++++- .../transcoder/strategy/TrackStrategy.java | 5 ++--- .../strategy/VideoTrackStrategy.java | 18 ++++++++++++++++++ .../transcode/VideoTrackTranscoder.java | 11 +++++++---- .../transcode/internal/VideoDecoderOutput.java | 15 +++++++++++++++ 8 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java diff --git a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java index f5e70b86..292f41bc 100644 --- a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java +++ b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java @@ -29,6 +29,7 @@ import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy; import com.otaliastudios.transcoder.strategy.RemoveTrackStrategy; import com.otaliastudios.transcoder.strategy.TrackStrategy; +import com.otaliastudios.transcoder.strategy.VideoTrackStrategy; import com.otaliastudios.transcoder.strategy.size.AspectRatioResizer; import com.otaliastudios.transcoder.strategy.size.FractionResizer; import com.otaliastudios.transcoder.strategy.size.PassThroughResizer; @@ -78,7 +79,7 @@ public class TranscoderActivity extends AppCompatActivity implements private Uri mAudioReplacementUri; private File mTranscodeOutputFile; private long mTranscodeStartTime; - private TrackStrategy mTranscodeVideoStrategy; + private VideoTrackStrategy mTranscodeVideoStrategy; private TrackStrategy mTranscodeAudioStrategy; private long mTrimStartUs = 0; private long mTrimEndUs = 0; diff --git a/lib/src/main/java/com/otaliastudios/transcoder/TranscoderOptions.java b/lib/src/main/java/com/otaliastudios/transcoder/TranscoderOptions.java index 81c12a4d..91836704 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/TranscoderOptions.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/TranscoderOptions.java @@ -18,6 +18,7 @@ import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy; import com.otaliastudios.transcoder.strategy.DefaultVideoStrategies; import com.otaliastudios.transcoder.strategy.TrackStrategy; +import com.otaliastudios.transcoder.strategy.VideoTrackStrategy; import com.otaliastudios.transcoder.stretch.AudioStretcher; import com.otaliastudios.transcoder.stretch.DefaultAudioStretcher; import com.otaliastudios.transcoder.time.DefaultTimeInterpolator; @@ -45,7 +46,7 @@ private TranscoderOptions() {} private List videoDataSources; private List audioDataSources; private TrackStrategy audioTrackStrategy; - private TrackStrategy videoTrackStrategy; + private VideoTrackStrategy videoTrackStrategy; private Validator validator; private int rotation; private TimeInterpolator timeInterpolator; @@ -76,7 +77,7 @@ public TrackStrategy getAudioTrackStrategy() { } @NonNull - public TrackStrategy getVideoTrackStrategy() { + public VideoTrackStrategy getVideoTrackStrategy() { return videoTrackStrategy; } @@ -111,7 +112,7 @@ public static class Builder { private TranscoderListener listener; private Handler listenerHandler; private TrackStrategy audioTrackStrategy; - private TrackStrategy videoTrackStrategy; + private VideoTrackStrategy videoTrackStrategy; private Validator validator; private int rotation; private TimeInterpolator timeInterpolator; @@ -199,13 +200,13 @@ public Builder setAudioTrackStrategy(@Nullable TrackStrategy trackStrategy) { * Sets the video output strategy. If absent, this defaults to the 16:9 * strategy returned by {@link DefaultVideoStrategies#for720x1280()}. * - * @param trackStrategy the desired strategy + * @param videoTrackStrategy the desired strategy * @return this for chaining */ @NonNull @SuppressWarnings("unused") - public Builder setVideoTrackStrategy(@Nullable TrackStrategy trackStrategy) { - this.videoTrackStrategy = trackStrategy; + public Builder setVideoTrackStrategy(@Nullable VideoTrackStrategy videoTrackStrategy) { + this.videoTrackStrategy = videoTrackStrategy; return this; } diff --git a/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java b/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java index 742fd1cf..17f5cc4b 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java @@ -171,6 +171,7 @@ private void openCurrentStep(@NonNull TrackType type, @NonNull TranscoderOptions case VIDEO: transcoder = new VideoTrackTranscoder(dataSource, mDataSink, interpolator, + options.getVideoTrackStrategy(), options.getVideoRotation()); break; case AUDIO: diff --git a/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java b/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java index bbab7255..82f9639f 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java @@ -16,6 +16,7 @@ import com.otaliastudios.transcoder.strategy.size.Resizer; import com.otaliastudios.transcoder.internal.Logger; import com.otaliastudios.transcoder.internal.MediaFormatConstants; +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; import androidx.annotation.NonNull; @@ -25,7 +26,7 @@ * An {@link TrackStrategy} for video that converts it AVC with the given size. * The input and output aspect ratio must match. */ -public class DefaultVideoStrategy implements TrackStrategy { +public class DefaultVideoStrategy implements VideoTrackStrategy { private final static String TAG = DefaultVideoStrategy.class.getSimpleName(); private final static Logger LOG = new Logger(TAG); @@ -290,6 +291,11 @@ public TrackStatus createOutputFormat(@NonNull List inputFormats, return TrackStatus.COMPRESSING; } + @Override + public void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY) { + videoDecoderOutput.setScale(scaleX, scaleY); + } + private boolean checkMimeType(@NonNull List formats) { for (MediaFormat format : formats) { if (!format.getString(MediaFormat.KEY_MIME).equalsIgnoreCase(options.targetMimeType)) { diff --git a/lib/src/main/java/com/otaliastudios/transcoder/strategy/TrackStrategy.java b/lib/src/main/java/com/otaliastudios/transcoder/strategy/TrackStrategy.java index fa2df2a4..a2210e51 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/strategy/TrackStrategy.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/strategy/TrackStrategy.java @@ -2,12 +2,11 @@ import android.media.MediaFormat; +import androidx.annotation.NonNull; + import com.otaliastudios.transcoder.engine.TrackStatus; import com.otaliastudios.transcoder.strategy.size.Resizer; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import java.util.List; /** diff --git a/lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java b/lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java new file mode 100644 index 00000000..d52e72da --- /dev/null +++ b/lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java @@ -0,0 +1,18 @@ +package com.otaliastudios.transcoder.strategy; + +import androidx.annotation.NonNull; +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; + +public interface VideoTrackStrategy extends TrackStrategy { + + /** + * Apply the scaling to the video decoder output + * + * It can be done using VideoDecoderOutput.setScale or VideoDecoderOutput.setDrawableScale + * + * @param videoDecoderOutput the video decoder output + * @param scaleX the expected x scaling + * @param scaleY the expected y scaling + */ + void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY); +} diff --git a/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java b/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java index 1f782566..ea1dea5d 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java @@ -16,20 +16,20 @@ package com.otaliastudios.transcoder.transcode; import android.media.MediaCodec; -import android.media.MediaExtractor; import android.media.MediaFormat; import androidx.annotation.NonNull; import com.otaliastudios.transcoder.engine.TrackType; +import com.otaliastudios.transcoder.internal.Logger; import com.otaliastudios.transcoder.internal.MediaCodecBuffers; +import com.otaliastudios.transcoder.internal.MediaFormatConstants; import com.otaliastudios.transcoder.sink.DataSink; import com.otaliastudios.transcoder.source.DataSource; +import com.otaliastudios.transcoder.strategy.VideoTrackStrategy; import com.otaliastudios.transcoder.time.TimeInterpolator; import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; import com.otaliastudios.transcoder.transcode.internal.VideoEncoderInput; -import com.otaliastudios.transcoder.internal.Logger; -import com.otaliastudios.transcoder.internal.MediaFormatConstants; import com.otaliastudios.transcoder.transcode.internal.VideoFrameDropper; import java.nio.ByteBuffer; @@ -46,6 +46,7 @@ public class VideoTrackTranscoder extends BaseTrackTranscoder { private MediaCodec mEncoder; // Keep this since we want to signal EOS on it. private VideoFrameDropper mFrameDropper; private final TimeInterpolator mTimeInterpolator; + private final VideoTrackStrategy mStrategy; private final int mSourceRotation; private final int mExtraRotation; @@ -53,9 +54,11 @@ public VideoTrackTranscoder( @NonNull DataSource dataSource, @NonNull DataSink dataSink, @NonNull TimeInterpolator timeInterpolator, + @NonNull VideoTrackStrategy strategy, int rotation) { super(dataSource, dataSink, TrackType.VIDEO); mTimeInterpolator = timeInterpolator; + mStrategy = strategy; mSourceRotation = dataSource.getOrientation(); mExtraRotation = rotation; } @@ -130,7 +133,7 @@ protected void onCodecsStarted(@NonNull MediaFormat inputFormat, @NonNull MediaF } else if (inputRatio < outputRatio) { // Input taller. We have a scaleY. scaleY = outputRatio / inputRatio; } - mDecoderOutputSurface.setScale(scaleX, scaleY); + mStrategy.scaleOutput(mDecoderOutputSurface, scaleX, scaleY); } @Override diff --git a/lib/src/main/java/com/otaliastudios/transcoder/transcode/internal/VideoDecoderOutput.java b/lib/src/main/java/com/otaliastudios/transcoder/transcode/internal/VideoDecoderOutput.java index 7f052561..efa20220 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/transcode/internal/VideoDecoderOutput.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/transcode/internal/VideoDecoderOutput.java @@ -87,6 +87,21 @@ public void setScale(float scaleX, float scaleY) { mScaleY = scaleY; } + /** + * Scale the canvas along the two axes. + * @param scaleX x scale + * @param scaleY y scale + */ + @SuppressWarnings("unused") + public void setDrawableScale(float scaleX, float scaleY) { + mDrawable.setRect( + -1.0F * scaleX, + -1.0F * scaleY, + 1.0F * scaleX, + 1.0F * scaleY + ); + } + /** * Sets the desired frame rotation with respect * to its natural orientation. From a772879875028293887b8452e641b187ad581370 Mon Sep 17 00:00:00 2001 From: Christian Bernier Date: Thu, 30 Apr 2020 21:10:30 -0400 Subject: [PATCH 2/2] - Replace the scaleOutput video strategy by a VideoScaler TranscoderOption - Add the VideoScaler TranscoderOptions to the demo app --- .../transcoder/demo/TranscoderActivity.java | 19 ++++++++-- .../main/res/layout/activity_transcoder.xml | 36 +++++++++++++++++++ .../transcoder/TranscoderOptions.java | 34 +++++++++++++++--- .../transcoder/engine/Engine.java | 2 +- .../transcoder/scale/DownVideoScaler.java | 20 +++++++++++ .../transcoder/scale/StretchVideoScaler.java | 16 +++++++++ .../transcoder/scale/UpVideoScaler.java | 16 +++++++++ .../VideoScaler.java} | 16 +++++---- .../strategy/DefaultVideoStrategy.java | 7 +--- .../transcode/VideoTrackTranscoder.java | 10 +++--- 10 files changed, 150 insertions(+), 26 deletions(-) create mode 100644 lib/src/main/java/com/otaliastudios/transcoder/scale/DownVideoScaler.java create mode 100644 lib/src/main/java/com/otaliastudios/transcoder/scale/StretchVideoScaler.java create mode 100644 lib/src/main/java/com/otaliastudios/transcoder/scale/UpVideoScaler.java rename lib/src/main/java/com/otaliastudios/transcoder/{strategy/VideoTrackStrategy.java => scale/VideoScaler.java} (53%) diff --git a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java index 292f41bc..6702624c 100644 --- a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java +++ b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java @@ -20,6 +20,10 @@ import com.otaliastudios.transcoder.engine.TrackStatus; import com.otaliastudios.transcoder.engine.TrackType; import com.otaliastudios.transcoder.internal.Logger; +import com.otaliastudios.transcoder.scale.DownVideoScaler; +import com.otaliastudios.transcoder.scale.StretchVideoScaler; +import com.otaliastudios.transcoder.scale.UpVideoScaler; +import com.otaliastudios.transcoder.scale.VideoScaler; import com.otaliastudios.transcoder.sink.DataSink; import com.otaliastudios.transcoder.sink.DefaultDataSink; import com.otaliastudios.transcoder.source.DataSource; @@ -29,7 +33,6 @@ import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy; import com.otaliastudios.transcoder.strategy.RemoveTrackStrategy; import com.otaliastudios.transcoder.strategy.TrackStrategy; -import com.otaliastudios.transcoder.strategy.VideoTrackStrategy; import com.otaliastudios.transcoder.strategy.size.AspectRatioResizer; import com.otaliastudios.transcoder.strategy.size.FractionResizer; import com.otaliastudios.transcoder.strategy.size.PassThroughResizer; @@ -60,6 +63,7 @@ public class TranscoderActivity extends AppCompatActivity implements private RadioGroup mVideoFramesGroup; private RadioGroup mVideoResolutionGroup; private RadioGroup mVideoAspectGroup; + private RadioGroup mVideoScalingGroup; private RadioGroup mVideoRotationGroup; private RadioGroup mSpeedGroup; private RadioGroup mAudioReplaceGroup; @@ -79,7 +83,7 @@ public class TranscoderActivity extends AppCompatActivity implements private Uri mAudioReplacementUri; private File mTranscodeOutputFile; private long mTranscodeStartTime; - private VideoTrackStrategy mTranscodeVideoStrategy; + private TrackStrategy mTranscodeVideoStrategy; private TrackStrategy mTranscodeAudioStrategy; private long mTrimStartUs = 0; private long mTrimEndUs = 0; @@ -135,6 +139,7 @@ protected void onCreate(Bundle savedInstanceState) { mVideoFramesGroup = findViewById(R.id.frames); mVideoResolutionGroup = findViewById(R.id.resolution); mVideoAspectGroup = findViewById(R.id.aspect); + mVideoScalingGroup = findViewById(R.id.scale); mVideoRotationGroup = findViewById(R.id.rotation); mSpeedGroup = findViewById(R.id.speed); mAudioSampleRateGroup = findViewById(R.id.sampleRate); @@ -144,6 +149,7 @@ protected void onCreate(Bundle savedInstanceState) { mVideoFramesGroup.setOnCheckedChangeListener(mRadioGroupListener); mVideoResolutionGroup.setOnCheckedChangeListener(mRadioGroupListener); mVideoAspectGroup.setOnCheckedChangeListener(mRadioGroupListener); + mVideoScalingGroup.setOnCheckedChangeListener(mRadioGroupListener); mAudioSampleRateGroup.setOnCheckedChangeListener(mRadioGroupListener); mTrimStartView.addTextChangedListener(mTextListener); mTrimEndView.addTextChangedListener(mTextListener); @@ -297,11 +303,18 @@ private void transcode() { default: speed = 1F; } + VideoScaler videoScaler; + switch (mVideoScalingGroup.getCheckedRadioButtonId()) { + case R.id.scale_down: videoScaler = new DownVideoScaler(); break; + case R.id.scale_stretch: videoScaler = new StretchVideoScaler(); break; + default: videoScaler = new UpVideoScaler(); break; + } + // Launch the transcoding operation. mTranscodeStartTime = SystemClock.uptimeMillis(); setIsTranscoding(true); DataSink sink = new DefaultDataSink(mTranscodeOutputFile.getAbsolutePath()); - TranscoderOptions.Builder builder = Transcoder.into(sink); + TranscoderOptions.Builder builder = Transcoder.into(sink).setVideoScaler(videoScaler); if (mAudioReplacementUri == null) { if (mTranscodeInputUri1 != null) { DataSource source = new UriDataSource(this, mTranscodeInputUri1); diff --git a/demo/src/main/res/layout/activity_transcoder.xml b/demo/src/main/res/layout/activity_transcoder.xml index 8034a75c..87ccda03 100644 --- a/demo/src/main/res/layout/activity_transcoder.xml +++ b/demo/src/main/res/layout/activity_transcoder.xml @@ -205,6 +205,42 @@ android:layout_height="wrap_content" /> + + + + + + + + videoDataSources; private List audioDataSources; private TrackStrategy audioTrackStrategy; - private VideoTrackStrategy videoTrackStrategy; + private TrackStrategy videoTrackStrategy; private Validator validator; private int rotation; private TimeInterpolator timeInterpolator; private AudioStretcher audioStretcher; private AudioResampler audioResampler; + private VideoScaler videoScaler; TranscoderListener listener; Handler listenerHandler; @@ -77,7 +79,7 @@ public TrackStrategy getAudioTrackStrategy() { } @NonNull - public VideoTrackStrategy getVideoTrackStrategy() { + public TrackStrategy getVideoTrackStrategy() { return videoTrackStrategy; } @@ -105,6 +107,11 @@ public AudioResampler getAudioResampler() { return audioResampler; } + @NonNull + public VideoScaler getVideoScaler() { + return videoScaler; + } + public static class Builder { private DataSink dataSink; private final List audioDataSources = new ArrayList<>(); @@ -112,12 +119,13 @@ public static class Builder { private TranscoderListener listener; private Handler listenerHandler; private TrackStrategy audioTrackStrategy; - private VideoTrackStrategy videoTrackStrategy; + private TrackStrategy videoTrackStrategy; private Validator validator; private int rotation; private TimeInterpolator timeInterpolator; private AudioStretcher audioStretcher; private AudioResampler audioResampler; + private VideoScaler videoScaler; Builder(@NonNull String outPath) { this.dataSink = new DefaultDataSink(outPath); @@ -205,7 +213,7 @@ public Builder setAudioTrackStrategy(@Nullable TrackStrategy trackStrategy) { */ @NonNull @SuppressWarnings("unused") - public Builder setVideoTrackStrategy(@Nullable VideoTrackStrategy videoTrackStrategy) { + public Builder setVideoTrackStrategy(@Nullable TrackStrategy videoTrackStrategy) { this.videoTrackStrategy = videoTrackStrategy; return this; } @@ -319,6 +327,18 @@ public Builder setAudioResampler(@NonNull AudioResampler audioResampler) { return this; } + /** + * Set an {@link VideoScaler} to change the resolution of the video frames + * so that they fit the new resolution + * + */ + @NonNull + @SuppressWarnings("unused") + public Builder setVideoScaler(@NonNull VideoScaler videoScaler) { + this.videoScaler = videoScaler; + return this; + } + /** * Generates muted audio data sources if needed * @return The list of audio data sources including the muted sources @@ -390,6 +410,9 @@ public TranscoderOptions build() { if (audioResampler == null) { audioResampler = new DefaultAudioResampler(); } + if (videoScaler == null) { + videoScaler = new UpVideoScaler(); + } TranscoderOptions options = new TranscoderOptions(); options.listener = listener; options.audioDataSources = buildAudioDataSources(); @@ -403,6 +426,7 @@ public TranscoderOptions build() { options.timeInterpolator = timeInterpolator; options.audioStretcher = audioStretcher; options.audioResampler = audioResampler; + options.videoScaler = videoScaler; return options; } diff --git a/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java b/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java index 17f5cc4b..18e2e683 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/engine/Engine.java @@ -171,7 +171,7 @@ private void openCurrentStep(@NonNull TrackType type, @NonNull TranscoderOptions case VIDEO: transcoder = new VideoTrackTranscoder(dataSource, mDataSink, interpolator, - options.getVideoTrackStrategy(), + options.getVideoScaler(), options.getVideoRotation()); break; case AUDIO: diff --git a/lib/src/main/java/com/otaliastudios/transcoder/scale/DownVideoScaler.java b/lib/src/main/java/com/otaliastudios/transcoder/scale/DownVideoScaler.java new file mode 100644 index 00000000..b239dc23 --- /dev/null +++ b/lib/src/main/java/com/otaliastudios/transcoder/scale/DownVideoScaler.java @@ -0,0 +1,20 @@ +package com.otaliastudios.transcoder.scale; + +import androidx.annotation.NonNull; + +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; + +/** + * An {@link VideoScaler} that scale the video down so that no side exceed the new resolution + * Sides that are too small will have black borders + */ +public class DownVideoScaler implements VideoScaler { + @Override + public void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY, boolean flipped) { + if (flipped) { // The drawable is not affected by the flip so we need to reverse it + videoDecoderOutput.setDrawableScale(1.0F / scaleX, 1.0F / scaleY); + } else { + videoDecoderOutput.setDrawableScale(1.0F / scaleY, 1.0F / scaleX); + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/otaliastudios/transcoder/scale/StretchVideoScaler.java b/lib/src/main/java/com/otaliastudios/transcoder/scale/StretchVideoScaler.java new file mode 100644 index 00000000..8862f229 --- /dev/null +++ b/lib/src/main/java/com/otaliastudios/transcoder/scale/StretchVideoScaler.java @@ -0,0 +1,16 @@ +package com.otaliastudios.transcoder.scale; + +import androidx.annotation.NonNull; + +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; + +/** + * An {@link VideoScaler} that strech the video so that they match the video resolution + * at the cost of deforming the images + */ +public class StretchVideoScaler implements VideoScaler { + @Override + public void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY, boolean flipped) { + //No scaling will automatically stretch the frames to fill all the drawable space + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/otaliastudios/transcoder/scale/UpVideoScaler.java b/lib/src/main/java/com/otaliastudios/transcoder/scale/UpVideoScaler.java new file mode 100644 index 00000000..444eab65 --- /dev/null +++ b/lib/src/main/java/com/otaliastudios/transcoder/scale/UpVideoScaler.java @@ -0,0 +1,16 @@ +package com.otaliastudios.transcoder.scale; + +import androidx.annotation.NonNull; + +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; + +/** + * An {@link VideoScaler} that scale the video up so that it touches all sides + * of the new resolution and exceeding parts will be truncated + */ +public class UpVideoScaler implements VideoScaler { + @Override + public void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY, boolean flipped) { + videoDecoderOutput.setScale(scaleX, scaleY); + } +} diff --git a/lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java b/lib/src/main/java/com/otaliastudios/transcoder/scale/VideoScaler.java similarity index 53% rename from lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java rename to lib/src/main/java/com/otaliastudios/transcoder/scale/VideoScaler.java index d52e72da..b012361f 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/strategy/VideoTrackStrategy.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/scale/VideoScaler.java @@ -1,18 +1,22 @@ -package com.otaliastudios.transcoder.strategy; +package com.otaliastudios.transcoder.scale; import androidx.annotation.NonNull; -import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; -public interface VideoTrackStrategy extends TrackStrategy { +import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; +/** + * Scale the frames when they are not of the expected resolution + */ +public interface VideoScaler { /** * Apply the scaling to the video decoder output * * It can be done using VideoDecoderOutput.setScale or VideoDecoderOutput.setDrawableScale * * @param videoDecoderOutput the video decoder output - * @param scaleX the expected x scaling - * @param scaleY the expected y scaling + * @param scaleX the input width/height + * @param scaleY the output width/height + * @param flipped whether or not the frame was rotated by 90 degrees */ - void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY); + void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY, boolean flipped); } diff --git a/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java b/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java index 82f9639f..5335b3ed 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/strategy/DefaultVideoStrategy.java @@ -26,7 +26,7 @@ * An {@link TrackStrategy} for video that converts it AVC with the given size. * The input and output aspect ratio must match. */ -public class DefaultVideoStrategy implements VideoTrackStrategy { +public class DefaultVideoStrategy implements TrackStrategy { private final static String TAG = DefaultVideoStrategy.class.getSimpleName(); private final static Logger LOG = new Logger(TAG); @@ -291,11 +291,6 @@ public TrackStatus createOutputFormat(@NonNull List inputFormats, return TrackStatus.COMPRESSING; } - @Override - public void scaleOutput(@NonNull VideoDecoderOutput videoDecoderOutput, float scaleX, float scaleY) { - videoDecoderOutput.setScale(scaleX, scaleY); - } - private boolean checkMimeType(@NonNull List formats) { for (MediaFormat format : formats) { if (!format.getString(MediaFormat.KEY_MIME).equalsIgnoreCase(options.targetMimeType)) { diff --git a/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java b/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java index ea1dea5d..c4e1e573 100644 --- a/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java +++ b/lib/src/main/java/com/otaliastudios/transcoder/transcode/VideoTrackTranscoder.java @@ -24,9 +24,9 @@ import com.otaliastudios.transcoder.internal.Logger; import com.otaliastudios.transcoder.internal.MediaCodecBuffers; import com.otaliastudios.transcoder.internal.MediaFormatConstants; +import com.otaliastudios.transcoder.scale.VideoScaler; import com.otaliastudios.transcoder.sink.DataSink; import com.otaliastudios.transcoder.source.DataSource; -import com.otaliastudios.transcoder.strategy.VideoTrackStrategy; import com.otaliastudios.transcoder.time.TimeInterpolator; import com.otaliastudios.transcoder.transcode.internal.VideoDecoderOutput; import com.otaliastudios.transcoder.transcode.internal.VideoEncoderInput; @@ -46,7 +46,7 @@ public class VideoTrackTranscoder extends BaseTrackTranscoder { private MediaCodec mEncoder; // Keep this since we want to signal EOS on it. private VideoFrameDropper mFrameDropper; private final TimeInterpolator mTimeInterpolator; - private final VideoTrackStrategy mStrategy; + private final VideoScaler mVideoScaler; private final int mSourceRotation; private final int mExtraRotation; @@ -54,11 +54,11 @@ public VideoTrackTranscoder( @NonNull DataSource dataSource, @NonNull DataSink dataSink, @NonNull TimeInterpolator timeInterpolator, - @NonNull VideoTrackStrategy strategy, + @NonNull VideoScaler videoScaler, int rotation) { super(dataSource, dataSink, TrackType.VIDEO); mTimeInterpolator = timeInterpolator; - mStrategy = strategy; + mVideoScaler = videoScaler; mSourceRotation = dataSource.getOrientation(); mExtraRotation = rotation; } @@ -133,7 +133,7 @@ protected void onCodecsStarted(@NonNull MediaFormat inputFormat, @NonNull MediaF } else if (inputRatio < outputRatio) { // Input taller. We have a scaleY. scaleY = outputRatio / inputRatio; } - mStrategy.scaleOutput(mDecoderOutputSurface, scaleX, scaleY); + mVideoScaler.scaleOutput(mDecoderOutputSurface, scaleX, scaleY, flip); } @Override