Fix Android video renderer to support video frames

with stride > width.

Recent libvpx update generates output video frames with stride
value greater than width, which was not supported by Android OpenGL
video renderer (Android GLES2 doesn't have GL_UNPACK_ROW_LENGTH
to provide stride information for buffer in glTexImage2D call).

Fix it by implementing native frame copying for Java
VideoRenderer.I420Frame implementation.

BUG=4248
R=braveyao@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/40639004

Cr-Commit-Position: refs/heads/master@{#8252}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8252 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
glaznev@webrtc.org 2015-02-05 17:29:59 +00:00
parent cc64a9cc4f
commit f6932297e7
3 changed files with 61 additions and 38 deletions

View File

@ -555,9 +555,9 @@ public class VideoRendererGui implements GLSurfaceView.Renderer {
} }
// Check input frame parameters. // Check input frame parameters.
if (frame.yuvFrame) { if (frame.yuvFrame) {
if (!(frame.yuvStrides[0] == frame.width && if (frame.yuvStrides[0] < frame.width ||
frame.yuvStrides[1] == frame.width / 2 && frame.yuvStrides[1] < frame.width / 2 ||
frame.yuvStrides[2] == frame.width / 2)) { frame.yuvStrides[2] < frame.width / 2) {
Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " + Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " +
frame.yuvStrides[1] + ", " + frame.yuvStrides[2]); frame.yuvStrides[1] + ", " + frame.yuvStrides[2]);
return; return;

View File

@ -3223,6 +3223,32 @@ JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
return (jlong)renderer.release(); return (jlong)renderer.release();
} }
JOW(void, VideoRenderer_nativeCopyPlane)(
JNIEnv *jni, jclass, jobject j_src_buffer, jint width, jint height,
jint src_stride, jobject j_dst_buffer, jint dst_stride) {
size_t src_size = jni->GetDirectBufferCapacity(j_src_buffer);
size_t dst_size = jni->GetDirectBufferCapacity(j_dst_buffer);
CHECK(src_stride >= width) << "Wrong source stride " << src_stride;
CHECK(dst_stride >= width) << "Wrong destination stride " << dst_stride;
CHECK(src_size >= src_stride * height)
<< "Insufficient source buffer capacity " << src_size;
CHECK(dst_size >= dst_stride * height)
<< "Isufficient destination buffer capacity " << dst_size;
uint8_t *src =
reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_src_buffer));
uint8_t *dst =
reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_dst_buffer));
if (src_stride == dst_stride) {
memcpy(dst, src, src_stride * height);
} else {
for (int i = 0; i < height; i++) {
memcpy(dst, src, width);
src += src_stride;
dst += dst_stride;
}
}
}
JOW(jlong, VideoSource_stop)(JNIEnv* jni, jclass, jlong j_p) { JOW(jlong, VideoSource_stop)(JNIEnv* jni, jclass, jlong j_p) {
cricket::VideoCapturer* capturer = cricket::VideoCapturer* capturer =
reinterpret_cast<VideoSourceInterface*>(j_p)->GetVideoCapturer(); reinterpret_cast<VideoSourceInterface*>(j_p)->GetVideoCapturer();

View File

@ -61,8 +61,8 @@ public class VideoRenderer {
if (yuvPlanes == null) { if (yuvPlanes == null) {
yuvPlanes = new ByteBuffer[3]; yuvPlanes = new ByteBuffer[3];
yuvPlanes[0] = ByteBuffer.allocateDirect(yuvStrides[0] * height); yuvPlanes[0] = ByteBuffer.allocateDirect(yuvStrides[0] * height);
yuvPlanes[1] = ByteBuffer.allocateDirect(yuvStrides[1] * height); yuvPlanes[1] = ByteBuffer.allocateDirect(yuvStrides[1] * height / 2);
yuvPlanes[2] = ByteBuffer.allocateDirect(yuvStrides[2] * height); yuvPlanes[2] = ByteBuffer.allocateDirect(yuvStrides[2] * height / 2);
} }
this.yuvPlanes = yuvPlanes; this.yuvPlanes = yuvPlanes;
this.yuvFrame = true; this.yuvFrame = true;
@ -89,14 +89,16 @@ public class VideoRenderer {
*/ */
public I420Frame copyFrom(I420Frame source) { public I420Frame copyFrom(I420Frame source) {
if (source.yuvFrame && yuvFrame) { if (source.yuvFrame && yuvFrame) {
if (!Arrays.equals(yuvStrides, source.yuvStrides) || if (width != source.width || height != source.height) {
width != source.width || height != source.height) {
throw new RuntimeException("Mismatched dimensions! Source: " + throw new RuntimeException("Mismatched dimensions! Source: " +
source.toString() + ", destination: " + toString()); source.toString() + ", destination: " + toString());
} }
copyPlane(source.yuvPlanes[0], yuvPlanes[0]); nativeCopyPlane(source.yuvPlanes[0], width, height,
copyPlane(source.yuvPlanes[1], yuvPlanes[1]); source.yuvStrides[0], yuvPlanes[0], yuvStrides[0]);
copyPlane(source.yuvPlanes[2], yuvPlanes[2]); nativeCopyPlane(source.yuvPlanes[1], width / 2, height / 2,
source.yuvStrides[1], yuvPlanes[1], yuvStrides[1]);
nativeCopyPlane(source.yuvPlanes[2], width / 2, height / 2,
source.yuvStrides[2], yuvPlanes[2], yuvStrides[2]);
return this; return this;
} else if (!source.yuvFrame && !yuvFrame) { } else if (!source.yuvFrame && !yuvFrame) {
textureObject = source.textureObject; textureObject = source.textureObject;
@ -130,21 +132,16 @@ public class VideoRenderer {
return this; return this;
} }
@Override @Override
public String toString() { public String toString() {
return width + "x" + height + ":" + yuvStrides[0] + ":" + yuvStrides[1] + return width + "x" + height + ":" + yuvStrides[0] + ":" + yuvStrides[1] +
":" + yuvStrides[2]; ":" + yuvStrides[2];
} }
}
// Copy the bytes out of |src| and into |dst|, ignoring and overwriting // Helper native function to do a video frame plane copying.
// positon & limit in both buffers. private static native void nativeCopyPlane(ByteBuffer src, int width,
private void copyPlane(ByteBuffer src, ByteBuffer dst) { int height, int srcStride, ByteBuffer dst, int dstStride);
src.position(0).limit(src.capacity());
dst.put(src);
dst.position(0).limit(dst.capacity());
}
}
/** The real meat of VideoRendererInterface. */ /** The real meat of VideoRendererInterface. */
public static interface Callbacks { public static interface Callbacks {