Add ability to queue bitmap by timestamp to the sampleConsumer

PiperOrigin-RevId: 557837922
This commit is contained in:
tofunmi 2023-08-17 16:53:46 +01:00 committed by Julia Bibik
parent a8944ef2f0
commit dd0f88490c
3 changed files with 83 additions and 2 deletions

View File

@ -22,6 +22,7 @@ import androidx.media3.common.ColorInfo;
import androidx.media3.common.OnInputFrameProcessedListener;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.decoder.DecoderInputBuffer;
import java.util.Iterator;
/** Consumer of encoded media samples, raw audio or raw video frames. */
@UnstableApi
@ -77,10 +78,25 @@ public interface SampleConsumer {
* @return Whether the {@link Bitmap} was successfully queued. If {@code false}, the caller should
* try again later.
*/
// TODO(b/262693274): Delete this method and usages in favor of the one below (Note it is not
// deprecated because transformer still relies on this method for frame duplication).
default boolean queueInputBitmap(Bitmap inputBitmap, long durationUs, int frameRate) {
throw new UnsupportedOperationException();
}
/**
* Attempts to provide an input {@link Bitmap} to the consumer.
*
* <p>Should only be used for image data.
*
* @param inputBitmap The {@link Bitmap} to queue to the consumer.
* @param inStreamOffsetsUs The times within the current stream that the bitmap should be
* displayed at. The timestamps should be monotonically increasing.
*/
default boolean queueInputBitmap(Bitmap inputBitmap, Iterator<Long> inStreamOffsetsUs) {
throw new UnsupportedOperationException();
}
// Methods to pass raw video input.
/**

View File

@ -39,6 +39,7 @@ import androidx.media3.decoder.DecoderInputBuffer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@ -347,6 +348,38 @@ import java.util.concurrent.atomic.AtomicInteger;
sequenceAssetLoaderListener.onError(exportException);
}
/**
* Given an {@link Iterator}, creates an iterator that includes all the values in the original
* iterator (in the same order) up to and including the first occurrence of the {@code
* clippingValue}.
*/
private static final class ClippingIterator implements Iterator<Long> {
private final Iterator<Long> iterator;
private final long clippingValue;
private boolean hasReachedClippingValue;
public ClippingIterator(Iterator<Long> iterator, long clippingValue) {
this.iterator = iterator;
this.clippingValue = clippingValue;
}
@Override
public boolean hasNext() {
return !hasReachedClippingValue && iterator.hasNext();
}
@Override
public Long next() {
checkState(hasNext());
Long next = iterator.next();
if (clippingValue == next) {
hasReachedClippingValue = true;
}
return next;
}
}
// Classes accessed from AssetLoader threads.
private final class SampleConsumerWrapper implements SampleConsumer {
@ -396,8 +429,6 @@ import java.util.concurrent.atomic.AtomicInteger;
return true;
}
// TODO(b/262693274): Test that concatenate 2 images or an image and a video works as expected
// once ImageAssetLoader implementation is complete.
@Override
public boolean queueInputBitmap(Bitmap inputBitmap, long durationUs, int frameRate) {
if (isLooping && totalDurationUs + durationUs > maxSequenceDurationUs) {
@ -418,6 +449,33 @@ import java.util.concurrent.atomic.AtomicInteger;
return sampleConsumer.queueInputBitmap(inputBitmap, durationUs, frameRate);
}
@Override
public boolean queueInputBitmap(Bitmap inputBitmap, Iterator<Long> inStreamOffsetsUs) {
Iterator<Long> iteratorToUse = inStreamOffsetsUs;
if (isLooping) {
long durationLeftUs = maxSequenceDurationUs - totalDurationUs;
if (durationLeftUs <= 0) {
if (!videoLoopingEnded) {
videoLoopingEnded = true;
signalEndOfVideoInput();
}
return false;
}
while (inStreamOffsetsUs.hasNext()) {
long offsetUs = inStreamOffsetsUs.next();
if (totalDurationUs + offsetUs > maxSequenceDurationUs) {
if (!isMaxSequenceDurationUsFinal) {
return false;
}
iteratorToUse = new ClippingIterator(inStreamOffsetsUs, offsetUs);
videoLoopingEnded = true;
break;
}
}
}
return sampleConsumer.queueInputBitmap(inputBitmap, iteratorToUse);
}
@Override
public void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener) {
sampleConsumer.setOnInputFrameProcessedListener(listener);

View File

@ -39,6 +39,7 @@ import androidx.media3.common.util.Consumer;
import androidx.media3.common.util.Size;
import androidx.media3.effect.Presentation;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
@ -173,6 +174,12 @@ import java.util.concurrent.atomic.AtomicLong;
return true;
}
@Override
public boolean queueInputBitmap(Bitmap inputBitmap, Iterator<Long> inStreamOffsetsUs) {
videoFrameProcessor.queueInputBitmap(inputBitmap, inStreamOffsetsUs);
return true;
}
@Override
public void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener) {
videoFrameProcessor.setOnInputFrameProcessedListener(listener);