Remove setInputFrameInfo
After this change, every queued bitmap is treated as an individual input stream (like a new MediaItems). This change merges the FrameDropTest and FrameDropPixelTest into one (while maintaining all the test cases) - This is accomplished by generating bitmaps with timestamps on it in FrameDropTest and compare them with goldens (one may call this a pixel test, please lmk if you want this to be renamed) - The most part of the change comes from DefaultVideoFrameProcessorVideoFrameRenderingTest. The overall working is - We bypass the input manager - The TestFrameGenerator generates frames based on timestamps. In this case, we generate frames with timestamps on it - The generated frame is sent to texture output and in turn saved to bitmaps - We then compare the generated bitmap with the goldens PiperOrigin-RevId: 551795770
This commit is contained in:
parent
ebbcec9a20
commit
c221958889
@ -168,8 +168,8 @@ public interface VideoFrameProcessor {
|
||||
/**
|
||||
* Provides an input texture ID to the {@code VideoFrameProcessor}.
|
||||
*
|
||||
* <p>It must be called after the {@link #setOnInputFrameProcessedListener
|
||||
* onInputFrameProcessedListener} and the {@link #setInputFrameInfo frameInfo} have been set.
|
||||
* <p>It must be only called after {@link #setOnInputFrameProcessedListener} and {@link
|
||||
* #registerInputStream} have been called.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
*
|
||||
@ -191,6 +191,10 @@ public interface VideoFrameProcessor {
|
||||
* Returns the input {@link Surface}, where {@link VideoFrameProcessor} consumes input frames
|
||||
* from.
|
||||
*
|
||||
* <p>The frames arriving on the {@link Surface} will not be consumed by the {@code
|
||||
* VideoFrameProcessor} until {@link #registerInputStream} is called with {@link
|
||||
* #INPUT_TYPE_SURFACE}.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
*
|
||||
* @throws UnsupportedOperationException If the {@code VideoFrameProcessor} does not accept
|
||||
@ -202,27 +206,11 @@ public interface VideoFrameProcessor {
|
||||
* Informs the {@code VideoFrameProcessor} that a new input stream will be queued with the list of
|
||||
* {@link Effect Effects} to apply to the new input stream.
|
||||
*
|
||||
* <p>Call {@link #setInputFrameInfo} before this method if the {@link FrameInfo} of the new input
|
||||
* stream differs from that of the current input stream.
|
||||
*
|
||||
* @param inputType The {@link InputType} of the new input stream.
|
||||
* @param effects The list of {@link Effect effects} to apply to the new input stream.
|
||||
* @param frameInfo The {@link FrameInfo} of the new input stream.
|
||||
*/
|
||||
// TODO(b/286032822): Merge this and setInputFrameInfo.
|
||||
void registerInputStream(@InputType int inputType, List<Effect> effects);
|
||||
|
||||
/**
|
||||
* Sets information about the input frames.
|
||||
*
|
||||
* <p>The new input information is applied from the next frame {@linkplain #registerInputFrame()
|
||||
* registered} or {@linkplain #queueInputTexture} queued} onwards.
|
||||
*
|
||||
* <p>Pixels are expanded using the {@link FrameInfo#pixelWidthHeightRatio} so that the output
|
||||
* frames' pixels have a ratio of 1.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
*/
|
||||
void setInputFrameInfo(FrameInfo inputFrameInfo);
|
||||
void registerInputStream(@InputType int inputType, List<Effect> effects, FrameInfo frameInfo);
|
||||
|
||||
/**
|
||||
* Informs the {@code VideoFrameProcessor} that a frame will be queued to its {@linkplain
|
||||
@ -235,7 +223,7 @@ public interface VideoFrameProcessor {
|
||||
* @throws UnsupportedOperationException If the {@code VideoFrameProcessor} does not accept
|
||||
* {@linkplain #INPUT_TYPE_SURFACE surface input}.
|
||||
* @throws IllegalStateException If called after {@link #signalEndOfInput()} or before {@link
|
||||
* #setInputFrameInfo(FrameInfo)}.
|
||||
* #registerInputStream}.
|
||||
*/
|
||||
void registerInputFrame();
|
||||
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Produces blank frames with the given timestamps. */
|
||||
/* package */ final class BlankFrameProducer implements GlShaderProgram {
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
private @MonotonicNonNull GlTextureInfo blankTexture;
|
||||
private @MonotonicNonNull OutputListener outputListener;
|
||||
|
||||
public BlankFrameProducer(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void configureGlObjects() throws VideoFrameProcessingException {
|
||||
try {
|
||||
int texId = GlUtil.createTexture(width, height, /* useHighPrecisionColorComponents= */ false);
|
||||
int fboId = GlUtil.createFboForTexture(texId);
|
||||
blankTexture = new GlTextureInfo(texId, fboId, /* rboId= */ C.INDEX_UNSET, width, height);
|
||||
GlUtil.focusFramebufferUsingCurrentContext(fboId, width, height);
|
||||
GlUtil.clearFocusedBuffers();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void produceBlankFrames(List<Long> presentationTimesUs) {
|
||||
checkNotNull(outputListener);
|
||||
for (long presentationTimeUs : presentationTimesUs) {
|
||||
outputListener.onOutputFrameAvailable(checkNotNull(blankTexture), presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputListener(InputListener inputListener) {}
|
||||
|
||||
@Override
|
||||
public void setOutputListener(OutputListener outputListener) {
|
||||
this.outputListener = outputListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorListener(Executor executor, ErrorListener errorListener) {}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
// No input is queued in these tests. The BlankFrameProducer is used to produce frames.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseOutputFrame(GlTextureInfo outputTexture) {}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
checkNotNull(outputListener).onCurrentOutputStreamEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
// Do nothing as destroying the OpenGL context destroys the texture.
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
@ -195,7 +194,6 @@ public class DefaultVideoFrameProcessorImageFrameOutputTest {
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory.Builder().build())
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setOnOutputFrameAvailableForRenderingListener(
|
||||
unused -> checkNotNull(framesProduced).incrementAndGet());
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
|
||||
@ -158,7 +157,6 @@ public final class DefaultVideoFrameProcessorPixelTest {
|
||||
String testId = "noEffects_withImageInput_matchesGoldenFile";
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.build();
|
||||
Bitmap originalBitmap = readBitmap(IMAGE_PNG_ASSET_PATH);
|
||||
@ -180,7 +178,6 @@ public final class DefaultVideoFrameProcessorPixelTest {
|
||||
String testId = "wrappedCrop_withImageInput_matchesGoldenFile";
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setEffects(
|
||||
new GlEffectWrapper(
|
||||
@ -214,7 +211,6 @@ public final class DefaultVideoFrameProcessorPixelTest {
|
||||
new DefaultVideoFrameProcessor.Factory.Builder()
|
||||
.setEnableColorTransfers(false)
|
||||
.build())
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setEffects(NO_OP_EFFECT)
|
||||
.build();
|
||||
|
@ -350,8 +350,8 @@ public final class DefaultVideoFrameProcessorVideoFrameRenderingTest {
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.registerInputStream(
|
||||
INPUT_TYPE_SURFACE,
|
||||
/* effects= */ ImmutableList.of((GlEffect) (context, useHdr) -> blankFrameProducer));
|
||||
defaultVideoFrameProcessor.setInputFrameInfo(new FrameInfo.Builder(WIDTH, HEIGHT).build());
|
||||
/* effects= */ ImmutableList.of((GlEffect) (context, useHdr) -> blankFrameProducer),
|
||||
new FrameInfo.Builder(WIDTH, HEIGHT).build());
|
||||
blankFrameProducer.produceBlankFramesAndQueueEndOfStream(inputPresentationTimesUs);
|
||||
defaultVideoFrameProcessor.signalEndOfInput();
|
||||
videoFrameProcessingEndedCountDownLatch.await();
|
||||
|
@ -5,7 +5,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@ -15,18 +15,38 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.test.utils.TextureBitmapReader;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
@ -38,108 +58,184 @@ import org.junit.runner.RunWith;
|
||||
/** Tests for {@link FrameDropEffect}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FrameDropTest {
|
||||
private static final String ORIGINAL_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
|
||||
private static final String ASSET_PATH = "media/bitmap/FrameDropTest";
|
||||
private static final int BLANK_FRAME_WIDTH = 100;
|
||||
private static final int BLANK_FRAME_HEIGHT = 50;
|
||||
|
||||
private static final String SCALE_WIDE_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/scale_wide.png";
|
||||
private @MonotonicNonNull TextureBitmapReader textureBitmapReader;
|
||||
private @MonotonicNonNull DefaultVideoFrameProcessor defaultVideoFrameProcessor;
|
||||
|
||||
private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner;
|
||||
|
||||
private @MonotonicNonNull Queue<Long> actualPresentationTimesUs;
|
||||
|
||||
@EnsuresNonNull("actualPresentationTimesUs")
|
||||
@EnsuresNonNull("textureBitmapReader")
|
||||
@Before
|
||||
public void setUp() {
|
||||
actualPresentationTimesUs = new ConcurrentLinkedQueue<>();
|
||||
textureBitmapReader = new TextureBitmapReader();
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() {
|
||||
checkNotNull(videoFrameProcessorTestRunner).release();
|
||||
public void tearDown() {
|
||||
checkNotNull(defaultVideoFrameProcessor).release();
|
||||
}
|
||||
|
||||
@RequiresNonNull("actualPresentationTimesUs")
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
@Test
|
||||
public void frameDrop_withDefaultStrategy_outputsFramesAtTheCorrectPresentationTimesUs()
|
||||
throws Exception {
|
||||
String testId = "frameDrop_withDefaultStrategy_outputsFramesAtTheCorrectPresentationTimesUs";
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(
|
||||
testId, FrameDropEffect.createDefaultFrameDropEffect(/* targetFrameRate= */ 30))
|
||||
.setOnOutputFrameAvailableForRenderingListener(actualPresentationTimesUs::add)
|
||||
.build();
|
||||
ImmutableList<Long> frameTimesUs =
|
||||
ImmutableList.of(0L, 16_000L, 32_000L, 48_000L, 58_000L, 71_000L, 86_000L);
|
||||
|
||||
ImmutableList<Integer> timestampsMs = ImmutableList.of(0, 16, 32, 48, 58, 71, 86);
|
||||
for (int timestampMs : timestampsMs) {
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(ORIGINAL_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ timestampMs * 1000L,
|
||||
/* frameRate= */ 1);
|
||||
}
|
||||
videoFrameProcessorTestRunner.endFrameProcessing();
|
||||
ImmutableList<Long> actualPresentationTimesUs =
|
||||
processFramesToEndOfStream(
|
||||
frameTimesUs, FrameDropEffect.createDefaultFrameDropEffect(/* targetFrameRate= */ 30));
|
||||
|
||||
assertThat(actualPresentationTimesUs).containsExactly(0L, 32_000L, 71_000L).inOrder();
|
||||
getAndAssertOutputBitmaps(textureBitmapReader, actualPresentationTimesUs, testId);
|
||||
}
|
||||
|
||||
@RequiresNonNull("actualPresentationTimesUs")
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
@Test
|
||||
public void frameDrop_withSimpleStrategy_outputsFramesAtTheCorrectPresentationTimesUs()
|
||||
throws Exception {
|
||||
String testId = "frameDrop_withSimpleStrategy_outputsFramesAtTheCorrectPresentationTimesUs";
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(
|
||||
testId,
|
||||
ImmutableList<Long> frameTimesUs =
|
||||
ImmutableList.of(0L, 250_000L, 500_000L, 750_000L, 1_000_000L, 1_500_000L);
|
||||
|
||||
ImmutableList<Long> actualPresentationTimesUs =
|
||||
processFramesToEndOfStream(
|
||||
frameTimesUs,
|
||||
FrameDropEffect.createSimpleFrameDropEffect(
|
||||
/* expectedFrameRate= */ 6, /* targetFrameRate= */ 2))
|
||||
.build();
|
||||
/* expectedFrameRate= */ 6, /* targetFrameRate= */ 2));
|
||||
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(ORIGINAL_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ 0L,
|
||||
/* frameRate= */ 4);
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(SCALE_WIDE_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ C.MICROS_PER_SECOND,
|
||||
/* frameRate= */ 2);
|
||||
videoFrameProcessorTestRunner.endFrameProcessing();
|
||||
|
||||
assertThat(actualPresentationTimesUs).containsExactly(500_000L, 1_500_000L).inOrder();
|
||||
assertThat(actualPresentationTimesUs).containsExactly(0L, 750_000L).inOrder();
|
||||
getAndAssertOutputBitmaps(textureBitmapReader, actualPresentationTimesUs, testId);
|
||||
}
|
||||
|
||||
@RequiresNonNull("actualPresentationTimesUs")
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
@Test
|
||||
public void frameDrop_withSimpleStrategy_outputsAllFrames() throws Exception {
|
||||
String testId = "frameDrop_withSimpleStrategy_outputsCorrectNumberOfFrames";
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(
|
||||
testId,
|
||||
FrameDropEffect.createSimpleFrameDropEffect(
|
||||
/* expectedFrameRate= */ 3, /* targetFrameRate= */ 3))
|
||||
.build();
|
||||
String testId = "frameDrop_withSimpleStrategy_outputsAllFrames";
|
||||
ImmutableList<Long> frameTimesUs = ImmutableList.of(0L, 333_333L, 666_667L);
|
||||
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(ORIGINAL_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ 0L,
|
||||
/* frameRate= */ 3);
|
||||
videoFrameProcessorTestRunner.endFrameProcessing();
|
||||
ImmutableList<Long> actualPresentationTimesUs =
|
||||
processFramesToEndOfStream(
|
||||
frameTimesUs,
|
||||
FrameDropEffect.createSimpleFrameDropEffect(
|
||||
/* expectedFrameRate= */ 3, /* targetFrameRate= */ 3));
|
||||
|
||||
assertThat(actualPresentationTimesUs).containsExactly(0L, 333_333L, 666_667L).inOrder();
|
||||
getAndAssertOutputBitmaps(textureBitmapReader, actualPresentationTimesUs, testId);
|
||||
}
|
||||
|
||||
@RequiresNonNull("actualPresentationTimesUs")
|
||||
private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
|
||||
String testId, FrameDropEffect frameDropEffect) {
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory.Builder().build())
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setEffects(frameDropEffect)
|
||||
.setOnOutputFrameAvailableForRenderingListener(actualPresentationTimesUs::add);
|
||||
private static void getAndAssertOutputBitmaps(
|
||||
TextureBitmapReader textureBitmapReader, List<Long> presentationTimesUs, String testId)
|
||||
throws IOException {
|
||||
for (int i = 0; i < presentationTimesUs.size(); i++) {
|
||||
long presentationTimeUs = presentationTimesUs.get(i);
|
||||
Bitmap actualBitmap = textureBitmapReader.getBitmap(presentationTimeUs);
|
||||
Bitmap expectedBitmap =
|
||||
readBitmap(Util.formatInvariant("%s/pts_%d.png", ASSET_PATH, presentationTimeUs));
|
||||
maybeSaveTestBitmap(
|
||||
testId, String.valueOf(presentationTimeUs), actualBitmap, /* path= */ null);
|
||||
float averagePixelAbsoluteDifference =
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, testId);
|
||||
assertThat(averagePixelAbsoluteDifference)
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||
}
|
||||
}
|
||||
|
||||
@EnsuresNonNull("defaultVideoFrameProcessor")
|
||||
private ImmutableList<Long> processFramesToEndOfStream(
|
||||
List<Long> inputPresentationTimesUs, FrameDropEffect frameDropEffect) throws Exception {
|
||||
AtomicReference<@NullableType VideoFrameProcessingException>
|
||||
videoFrameProcessingExceptionReference = new AtomicReference<>();
|
||||
BlankFrameProducer blankFrameProducer =
|
||||
new BlankFrameProducer(BLANK_FRAME_WIDTH, BLANK_FRAME_HEIGHT);
|
||||
CountDownLatch videoFrameProcessingEndedCountDownLatch = new CountDownLatch(1);
|
||||
ImmutableList.Builder<Long> actualPresentationTimesUs = new ImmutableList.Builder<>();
|
||||
|
||||
defaultVideoFrameProcessor =
|
||||
checkNotNull(
|
||||
new DefaultVideoFrameProcessor.Factory.Builder()
|
||||
.setTextureOutput(
|
||||
(outputTexture, presentationTimeUs, releaseOutputTextureCallback, token) -> {
|
||||
checkNotNull(textureBitmapReader)
|
||||
.readBitmap(outputTexture, presentationTimeUs);
|
||||
releaseOutputTextureCallback.release(presentationTimeUs);
|
||||
},
|
||||
/* textureOutputCapacity= */ 1)
|
||||
.build()
|
||||
.create(
|
||||
getApplicationContext(),
|
||||
DebugViewProvider.NONE,
|
||||
/* inputColorInfo= */ ColorInfo.SDR_BT709_LIMITED,
|
||||
/* outputColorInfo= */ ColorInfo.SDR_BT709_LIMITED,
|
||||
/* renderFramesAutomatically= */ true,
|
||||
MoreExecutors.directExecutor(),
|
||||
new VideoFrameProcessor.Listener() {
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {}
|
||||
|
||||
@Override
|
||||
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {
|
||||
actualPresentationTimesUs.add(presentationTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
videoFrameProcessingExceptionReference.set(exception);
|
||||
videoFrameProcessingEndedCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnded() {
|
||||
videoFrameProcessingEndedCountDownLatch.countDown();
|
||||
}
|
||||
}));
|
||||
|
||||
defaultVideoFrameProcessor.getTaskExecutor().submit(blankFrameProducer::configureGlObjects);
|
||||
// A frame needs to be registered despite not queuing any external input to ensure
|
||||
// that the video frame processor knows about the stream offset.
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.registerInputStream(
|
||||
INPUT_TYPE_SURFACE,
|
||||
/* effects= */ ImmutableList.of(
|
||||
(GlEffect) (context, useHdr) -> blankFrameProducer,
|
||||
// Use an overlay effect to generate bitmaps with timestamps on it.
|
||||
new OverlayEffect(
|
||||
ImmutableList.of(
|
||||
new TextOverlay() {
|
||||
@Override
|
||||
public SpannableString getText(long presentationTimeUs) {
|
||||
SpannableString text =
|
||||
new SpannableString(String.valueOf(presentationTimeUs));
|
||||
text.setSpan(
|
||||
new ForegroundColorSpan(Color.BLACK),
|
||||
/* start= */ 0,
|
||||
text.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
text.setSpan(
|
||||
new AbsoluteSizeSpan(/* size= */ 24),
|
||||
/* start= */ 0,
|
||||
text.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
text.setSpan(
|
||||
new TypefaceSpan(/* family= */ "sans-serif"),
|
||||
/* start= */ 0,
|
||||
text.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return text;
|
||||
}
|
||||
})),
|
||||
frameDropEffect),
|
||||
new FrameInfo.Builder(BLANK_FRAME_WIDTH, BLANK_FRAME_HEIGHT).build());
|
||||
blankFrameProducer.produceBlankFrames(inputPresentationTimesUs);
|
||||
defaultVideoFrameProcessor.signalEndOfInput();
|
||||
videoFrameProcessingEndedCountDownLatch.await();
|
||||
@Nullable
|
||||
Exception videoFrameProcessingException = videoFrameProcessingExceptionReference.get();
|
||||
if (videoFrameProcessingException != null) {
|
||||
throw videoFrameProcessingException;
|
||||
}
|
||||
return actualPresentationTimesUs.build();
|
||||
}
|
||||
}
|
||||
|
@ -88,8 +88,10 @@ import androidx.media3.common.util.Size;
|
||||
|
||||
copyTextureToPreviousFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
getInputListener().onInputFrameProcessed(inputTexture);
|
||||
if (outputTexturePool.freeTextureCount() > 0) {
|
||||
getInputListener().onReadyToAcceptInputFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
|
@ -418,7 +418,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
public void queueInputBitmap(Bitmap inputBitmap, long durationUs, float frameRate) {
|
||||
checkState(
|
||||
hasRefreshedNextInputFrameInfo,
|
||||
"setInputFrameInfo must be called before queueing another bitmap");
|
||||
"registerInputStream must be called before queueing another bitmap");
|
||||
inputSwitcher
|
||||
.activeTextureManager()
|
||||
.queueInputBitmap(
|
||||
@ -442,15 +442,19 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
|
||||
@Override
|
||||
public Surface getInputSurface() {
|
||||
return inputSwitcher.activeTextureManager().getInputSurface();
|
||||
return inputSwitcher.getInputSurface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInputStream(@InputType int inputType, List<Effect> effects) {
|
||||
public void registerInputStream(
|
||||
@InputType int inputType, List<Effect> effects, FrameInfo frameInfo) {
|
||||
nextInputFrameInfo = adjustForPixelWidthHeightRatio(frameInfo);
|
||||
hasRefreshedNextInputFrameInfo = true;
|
||||
synchronized (lock) {
|
||||
if (!processingInput) {
|
||||
videoFrameProcessingTaskExecutor.submitAndBlock(() -> configureEffects(effects));
|
||||
inputSwitcher.switchToInput(inputType);
|
||||
inputSwitcher.switchToInput(inputType, nextInputFrameInfo);
|
||||
inputSwitcher.activeTextureManager().setInputFrameInfo(nextInputFrameInfo);
|
||||
processingInput = true;
|
||||
return;
|
||||
}
|
||||
@ -477,21 +481,14 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
// a new frame from the new input stream prematurely.
|
||||
videoFrameProcessingTaskExecutor.submitAndBlock(() -> configureEffects(effects));
|
||||
}
|
||||
inputSwitcher.switchToInput(inputType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFrameInfo(FrameInfo inputFrameInfo) {
|
||||
nextInputFrameInfo = adjustForPixelWidthHeightRatio(inputFrameInfo);
|
||||
inputSwitcher.activeTextureManager().setInputFrameInfo(nextInputFrameInfo);
|
||||
hasRefreshedNextInputFrameInfo = true;
|
||||
inputSwitcher.switchToInput(inputType, nextInputFrameInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInputFrame() {
|
||||
checkState(!inputStreamEnded);
|
||||
checkStateNotNull(
|
||||
nextInputFrameInfo, "setInputFrameInfo must be called before registering input frames");
|
||||
nextInputFrameInfo, "registerInputStream must be called before registering input frames");
|
||||
|
||||
inputSwitcher.activeTextureManager().registerInputFrame(nextInputFrameInfo);
|
||||
hasRefreshedNextInputFrameInfo = false;
|
||||
|
@ -17,6 +17,9 @@
|
||||
|
||||
package androidx.media3.effect;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
@ -24,7 +27,9 @@ import static androidx.media3.common.util.Util.containsKey;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
@ -84,7 +89,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
TextureManager textureManager;
|
||||
// TODO(b/274109008): Refactor DefaultShaderProgram to create a class just for sampling.
|
||||
switch (inputType) {
|
||||
case VideoFrameProcessor.INPUT_TYPE_SURFACE:
|
||||
case INPUT_TYPE_SURFACE:
|
||||
samplingShaderProgram =
|
||||
DefaultShaderProgram.createWithExternalSampler(
|
||||
context,
|
||||
@ -98,7 +103,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
glObjectsProvider, samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputs.put(inputType, new Input(textureManager, samplingShaderProgram));
|
||||
break;
|
||||
case VideoFrameProcessor.INPUT_TYPE_BITMAP:
|
||||
case INPUT_TYPE_BITMAP:
|
||||
samplingShaderProgram =
|
||||
DefaultShaderProgram.createWithInternalSampler(
|
||||
context,
|
||||
@ -113,7 +118,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
glObjectsProvider, samplingShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputs.put(inputType, new Input(textureManager, samplingShaderProgram));
|
||||
break;
|
||||
case VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID:
|
||||
case INPUT_TYPE_TEXTURE_ID:
|
||||
samplingShaderProgram =
|
||||
DefaultShaderProgram.createWithInternalSampler(
|
||||
context,
|
||||
@ -145,8 +150,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* registered}.
|
||||
*
|
||||
* @param newInputType The new {@link VideoFrameProcessor.InputType} to switch to.
|
||||
* @param inputFrameInfo The {@link FrameInfo} associated with the new input.
|
||||
*/
|
||||
public void switchToInput(@VideoFrameProcessor.InputType int newInputType) {
|
||||
public void switchToInput(
|
||||
@VideoFrameProcessor.InputType int newInputType, FrameInfo inputFrameInfo) {
|
||||
checkStateNotNull(downstreamShaderProgram);
|
||||
checkState(containsKey(inputs, newInputType), "Input type not registered: " + newInputType);
|
||||
|
||||
@ -167,6 +174,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
input.setActive(false);
|
||||
}
|
||||
}
|
||||
checkNotNull(activeTextureManager).setInputFrameInfo(inputFrameInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,6 +194,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
checkNotNull(activeTextureManager).signalEndOfCurrentInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input {@link Surface}.
|
||||
*
|
||||
* @return The input {@link Surface}, regardless if the current input is {@linkplain
|
||||
* #switchToInput set} to {@link VideoFrameProcessor#INPUT_TYPE_SURFACE}.
|
||||
* @throws IllegalStateException If {@link VideoFrameProcessor#INPUT_TYPE_SURFACE} is not
|
||||
* {@linkplain #registerInput registered}.
|
||||
*/
|
||||
public Surface getInputSurface() {
|
||||
checkState(containsKey(inputs, INPUT_TYPE_SURFACE));
|
||||
return inputs.get(INPUT_TYPE_SURFACE).textureManager.getInputSurface();
|
||||
}
|
||||
|
||||
/** Releases the resources. */
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
|
@ -57,13 +57,13 @@ import androidx.media3.common.VideoFrameProcessingException;
|
||||
@Override
|
||||
public void queueInputFrame(
|
||||
GlObjectsProvider glObjectsProvider, GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
framesReceived++;
|
||||
if (framesReceived % n == 0) {
|
||||
super.queueInputFrame(glObjectsProvider, inputTexture, presentationTimeUs);
|
||||
} else {
|
||||
getInputListener().onInputFrameProcessed(inputTexture);
|
||||
getInputListener().onReadyToAcceptInputFrame();
|
||||
}
|
||||
framesReceived++;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,7 +73,11 @@ import androidx.media3.common.VideoFrameProcessor;
|
||||
/**
|
||||
* Sets information about the input frames.
|
||||
*
|
||||
* @see VideoFrameProcessor#setInputFrameInfo
|
||||
* <p>The new input information is applied from the next frame {@linkplain #registerInputFrame
|
||||
* registered} or {@linkplain #queueInputTexture queued} onwards.
|
||||
*
|
||||
* <p>Pixels are expanded using the {@link FrameInfo#pixelWidthHeightRatio} so that the output
|
||||
* frames' pixels have a ratio of 1.
|
||||
*/
|
||||
default void setInputFrameInfo(FrameInfo inputFrameInfo) {
|
||||
// Do nothing.
|
||||
|
@ -2142,8 +2142,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
});
|
||||
|
||||
videoFrameProcessor.registerInputStream(
|
||||
VideoFrameProcessor.INPUT_TYPE_SURFACE, videoEffects);
|
||||
this.initialStreamOffsetUs = initialStreamOffsetUs;
|
||||
} catch (Exception e) {
|
||||
throw renderer.createRendererException(
|
||||
@ -2226,7 +2224,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
*/
|
||||
public void setInputFormat(Format inputFormat) {
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setInputFrameInfo(
|
||||
.registerInputStream(
|
||||
VideoFrameProcessor.INPUT_TYPE_SURFACE,
|
||||
checkNotNull(videoEffects),
|
||||
new FrameInfo.Builder(inputFormat.width, inputFormat.height)
|
||||
.setPixelWidthHeightRatio(inputFormat.pixelWidthHeightRatio)
|
||||
.build());
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 591 B |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -13,32 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.transformer;
|
||||
|
||||
package androidx.media3.test.utils;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>Reads from an OpenGL texture. Only for use on physical devices.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.BitmapReader {
|
||||
|
||||
// TODO(b/239172735): This outputs an incorrect black output image on emulators.
|
||||
private final Map<Long, Bitmap> outputTimestampsToBitmaps;
|
||||
private boolean useHighPrecisionColorComponents;
|
||||
@ -60,6 +61,10 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
||||
return checkStateNotNull(outputBitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The output {@link Bitmap} at a given {@code presentationTimeUs}.
|
||||
* @throws IllegalStateException If no such bitmap is produced.
|
||||
*/
|
||||
public Bitmap getBitmap(long presentationTimeUs) {
|
||||
return checkStateNotNull(outputTimestampsToBitmaps.get(presentationTimeUs));
|
||||
}
|
||||
@ -69,6 +74,11 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
||||
return outputTimestampsToBitmaps.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given {@code outputTexture}.
|
||||
*
|
||||
* <p>The read result can be fetched by calling one of me {@link #getBitmap} methods.
|
||||
*/
|
||||
public void readBitmap(GlTextureInfo outputTexture, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
@ -83,15 +93,6 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
||||
}
|
||||
}
|
||||
|
||||
public void readBitmapAndReleaseTexture(
|
||||
GlTextureInfo outputTexture,
|
||||
long presentationTimeUs,
|
||||
DefaultVideoFrameProcessor.ReleaseOutputTextureCallback releaseOutputTextureCallback)
|
||||
throws VideoFrameProcessingException {
|
||||
readBitmap(outputTexture, presentationTimeUs);
|
||||
releaseOutputTextureCallback.release(presentationTimeUs);
|
||||
}
|
||||
|
||||
private static Bitmap createBitmapFromCurrentGlFrameBuffer(
|
||||
int width, int height, boolean useHighPrecisionColorComponents) throws GlUtil.GlException {
|
||||
if (!useHighPrecisionColorComponents) {
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package androidx.media3.test.utils;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.createArgb8888BitmapFromRgba8888Image;
|
||||
@ -41,7 +43,6 @@ import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessor.InputType;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -71,13 +72,11 @@ public final class VideoFrameProcessorTestRunner {
|
||||
private float pixelWidthHeightRatio;
|
||||
private @MonotonicNonNull ColorInfo inputColorInfo;
|
||||
private @MonotonicNonNull ColorInfo outputColorInfo;
|
||||
private @InputType int inputType;
|
||||
private OnOutputFrameAvailableForRenderingListener onOutputFrameAvailableListener;
|
||||
|
||||
/** Creates a new instance with default values. */
|
||||
public Builder() {
|
||||
pixelWidthHeightRatio = DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO;
|
||||
inputType = INPUT_TYPE_SURFACE;
|
||||
onOutputFrameAvailableListener = unused -> {};
|
||||
}
|
||||
|
||||
@ -194,18 +193,6 @@ public final class VideoFrameProcessorTestRunner {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether input comes from an external texture. See {@link
|
||||
* VideoFrameProcessor.Factory#create}.
|
||||
*
|
||||
* <p>The default value is {@link VideoFrameProcessor#INPUT_TYPE_SURFACE}.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setInputType(@InputType int inputType) {
|
||||
this.inputType = inputType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the method to be called in {@link
|
||||
* VideoFrameProcessor.Listener#onOutputFrameAvailableForRendering}.
|
||||
@ -233,7 +220,6 @@ public final class VideoFrameProcessorTestRunner {
|
||||
pixelWidthHeightRatio,
|
||||
inputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : inputColorInfo,
|
||||
outputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : outputColorInfo,
|
||||
inputType,
|
||||
onOutputFrameAvailableListener);
|
||||
}
|
||||
}
|
||||
@ -251,6 +237,7 @@ public final class VideoFrameProcessorTestRunner {
|
||||
private final @MonotonicNonNull CountDownLatch videoFrameProcessingEndedLatch;
|
||||
private final AtomicReference<VideoFrameProcessingException> videoFrameProcessingException;
|
||||
private final VideoFrameProcessor videoFrameProcessor;
|
||||
private final ImmutableList<Effect> effects;
|
||||
|
||||
private @MonotonicNonNull BitmapReader bitmapReader;
|
||||
|
||||
@ -264,7 +251,6 @@ public final class VideoFrameProcessorTestRunner {
|
||||
float pixelWidthHeightRatio,
|
||||
ColorInfo inputColorInfo,
|
||||
ColorInfo outputColorInfo,
|
||||
@InputType int inputType,
|
||||
OnOutputFrameAvailableForRenderingListener onOutputFrameAvailableForRenderingListener)
|
||||
throws VideoFrameProcessingException {
|
||||
this.testId = testId;
|
||||
@ -314,7 +300,7 @@ public final class VideoFrameProcessorTestRunner {
|
||||
checkNotNull(videoFrameProcessingEndedLatch).countDown();
|
||||
}
|
||||
});
|
||||
videoFrameProcessor.registerInputStream(inputType, effects);
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
public void processFirstFrameAndEnd() throws Exception {
|
||||
@ -323,7 +309,9 @@ public final class VideoFrameProcessorTestRunner {
|
||||
new DecodeOneFrameUtil.Listener() {
|
||||
@Override
|
||||
public void onContainerExtracted(MediaFormat mediaFormat) {
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.registerInputStream(
|
||||
INPUT_TYPE_SURFACE,
|
||||
effects,
|
||||
new FrameInfo.Builder(
|
||||
mediaFormat.getInteger(MediaFormat.KEY_WIDTH),
|
||||
mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
|
||||
@ -343,7 +331,9 @@ public final class VideoFrameProcessorTestRunner {
|
||||
|
||||
public void queueInputBitmap(
|
||||
Bitmap inputBitmap, long durationUs, long offsetToAddUs, float frameRate) {
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.registerInputStream(
|
||||
INPUT_TYPE_BITMAP,
|
||||
effects,
|
||||
new FrameInfo.Builder(inputBitmap.getWidth(), inputBitmap.getHeight())
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
.setOffsetToAddUs(offsetToAddUs)
|
||||
@ -352,7 +342,9 @@ public final class VideoFrameProcessorTestRunner {
|
||||
}
|
||||
|
||||
public void queueInputTexture(GlTextureInfo inputTexture, long pts) {
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.registerInputStream(
|
||||
INPUT_TYPE_TEXTURE_ID,
|
||||
effects,
|
||||
new FrameInfo.Builder(inputTexture.width, inputTexture.height)
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
.build());
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
||||
@ -39,6 +38,7 @@ import androidx.media3.effect.RgbFilter;
|
||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||
import androidx.media3.effect.VideoCompositor;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.media3.test.utils.TextureBitmapReader;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
@ -334,7 +334,6 @@ public final class VideoCompositorPixelTest {
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactoryBuilder.build())
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setBitmapReader(textureBitmapReader);
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package androidx.media3.transformer.mh;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
|
||||
@ -28,8 +27,8 @@ import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.media3.test.utils.TextureBitmapReader;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.media3.transformer.TextureBitmapReader;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
@ -142,16 +141,15 @@ public class DefaultVideoFrameProcessorMultipleTextureOutputPixelTest {
|
||||
(outputTexture,
|
||||
presentationTimeUs,
|
||||
releaseOutputTextureCallback,
|
||||
unusedSyncObject) ->
|
||||
checkNotNull(textureBitmapReader)
|
||||
.readBitmapAndReleaseTexture(
|
||||
outputTexture, presentationTimeUs, releaseOutputTextureCallback),
|
||||
unusedSyncObject) -> {
|
||||
checkNotNull(textureBitmapReader).readBitmap(outputTexture, presentationTimeUs);
|
||||
releaseOutputTextureCallback.release(presentationTimeUs);
|
||||
},
|
||||
/* textureOutputCapacity= */ 1)
|
||||
.build();
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory)
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setBitmapReader(textureBitmapReader);
|
||||
}
|
||||
|
@ -38,17 +38,16 @@ import androidx.media3.common.Format;
|
||||
import androidx.media3.common.GlObjectsProvider;
|
||||
import androidx.media3.common.GlTextureInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.effect.BitmapOverlay;
|
||||
import androidx.media3.effect.DefaultGlObjectsProvider;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.effect.OverlayEffect;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.media3.test.utils.TextureBitmapReader;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.media3.transformer.AndroidTestUtil;
|
||||
import androidx.media3.transformer.EncoderUtil;
|
||||
import androidx.media3.transformer.TextureBitmapReader;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
@ -537,9 +536,10 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
|
||||
(outputTexture,
|
||||
presentationTimeUs1,
|
||||
releaseOutputTextureCallback1,
|
||||
unusedSyncObject) ->
|
||||
bitmapReader.readBitmapAndReleaseTexture(
|
||||
outputTexture, presentationTimeUs1, releaseOutputTextureCallback1),
|
||||
unusedSyncObject) -> {
|
||||
bitmapReader.readBitmap(outputTexture, presentationTimeUs1);
|
||||
releaseOutputTextureCallback1.release(presentationTimeUs1);
|
||||
},
|
||||
/* textureOutputCapacity= */ 1)
|
||||
.setGlObjectsProvider(contextSharingGlObjectsProvider)
|
||||
.build();
|
||||
@ -550,7 +550,6 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
|
||||
.setInputColorInfo(colorInfo)
|
||||
.setOutputColorInfo(colorInfo)
|
||||
.setBitmapReader(bitmapReader)
|
||||
.setInputType(VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID)
|
||||
.setEffects(effects)
|
||||
.build();
|
||||
GlUtil.awaitSyncObject(syncObject);
|
||||
@ -573,9 +572,10 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
|
||||
(outputTexture,
|
||||
presentationTimeUs,
|
||||
releaseOutputTextureCallback,
|
||||
unusedSyncObject) ->
|
||||
textureBitmapReader.readBitmapAndReleaseTexture(
|
||||
outputTexture, presentationTimeUs, releaseOutputTextureCallback),
|
||||
unusedSyncObject) -> {
|
||||
textureBitmapReader.readBitmap(outputTexture, presentationTimeUs);
|
||||
releaseOutputTextureCallback.release(presentationTimeUs);
|
||||
},
|
||||
/* textureOutputCapacity= */ 1)
|
||||
.build();
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
|
@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.transformer.mh;
|
||||
|
||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_BITMAP;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
|
||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.effect.FrameDropEffect;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.media3.transformer.TextureBitmapReader;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests to ensure {@link FrameDropEffect} outputs the correct frame associated with a chosen
|
||||
* timestamp.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FrameDropPixelTest {
|
||||
private static final String ORIGINAL_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
|
||||
private static final String MEDIA3_TEST_PNG_ASSET_PATH =
|
||||
"media/bitmap/input_images/media3test.png";
|
||||
private static final String ROTATE_90_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/rotate90.png";
|
||||
private static final String SRGB_TO_ELECTRICAL_ORIGINAL_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/srgb_to_electrical_original.png";
|
||||
private static final String SRGB_TO_ELECTRICAL_MEDIA3_TEST_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/srgb_to_electrical_media3test.png";
|
||||
|
||||
private @MonotonicNonNull TextureBitmapReader textureBitmapReader;
|
||||
private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner;
|
||||
|
||||
@EnsuresNonNull("textureBitmapReader")
|
||||
@Before
|
||||
public void setUp() {
|
||||
textureBitmapReader = new TextureBitmapReader();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
checkNotNull(videoFrameProcessorTestRunner).release();
|
||||
}
|
||||
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
@Test
|
||||
public void frameDrop_withDefaultStrategy_outputsCorrectFramesAtTheCorrectPresentationTimesUs()
|
||||
throws Exception {
|
||||
String testId =
|
||||
"frameDrop_withDefaultStrategy_outputsCorrectFramesAtTheCorrectPresentationTimesUs";
|
||||
videoFrameProcessorTestRunner =
|
||||
createDefaultFrameProcessorTestRunnerBuilder(
|
||||
testId, FrameDropEffect.createDefaultFrameDropEffect(/* targetFrameRate= */ 30));
|
||||
|
||||
long expectedPresentationTimeUs1 = 0;
|
||||
long expectedPresentationTimeUs2 = 32_000;
|
||||
long expectedPresentationTimeUs3 = 71_000;
|
||||
Bitmap chosenBitmap1 = readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||
Bitmap chosenBitmap2 = readBitmap(MEDIA3_TEST_PNG_ASSET_PATH);
|
||||
Bitmap droppedFrameBitmap = readBitmap(ROTATE_90_PNG_ASSET_PATH);
|
||||
queueOneFrameAt(chosenBitmap1, expectedPresentationTimeUs1);
|
||||
queueOneFrameAt(droppedFrameBitmap, /* presentationTimeUs= */ 16_000L);
|
||||
queueOneFrameAt(chosenBitmap2, expectedPresentationTimeUs2);
|
||||
queueOneFrameAt(droppedFrameBitmap, /* presentationTimeUs= */ 48_000L);
|
||||
queueOneFrameAt(droppedFrameBitmap, /* presentationTimeUs= */ 58_000L);
|
||||
queueOneFrameAt(chosenBitmap1, expectedPresentationTimeUs3);
|
||||
queueOneFrameAt(droppedFrameBitmap, /* presentationTimeUs= */ 86_000L);
|
||||
videoFrameProcessorTestRunner.endFrameProcessing();
|
||||
|
||||
assertThat(textureBitmapReader.getOutputTimestamps())
|
||||
.containsExactly(
|
||||
expectedPresentationTimeUs1, expectedPresentationTimeUs2, expectedPresentationTimeUs3)
|
||||
.inOrder();
|
||||
assertThat(
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||
readBitmap(SRGB_TO_ELECTRICAL_ORIGINAL_PNG_ASSET_PATH),
|
||||
textureBitmapReader.getBitmap(expectedPresentationTimeUs1),
|
||||
testId))
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
|
||||
assertThat(
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||
readBitmap(SRGB_TO_ELECTRICAL_MEDIA3_TEST_PNG_ASSET_PATH),
|
||||
textureBitmapReader.getBitmap(expectedPresentationTimeUs2),
|
||||
testId))
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
|
||||
assertThat(
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||
readBitmap(SRGB_TO_ELECTRICAL_ORIGINAL_PNG_ASSET_PATH),
|
||||
textureBitmapReader.getBitmap(expectedPresentationTimeUs3),
|
||||
testId))
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
|
||||
}
|
||||
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
@Test
|
||||
public void frameDrop_withSimpleStrategy_outputsCorrectFramesAtTheCorrectPresentationTimesUs()
|
||||
throws Exception {
|
||||
String testId =
|
||||
"frameDrop_withSimpleStrategy_outputsCorrectFramesAtTheCorrectPresentationTimesUs";
|
||||
videoFrameProcessorTestRunner =
|
||||
createDefaultFrameProcessorTestRunnerBuilder(
|
||||
testId,
|
||||
FrameDropEffect.createSimpleFrameDropEffect(
|
||||
/* expectedFrameRate= */ 6, /* targetFrameRate= */ 2));
|
||||
long expectedPresentationTimeUs1 = 500_000;
|
||||
long expectedPresentationTimeUs2 = 1_500_000;
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(ORIGINAL_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ 0L,
|
||||
/* frameRate= */ 4);
|
||||
videoFrameProcessorTestRunner.queueInputBitmap(
|
||||
readBitmap(MEDIA3_TEST_PNG_ASSET_PATH),
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ C.MICROS_PER_SECOND,
|
||||
/* frameRate= */ 2);
|
||||
videoFrameProcessorTestRunner.endFrameProcessing();
|
||||
|
||||
assertThat(textureBitmapReader.getOutputTimestamps())
|
||||
.containsExactly(expectedPresentationTimeUs1, expectedPresentationTimeUs2)
|
||||
.inOrder();
|
||||
Bitmap actualBitmap1 = textureBitmapReader.getBitmap(expectedPresentationTimeUs1);
|
||||
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual1", actualBitmap1, /* path= */ null);
|
||||
Bitmap actualBitmap2 = textureBitmapReader.getBitmap(expectedPresentationTimeUs2);
|
||||
maybeSaveTestBitmap(testId, /* bitmapLabel= */ "actual2", actualBitmap2, /* path= */ null);
|
||||
assertThat(
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||
readBitmap(SRGB_TO_ELECTRICAL_ORIGINAL_PNG_ASSET_PATH), actualBitmap1, testId))
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
|
||||
assertThat(
|
||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||
readBitmap(SRGB_TO_ELECTRICAL_MEDIA3_TEST_PNG_ASSET_PATH), actualBitmap2, testId))
|
||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
|
||||
}
|
||||
|
||||
@RequiresNonNull("textureBitmapReader")
|
||||
private VideoFrameProcessorTestRunner createDefaultFrameProcessorTestRunnerBuilder(
|
||||
String testId, FrameDropEffect frameDropEffect) throws VideoFrameProcessingException {
|
||||
VideoFrameProcessor.Factory defaultVideoFrameProcessorFactory =
|
||||
new DefaultVideoFrameProcessor.Factory.Builder()
|
||||
.setTextureOutput(
|
||||
(outputTexture, presentationTimeUs, releaseOutputTextureCallback, token) ->
|
||||
checkNotNull(textureBitmapReader)
|
||||
.readBitmapAndReleaseTexture(
|
||||
outputTexture, presentationTimeUs, releaseOutputTextureCallback),
|
||||
/* textureOutputCapacity= */ 1)
|
||||
.build();
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory)
|
||||
.setInputType(INPUT_TYPE_BITMAP)
|
||||
.setInputColorInfo(ColorInfo.SRGB_BT709_FULL)
|
||||
.setEffects(frameDropEffect)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a {@link Bitmap} into the {@link VideoFrameProcessor} so that exactly one frame is
|
||||
* produced at the given {@code presentationTimeUs}.
|
||||
*/
|
||||
private void queueOneFrameAt(Bitmap bitmap, long presentationTimeUs) {
|
||||
checkNotNull(videoFrameProcessorTestRunner)
|
||||
.queueInputBitmap(
|
||||
bitmap,
|
||||
/* durationUs= */ C.MICROS_PER_SECOND,
|
||||
/* offsetToAddUs= */ presentationTimeUs,
|
||||
/* frameRate= */ 1);
|
||||
}
|
||||
}
|
@ -158,8 +158,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
Size decodedSize = getDecodedSize(trackFormat);
|
||||
videoFrameProcessor.registerInputStream(
|
||||
getInputType(checkNotNull(trackFormat.sampleMimeType)),
|
||||
createEffectListWithPresentation(editedMediaItem.effects.videoEffects, presentation));
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
createEffectListWithPresentation(editedMediaItem.effects.videoEffects, presentation),
|
||||
new FrameInfo.Builder(decodedSize.getWidth(), decodedSize.getHeight())
|
||||
.setPixelWidthHeightRatio(trackFormat.pixelWidthHeightRatio)
|
||||
.setOffsetToAddUs(mediaItemOffsetUs.get())
|
||||
|
Loading…
x
Reference in New Issue
Block a user