Commit 55f2b093 by olly Committed by Oliver Woodman

Use stable order for subtitle buffers with identical timestamps

Issue: #3782

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185673731
parent 115d1995
...@@ -76,8 +76,12 @@ ...@@ -76,8 +76,12 @@
* Support resampling 24-bit and 32-bit integer to 32-bit float for high * Support resampling 24-bit and 32-bit integer to 32-bit float for high
resolution output in `DefaultAudioSink` resolution output in `DefaultAudioSink`
([#3635](https://github.com/google/ExoPlayer/pull/3635)). ([#3635](https://github.com/google/ExoPlayer/pull/3635)).
* Captions: Initial support for PGS subtitles * Captions:
* Initial support for PGS subtitles
([#3008](https://github.com/google/ExoPlayer/issues/3008)). ([#3008](https://github.com/google/ExoPlayer/issues/3008)).
* Fix issue handling CEA-608 captions where multiple buffers have the same
presentation timestamp
([#3782](https://github.com/google/ExoPlayer/issues/3782)).
* CacheDataSource: Check periodically if it's possible to read from/write to * CacheDataSource: Check periodically if it's possible to read from/write to
cache after deciding to bypass cache. cache after deciding to bypass cache.
* IMA extension: * IMA extension:
......
...@@ -15,15 +15,11 @@ ...@@ -15,15 +15,11 @@
*/ */
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
/** /** A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}. */
* A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}. public class SubtitleInputBuffer extends DecoderInputBuffer {
*/
public final class SubtitleInputBuffer extends DecoderInputBuffer
implements Comparable<SubtitleInputBuffer> {
/** /**
* An offset that must be added to the subtitle's event times after it's been decoded, or * An offset that must be added to the subtitle's event times after it's been decoded, or
...@@ -35,16 +31,4 @@ public final class SubtitleInputBuffer extends DecoderInputBuffer ...@@ -35,16 +31,4 @@ public final class SubtitleInputBuffer extends DecoderInputBuffer
super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
} }
@Override
public int compareTo(@NonNull SubtitleInputBuffer other) {
if (isEndOfStream() != other.isEndOfStream()) {
return isEndOfStream() ? 1 : -1;
}
long delta = timeUs - other.timeUs;
if (delta == 0) {
return 0;
}
return delta > 0 ? 1 : -1;
}
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.text.cea; package com.google.android.exoplayer2.text.cea;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.Subtitle;
...@@ -34,21 +35,22 @@ import java.util.PriorityQueue; ...@@ -34,21 +35,22 @@ import java.util.PriorityQueue;
private static final int NUM_INPUT_BUFFERS = 10; private static final int NUM_INPUT_BUFFERS = 10;
private static final int NUM_OUTPUT_BUFFERS = 2; private static final int NUM_OUTPUT_BUFFERS = 2;
private final LinkedList<SubtitleInputBuffer> availableInputBuffers; private final LinkedList<CeaInputBuffer> availableInputBuffers;
private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers; private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers;
private final PriorityQueue<SubtitleInputBuffer> queuedInputBuffers; private final PriorityQueue<CeaInputBuffer> queuedInputBuffers;
private SubtitleInputBuffer dequeuedInputBuffer; private CeaInputBuffer dequeuedInputBuffer;
private long playbackPositionUs; private long playbackPositionUs;
private long queuedInputBufferCount;
public CeaDecoder() { public CeaDecoder() {
availableInputBuffers = new LinkedList<>(); availableInputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
availableInputBuffers.add(new SubtitleInputBuffer()); availableInputBuffers.add(new CeaInputBuffer());
} }
availableOutputBuffers = new LinkedList<>(); availableOutputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) {
availableOutputBuffers.add(new CeaOutputBuffer(this)); availableOutputBuffers.add(new CeaOutputBuffer());
} }
queuedInputBuffers = new PriorityQueue<>(); queuedInputBuffers = new PriorityQueue<>();
} }
...@@ -77,9 +79,10 @@ import java.util.PriorityQueue; ...@@ -77,9 +79,10 @@ import java.util.PriorityQueue;
if (inputBuffer.isDecodeOnly()) { if (inputBuffer.isDecodeOnly()) {
// We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow // We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow
// for decoding to begin mid-stream. // for decoding to begin mid-stream.
releaseInputBuffer(inputBuffer); releaseInputBuffer(dequeuedInputBuffer);
} else { } else {
queuedInputBuffers.add(inputBuffer); dequeuedInputBuffer.queuedInputBufferCount = queuedInputBufferCount++;
queuedInputBuffers.add(dequeuedInputBuffer);
} }
dequeuedInputBuffer = null; dequeuedInputBuffer = null;
} }
...@@ -94,7 +97,7 @@ import java.util.PriorityQueue; ...@@ -94,7 +97,7 @@ import java.util.PriorityQueue;
// be deferred until they would be applicable // be deferred until they would be applicable
while (!queuedInputBuffers.isEmpty() while (!queuedInputBuffers.isEmpty()
&& queuedInputBuffers.peek().timeUs <= playbackPositionUs) { && queuedInputBuffers.peek().timeUs <= playbackPositionUs) {
SubtitleInputBuffer inputBuffer = queuedInputBuffers.poll(); CeaInputBuffer inputBuffer = queuedInputBuffers.poll();
// If the input buffer indicates we've reached the end of the stream, we can // If the input buffer indicates we've reached the end of the stream, we can
// return immediately with an output buffer propagating that // return immediately with an output buffer propagating that
...@@ -126,7 +129,7 @@ import java.util.PriorityQueue; ...@@ -126,7 +129,7 @@ import java.util.PriorityQueue;
return null; return null;
} }
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { private void releaseInputBuffer(CeaInputBuffer inputBuffer) {
inputBuffer.clear(); inputBuffer.clear();
availableInputBuffers.add(inputBuffer); availableInputBuffers.add(inputBuffer);
} }
...@@ -138,6 +141,7 @@ import java.util.PriorityQueue; ...@@ -138,6 +141,7 @@ import java.util.PriorityQueue;
@Override @Override
public void flush() { public void flush() {
queuedInputBufferCount = 0;
playbackPositionUs = 0; playbackPositionUs = 0;
while (!queuedInputBuffers.isEmpty()) { while (!queuedInputBuffers.isEmpty()) {
releaseInputBuffer(queuedInputBuffers.poll()); releaseInputBuffer(queuedInputBuffers.poll());
...@@ -169,4 +173,32 @@ import java.util.PriorityQueue; ...@@ -169,4 +173,32 @@ import java.util.PriorityQueue;
*/ */
protected abstract void decode(SubtitleInputBuffer inputBuffer); protected abstract void decode(SubtitleInputBuffer inputBuffer);
private static final class CeaInputBuffer extends SubtitleInputBuffer
implements Comparable<CeaInputBuffer> {
private long queuedInputBufferCount;
@Override
public int compareTo(@NonNull CeaInputBuffer other) {
if (isEndOfStream() != other.isEndOfStream()) {
return isEndOfStream() ? 1 : -1;
}
long delta = timeUs - other.timeUs;
if (delta == 0) {
delta = queuedInputBufferCount - other.queuedInputBufferCount;
if (delta == 0) {
return 0;
}
}
return delta > 0 ? 1 : -1;
}
}
private final class CeaOutputBuffer extends SubtitleOutputBuffer {
@Override
public final void release() {
releaseOutputBuffer(this);
}
}
} }
/*
* Copyright (C) 2016 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 com.google.android.exoplayer2.text.cea;
import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
/**
* A {@link SubtitleOutputBuffer} for {@link CeaDecoder}s.
*/
public final class CeaOutputBuffer extends SubtitleOutputBuffer {
private final CeaDecoder owner;
/**
* @param owner The decoder that owns this buffer.
*/
public CeaOutputBuffer(CeaDecoder owner) {
super();
this.owner = owner;
}
@Override
public final void release() {
owner.releaseOutputBuffer(this);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment