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.
if (frame.yuvFrame) {
if (!(frame.yuvStrides[0] == frame.width &&
frame.yuvStrides[1] == frame.width / 2 &&
frame.yuvStrides[2] == frame.width / 2)) {
if (frame.yuvStrides[0] < frame.width ||
frame.yuvStrides[1] < frame.width / 2 ||
frame.yuvStrides[2] < frame.width / 2) {
Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " +
frame.yuvStrides[1] + ", " + frame.yuvStrides[2]);
return;

View File

@ -3223,6 +3223,32 @@ JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
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) {
cricket::VideoCapturer* capturer =
reinterpret_cast<VideoSourceInterface*>(j_p)->GetVideoCapturer();

View File

@ -61,8 +61,8 @@ public class VideoRenderer {
if (yuvPlanes == null) {
yuvPlanes = new ByteBuffer[3];
yuvPlanes[0] = ByteBuffer.allocateDirect(yuvStrides[0] * height);
yuvPlanes[1] = ByteBuffer.allocateDirect(yuvStrides[1] * height);
yuvPlanes[2] = ByteBuffer.allocateDirect(yuvStrides[2] * height);
yuvPlanes[1] = ByteBuffer.allocateDirect(yuvStrides[1] * height / 2);
yuvPlanes[2] = ByteBuffer.allocateDirect(yuvStrides[2] * height / 2);
}
this.yuvPlanes = yuvPlanes;
this.yuvFrame = true;
@ -89,14 +89,16 @@ public class VideoRenderer {
*/
public I420Frame copyFrom(I420Frame source) {
if (source.yuvFrame && yuvFrame) {
if (!Arrays.equals(yuvStrides, source.yuvStrides) ||
width != source.width || height != source.height) {
if (width != source.width || height != source.height) {
throw new RuntimeException("Mismatched dimensions! Source: " +
source.toString() + ", destination: " + toString());
}
copyPlane(source.yuvPlanes[0], yuvPlanes[0]);
copyPlane(source.yuvPlanes[1], yuvPlanes[1]);
copyPlane(source.yuvPlanes[2], yuvPlanes[2]);
nativeCopyPlane(source.yuvPlanes[0], width, height,
source.yuvStrides[0], yuvPlanes[0], yuvStrides[0]);
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;
} else if (!source.yuvFrame && !yuvFrame) {
textureObject = source.textureObject;
@ -109,42 +111,37 @@ public class VideoRenderer {
}
public I420Frame copyFrom(byte[] yuvData) {
if (yuvData.length < width * height * 3 / 2) {
throw new RuntimeException("Wrong arrays size: " + yuvData.length);
}
if (!yuvFrame) {
throw new RuntimeException("Can not feed yuv data to texture frame");
}
int planeSize = width * height;
ByteBuffer[] planes = new ByteBuffer[3];
planes[0] = ByteBuffer.wrap(yuvData, 0, planeSize);
planes[1] = ByteBuffer.wrap(yuvData, planeSize, planeSize / 4);
planes[2] = ByteBuffer.wrap(yuvData, planeSize + planeSize / 4,
planeSize / 4);
for (int i = 0; i < 3; i++) {
yuvPlanes[i].position(0);
yuvPlanes[i].put(planes[i]);
yuvPlanes[i].position(0);
yuvPlanes[i].limit(yuvPlanes[i].capacity());
}
return this;
if (yuvData.length < width * height * 3 / 2) {
throw new RuntimeException("Wrong arrays size: " + yuvData.length);
}
if (!yuvFrame) {
throw new RuntimeException("Can not feed yuv data to texture frame");
}
int planeSize = width * height;
ByteBuffer[] planes = new ByteBuffer[3];
planes[0] = ByteBuffer.wrap(yuvData, 0, planeSize);
planes[1] = ByteBuffer.wrap(yuvData, planeSize, planeSize / 4);
planes[2] = ByteBuffer.wrap(yuvData, planeSize + planeSize / 4,
planeSize / 4);
for (int i = 0; i < 3; i++) {
yuvPlanes[i].position(0);
yuvPlanes[i].put(planes[i]);
yuvPlanes[i].position(0);
yuvPlanes[i].limit(yuvPlanes[i].capacity());
}
return this;
}
@Override
public String toString() {
return width + "x" + height + ":" + yuvStrides[0] + ":" + yuvStrides[1] +
":" + yuvStrides[2];
}
}
// Copy the bytes out of |src| and into |dst|, ignoring and overwriting
// positon & limit in both buffers.
private void copyPlane(ByteBuffer src, ByteBuffer dst) {
src.position(0).limit(src.capacity());
dst.put(src);
dst.position(0).limit(dst.capacity());
}
}
// Helper native function to do a video frame plane copying.
private static native void nativeCopyPlane(ByteBuffer src, int width,
int height, int srcStride, ByteBuffer dst, int dstStride);
/** The real meat of VideoRendererInterface. */
public static interface Callbacks {