mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Remove most allocations in SampleQueue.release
SampleQueues may be released in the context of a finally block after an out of memory error. Allocating in that scenario can throw yet a new OutOfMemoryError. By safely releasing SampleQueue memory, we increase the possibility of handling the error gracefully. PiperOrigin-RevId: 420859022
This commit is contained in:
parent
8c89c9c688
commit
c3b470f308
@ -27,6 +27,7 @@ import com.google.android.exoplayer2.source.SampleQueue.SampleExtrasHolder;
|
|||||||
import com.google.android.exoplayer2.upstream.Allocation;
|
import com.google.android.exoplayer2.upstream.Allocation;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataReader;
|
import com.google.android.exoplayer2.upstream.DataReader;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@ -79,6 +80,7 @@ import java.util.Arrays;
|
|||||||
* discarded, or 0 if the queue is now empty.
|
* discarded, or 0 if the queue is now empty.
|
||||||
*/
|
*/
|
||||||
public void discardUpstreamSampleBytes(long totalBytesWritten) {
|
public void discardUpstreamSampleBytes(long totalBytesWritten) {
|
||||||
|
Assertions.checkArgument(totalBytesWritten <= this.totalBytesWritten);
|
||||||
this.totalBytesWritten = totalBytesWritten;
|
this.totalBytesWritten = totalBytesWritten;
|
||||||
if (this.totalBytesWritten == 0
|
if (this.totalBytesWritten == 0
|
||||||
|| this.totalBytesWritten == firstAllocationNode.startPosition) {
|
|| this.totalBytesWritten == firstAllocationNode.startPosition) {
|
||||||
@ -92,8 +94,8 @@ import java.util.Arrays;
|
|||||||
while (this.totalBytesWritten > lastNodeToKeep.endPosition) {
|
while (this.totalBytesWritten > lastNodeToKeep.endPosition) {
|
||||||
lastNodeToKeep = lastNodeToKeep.next;
|
lastNodeToKeep = lastNodeToKeep.next;
|
||||||
}
|
}
|
||||||
// Discard all subsequent nodes.
|
// Discard all subsequent nodes. lastNodeToKeep is initialized, therefore next cannot be null.
|
||||||
AllocationNode firstNodeToDiscard = lastNodeToKeep.next;
|
AllocationNode firstNodeToDiscard = Assertions.checkNotNull(lastNodeToKeep.next);
|
||||||
clearAllocationNodes(firstNodeToDiscard);
|
clearAllocationNodes(firstNodeToDiscard);
|
||||||
// Reset the successor of the last node to be an uninitialized node.
|
// Reset the successor of the last node to be an uninitialized node.
|
||||||
lastNodeToKeep.next = new AllocationNode(lastNodeToKeep.endPosition, allocationLength);
|
lastNodeToKeep.next = new AllocationNode(lastNodeToKeep.endPosition, allocationLength);
|
||||||
@ -213,17 +215,8 @@ import java.util.Arrays;
|
|||||||
// Bulk release allocations for performance (it's significantly faster when using
|
// Bulk release allocations for performance (it's significantly faster when using
|
||||||
// DefaultAllocator because the allocator's lock only needs to be acquired and released once)
|
// DefaultAllocator because the allocator's lock only needs to be acquired and released once)
|
||||||
// [Internal: See b/29542039].
|
// [Internal: See b/29542039].
|
||||||
int allocationCount =
|
allocator.release(fromNode);
|
||||||
(writeAllocationNode.allocation != null ? 1 : 0)
|
fromNode.clear();
|
||||||
+ ((int) (writeAllocationNode.startPosition - fromNode.startPosition)
|
|
||||||
/ allocationLength);
|
|
||||||
Allocation[] allocationsToRelease = new Allocation[allocationCount];
|
|
||||||
AllocationNode currentNode = fromNode;
|
|
||||||
for (int i = 0; i < allocationsToRelease.length; i++) {
|
|
||||||
allocationsToRelease[i] = currentNode.allocation;
|
|
||||||
currentNode = currentNode.clear();
|
|
||||||
}
|
|
||||||
allocator.release(allocationsToRelease);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -466,7 +459,7 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A node in a linked list of {@link Allocation}s held by the output. */
|
/** A node in a linked list of {@link Allocation}s held by the output. */
|
||||||
private static final class AllocationNode {
|
private static final class AllocationNode implements Allocator.AllocationNode {
|
||||||
|
|
||||||
/** The absolute position of the start of the data (inclusive). */
|
/** The absolute position of the start of the data (inclusive). */
|
||||||
public final long startPosition;
|
public final long startPosition;
|
||||||
@ -525,5 +518,22 @@ import java.util.Arrays;
|
|||||||
next = null;
|
next = null;
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllocationChainNode implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Allocation getAllocation() {
|
||||||
|
return Assertions.checkNotNull(allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Allocator.AllocationNode next() {
|
||||||
|
if (next == null || next.allocation == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/** A source of allocations. */
|
/** A source of allocations. */
|
||||||
public interface Allocator {
|
public interface Allocator {
|
||||||
|
|
||||||
|
/** A node in a chain of {@link Allocation Allocations}. */
|
||||||
|
interface AllocationNode {
|
||||||
|
|
||||||
|
/** Returns the {@link Allocation} associated to this chain node. */
|
||||||
|
Allocation getAllocation();
|
||||||
|
|
||||||
|
/** Returns the next chain node, or {@code null} if this is the last node in the chain. */
|
||||||
|
@Nullable
|
||||||
|
AllocationNode next();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain an {@link Allocation}.
|
* Obtain an {@link Allocation}.
|
||||||
*
|
*
|
||||||
@ -36,15 +49,23 @@ public interface Allocator {
|
|||||||
void release(Allocation allocation);
|
void release(Allocation allocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases an array of {@link Allocation}s back to the allocator.
|
* Releases an array of {@link Allocation Allocations} back to the allocator.
|
||||||
*
|
*
|
||||||
* @param allocations The array of {@link Allocation}s being released.
|
* @param allocations The array of {@link Allocation}s being released.
|
||||||
*/
|
*/
|
||||||
void release(Allocation[] allocations);
|
void release(Allocation[] allocations);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases all {@link Allocation Allocations} in the chain starting at the given {@link
|
||||||
|
* AllocationNode}.
|
||||||
|
*
|
||||||
|
* <p>Implementations must not make memory allocations.
|
||||||
|
*/
|
||||||
|
void release(AllocationNode allocationNode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hints to the allocator that it should make a best effort to release any excess {@link
|
* Hints to the allocator that it should make a best effort to release any excess {@link
|
||||||
* Allocation}s.
|
* Allocation Allocations}.
|
||||||
*/
|
*/
|
||||||
void trim();
|
void trim();
|
||||||
|
|
||||||
|
@ -128,6 +128,17 @@ public final class DefaultAllocator implements Allocator {
|
|||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void release(@Nullable AllocationNode allocationNode) {
|
||||||
|
while (allocationNode != null) {
|
||||||
|
availableAllocations[availableCount++] = allocationNode.getAllocation();
|
||||||
|
allocatedCount--;
|
||||||
|
allocationNode = allocationNode.next();
|
||||||
|
}
|
||||||
|
// Wake up threads waiting for the allocated size to drop.
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void trim() {
|
public synchronized void trim() {
|
||||||
int targetAllocationCount = Util.ceilDivide(targetBufferSize, individualAllocationSize);
|
int targetAllocationCount = Util.ceilDivide(targetBufferSize, individualAllocationSize);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user