Bug 1295106 - Part 5: allocate samples from a pool. r=snorp

MozReview-Commit-ID: CIn9CH9k9i4

--HG--
extra : rebase_source : 6e0178881ee8c46832c064027907f43310f6f548
This commit is contained in:
John Lin 2016-10-06 17:05:20 +08:00
Родитель ce9e7de84b
Коммит 9d4d55e04d
3 изменённых файлов: 182 добавлений и 6 удалений

Просмотреть файл

@ -17,6 +17,7 @@ import android.view.Surface;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Queue; import java.util.Queue;
/* package */ final class Codec extends ICodec.Stub implements IBinder.DeathRecipient { /* package */ final class Codec extends ICodec.Stub implements IBinder.DeathRecipient {
@ -29,6 +30,8 @@ import java.util.Queue;
private final class Callbacks implements AsyncCodec.Callbacks { private final class Callbacks implements AsyncCodec.Callbacks {
private ICodecCallbacks mRemote; private ICodecCallbacks mRemote;
private boolean mHasInputCapacitySet;
private boolean mHasOutputCapacitySet;
public Callbacks(ICodecCallbacks remote) { public Callbacks(ICodecCallbacks remote) {
mRemote = remote; mRemote = remote;
@ -40,6 +43,13 @@ import java.util.Queue;
// Flush invalidates all buffers. // Flush invalidates all buffers.
return; return;
} }
if (!mHasInputCapacitySet) {
int capacity = codec.getInputBuffer(index).capacity();
if (capacity > 0) {
mSamplePool.setInputBufferSize(capacity);
mHasInputCapacitySet = true;
}
}
if (!mInputProcessor.onBuffer(index)) { if (!mInputProcessor.onBuffer(index)) {
reportError(Error.FATAL, new Exception("FAIL: input buffer queue is full")); reportError(Error.FATAL, new Exception("FAIL: input buffer queue is full"));
} }
@ -51,8 +61,24 @@ import java.util.Queue;
// Flush invalidates all buffers. // Flush invalidates all buffers.
return; return;
} }
ByteBuffer output = codec.getOutputBuffer(index);
if (!mHasOutputCapacitySet) {
int capacity = output.capacity();
if (capacity > 0) {
mSamplePool.setOutputBufferSize(capacity);
mHasOutputCapacitySet = true;
}
}
Sample copy = mSamplePool.obtainOutput(info);
try { try {
mRemote.onOutput(Sample.create(codec.getOutputBuffer(index), info, null)); if (info.size > 0) {
copy.buffer.readFromByteBuffer(output, info.offset, info.size);
}
mSentOutputs.add(copy);
mRemote.onOutput(copy);
} catch (IOException e) {
Log.e(LOGTAG, "Fail to read output buffer:" + e.getMessage());
outputDummy(info);
} catch (TransactionTooLargeException ttle) { } catch (TransactionTooLargeException ttle) {
Log.e(LOGTAG, "Output is too large:" + ttle.getMessage()); Log.e(LOGTAG, "Output is too large:" + ttle.getMessage());
outputDummy(info); outputDummy(info);
@ -60,6 +86,7 @@ import java.util.Queue;
// Dead recipient. // Dead recipient.
e.printStackTrace(); e.printStackTrace();
} }
mCodec.releaseOutputBuffer(index, true); mCodec.releaseOutputBuffer(index, true);
boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0; boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
if (DEBUG && eos) { if (DEBUG && eos) {
@ -94,10 +121,29 @@ import java.util.Queue;
} }
private final class InputProcessor { private final class InputProcessor {
private Queue<Sample> mInputSamples = new LinkedList<Sample>(); private Queue<Sample> mInputSamples = new LinkedList<>();
private Queue<Integer> mAvailableInputBuffers = new LinkedList<Integer>(); private Queue<Integer> mAvailableInputBuffers = new LinkedList<>();
private Queue<Sample> mDequeuedSamples = new LinkedList<>();
private synchronized Sample onAllocate(int size) {
Sample sample = mSamplePool.obtainInput(size);
mDequeuedSamples.add(sample);
return sample;
}
private synchronized boolean onSample(Sample sample) { private synchronized boolean onSample(Sample sample) {
if (sample == null) {
return false;
}
if (!sample.isEOS()) {
Sample temp = sample;
sample = mDequeuedSamples.remove();
sample.info = temp.info;
sample.cryptoInfo = temp.cryptoInfo;
temp.dispose();
}
if (!mInputSamples.offer(sample)) { if (!mInputSamples.offer(sample)) {
return false; return false;
} }
@ -116,8 +162,10 @@ import java.util.Queue;
private void feedSampleToBuffer() { private void feedSampleToBuffer() {
while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) { while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) {
int index = mAvailableInputBuffers.poll(); int index = mAvailableInputBuffers.poll();
Sample sample = mInputSamples.poll();
int len = 0; int len = 0;
Sample sample = mInputSamples.poll();
long pts = sample.info.presentationTimeUs;
int flags = sample.info.flags;
if (!sample.isEOS() && sample.buffer != null) { if (!sample.isEOS() && sample.buffer != null) {
len = sample.info.size; len = sample.info.size;
ByteBuffer buf = mCodec.getInputBuffer(index); ByteBuffer buf = mCodec.getInputBuffer(index);
@ -129,8 +177,9 @@ import java.util.Queue;
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
} }
mSamplePool.recycleInput(sample);
} }
mCodec.queueInputBuffer(index, 0, len, sample.info.presentationTimeUs, sample.info.flags); mCodec.queueInputBuffer(index, 0, len, pts, flags);
} }
} }
@ -143,6 +192,8 @@ import java.util.Queue;
private AsyncCodec mCodec; private AsyncCodec mCodec;
private InputProcessor mInputProcessor; private InputProcessor mInputProcessor;
private volatile boolean mFlushing = false; private volatile boolean mFlushing = false;
private SamplePool mSamplePool;
private Queue<Sample> mSentOutputs = new LinkedList<>();
public synchronized void setCallbacks(ICodecCallbacks callbacks) throws RemoteException { public synchronized void setCallbacks(ICodecCallbacks callbacks) throws RemoteException {
mCallbacks = callbacks; mCallbacks = callbacks;
@ -187,6 +238,7 @@ import java.util.Queue;
codec.configure(fmt, surface, flags); codec.configure(fmt, surface, flags);
mCodec = codec; mCodec = codec;
mInputProcessor = new InputProcessor(); mInputProcessor = new InputProcessor();
mSamplePool = new SamplePool(codecName);
if (DEBUG) Log.d(LOGTAG, codec.toString() + " created"); if (DEBUG) Log.d(LOGTAG, codec.toString() + " created");
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -279,7 +331,7 @@ import java.util.Queue;
@Override @Override
public synchronized Sample dequeueInput(int size) { public synchronized Sample dequeueInput(int size) {
return Sample.create(); return mInputProcessor.onAllocate(size);
} }
@Override @Override
@ -291,12 +343,20 @@ import java.util.Queue;
@Override @Override
public synchronized void releaseOutput(Sample sample) { public synchronized void releaseOutput(Sample sample) {
try {
mSamplePool.recycleOutput(mSentOutputs.remove());
} catch (NoSuchElementException e) {
Log.e(LOGTAG, "releaseOutput not found: " + sample + "sent: " + mSentOutputs);
}
sample.dispose();
} }
@Override @Override
public synchronized void release() throws RemoteException { public synchronized void release() throws RemoteException {
if (DEBUG) Log.d(LOGTAG, "release " + this); if (DEBUG) Log.d(LOGTAG, "release " + this);
releaseCodec(); releaseCodec();
mSamplePool.reset();
mSamplePool = null;
mCallbacks.asBinder().unlinkToDeath(this, 0); mCallbacks.asBinder().unlinkToDeath(this, 0);
mCallbacks = null; mCallbacks = null;
} }

Просмотреть файл

@ -0,0 +1,115 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.media;
import android.media.MediaCodec;
import org.mozilla.gecko.mozglue.SharedMemory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
final class SamplePool {
private final class Impl {
private final String mName;
private int mNextId = 0;
private int mDefaultBufferSize = 4096;
private final List<Sample> mRecycledSamples = new ArrayList<>();
private Impl(String name) {
mName = name;
}
private void setDefaultBufferSize(int size) {
mDefaultBufferSize = size;
}
private synchronized Sample allocate(int size) {
Sample sample;
if (!mRecycledSamples.isEmpty()) {
sample = mRecycledSamples.remove(0);
sample.info.set(0, 0, 0, 0);
} else {
SharedMemory shm = null;
try {
shm = new SharedMemory(mNextId++, Math.max(size, mDefaultBufferSize));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (shm != null) {
sample = Sample.create(shm);
} else {
sample = Sample.create();
}
}
return sample;
}
private synchronized void recycle(Sample recycled) {
if (recycled.buffer.capacity() >= mDefaultBufferSize) {
mRecycledSamples.add(recycled);
} else {
recycled.dispose();
}
}
private synchronized void clear() {
for (Sample s : mRecycledSamples) {
s.dispose();
}
mRecycledSamples.clear();
}
@Override
protected void finalize() {
clear();
}
}
private final Impl mInputs;
private final Impl mOutputs;
/* package */ SamplePool(String name) {
mInputs = new Impl(name + " input buffer pool");
mOutputs = new Impl(name + " output buffer pool");
}
/* package */ void setInputBufferSize(int size) {
mInputs.setDefaultBufferSize(size);
}
/* package */ void setOutputBufferSize(int size) {
mOutputs.setDefaultBufferSize(size);
}
/* package */ Sample obtainInput(int size) {
return mInputs.allocate(size);
}
/* package */ Sample obtainOutput(MediaCodec.BufferInfo info) {
Sample output = mOutputs.allocate(info.size);
output.info.set(0, info.size, info.presentationTimeUs, info.flags);
return output;
}
/* package */ void recycleInput(Sample sample) {
sample.cryptoInfo = null;
mInputs.recycle(sample);
}
/* package */ void recycleOutput(Sample sample) {
mOutputs.recycle(sample);
}
/* package */ void reset() {
mInputs.clear();
mOutputs.clear();
}
}

Просмотреть файл

@ -561,6 +561,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'media/MediaManager.java', 'media/MediaManager.java',
'media/RemoteManager.java', 'media/RemoteManager.java',
'media/Sample.java', 'media/Sample.java',
'media/SamplePool.java',
'media/VideoPlayer.java', 'media/VideoPlayer.java',
'MediaCastingBar.java', 'MediaCastingBar.java',
'MemoryMonitor.java', 'MemoryMonitor.java',