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