提交 7b5bdd3c 编写于 作者: A andrewlewis 提交者: Oliver Woodman

Release Extractors on the loading thread

Releasing the player released the internal playback thread once renderers were
released. Releasing a MediaPeriod queued a Loader.ReleaseTask on the loading
thread which would post back to the playback thread. If the playback thread had
been quit by the time this happened, the release task wouldn't be run.

Release on the loading thread instead of the playback thread. This avoids
needing to block releasing the player until the loading threads have ended, and
ensures that release tasks will run eventually. As part of this change,
ExtractorMediaPeriod's call to Extractor.release will now run on the loading
thread (which means that all Extractor methods are called on that thread) and
other cleanup in ReleaseCallback will run on the loading thread instead of the
playback thread.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185651320
上级 99e5a3c9
...@@ -99,6 +99,9 @@ ...@@ -99,6 +99,9 @@
([#3796](https://github.com/google/ExoPlayer/issues/3796)). ([#3796](https://github.com/google/ExoPlayer/issues/3796)).
* Check `sys.display-size` on Philips ATVs * Check `sys.display-size` on Philips ATVs
([#3807](https://github.com/google/ExoPlayer/issues/3807)). ([#3807](https://github.com/google/ExoPlayer/issues/3807)).
* Release `Extractor`s on the loading thread to avoid potentially leaking
resources when the playback thread has quit by the time the loading task has
completed.
### 2.6.1 ### ### 2.6.1 ###
......
...@@ -179,24 +179,24 @@ import java.util.Arrays; ...@@ -179,24 +179,24 @@ import java.util.Arrays;
} }
public void release() { public void release() {
boolean releasedSynchronously = loader.release(this); if (prepared) {
if (prepared && !releasedSynchronously) {
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise // Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// sampleQueues may still be being modified by the loading thread. // sampleQueues may still be being modified by the loading thread.
for (SampleQueue sampleQueue : sampleQueues) { for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.discardToEnd(); sampleQueue.discardToEnd();
} }
} }
loader.release(this);
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
released = true; released = true;
} }
@Override @Override
public void onLoaderReleased() { public void onLoaderReleased() {
extractorHolder.release();
for (SampleQueue sampleQueue : sampleQueues) { for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset(); sampleQueue.reset();
} }
extractorHolder.release();
} }
@Override @Override
......
...@@ -73,7 +73,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S ...@@ -73,7 +73,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final BaseMediaChunkOutput mediaChunkOutput; private final BaseMediaChunkOutput mediaChunkOutput;
private Format primaryDownstreamTrackFormat; private Format primaryDownstreamTrackFormat;
private ReleaseCallback<T> releaseCallback; private @Nullable ReleaseCallback<T> releaseCallback;
private long pendingResetPositionUs; private long pendingResetPositionUs;
private long lastSeekPositionUs; private long lastSeekPositionUs;
/* package */ long decodeOnlyUntilPositionUs; /* package */ long decodeOnlyUntilPositionUs;
...@@ -306,20 +306,17 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S ...@@ -306,20 +306,17 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
* <p>This method should be called when the stream is no longer required. Either this method or * <p>This method should be called when the stream is no longer required. Either this method or
* {@link #release()} can be used to release this stream. * {@link #release()} can be used to release this stream.
* *
* @param callback A callback to be called when the release ends. Will be called synchronously * @param callback An optional callback to be called on the loading thread once the loader has
* from this method if no load is in progress, or asynchronously once the load has been * been released.
* canceled otherwise.
*/ */
public void release(@Nullable ReleaseCallback<T> callback) { public void release(@Nullable ReleaseCallback<T> callback) {
this.releaseCallback = callback; this.releaseCallback = callback;
boolean releasedSynchronously = loader.release(this); // Discard as much as we can synchronously.
if (!releasedSynchronously) { primarySampleQueue.discardToEnd();
// Discard as much as we can synchronously. for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
primarySampleQueue.discardToEnd(); embeddedSampleQueue.discardToEnd();
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.discardToEnd();
}
} }
loader.release(this);
} }
@Override @Override
......
...@@ -20,6 +20,7 @@ import android.os.Handler; ...@@ -20,6 +20,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
...@@ -197,25 +198,17 @@ public final class Loader implements LoaderErrorThrower { ...@@ -197,25 +198,17 @@ public final class Loader implements LoaderErrorThrower {
* Releases the {@link Loader}. This method should be called when the {@link Loader} is no longer * Releases the {@link Loader}. This method should be called when the {@link Loader} is no longer
* required. * required.
* *
* @param callback A callback to be called when the release ends. Will be called synchronously * @param callback An optional callback to be called on the loading thread once the loader has
* from this method if no load is in progress, or asynchronously once the load has been * been released.
* canceled otherwise. May be null.
* @return True if {@code callback} was called synchronously. False if it will be called
* asynchronously or if {@code callback} is null.
*/ */
public boolean release(ReleaseCallback callback) { public void release(@Nullable ReleaseCallback callback) {
boolean callbackInvoked = false;
if (currentTask != null) { if (currentTask != null) {
currentTask.cancel(true); currentTask.cancel(true);
if (callback != null) { }
downloadExecutorService.execute(new ReleaseTask(callback)); if (callback != null) {
} downloadExecutorService.execute(new ReleaseTask(callback));
} else if (callback != null) {
callback.onLoaderReleased();
callbackInvoked = true;
} }
downloadExecutorService.shutdown(); downloadExecutorService.shutdown();
return callbackInvoked;
} }
// LoaderErrorThrower implementation. // LoaderErrorThrower implementation.
...@@ -419,7 +412,7 @@ public final class Loader implements LoaderErrorThrower { ...@@ -419,7 +412,7 @@ public final class Loader implements LoaderErrorThrower {
} }
private static final class ReleaseTask extends Handler implements Runnable { private static final class ReleaseTask implements Runnable {
private final ReleaseCallback callback; private final ReleaseCallback callback;
...@@ -429,13 +422,6 @@ public final class Loader implements LoaderErrorThrower { ...@@ -429,13 +422,6 @@ public final class Loader implements LoaderErrorThrower {
@Override @Override
public void run() { public void run() {
if (getLooper().getThread().isAlive()) {
sendEmptyMessage(0);
}
}
@Override
public void handleMessage(Message msg) {
callback.onLoaderReleased(); callback.onLoaderReleased();
} }
......
...@@ -153,7 +153,7 @@ import java.util.List; ...@@ -153,7 +153,7 @@ import java.util.List;
// ChunkSampleStream.ReleaseCallback implementation. // ChunkSampleStream.ReleaseCallback implementation.
@Override @Override
public void onSampleStreamReleased(ChunkSampleStream<DashChunkSource> stream) { public synchronized void onSampleStreamReleased(ChunkSampleStream<DashChunkSource> stream) {
PlayerTrackEmsgHandler trackEmsgHandler = trackEmsgHandlerBySampleStream.remove(stream); PlayerTrackEmsgHandler trackEmsgHandler = trackEmsgHandlerBySampleStream.remove(stream);
if (trackEmsgHandler != null) { if (trackEmsgHandler != null) {
trackEmsgHandler.release(); trackEmsgHandler.release();
...@@ -565,7 +565,10 @@ import java.util.List; ...@@ -565,7 +565,10 @@ import java.util.List;
positionUs, positionUs,
minLoadableRetryCount, minLoadableRetryCount,
eventDispatcher); eventDispatcher);
trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler); synchronized (this) {
// The map is also accessed on the loading thread so synchronize access.
trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler);
}
return stream; return stream;
} }
......
...@@ -386,14 +386,14 @@ import java.util.Arrays; ...@@ -386,14 +386,14 @@ import java.util.Arrays;
} }
public void release() { public void release() {
boolean releasedSynchronously = loader.release(this); if (prepared) {
if (prepared && !releasedSynchronously) {
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise // Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// sampleQueues may still be being modified by the loading thread. // sampleQueues may still be being modified by the loading thread.
for (SampleQueue sampleQueue : sampleQueues) { for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.discardToEnd(); sampleQueue.discardToEnd();
} }
} }
loader.release(this);
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
released = true; released = true;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册