Improve customisability of FakeSampleStream.

PiperOrigin-RevId: 289829592
This commit is contained in:
samrobinson 2020-01-15 11:44:31 +00:00 committed by Oliver Woodman
parent a225e887fa
commit 96c648c2d6
2 changed files with 112 additions and 35 deletions

View File

@ -27,10 +27,12 @@ import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.metadata.scte35.TimeSignalCommand; import com.google.android.exoplayer2.metadata.scte35.TimeSignalCommand;
import com.google.android.exoplayer2.testutil.FakeSampleStream; import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -141,7 +143,11 @@ public class MetadataRendererTest {
MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null); MetadataRenderer renderer = new MetadataRenderer(metadata::add, /* outputLooper= */ null);
renderer.replaceStream( renderer.replaceStream(
new Format[] {EMSG_FORMAT}, new Format[] {EMSG_FORMAT},
new FakeSampleStream(EMSG_FORMAT, /* eventDispatcher= */ null, input), new FakeSampleStream(
EMSG_FORMAT,
/* eventDispatcher= */ null,
Arrays.asList(new FakeSampleStreamItem(input)),
0),
/* offsetUs= */ 0L); /* offsetUs= */ 0L);
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the format
renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the data renderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0); // Read the data

View File

@ -23,20 +23,64 @@ import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** /**
* Fake {@link SampleStream} that outputs a given {@link Format}, an optional sample containing a * Fake {@link SampleStream} that outputs a given {@link Format}, any amount of {@link
* single zero byte, then end of stream. * FakeSampleStreamItem items}, then end of stream.
*/ */
public final class FakeSampleStream implements SampleStream { public final class FakeSampleStream implements SampleStream {
private final Format format; /** Item to customize a return value of {@link FakeSampleStream#readData}. */
@Nullable private final EventDispatcher eventDispatcher; public static final class FakeSampleStreamItem {
private final byte[] sampleData; @Nullable Format format;
@Nullable byte[] sampleData;
int flags;
private boolean notifiedDownstreamFormat; /**
* Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will
* return {@link C#RESULT_FORMAT_READ} with the new format.
*
* @param format The format to be returned.
*/
public FakeSampleStreamItem(Format format) {
this.format = format;
}
/**
* Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will
* return {@link C#RESULT_BUFFER_READ} with the sample data.
*
* @param sampleData The sample data to be read.
*/
public FakeSampleStreamItem(byte[] sampleData) {
this.sampleData = sampleData.clone();
}
/**
* Item that, when {@link #readData(FormatHolder, DecoderInputBuffer, boolean)} is called, will
* return {@link C#RESULT_BUFFER_READ} with the sample data.
*
* @param sampleData The sample data to be read.
* @param flags The buffer flags to be set.
*/
public FakeSampleStreamItem(byte[] sampleData, int flags) {
this.sampleData = sampleData.clone();
this.flags = flags;
}
}
private final ArrayDeque<FakeSampleStreamItem> fakeSampleStreamItems;
private final int timeUsIncrement;
@Nullable private final EventDispatcher eventDispatcher;
private Format format;
private int timeUs;
private boolean readFormat; private boolean readFormat;
private boolean readSample;
/** /**
* Creates fake sample stream which outputs the given {@link Format}, optionally one sample with * Creates fake sample stream which outputs the given {@link Format}, optionally one sample with
@ -48,23 +92,34 @@ public final class FakeSampleStream implements SampleStream {
*/ */
public FakeSampleStream( public FakeSampleStream(
Format format, @Nullable EventDispatcher eventDispatcher, boolean shouldOutputSample) { Format format, @Nullable EventDispatcher eventDispatcher, boolean shouldOutputSample) {
this(format, eventDispatcher, new byte[] {0}); this(
readSample = !shouldOutputSample; format,
eventDispatcher,
shouldOutputSample
? Arrays.asList(new FakeSampleStreamItem(new byte[] {0}))
: Collections.emptyList(),
/* timeUsIncrement= */ 0);
} }
/** /**
* Creates fake sample stream which outputs the given {@link Format}, one sample with the provided * Creates a fake sample stream which outputs the given {@link Format}, any amount of {@link
* bytes, then end of stream. * FakeSampleStreamItem items}, then end of stream.
* *
* @param format The {@link Format} to output. * @param format The {@link Format} to output.
* @param eventDispatcher An {@link EventDispatcher} to notify of read events. * @param eventDispatcher An {@link EventDispatcher} to notify of read events.
* @param sampleData The sample data to output. * @param fakeSampleStreamItems The list of {@link FakeSampleStreamItem items} to customize the
* return values of {@link #readData(FormatHolder, DecoderInputBuffer, boolean)}.
* @param timeUsIncrement The time each sample should increase by, in microseconds.
*/ */
public FakeSampleStream( public FakeSampleStream(
Format format, @Nullable EventDispatcher eventDispatcher, byte[] sampleData) { Format format,
@Nullable EventDispatcher eventDispatcher,
List<FakeSampleStreamItem> fakeSampleStreamItems,
int timeUsIncrement) {
this.format = format; this.format = format;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.sampleData = sampleData; this.fakeSampleStreamItems = new ArrayDeque<>(fakeSampleStreamItems);
this.timeUsIncrement = timeUsIncrement;
} }
@Override @Override
@ -75,30 +130,35 @@ public final class FakeSampleStream implements SampleStream {
@Override @Override
public int readData( public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
if (eventDispatcher != null && !notifiedDownstreamFormat) { if (!readFormat || formatRequired) {
eventDispatcher.downstreamFormatChanged(
C.TRACK_TYPE_UNKNOWN,
format,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaTimeUs= */ 0);
notifiedDownstreamFormat = true;
}
if (formatRequired || !readFormat) {
formatHolder.format = format;
readFormat = true; readFormat = true;
formatHolder.format = format;
notifyEventDispatcher(formatHolder);
return C.RESULT_FORMAT_READ; return C.RESULT_FORMAT_READ;
} else if (!readSample) { }
buffer.timeUs = 0; if (!fakeSampleStreamItems.isEmpty()) {
FakeSampleStreamItem fakeSampleStreamItem = fakeSampleStreamItems.remove();
if (fakeSampleStreamItem.format != null) {
format = fakeSampleStreamItem.format;
formatHolder.format = format;
notifyEventDispatcher(formatHolder);
return C.RESULT_FORMAT_READ;
}
if (fakeSampleStreamItem.sampleData != null) {
byte[] sampleData = fakeSampleStreamItem.sampleData;
buffer.timeUs = timeUs;
timeUs += timeUsIncrement;
buffer.ensureSpaceForWrite(sampleData.length); buffer.ensureSpaceForWrite(sampleData.length);
buffer.data.put(sampleData); buffer.data.put(sampleData);
readSample = true; if (fakeSampleStreamItem.flags != 0) {
return C.RESULT_BUFFER_READ; buffer.setFlags(fakeSampleStreamItem.flags);
} else { }
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ; return C.RESULT_BUFFER_READ;
} }
} }
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
}
@Override @Override
public void maybeThrowError() throws IOException { public void maybeThrowError() throws IOException {
@ -109,4 +169,15 @@ public final class FakeSampleStream implements SampleStream {
public int skipData(long positionUs) { public int skipData(long positionUs) {
return 0; return 0;
} }
private void notifyEventDispatcher(FormatHolder formatHolder) {
if (eventDispatcher != null) {
eventDispatcher.downstreamFormatChanged(
C.TRACK_TYPE_UNKNOWN,
formatHolder.format,
C.SELECTION_REASON_UNKNOWN,
/* trackSelectionData= */ null,
/* mediaTimeUs= */ timeUs);
}
}
} }