mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Rename ReorderingSeiMessageQueue
to remove reference to SEI
In a follow-up change I am going to use this class in a non-SEI context for H.262 'user data'. Issue: androidx/media#2372 PiperOrigin-RevId: 752683541
This commit is contained in:
parent
721f3c517b
commit
5775abd7e3
@ -30,42 +30,42 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
/** A queue of SEI messages, ordered by presentation timestamp. */
|
/** A queue of buffers, ordered by presentation timestamp. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RestrictTo(LIBRARY_GROUP)
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
public final class ReorderingSeiMessageQueue {
|
public final class ReorderingBufferQueue {
|
||||||
|
|
||||||
/** Functional interface to handle an SEI message that is being removed from the queue. */
|
/** Functional interface to handle a buffer that is being removed from the queue. */
|
||||||
public interface SeiConsumer {
|
public interface OutputConsumer {
|
||||||
/** Handles an SEI message that is being removed from the queue. */
|
/** Handles a buffer that is being removed from the queue. */
|
||||||
void consume(long presentationTimeUs, ParsableByteArray seiBuffer);
|
void consume(long presentationTimeUs, ParsableByteArray buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SeiConsumer seiConsumer;
|
private final OutputConsumer outputConsumer;
|
||||||
|
|
||||||
/** Pool of re-usable {@link ParsableByteArray} objects to avoid repeated allocations. */
|
/** Pool of re-usable {@link ParsableByteArray} objects to avoid repeated allocations. */
|
||||||
private final ArrayDeque<ParsableByteArray> unusedParsableByteArrays;
|
private final ArrayDeque<ParsableByteArray> unusedParsableByteArrays;
|
||||||
|
|
||||||
/** Pool of re-usable {@link SampleSeiMessages} objects to avoid repeated allocations. */
|
/** Pool of re-usable {@link BuffersWithTimestamp} objects to avoid repeated allocations. */
|
||||||
private final ArrayDeque<SampleSeiMessages> unusedSampleSeiMessages;
|
private final ArrayDeque<BuffersWithTimestamp> unusedBuffersWithTimestamp;
|
||||||
|
|
||||||
private final PriorityQueue<SampleSeiMessages> pendingSeiMessages;
|
private final PriorityQueue<BuffersWithTimestamp> pendingBuffers;
|
||||||
|
|
||||||
private int reorderingQueueSize;
|
private int reorderingQueueSize;
|
||||||
@Nullable private SampleSeiMessages lastQueuedMessage;
|
@Nullable private BuffersWithTimestamp lastQueuedBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance, initially with no max size.
|
* Creates an instance, initially with no max size.
|
||||||
*
|
*
|
||||||
* @param seiConsumer Callback to invoke when SEI messages are removed from the head of queue,
|
* @param outputConsumer Callback to invoke when buffers are removed from the head of the queue,
|
||||||
* either due to exceeding the {@linkplain #setMaxSize(int) max queue size} during a call to
|
* either due to exceeding the {@linkplain #setMaxSize(int) max queue size} during a call to
|
||||||
* {@link #add(long, ParsableByteArray)}, or due to {@link #flush()}.
|
* {@link #add(long, ParsableByteArray)}, or due to {@link #flush()}.
|
||||||
*/
|
*/
|
||||||
public ReorderingSeiMessageQueue(SeiConsumer seiConsumer) {
|
public ReorderingBufferQueue(OutputConsumer outputConsumer) {
|
||||||
this.seiConsumer = seiConsumer;
|
this.outputConsumer = outputConsumer;
|
||||||
unusedParsableByteArrays = new ArrayDeque<>();
|
unusedParsableByteArrays = new ArrayDeque<>();
|
||||||
unusedSampleSeiMessages = new ArrayDeque<>();
|
unusedBuffersWithTimestamp = new ArrayDeque<>();
|
||||||
pendingSeiMessages = new PriorityQueue<>();
|
pendingBuffers = new PriorityQueue<>();
|
||||||
reorderingQueueSize = C.LENGTH_UNSET;
|
reorderingQueueSize = C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,18 +73,18 @@ public final class ReorderingSeiMessageQueue {
|
|||||||
* Sets the max size of the re-ordering queue.
|
* Sets the max size of the re-ordering queue.
|
||||||
*
|
*
|
||||||
* <p>The size is defined in terms of the number of unique presentation timestamps, rather than
|
* <p>The size is defined in terms of the number of unique presentation timestamps, rather than
|
||||||
* the number of messages. This ensures that properties like H.264's {@code
|
* the number of buffers. This ensures that properties like H.264's {@code
|
||||||
* max_number_reorder_frames} can be used to set this max size in the case of multiple SEI
|
* max_number_reorder_frames} can be used to set this max size in the case of multiple SEI
|
||||||
* messages per sample (where multiple SEI messages therefore have the same presentation
|
* messages per sample (where multiple SEI messages therefore have the same presentation
|
||||||
* timestamp).
|
* timestamp).
|
||||||
*
|
*
|
||||||
* <p>When the queue exceeds this size during a call to {@link #add(long, ParsableByteArray)}, the
|
* <p>When the queue exceeds this size during a call to {@link #add(long, ParsableByteArray)}, the
|
||||||
* messages associated with the least timestamp are passed to the {@link SeiConsumer} provided
|
* buffers associated with the least timestamp are passed to the {@link OutputConsumer} provided
|
||||||
* during construction.
|
* during construction.
|
||||||
*
|
*
|
||||||
* <p>If the new size is larger than the number of elements currently in the queue, items are
|
* <p>If the new size is larger than the number of elements currently in the queue, items are
|
||||||
* removed from the head of the queue (least first) and passed to the {@link SeiConsumer} provided
|
* removed from the head of the queue (least first) and passed to the {@link OutputConsumer}
|
||||||
* during construction.
|
* provided during construction.
|
||||||
*/
|
*/
|
||||||
public void setMaxSize(int reorderingQueueSize) {
|
public void setMaxSize(int reorderingQueueSize) {
|
||||||
checkState(reorderingQueueSize >= 0);
|
checkState(reorderingQueueSize >= 0);
|
||||||
@ -102,39 +102,41 @@ public final class ReorderingSeiMessageQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a message to the queue.
|
* Adds a buffer to the queue.
|
||||||
*
|
*
|
||||||
* <p>If this causes the queue to exceed its {@linkplain #setMaxSize(int) max size}, messages
|
* <p>If this causes the queue to exceed its {@linkplain #setMaxSize(int) max size}, buffers
|
||||||
* associated with the least timestamp (which may be the message passed to this method) are passed
|
* associated with the least timestamp (which may be the buffer passed to this method) are passed
|
||||||
* to the {@link SeiConsumer} provided during construction.
|
* to the {@link OutputConsumer} provided during construction.
|
||||||
*
|
*
|
||||||
* <p>Messages with matching timestamps must be added consecutively (this will naturally happen
|
* <p>buffers with matching timestamps must be added consecutively (this will naturally happen
|
||||||
* when parsing messages from a container).
|
* when parsing buffers from a container).
|
||||||
*
|
*
|
||||||
* @param presentationTimeUs The presentation time of the SEI message.
|
* @param presentationTimeUs The presentation time of the buffer.
|
||||||
* @param seiBuffer The SEI data. The data will be copied, so the provided object can be re-used
|
* @param buffer The buffer data. The data will be copied, so the provided object can be re-used
|
||||||
* after this method returns.
|
* after this method returns.
|
||||||
*/
|
*/
|
||||||
public void add(long presentationTimeUs, ParsableByteArray seiBuffer) {
|
public void add(long presentationTimeUs, ParsableByteArray buffer) {
|
||||||
if (reorderingQueueSize == 0
|
if (reorderingQueueSize == 0
|
||||||
|| (reorderingQueueSize != C.LENGTH_UNSET
|
|| (reorderingQueueSize != C.LENGTH_UNSET
|
||||||
&& pendingSeiMessages.size() >= reorderingQueueSize
|
&& pendingBuffers.size() >= reorderingQueueSize
|
||||||
&& presentationTimeUs < castNonNull(pendingSeiMessages.peek()).presentationTimeUs)) {
|
&& presentationTimeUs < castNonNull(pendingBuffers.peek()).presentationTimeUs)) {
|
||||||
seiConsumer.consume(presentationTimeUs, seiBuffer);
|
outputConsumer.consume(presentationTimeUs, buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Make a local copy of the SEI data so we can store it in the queue and allow the seiBuffer
|
// Make a local copy of the buffer data so we can store it in the queue and allow the buffer
|
||||||
// parameter to be safely re-used after this add() method returns.
|
// parameter to be safely re-used after this add() method returns.
|
||||||
ParsableByteArray seiBufferCopy = copy(seiBuffer);
|
ParsableByteArray bufferCopy = copy(buffer);
|
||||||
if (lastQueuedMessage != null && presentationTimeUs == lastQueuedMessage.presentationTimeUs) {
|
if (lastQueuedBuffer != null && presentationTimeUs == lastQueuedBuffer.presentationTimeUs) {
|
||||||
lastQueuedMessage.nalBuffers.add(seiBufferCopy);
|
lastQueuedBuffer.nalBuffers.add(bufferCopy);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SampleSeiMessages sampleSeiMessages =
|
BuffersWithTimestamp buffersWithTimestamp =
|
||||||
unusedSampleSeiMessages.isEmpty() ? new SampleSeiMessages() : unusedSampleSeiMessages.pop();
|
unusedBuffersWithTimestamp.isEmpty()
|
||||||
sampleSeiMessages.init(presentationTimeUs, seiBufferCopy);
|
? new BuffersWithTimestamp()
|
||||||
pendingSeiMessages.add(sampleSeiMessages);
|
: unusedBuffersWithTimestamp.pop();
|
||||||
lastQueuedMessage = sampleSeiMessages;
|
buffersWithTimestamp.init(presentationTimeUs, bufferCopy);
|
||||||
|
pendingBuffers.add(buffersWithTimestamp);
|
||||||
|
lastQueuedBuffer = buffersWithTimestamp;
|
||||||
if (reorderingQueueSize != C.LENGTH_UNSET) {
|
if (reorderingQueueSize != C.LENGTH_UNSET) {
|
||||||
flushQueueDownToSize(reorderingQueueSize);
|
flushQueueDownToSize(reorderingQueueSize);
|
||||||
}
|
}
|
||||||
@ -159,13 +161,13 @@ public final class ReorderingSeiMessageQueue {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Empties the queue, discarding all previously {@linkplain #add added} messages. */
|
/** Empties the queue, discarding all previously {@linkplain #add added} buffers. */
|
||||||
public void clear() {
|
public void clear() {
|
||||||
pendingSeiMessages.clear();
|
pendingBuffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empties the queue, passing all messages (least first) to the {@link SeiConsumer} provided
|
* Empties the queue, passing all buffers (least first) to the {@link OutputConsumer} provided
|
||||||
* during construction.
|
* during construction.
|
||||||
*/
|
*/
|
||||||
public void flush() {
|
public void flush() {
|
||||||
@ -173,29 +175,29 @@ public final class ReorderingSeiMessageQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void flushQueueDownToSize(int targetSize) {
|
private void flushQueueDownToSize(int targetSize) {
|
||||||
while (pendingSeiMessages.size() > targetSize) {
|
while (pendingBuffers.size() > targetSize) {
|
||||||
SampleSeiMessages sampleSeiMessages = castNonNull(pendingSeiMessages.poll());
|
BuffersWithTimestamp buffersWithTimestamp = castNonNull(pendingBuffers.poll());
|
||||||
for (int i = 0; i < sampleSeiMessages.nalBuffers.size(); i++) {
|
for (int i = 0; i < buffersWithTimestamp.nalBuffers.size(); i++) {
|
||||||
seiConsumer.consume(
|
outputConsumer.consume(
|
||||||
sampleSeiMessages.presentationTimeUs, sampleSeiMessages.nalBuffers.get(i));
|
buffersWithTimestamp.presentationTimeUs, buffersWithTimestamp.nalBuffers.get(i));
|
||||||
unusedParsableByteArrays.push(sampleSeiMessages.nalBuffers.get(i));
|
unusedParsableByteArrays.push(buffersWithTimestamp.nalBuffers.get(i));
|
||||||
}
|
}
|
||||||
sampleSeiMessages.nalBuffers.clear();
|
buffersWithTimestamp.nalBuffers.clear();
|
||||||
if (lastQueuedMessage != null
|
if (lastQueuedBuffer != null
|
||||||
&& lastQueuedMessage.presentationTimeUs == sampleSeiMessages.presentationTimeUs) {
|
&& lastQueuedBuffer.presentationTimeUs == buffersWithTimestamp.presentationTimeUs) {
|
||||||
lastQueuedMessage = null;
|
lastQueuedBuffer = null;
|
||||||
}
|
}
|
||||||
unusedSampleSeiMessages.push(sampleSeiMessages);
|
unusedBuffersWithTimestamp.push(buffersWithTimestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds the presentation timestamp of a sample and the data from associated SEI messages. */
|
/** Holds the presentation timestamp of a sample and its associated buffers. */
|
||||||
private static final class SampleSeiMessages implements Comparable<SampleSeiMessages> {
|
private static final class BuffersWithTimestamp implements Comparable<BuffersWithTimestamp> {
|
||||||
|
|
||||||
public final List<ParsableByteArray> nalBuffers;
|
public final List<ParsableByteArray> nalBuffers;
|
||||||
public long presentationTimeUs;
|
public long presentationTimeUs;
|
||||||
|
|
||||||
public SampleSeiMessages() {
|
public BuffersWithTimestamp() {
|
||||||
presentationTimeUs = C.TIME_UNSET;
|
presentationTimeUs = C.TIME_UNSET;
|
||||||
nalBuffers = new ArrayList<>();
|
nalBuffers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
@ -208,7 +210,7 @@ public final class ReorderingSeiMessageQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(SampleSeiMessages other) {
|
public int compareTo(BuffersWithTimestamp other) {
|
||||||
return Long.compare(this.presentationTimeUs, other.presentationTimeUs);
|
return Long.compare(this.presentationTimeUs, other.presentationTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,6 +25,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.container.ReorderingBufferQueue.OutputConsumer;
|
||||||
import androidx.media3.test.utils.TestUtil;
|
import androidx.media3.test.utils.TestUtil;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -33,17 +34,17 @@ import java.util.Objects;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/** Tests for {@link ReorderingSeiMessageQueue}. */
|
/** Tests for {@link ReorderingBufferQueue}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class ReorderingSeiMessageQueueTest {
|
public final class ReorderingBufferQueueTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noMaxSize_queueOnlyEmitsOnExplicitFlushCall() {
|
public void noMaxSize_queueOnlyEmitsOnExplicitFlushCall() {
|
||||||
ArrayList<SeiMessage> emittedMessages = new ArrayList<>();
|
ArrayList<Buffer> emittedMessages = new ArrayList<>();
|
||||||
ReorderingSeiMessageQueue reorderingQueue =
|
ReorderingBufferQueue reorderingQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
(presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, buffer) ->
|
||||||
emittedMessages.add(new SeiMessage(presentationTimeUs, seiBuffer)));
|
emittedMessages.add(new Buffer(presentationTimeUs, buffer)));
|
||||||
|
|
||||||
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
||||||
// making copies as required.
|
// making copies as required.
|
||||||
@ -61,18 +62,18 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
|
|
||||||
assertThat(emittedMessages)
|
assertThat(emittedMessages)
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
new SeiMessage(/* presentationTimeUs= */ 123, data2),
|
new Buffer(/* presentationTimeUs= */ 123, data2),
|
||||||
new SeiMessage(/* presentationTimeUs= */ 345, data1))
|
new Buffer(/* presentationTimeUs= */ 345, data1))
|
||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setMaxSize_emitsImmediatelyIfQueueIsOversized() {
|
public void setMaxSize_emitsImmediatelyIfQueueIsOversized() {
|
||||||
ArrayList<SeiMessage> emittedMessages = new ArrayList<>();
|
ArrayList<Buffer> emittedMessages = new ArrayList<>();
|
||||||
ReorderingSeiMessageQueue reorderingQueue =
|
ReorderingBufferQueue reorderingQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
(presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, buffer) ->
|
||||||
emittedMessages.add(new SeiMessage(presentationTimeUs, seiBuffer)));
|
emittedMessages.add(new Buffer(presentationTimeUs, buffer)));
|
||||||
ParsableByteArray scratchData = new ParsableByteArray();
|
ParsableByteArray scratchData = new ParsableByteArray();
|
||||||
byte[] data1 = TestUtil.buildTestData(5);
|
byte[] data1 = TestUtil.buildTestData(5);
|
||||||
scratchData.reset(data1);
|
scratchData.reset(data1);
|
||||||
@ -85,17 +86,16 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
|
|
||||||
reorderingQueue.setMaxSize(1);
|
reorderingQueue.setMaxSize(1);
|
||||||
|
|
||||||
assertThat(emittedMessages)
|
assertThat(emittedMessages).containsExactly(new Buffer(/* presentationTimeUs= */ 123, data2));
|
||||||
.containsExactly(new SeiMessage(/* presentationTimeUs= */ 123, data2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withMaxSize_addEmitsWhenQueueIsFull() {
|
public void withMaxSize_addEmitsWhenQueueIsFull() {
|
||||||
ArrayList<SeiMessage> emittedMessages = new ArrayList<>();
|
ArrayList<Buffer> emittedMessages = new ArrayList<>();
|
||||||
ReorderingSeiMessageQueue reorderingQueue =
|
ReorderingBufferQueue reorderingQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
(presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, buffer) ->
|
||||||
emittedMessages.add(new SeiMessage(presentationTimeUs, seiBuffer)));
|
emittedMessages.add(new Buffer(presentationTimeUs, buffer)));
|
||||||
reorderingQueue.setMaxSize(1);
|
reorderingQueue.setMaxSize(1);
|
||||||
|
|
||||||
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
||||||
@ -111,17 +111,16 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
scratchData.reset(data2);
|
scratchData.reset(data2);
|
||||||
reorderingQueue.add(/* presentationTimeUs= */ -123, scratchData);
|
reorderingQueue.add(/* presentationTimeUs= */ -123, scratchData);
|
||||||
|
|
||||||
assertThat(emittedMessages)
|
assertThat(emittedMessages).containsExactly(new Buffer(/* presentationTimeUs= */ -123, data2));
|
||||||
.containsExactly(new SeiMessage(/* presentationTimeUs= */ -123, data2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withMaxSize_addEmitsWhenQueueIsFull_handlesDuplicateTimestamps() {
|
public void withMaxSize_addEmitsWhenQueueIsFull_handlesDuplicateTimestamps() {
|
||||||
ArrayList<SeiMessage> emittedMessages = new ArrayList<>();
|
ArrayList<Buffer> emittedMessages = new ArrayList<>();
|
||||||
ReorderingSeiMessageQueue reorderingQueue =
|
ReorderingBufferQueue reorderingQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
(presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, buffer) ->
|
||||||
emittedMessages.add(new SeiMessage(presentationTimeUs, seiBuffer)));
|
emittedMessages.add(new Buffer(presentationTimeUs, buffer)));
|
||||||
reorderingQueue.setMaxSize(1);
|
reorderingQueue.setMaxSize(1);
|
||||||
|
|
||||||
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
// Deliberately re-use a single ParsableByteArray instance to ensure the implementation is
|
||||||
@ -144,9 +143,9 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
|
|
||||||
assertThat(emittedMessages)
|
assertThat(emittedMessages)
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
new SeiMessage(/* presentationTimeUs= */ -123, data3),
|
new Buffer(/* presentationTimeUs= */ -123, data3),
|
||||||
new SeiMessage(/* presentationTimeUs= */ 345, data1),
|
new Buffer(/* presentationTimeUs= */ 345, data1),
|
||||||
new SeiMessage(/* presentationTimeUs= */ 345, data2))
|
new Buffer(/* presentationTimeUs= */ 345, data2))
|
||||||
.inOrder();
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,9 +156,8 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void withMaxSize_addEmitsWhenQueueIsFull_skippingQueueReusesPbaInstance() {
|
public void withMaxSize_addEmitsWhenQueueIsFull_skippingQueueReusesPbaInstance() {
|
||||||
ReorderingSeiMessageQueue.SeiConsumer mockSeiConsumer =
|
OutputConsumer mockOutputConsumer = mock(OutputConsumer.class);
|
||||||
mock(ReorderingSeiMessageQueue.SeiConsumer.class);
|
ReorderingBufferQueue reorderingQueue = new ReorderingBufferQueue(mockOutputConsumer);
|
||||||
ReorderingSeiMessageQueue reorderingQueue = new ReorderingSeiMessageQueue(mockSeiConsumer);
|
|
||||||
reorderingQueue.setMaxSize(1);
|
reorderingQueue.setMaxSize(1);
|
||||||
|
|
||||||
ParsableByteArray scratchData = new ParsableByteArray();
|
ParsableByteArray scratchData = new ParsableByteArray();
|
||||||
@ -167,28 +165,28 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
scratchData.reset(data1);
|
scratchData.reset(data1);
|
||||||
reorderingQueue.add(/* presentationTimeUs= */ 345, scratchData);
|
reorderingQueue.add(/* presentationTimeUs= */ 345, scratchData);
|
||||||
|
|
||||||
verifyNoInteractions(mockSeiConsumer);
|
verifyNoInteractions(mockOutputConsumer);
|
||||||
|
|
||||||
byte[] data2 = TestUtil.buildTestData(10);
|
byte[] data2 = TestUtil.buildTestData(10);
|
||||||
scratchData.reset(data2);
|
scratchData.reset(data2);
|
||||||
reorderingQueue.add(/* presentationTimeUs= */ 123, scratchData);
|
reorderingQueue.add(/* presentationTimeUs= */ 123, scratchData);
|
||||||
|
|
||||||
verify(mockSeiConsumer).consume(eq(123L), same(scratchData));
|
verify(mockOutputConsumer).consume(eq(123L), same(scratchData));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class SeiMessage {
|
private static final class Buffer {
|
||||||
public final long presentationTimeUs;
|
public final long presentationTimeUs;
|
||||||
public final byte[] data;
|
public final byte[] data;
|
||||||
|
|
||||||
public SeiMessage(long presentationTimeUs, ParsableByteArray seiBuffer) {
|
public Buffer(long presentationTimeUs, ParsableByteArray bufferData) {
|
||||||
this(
|
this(
|
||||||
presentationTimeUs,
|
presentationTimeUs,
|
||||||
Arrays.copyOfRange(seiBuffer.getData(), seiBuffer.getPosition(), seiBuffer.limit()));
|
Arrays.copyOfRange(bufferData.getData(), bufferData.getPosition(), bufferData.limit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SeiMessage(long presentationTimeUs, byte[] seiBuffer) {
|
public Buffer(long presentationTimeUs, byte[] bufferData) {
|
||||||
this.presentationTimeUs = presentationTimeUs;
|
this.presentationTimeUs = presentationTimeUs;
|
||||||
this.data = seiBuffer;
|
this.data = bufferData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -198,17 +196,17 @@ public final class ReorderingSeiMessageQueueTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof SeiMessage)) {
|
if (!(obj instanceof Buffer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SeiMessage that = (SeiMessage) obj;
|
Buffer that = (Buffer) obj;
|
||||||
return this.presentationTimeUs == that.presentationTimeUs
|
return this.presentationTimeUs == that.presentationTimeUs
|
||||||
&& Arrays.equals(this.data, that.data);
|
&& Arrays.equals(this.data, that.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SeiMessage { ts=" + presentationTimeUs + ",data=0x" + Util.toHexString(data) + " }";
|
return "Buffer { ts=" + presentationTimeUs + ",data=0x" + Util.toHexString(data) + " }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -43,7 +43,7 @@ import androidx.media3.container.Mp4Box;
|
|||||||
import androidx.media3.container.Mp4Box.ContainerBox;
|
import androidx.media3.container.Mp4Box.ContainerBox;
|
||||||
import androidx.media3.container.Mp4Box.LeafBox;
|
import androidx.media3.container.Mp4Box.LeafBox;
|
||||||
import androidx.media3.container.NalUnitUtil;
|
import androidx.media3.container.NalUnitUtil;
|
||||||
import androidx.media3.container.ReorderingSeiMessageQueue;
|
import androidx.media3.container.ReorderingBufferQueue;
|
||||||
import androidx.media3.extractor.Ac4Util;
|
import androidx.media3.extractor.Ac4Util;
|
||||||
import androidx.media3.extractor.CeaUtil;
|
import androidx.media3.extractor.CeaUtil;
|
||||||
import androidx.media3.extractor.ChunkIndex;
|
import androidx.media3.extractor.ChunkIndex;
|
||||||
@ -216,7 +216,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
|||||||
private final ParsableByteArray atomHeader;
|
private final ParsableByteArray atomHeader;
|
||||||
private final ArrayDeque<ContainerBox> containerAtoms;
|
private final ArrayDeque<ContainerBox> containerAtoms;
|
||||||
private final ArrayDeque<MetadataSampleInfo> pendingMetadataSampleInfos;
|
private final ArrayDeque<MetadataSampleInfo> pendingMetadataSampleInfos;
|
||||||
private final ReorderingSeiMessageQueue reorderingSeiMessageQueue;
|
private final ReorderingBufferQueue reorderingBufferQueue;
|
||||||
@Nullable private final TrackOutput additionalEmsgTrackOutput;
|
@Nullable private final TrackOutput additionalEmsgTrackOutput;
|
||||||
|
|
||||||
private ImmutableList<SniffFailure> lastSniffFailures;
|
private ImmutableList<SniffFailure> lastSniffFailures;
|
||||||
@ -424,10 +424,10 @@ public class FragmentedMp4Extractor implements Extractor {
|
|||||||
extractorOutput = ExtractorOutput.PLACEHOLDER;
|
extractorOutput = ExtractorOutput.PLACEHOLDER;
|
||||||
emsgTrackOutputs = new TrackOutput[0];
|
emsgTrackOutputs = new TrackOutput[0];
|
||||||
ceaTrackOutputs = new TrackOutput[0];
|
ceaTrackOutputs = new TrackOutput[0];
|
||||||
reorderingSeiMessageQueue =
|
reorderingBufferQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
(presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, buffer) ->
|
||||||
CeaUtil.consume(presentationTimeUs, seiBuffer, ceaTrackOutputs));
|
CeaUtil.consume(presentationTimeUs, buffer, ceaTrackOutputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -498,7 +498,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
|||||||
}
|
}
|
||||||
pendingMetadataSampleInfos.clear();
|
pendingMetadataSampleInfos.clear();
|
||||||
pendingMetadataSampleBytes = 0;
|
pendingMetadataSampleBytes = 0;
|
||||||
reorderingSeiMessageQueue.clear();
|
reorderingBufferQueue.clear();
|
||||||
pendingSeekTimeUs = timeUs;
|
pendingSeekTimeUs = timeUs;
|
||||||
containerAtoms.clear();
|
containerAtoms.clear();
|
||||||
enterReadingAtomHeaderState();
|
enterReadingAtomHeaderState();
|
||||||
@ -515,7 +515,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
|||||||
switch (parserState) {
|
switch (parserState) {
|
||||||
case STATE_READING_ATOM_HEADER:
|
case STATE_READING_ATOM_HEADER:
|
||||||
if (!readAtomHeader(input)) {
|
if (!readAtomHeader(input)) {
|
||||||
reorderingSeiMessageQueue.flush();
|
reorderingBufferQueue.flush();
|
||||||
return Extractor.RESULT_END_OF_INPUT;
|
return Extractor.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1680,17 +1680,16 @@ public class FragmentedMp4Extractor implements Extractor {
|
|||||||
nalUnitWithoutHeaderBuffer.setLimit(unescapedLength);
|
nalUnitWithoutHeaderBuffer.setLimit(unescapedLength);
|
||||||
|
|
||||||
if (track.format.maxNumReorderSamples == Format.NO_VALUE) {
|
if (track.format.maxNumReorderSamples == Format.NO_VALUE) {
|
||||||
if (reorderingSeiMessageQueue.getMaxSize() != 0) {
|
if (reorderingBufferQueue.getMaxSize() != 0) {
|
||||||
reorderingSeiMessageQueue.setMaxSize(0);
|
reorderingBufferQueue.setMaxSize(0);
|
||||||
}
|
}
|
||||||
} else if (reorderingSeiMessageQueue.getMaxSize()
|
} else if (reorderingBufferQueue.getMaxSize() != track.format.maxNumReorderSamples) {
|
||||||
!= track.format.maxNumReorderSamples) {
|
reorderingBufferQueue.setMaxSize(track.format.maxNumReorderSamples);
|
||||||
reorderingSeiMessageQueue.setMaxSize(track.format.maxNumReorderSamples);
|
|
||||||
}
|
}
|
||||||
reorderingSeiMessageQueue.add(sampleTimeUs, nalUnitWithoutHeaderBuffer);
|
reorderingBufferQueue.add(sampleTimeUs, nalUnitWithoutHeaderBuffer);
|
||||||
|
|
||||||
if ((trackBundle.getCurrentSampleFlags() & C.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
if ((trackBundle.getCurrentSampleFlags() & C.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||||
reorderingSeiMessageQueue.flush();
|
reorderingBufferQueue.flush();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Write the payload of the NAL unit.
|
// Write the payload of the NAL unit.
|
||||||
|
@ -22,7 +22,7 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.container.ReorderingSeiMessageQueue;
|
import androidx.media3.container.ReorderingBufferQueue;
|
||||||
import androidx.media3.extractor.CeaUtil;
|
import androidx.media3.extractor.CeaUtil;
|
||||||
import androidx.media3.extractor.ExtractorOutput;
|
import androidx.media3.extractor.ExtractorOutput;
|
||||||
import androidx.media3.extractor.TrackOutput;
|
import androidx.media3.extractor.TrackOutput;
|
||||||
@ -36,7 +36,7 @@ public final class SeiReader {
|
|||||||
private final List<Format> closedCaptionFormats;
|
private final List<Format> closedCaptionFormats;
|
||||||
private final String containerMimeType;
|
private final String containerMimeType;
|
||||||
private final TrackOutput[] outputs;
|
private final TrackOutput[] outputs;
|
||||||
private final ReorderingSeiMessageQueue reorderingSeiMessageQueue;
|
private final ReorderingBufferQueue reorderingBufferQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param closedCaptionFormats A list of formats for the closed caption channels to expose.
|
* @param closedCaptionFormats A list of formats for the closed caption channels to expose.
|
||||||
@ -46,10 +46,10 @@ public final class SeiReader {
|
|||||||
this.closedCaptionFormats = closedCaptionFormats;
|
this.closedCaptionFormats = closedCaptionFormats;
|
||||||
this.containerMimeType = containerMimeType;
|
this.containerMimeType = containerMimeType;
|
||||||
outputs = new TrackOutput[closedCaptionFormats.size()];
|
outputs = new TrackOutput[closedCaptionFormats.size()];
|
||||||
reorderingSeiMessageQueue =
|
reorderingBufferQueue =
|
||||||
new ReorderingSeiMessageQueue(
|
new ReorderingBufferQueue(
|
||||||
((presentationTimeUs, seiBuffer) ->
|
(presentationTimeUs, seiBuffer) ->
|
||||||
CeaUtil.consume(presentationTimeUs, seiBuffer, outputs)));
|
CeaUtil.consume(presentationTimeUs, seiBuffer, outputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||||
@ -82,11 +82,11 @@ public final class SeiReader {
|
|||||||
* presentation order.
|
* presentation order.
|
||||||
*/
|
*/
|
||||||
public void setReorderingQueueSize(int reorderingQueueSize) {
|
public void setReorderingQueueSize(int reorderingQueueSize) {
|
||||||
reorderingSeiMessageQueue.setMaxSize(reorderingQueueSize);
|
reorderingBufferQueue.setMaxSize(reorderingQueueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void consume(long pesTimeUs, ParsableByteArray seiBuffer) {
|
public void consume(long pesTimeUs, ParsableByteArray seiBuffer) {
|
||||||
reorderingSeiMessageQueue.add(pesTimeUs, seiBuffer);
|
reorderingBufferQueue.add(pesTimeUs, seiBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,11 +95,11 @@ public final class SeiReader {
|
|||||||
* TrackOutput[])}.
|
* TrackOutput[])}.
|
||||||
*/
|
*/
|
||||||
public void flush() {
|
public void flush() {
|
||||||
reorderingSeiMessageQueue.flush();
|
reorderingBufferQueue.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Drops any 'buffered for re-ordering' messages. */
|
/** Drops any 'buffered for re-ordering' messages. */
|
||||||
public void clear() {
|
public void clear() {
|
||||||
reorderingSeiMessageQueue.flush();
|
reorderingBufferQueue.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user