зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ce9e7de84b
Коммит
9d4d55e04d
|
@ -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',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче