1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 package org.mozilla.gecko.media; 6 7 import android.media.MediaCodec; 8 9 import org.mozilla.gecko.mozglue.SharedMemory; 10 11 import java.io.IOException; 12 import java.util.ArrayList; 13 import java.util.List; 14 import java.util.Map; 15 import java.util.HashMap; 16 17 final class SamplePool { 18 private static final class Impl { 19 private final String mName; 20 private int mDefaultBufferSize = 4096; 21 private final List<Sample> mRecycledSamples = new ArrayList<>(); 22 private final boolean mBufferless; 23 24 private int mNextBufferId = Sample.NO_BUFFER + 1; 25 private Map<Integer, SampleBuffer> mBuffers = new HashMap<>(); 26 Impl(final String name, final boolean bufferless)27 private Impl(final String name, final boolean bufferless) { 28 mName = name; 29 mBufferless = bufferless; 30 } 31 setDefaultBufferSize(final int size)32 private void setDefaultBufferSize(final int size) { 33 if (mBufferless) { 34 throw new IllegalStateException("Setting buffer size of a bufferless pool is not allowed"); 35 } 36 mDefaultBufferSize = size; 37 } 38 obtain(final int size)39 private synchronized Sample obtain(final int size) { 40 if (!mRecycledSamples.isEmpty()) { 41 return mRecycledSamples.remove(0); 42 } 43 44 if (mBufferless) { 45 return Sample.obtain(); 46 } else { 47 return allocateSampleAndBuffer(size); 48 } 49 } 50 allocateSampleAndBuffer(final int size)51 private Sample allocateSampleAndBuffer(final int size) { 52 final int id = mNextBufferId++; 53 try { 54 final SharedMemory shm = new SharedMemory(id, Math.max(size, mDefaultBufferSize)); 55 mBuffers.put((Integer)id, new SampleBuffer(shm)); 56 final Sample s = Sample.obtain(); 57 s.bufferId = id; 58 return s; 59 } catch (final NoSuchMethodException | IOException e) { 60 mBuffers.remove(id); 61 throw new UnsupportedOperationException(e); 62 } 63 } 64 getBuffer(final int id)65 private synchronized SampleBuffer getBuffer(final int id) { 66 return mBuffers.get(id); 67 } 68 recycle(final Sample recycled)69 private synchronized void recycle(final Sample recycled) { 70 if (mBufferless || isUsefulSample(recycled)) { 71 mRecycledSamples.add(recycled); 72 } else { 73 disposeSample(recycled); 74 } 75 } 76 isUsefulSample(final Sample sample)77 private boolean isUsefulSample(final Sample sample) { 78 return mBuffers.get(sample.bufferId).capacity() >= mDefaultBufferSize; 79 } 80 clear()81 private synchronized void clear() { 82 for (final Sample s : mRecycledSamples) { 83 disposeSample(s); 84 } 85 mRecycledSamples.clear(); 86 87 for (final SampleBuffer b: mBuffers.values()) { 88 b.dispose(); 89 } 90 mBuffers.clear(); 91 } 92 disposeSample(final Sample sample)93 private void disposeSample(final Sample sample) { 94 if (sample.bufferId != Sample.NO_BUFFER) { 95 mBuffers.remove(sample.bufferId).dispose(); 96 } 97 sample.dispose(); 98 } 99 100 @Override finalize()101 protected void finalize() { 102 clear(); 103 } 104 } 105 106 private final Impl mInputs; 107 private final Impl mOutputs; 108 SamplePool(final String name, final boolean renderToSurface)109 /* package */ SamplePool(final String name, final boolean renderToSurface) { 110 mInputs = new Impl(name + " input sample pool", false); 111 // Buffers are useless when rendering to surface. 112 mOutputs = new Impl(name + " output sample pool", renderToSurface); 113 } 114 setInputBufferSize(final int size)115 /* package */ void setInputBufferSize(final int size) { 116 mInputs.setDefaultBufferSize(size); 117 } 118 setOutputBufferSize(final int size)119 /* package */ void setOutputBufferSize(final int size) { 120 mOutputs.setDefaultBufferSize(size); 121 } 122 obtainInput(final int size)123 /* package */ Sample obtainInput(final int size) { 124 final Sample input = mInputs.obtain(size); 125 input.info.set(0, 0, 0, 0); 126 return input; 127 } 128 obtainOutput(final MediaCodec.BufferInfo info)129 /* package */ Sample obtainOutput(final MediaCodec.BufferInfo info) { 130 final Sample output = mOutputs.obtain(info.size); 131 output.info.set(0, info.size, info.presentationTimeUs, info.flags); 132 return output; 133 } 134 recycleInput(final Sample sample)135 /* package */ void recycleInput(final Sample sample) { 136 sample.cryptoInfo = null; 137 mInputs.recycle(sample); 138 } 139 recycleOutput(final Sample sample)140 /* package */ void recycleOutput(final Sample sample) { 141 mOutputs.recycle(sample); 142 } 143 reset()144 /* package */ void reset() { 145 mInputs.clear(); 146 mOutputs.clear(); 147 } 148 getInputBuffer(final int id)149 /* package */ SampleBuffer getInputBuffer(final int id) { 150 return mInputs.getBuffer(id); 151 } 152 getOutputBuffer(final int id)153 /* package */ SampleBuffer getOutputBuffer(final int id) { 154 return mOutputs.getBuffer(id); 155 } 156 } 157