Always copy output byte buffers in HardwareVideoDecoder.
This simplifies the code and ensures we don't starve the decoder if there are multiple output buffers queued. Bug: webrtc:7760 Change-Id: I42c31f5045fca96847001260b8796d6756900d0f Reviewed-on: https://webrtc-review.googlesource.com/5522 Commit-Queue: Sami Kalliomäki <sakal@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20161}
This commit is contained in:
committed by
Commit Bot
parent
13044c1b53
commit
0ca8b53603
@ -83,10 +83,6 @@ class HardwareVideoDecoder
|
|||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
private volatile Exception shutdownException = null;
|
private volatile Exception shutdownException = null;
|
||||||
|
|
||||||
// Prevents the decoder from being released before all output buffers have been released.
|
|
||||||
private final Object activeOutputBuffersLock = new Object();
|
|
||||||
private int activeOutputBuffers = 0; // Guarded by activeOutputBuffersLock
|
|
||||||
|
|
||||||
// Dimensions (width, height, stride, and sliceHeight) may be accessed by either the decode thread
|
// Dimensions (width, height, stride, and sliceHeight) may be accessed by either the decode thread
|
||||||
// or the output thread. Accesses should be protected with this lock.
|
// or the output thread. Accesses should be protected with this lock.
|
||||||
private final Object dimensionLock = new Object();
|
private final Object dimensionLock = new Object();
|
||||||
@ -488,19 +484,15 @@ class HardwareVideoDecoder
|
|||||||
buffer.position(info.offset);
|
buffer.position(info.offset);
|
||||||
buffer.limit(info.offset + info.size);
|
buffer.limit(info.offset + info.size);
|
||||||
buffer = buffer.slice();
|
buffer = buffer.slice();
|
||||||
final VideoFrame.Buffer frameBuffer;
|
|
||||||
|
|
||||||
|
final VideoFrame.Buffer frameBuffer;
|
||||||
if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar) {
|
if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar) {
|
||||||
if (sliceHeight % 2 == 0) {
|
frameBuffer = copyI420Buffer(buffer, stride, sliceHeight, width, height);
|
||||||
frameBuffer = wrapI420Buffer(buffer, result, stride, sliceHeight, width, height);
|
|
||||||
} else {
|
|
||||||
// WebRTC rounds chroma plane size conversions up so we have to repeat the last row.
|
|
||||||
frameBuffer = copyI420Buffer(buffer, result, stride, sliceHeight, width, height);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// All other supported color formats are NV12.
|
// All other supported color formats are NV12.
|
||||||
frameBuffer = wrapNV12Buffer(buffer, result, stride, sliceHeight, width, height);
|
frameBuffer = copyNV12ToI420Buffer(buffer, stride, sliceHeight, width, height);
|
||||||
}
|
}
|
||||||
|
codec.releaseOutputBuffer(result, false);
|
||||||
|
|
||||||
long presentationTimeNs = info.presentationTimeUs * 1000;
|
long presentationTimeNs = info.presentationTimeUs * 1000;
|
||||||
VideoFrame frame = new VideoFrame(frameBuffer, rotation, presentationTimeNs);
|
VideoFrame frame = new VideoFrame(frameBuffer, rotation, presentationTimeNs);
|
||||||
@ -510,23 +502,15 @@ class HardwareVideoDecoder
|
|||||||
frame.release();
|
frame.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private VideoFrame.Buffer wrapNV12Buffer(ByteBuffer buffer, int outputBufferIndex, int stride,
|
private VideoFrame.Buffer copyNV12ToI420Buffer(
|
||||||
int sliceHeight, int width, int height) {
|
ByteBuffer buffer, int stride, int sliceHeight, int width, int height) {
|
||||||
synchronized (activeOutputBuffersLock) {
|
// toI420 copies the buffer.
|
||||||
activeOutputBuffers++;
|
return new NV12Buffer(width, height, stride, sliceHeight, buffer, null /* releaseCallback */)
|
||||||
}
|
.toI420();
|
||||||
|
|
||||||
return new NV12Buffer(width, height, stride, sliceHeight, buffer, () -> {
|
|
||||||
codec.releaseOutputBuffer(outputBufferIndex, false);
|
|
||||||
synchronized (activeOutputBuffersLock) {
|
|
||||||
activeOutputBuffers--;
|
|
||||||
activeOutputBuffersLock.notifyAll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VideoFrame.Buffer copyI420Buffer(ByteBuffer buffer, int outputBufferIndex, int stride,
|
private VideoFrame.Buffer copyI420Buffer(
|
||||||
int sliceHeight, int width, int height) {
|
ByteBuffer buffer, int stride, int sliceHeight, int width, int height) {
|
||||||
final int uvStride = stride / 2;
|
final int uvStride = stride / 2;
|
||||||
|
|
||||||
final int yPos = 0;
|
final int yPos = 0;
|
||||||
@ -538,14 +522,11 @@ class HardwareVideoDecoder
|
|||||||
VideoFrame.I420Buffer frameBuffer = JavaI420Buffer.allocate(width, height);
|
VideoFrame.I420Buffer frameBuffer = JavaI420Buffer.allocate(width, height);
|
||||||
|
|
||||||
ByteBuffer dataY = frameBuffer.getDataY();
|
ByteBuffer dataY = frameBuffer.getDataY();
|
||||||
dataY.position(0); // Ensure we are in the beginning.
|
|
||||||
buffer.position(yPos);
|
buffer.position(yPos);
|
||||||
buffer.limit(uPos);
|
buffer.limit(uPos);
|
||||||
dataY.put(buffer);
|
dataY.put(buffer);
|
||||||
dataY.position(0); // Go back to beginning.
|
|
||||||
|
|
||||||
ByteBuffer dataU = frameBuffer.getDataU();
|
ByteBuffer dataU = frameBuffer.getDataU();
|
||||||
dataU.position(0); // Ensure we are in the beginning.
|
|
||||||
buffer.position(uPos);
|
buffer.position(uPos);
|
||||||
buffer.limit(uEnd);
|
buffer.limit(uEnd);
|
||||||
dataU.put(buffer);
|
dataU.put(buffer);
|
||||||
@ -553,10 +534,8 @@ class HardwareVideoDecoder
|
|||||||
buffer.position(uEnd - uvStride); // Repeat the last row.
|
buffer.position(uEnd - uvStride); // Repeat the last row.
|
||||||
dataU.put(buffer);
|
dataU.put(buffer);
|
||||||
}
|
}
|
||||||
dataU.position(0); // Go back to beginning.
|
|
||||||
|
|
||||||
ByteBuffer dataV = frameBuffer.getDataU();
|
ByteBuffer dataV = frameBuffer.getDataV();
|
||||||
dataV.position(0); // Ensure we are in the beginning.
|
|
||||||
buffer.position(vPos);
|
buffer.position(vPos);
|
||||||
buffer.limit(vEnd);
|
buffer.limit(vEnd);
|
||||||
dataV.put(buffer);
|
dataV.put(buffer);
|
||||||
@ -564,51 +543,10 @@ class HardwareVideoDecoder
|
|||||||
buffer.position(vEnd - uvStride); // Repeat the last row.
|
buffer.position(vEnd - uvStride); // Repeat the last row.
|
||||||
dataV.put(buffer);
|
dataV.put(buffer);
|
||||||
}
|
}
|
||||||
dataV.position(0); // Go back to beginning.
|
|
||||||
|
|
||||||
codec.releaseOutputBuffer(outputBufferIndex, false);
|
|
||||||
|
|
||||||
return frameBuffer;
|
return frameBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VideoFrame.Buffer wrapI420Buffer(ByteBuffer buffer, int outputBufferIndex, int stride,
|
|
||||||
int sliceHeight, int width, int height) {
|
|
||||||
final int uvStride = stride / 2;
|
|
||||||
|
|
||||||
final int yPos = 0;
|
|
||||||
final int uPos = yPos + stride * sliceHeight;
|
|
||||||
final int uEnd = uPos + uvStride * (sliceHeight / 2);
|
|
||||||
final int vPos = uPos + uvStride * sliceHeight / 2;
|
|
||||||
final int vEnd = vPos + uvStride * (sliceHeight / 2);
|
|
||||||
|
|
||||||
synchronized (activeOutputBuffersLock) {
|
|
||||||
activeOutputBuffers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Runnable releaseCallback = () -> {
|
|
||||||
codec.releaseOutputBuffer(outputBufferIndex, false);
|
|
||||||
synchronized (activeOutputBuffersLock) {
|
|
||||||
activeOutputBuffers--;
|
|
||||||
activeOutputBuffersLock.notifyAll();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
buffer.position(yPos);
|
|
||||||
buffer.limit(uPos);
|
|
||||||
ByteBuffer dataY = buffer.slice();
|
|
||||||
|
|
||||||
buffer.position(uPos);
|
|
||||||
buffer.limit(uEnd);
|
|
||||||
ByteBuffer dataU = buffer.slice();
|
|
||||||
|
|
||||||
buffer.position(vPos);
|
|
||||||
buffer.limit(vEnd);
|
|
||||||
ByteBuffer dataV = buffer.slice();
|
|
||||||
|
|
||||||
return JavaI420Buffer.wrap(
|
|
||||||
width, height, dataY, stride, dataU, uvStride, dataV, uvStride, releaseCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reformat(MediaFormat format) {
|
private void reformat(MediaFormat format) {
|
||||||
outputThreadChecker.checkIsOnValidThread();
|
outputThreadChecker.checkIsOnValidThread();
|
||||||
Logging.d(TAG, "Decoder format changed: " + format.toString());
|
Logging.d(TAG, "Decoder format changed: " + format.toString());
|
||||||
@ -665,7 +603,6 @@ class HardwareVideoDecoder
|
|||||||
private void releaseCodecOnOutputThread() {
|
private void releaseCodecOnOutputThread() {
|
||||||
outputThreadChecker.checkIsOnValidThread();
|
outputThreadChecker.checkIsOnValidThread();
|
||||||
Logging.d(TAG, "Releasing MediaCodec on output thread");
|
Logging.d(TAG, "Releasing MediaCodec on output thread");
|
||||||
waitOutputBuffersReleasedOnOutputThread();
|
|
||||||
try {
|
try {
|
||||||
codec.stop();
|
codec.stop();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -681,21 +618,6 @@ class HardwareVideoDecoder
|
|||||||
Logging.d(TAG, "Release on output thread done");
|
Logging.d(TAG, "Release on output thread done");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitOutputBuffersReleasedOnOutputThread() {
|
|
||||||
outputThreadChecker.checkIsOnValidThread();
|
|
||||||
synchronized (activeOutputBuffersLock) {
|
|
||||||
while (activeOutputBuffers > 0) {
|
|
||||||
Logging.d(TAG, "Waiting for all frames to be released.");
|
|
||||||
try {
|
|
||||||
activeOutputBuffersLock.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Logging.e(TAG, "Interrupted while waiting for output buffers to be released.", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopOnOutputThread(Exception e) {
|
private void stopOnOutputThread(Exception e) {
|
||||||
outputThreadChecker.checkIsOnValidThread();
|
outputThreadChecker.checkIsOnValidThread();
|
||||||
running = false;
|
running = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user