Add option to inject YuvConverter to SurfaceTextureHelper.
Add option to inject VideoFrameDrawer to YuvConverter and EglRenderer. Bug: none Change-Id: I0aab0026c30b41d72f70fb00b251aed5e4a4a774 Reviewed-on: https://webrtc-review.googlesource.com/c/123443 Commit-Queue: Åsa Persson <asapersson@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26848}
This commit is contained in:
parent
b4f03931b5
commit
f2889bbaf4
@ -123,7 +123,7 @@ public class EglRenderer implements VideoSink {
|
|||||||
// EGL and GL resources for drawing YUV/OES textures. After initilization, these are only accessed
|
// EGL and GL resources for drawing YUV/OES textures. After initilization, these are only accessed
|
||||||
// from the render thread.
|
// from the render thread.
|
||||||
@Nullable private EglBase eglBase;
|
@Nullable private EglBase eglBase;
|
||||||
private final VideoFrameDrawer frameDrawer = new VideoFrameDrawer();
|
private final VideoFrameDrawer frameDrawer;
|
||||||
@Nullable private RendererCommon.GlDrawer drawer;
|
@Nullable private RendererCommon.GlDrawer drawer;
|
||||||
private boolean usePresentationTimeStamp;
|
private boolean usePresentationTimeStamp;
|
||||||
private final Matrix drawMatrix = new Matrix();
|
private final Matrix drawMatrix = new Matrix();
|
||||||
@ -181,7 +181,12 @@ public class EglRenderer implements VideoSink {
|
|||||||
* logging. In order to render something, you must first call init() and createEglSurface.
|
* logging. In order to render something, you must first call init() and createEglSurface.
|
||||||
*/
|
*/
|
||||||
public EglRenderer(String name) {
|
public EglRenderer(String name) {
|
||||||
|
this(name, new VideoFrameDrawer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public EglRenderer(String name, VideoFrameDrawer videoFrameDrawer) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.frameDrawer = videoFrameDrawer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -42,8 +42,9 @@ public class SurfaceTextureHelper {
|
|||||||
* PeerConnectionFactory.createVideoSource(). This makes the timestamps more accurate and
|
* PeerConnectionFactory.createVideoSource(). This makes the timestamps more accurate and
|
||||||
* closer to actual creation time.
|
* closer to actual creation time.
|
||||||
*/
|
*/
|
||||||
public static SurfaceTextureHelper create(
|
public static SurfaceTextureHelper create(final String threadName,
|
||||||
final String threadName, final EglBase.Context sharedContext, boolean alignTimestamps) {
|
final EglBase.Context sharedContext, boolean alignTimestamps,
|
||||||
|
final YuvConverter yuvConverter) {
|
||||||
final HandlerThread thread = new HandlerThread(threadName);
|
final HandlerThread thread = new HandlerThread(threadName);
|
||||||
thread.start();
|
thread.start();
|
||||||
final Handler handler = new Handler(thread.getLooper());
|
final Handler handler = new Handler(thread.getLooper());
|
||||||
@ -57,7 +58,7 @@ public class SurfaceTextureHelper {
|
|||||||
@Override
|
@Override
|
||||||
public SurfaceTextureHelper call() {
|
public SurfaceTextureHelper call() {
|
||||||
try {
|
try {
|
||||||
return new SurfaceTextureHelper(sharedContext, handler, alignTimestamps);
|
return new SurfaceTextureHelper(sharedContext, handler, alignTimestamps, yuvConverter);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Logging.e(TAG, threadName + " create failure", e);
|
Logging.e(TAG, threadName + " create failure", e);
|
||||||
return null;
|
return null;
|
||||||
@ -67,20 +68,30 @@ public class SurfaceTextureHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as above with alignTimestamps set to false.
|
* Same as above with alignTimestamps set to false and yuvConverter set to new YuvConverter.
|
||||||
*
|
*
|
||||||
* @see #create(String, EglBase.Context, boolean)
|
* @see #create(String, EglBase.Context, boolean, YuvConverter)
|
||||||
*/
|
*/
|
||||||
public static SurfaceTextureHelper create(
|
public static SurfaceTextureHelper create(
|
||||||
final String threadName, final EglBase.Context sharedContext) {
|
final String threadName, final EglBase.Context sharedContext) {
|
||||||
return create(threadName, sharedContext, /* alignTimestamps= */ false);
|
return create(threadName, sharedContext, /* alignTimestamps= */ false, new YuvConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as above with yuvConverter set to new YuvConverter.
|
||||||
|
*
|
||||||
|
* @see #create(String, EglBase.Context, boolean, YuvConverter)
|
||||||
|
*/
|
||||||
|
public static SurfaceTextureHelper create(
|
||||||
|
final String threadName, final EglBase.Context sharedContext, boolean alignTimestamps) {
|
||||||
|
return create(threadName, sharedContext, alignTimestamps, new YuvConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
private final EglBase eglBase;
|
private final EglBase eglBase;
|
||||||
private final SurfaceTexture surfaceTexture;
|
private final SurfaceTexture surfaceTexture;
|
||||||
private final int oesTextureId;
|
private final int oesTextureId;
|
||||||
private final YuvConverter yuvConverter = new YuvConverter();
|
private final YuvConverter yuvConverter;
|
||||||
@Nullable private final TimestampAligner timestampAligner;
|
@Nullable private final TimestampAligner timestampAligner;
|
||||||
|
|
||||||
// These variables are only accessed from the |handler| thread.
|
// These variables are only accessed from the |handler| thread.
|
||||||
@ -110,13 +121,14 @@ public class SurfaceTextureHelper {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private SurfaceTextureHelper(
|
private SurfaceTextureHelper(EglBase.Context sharedContext, Handler handler,
|
||||||
EglBase.Context sharedContext, Handler handler, boolean alignTimestamps) {
|
boolean alignTimestamps, YuvConverter yuvConverter) {
|
||||||
if (handler.getLooper().getThread() != Thread.currentThread()) {
|
if (handler.getLooper().getThread() != Thread.currentThread()) {
|
||||||
throw new IllegalStateException("SurfaceTextureHelper must be created on the handler thread");
|
throw new IllegalStateException("SurfaceTextureHelper must be created on the handler thread");
|
||||||
}
|
}
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.timestampAligner = alignTimestamps ? new TimestampAligner() : null;
|
this.timestampAligner = alignTimestamps ? new TimestampAligner() : null;
|
||||||
|
this.yuvConverter = yuvConverter;
|
||||||
|
|
||||||
eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
|
eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -135,6 +135,14 @@ public class TextureBufferImpl implements VideoFrame.TextureBuffer {
|
|||||||
return unscaledHeight;
|
return unscaledHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Handler getToI420Handler() {
|
||||||
|
return toI420Handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public YuvConverter getYuvConverter() {
|
||||||
|
return yuvConverter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new TextureBufferImpl with an applied transform matrix and a new size. The
|
* Create a new TextureBufferImpl with an applied transform matrix and a new size. The
|
||||||
* existing buffer is unchanged. The given transform matrix is applied first when texture
|
* existing buffer is unchanged. The given transform matrix is applied first when texture
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public class VideoFrameDrawer {
|
|||||||
* used multiplied together with the transformation matrix of the frame. (M = renderMatrix *
|
* used multiplied together with the transformation matrix of the frame. (M = renderMatrix *
|
||||||
* transformationMatrix)
|
* transformationMatrix)
|
||||||
*/
|
*/
|
||||||
static void drawTexture(RendererCommon.GlDrawer drawer, VideoFrame.TextureBuffer buffer,
|
public static void drawTexture(RendererCommon.GlDrawer drawer, VideoFrame.TextureBuffer buffer,
|
||||||
Matrix renderMatrix, int frameWidth, int frameHeight, int viewportX, int viewportY,
|
Matrix renderMatrix, int frameWidth, int frameHeight, int viewportX, int viewportY,
|
||||||
int viewportWidth, int viewportHeight) {
|
int viewportWidth, int viewportHeight) {
|
||||||
Matrix finalMatrix = new Matrix(buffer.getTransformMatrix());
|
Matrix finalMatrix = new Matrix(buffer.getTransformMatrix());
|
||||||
@ -224,6 +224,12 @@ public class VideoFrameDrawer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VideoFrame.Buffer prepareBufferForViewportSize(
|
||||||
|
VideoFrame.Buffer buffer, int width, int height) {
|
||||||
|
buffer.retain();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
yuvUploader.release();
|
yuvUploader.release();
|
||||||
lastI420Frame = null;
|
lastI420Frame = null;
|
||||||
|
|||||||
@ -107,17 +107,27 @@ public class YuvConverter {
|
|||||||
new GlTextureFrameBuffer(GLES20.GL_RGBA);
|
new GlTextureFrameBuffer(GLES20.GL_RGBA);
|
||||||
private final ShaderCallbacks shaderCallbacks = new ShaderCallbacks();
|
private final ShaderCallbacks shaderCallbacks = new ShaderCallbacks();
|
||||||
private final GlGenericDrawer drawer = new GlGenericDrawer(FRAGMENT_SHADER, shaderCallbacks);
|
private final GlGenericDrawer drawer = new GlGenericDrawer(FRAGMENT_SHADER, shaderCallbacks);
|
||||||
|
private final VideoFrameDrawer videoFrameDrawer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class should be constructed on a thread that has an active EGL context.
|
* This class should be constructed on a thread that has an active EGL context.
|
||||||
*/
|
*/
|
||||||
public YuvConverter() {
|
public YuvConverter() {
|
||||||
|
this(new VideoFrameDrawer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public YuvConverter(VideoFrameDrawer videoFrameDrawer) {
|
||||||
|
this.videoFrameDrawer = videoFrameDrawer;
|
||||||
threadChecker.detachThread();
|
threadChecker.detachThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts the texture buffer to I420. */
|
/** Converts the texture buffer to I420. */
|
||||||
public I420Buffer convert(TextureBuffer inputTextureBuffer) {
|
public I420Buffer convert(TextureBuffer inputTextureBuffer) {
|
||||||
threadChecker.checkIsOnValidThread();
|
threadChecker.checkIsOnValidThread();
|
||||||
|
|
||||||
|
TextureBuffer preparedBuffer = (TextureBuffer) videoFrameDrawer.prepareBufferForViewportSize(
|
||||||
|
inputTextureBuffer, inputTextureBuffer.getWidth(), inputTextureBuffer.getHeight());
|
||||||
|
|
||||||
// We draw into a buffer laid out like
|
// We draw into a buffer laid out like
|
||||||
//
|
//
|
||||||
// +---------+
|
// +---------+
|
||||||
@ -146,8 +156,8 @@ public class YuvConverter {
|
|||||||
// Since the V data needs to start on a boundary of such a
|
// Since the V data needs to start on a boundary of such a
|
||||||
// larger pixel, it is not sufficient that |stride| is even, it
|
// larger pixel, it is not sufficient that |stride| is even, it
|
||||||
// has to be a multiple of 8 pixels.
|
// has to be a multiple of 8 pixels.
|
||||||
final int frameWidth = inputTextureBuffer.getWidth();
|
final int frameWidth = preparedBuffer.getWidth();
|
||||||
final int frameHeight = inputTextureBuffer.getHeight();
|
final int frameHeight = preparedBuffer.getHeight();
|
||||||
final int stride = ((frameWidth + 7) / 8) * 8;
|
final int stride = ((frameWidth + 7) / 8) * 8;
|
||||||
final int uvHeight = (frameHeight + 1) / 2;
|
final int uvHeight = (frameHeight + 1) / 2;
|
||||||
// Total height of the combined memory layout.
|
// Total height of the combined memory layout.
|
||||||
@ -171,19 +181,19 @@ public class YuvConverter {
|
|||||||
|
|
||||||
// Draw Y.
|
// Draw Y.
|
||||||
shaderCallbacks.setPlaneY();
|
shaderCallbacks.setPlaneY();
|
||||||
VideoFrameDrawer.drawTexture(drawer, inputTextureBuffer, renderMatrix, frameWidth, frameHeight,
|
VideoFrameDrawer.drawTexture(drawer, preparedBuffer, renderMatrix, frameWidth, frameHeight,
|
||||||
/* viewportX= */ 0, /* viewportY= */ 0, viewportWidth,
|
/* viewportX= */ 0, /* viewportY= */ 0, viewportWidth,
|
||||||
/* viewportHeight= */ frameHeight);
|
/* viewportHeight= */ frameHeight);
|
||||||
|
|
||||||
// Draw U.
|
// Draw U.
|
||||||
shaderCallbacks.setPlaneU();
|
shaderCallbacks.setPlaneU();
|
||||||
VideoFrameDrawer.drawTexture(drawer, inputTextureBuffer, renderMatrix, frameWidth, frameHeight,
|
VideoFrameDrawer.drawTexture(drawer, preparedBuffer, renderMatrix, frameWidth, frameHeight,
|
||||||
/* viewportX= */ 0, /* viewportY= */ frameHeight, viewportWidth / 2,
|
/* viewportX= */ 0, /* viewportY= */ frameHeight, viewportWidth / 2,
|
||||||
/* viewportHeight= */ uvHeight);
|
/* viewportHeight= */ uvHeight);
|
||||||
|
|
||||||
// Draw V.
|
// Draw V.
|
||||||
shaderCallbacks.setPlaneV();
|
shaderCallbacks.setPlaneV();
|
||||||
VideoFrameDrawer.drawTexture(drawer, inputTextureBuffer, renderMatrix, frameWidth, frameHeight,
|
VideoFrameDrawer.drawTexture(drawer, preparedBuffer, renderMatrix, frameWidth, frameHeight,
|
||||||
/* viewportX= */ viewportWidth / 2, /* viewportY= */ frameHeight, viewportWidth / 2,
|
/* viewportX= */ viewportWidth / 2, /* viewportY= */ frameHeight, viewportWidth / 2,
|
||||||
/* viewportHeight= */ uvHeight);
|
/* viewportHeight= */ uvHeight);
|
||||||
|
|
||||||
@ -215,6 +225,8 @@ public class YuvConverter {
|
|||||||
i420ByteBuffer.limit(vPos + uvSize);
|
i420ByteBuffer.limit(vPos + uvSize);
|
||||||
final ByteBuffer dataV = i420ByteBuffer.slice();
|
final ByteBuffer dataV = i420ByteBuffer.slice();
|
||||||
|
|
||||||
|
preparedBuffer.release();
|
||||||
|
|
||||||
return JavaI420Buffer.wrap(frameWidth, frameHeight, dataY, stride, dataU, stride, dataV, stride,
|
return JavaI420Buffer.wrap(frameWidth, frameHeight, dataY, stride, dataU, stride, dataV, stride,
|
||||||
() -> { JniCommon.nativeFreeByteBuffer(i420ByteBuffer); });
|
() -> { JniCommon.nativeFreeByteBuffer(i420ByteBuffer); });
|
||||||
}
|
}
|
||||||
@ -223,6 +235,7 @@ public class YuvConverter {
|
|||||||
threadChecker.checkIsOnValidThread();
|
threadChecker.checkIsOnValidThread();
|
||||||
drawer.release();
|
drawer.release();
|
||||||
i420TextureFrameBuffer.release();
|
i420TextureFrameBuffer.release();
|
||||||
|
videoFrameDrawer.release();
|
||||||
// Allow this class to be reused.
|
// Allow this class to be reused.
|
||||||
threadChecker.detachThread();
|
threadChecker.detachThread();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user