diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java index c7f3fffbc..32544a6f1 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java @@ -260,7 +260,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { assertTrue(observer.WaitForCapturerToStart()); observer.WaitForNextCapturedFrame(); // Check the frame size. - assertEquals((format.width*format.height*3)/2, observer.frameSize()); + assertEquals(format.frameSize(), observer.frameSize()); capturer.stopCapture(); } capturer.dispose(); diff --git a/talk/app/webrtc/androidvideocapturer.cc b/talk/app/webrtc/androidvideocapturer.cc index 6711b6ed0..89ab48696 100644 --- a/talk/app/webrtc/androidvideocapturer.cc +++ b/talk/app/webrtc/androidvideocapturer.cc @@ -33,6 +33,7 @@ #include "webrtc/base/json.h" #include "webrtc/base/timeutils.h" #include "webrtc/base/thread.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" namespace webrtc { @@ -93,12 +94,16 @@ class AndroidVideoCapturer::FrameFactory : public cricket::VideoFrameFactory { if (!apply_rotation_ || captured_frame->rotation == kVideoRotation_0) { DCHECK(captured_frame->fourcc == cricket::FOURCC_YV12); const uint8_t* y_plane = static_cast(captured_frame_.data); - const int y_stride = captured_frame->width; - const uint8_t* v_plane = y_plane + - captured_frame->width * captured_frame->height; - const int uv_stride = (captured_frame->width + 1) / 2; - const int uv_height = (captured_frame->height + 1) / 2; - const uint8_t* u_plane = v_plane + uv_stride * uv_height; + + // Android guarantees that the stride is a multiple of 16. + // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat%28int%29 + int y_stride; + int uv_stride; + webrtc::Calc16ByteAlignedStride(captured_frame->width, &y_stride, + &uv_stride); + const uint8_t* v_plane = y_plane + y_stride * captured_frame->height; + const uint8_t* u_plane = + v_plane + uv_stride * webrtc::AlignInt(captured_frame->height, 2) / 2; // Create a WrappedI420Buffer and bind the |no_longer_used| callback // to the static method ReturnFrame. The |delegate_| is bound as an diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java index 0039000cd..ea4a512d5 100644 --- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java +++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java @@ -28,6 +28,7 @@ package org.webrtc; import static java.lang.Math.abs; +import static java.lang.Math.ceil; import android.content.Context; import android.graphics.ImageFormat; @@ -264,6 +265,11 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba public final int height; public final int maxFramerate; public final int minFramerate; + // TODO(hbos): If VideoCapturerAndroid.startCapture is updated to support + // other image formats then this needs to be updated and + // VideoCapturerAndroid.getSupportedFormats need to return CaptureFormats of + // all imageFormats. + public final int imageFormat = ImageFormat.YV12; public CaptureFormat(int width, int height, int minFramerate, int maxFramerate) { @@ -272,6 +278,33 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba this.minFramerate = minFramerate; this.maxFramerate = maxFramerate; } + + // Calculates the frame size of this capture format. + public int frameSize() { + return frameSize(width, height, imageFormat); + } + + // Calculates the frame size of the specified image format. Currently only + // supporting ImageFormat.YV12. The YV12's stride is the closest rounded up + // multiple of 16 of the width and width and height are always even. + // Android guarantees this: + // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat%28int%29 + public static int frameSize(int width, int height, int imageFormat) { + if (imageFormat != ImageFormat.YV12) { + throw new UnsupportedOperationException("Don't know how to calculate " + + "the frame size of non-YV12 image formats."); + } + int yStride = roundUp(width, 16); + int uvStride = roundUp(yStride / 2, 16); + int ySize = yStride * height; + int uvSize = uvStride * height / 2; + return ySize + uvSize * 2; + } + + // Rounds up |x| to the closest value that is a multiple of |alignment|. + private static int roundUp(int x, int alignment) { + return (int)ceil(x / (double)alignment) * alignment; + } } private static String getSupportedFormatsAsJson(int id) throws JSONException { @@ -312,13 +345,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba List supportedSizes = parameters.getSupportedPreviewSizes(); for (Camera.Size size : supportedSizes) { - if (size.width % 16 != 0) { - // If the width is not a multiple of 16, the frames received from the - // camera will have a stride != width when YV12 is used. Since we - // currently only support tightly packed images, we simply ignore - // those resolutions. - continue; - } formatList.add(new CaptureFormat(size.width, size.height, range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])); @@ -359,9 +385,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba if (frameObserver == null) { throw new RuntimeException("frameObserver not set."); } - if (width % 16 != 0) { - throw new RuntimeException("width must be a multiple of 16." ); - } if (cameraThreadHandler != null) { throw new RuntimeException("Camera has already been started."); } @@ -444,6 +467,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba Camera.Size pictureSize = getPictureSize(parameters, width, height); parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPreviewSize(width, height); + // TODO(hbos): If other ImageFormats are to be supported then + // CaptureFormat needs to be updated (currently hard-coded to say YV12, + // getSupportedFormats only returns YV12). int format = ImageFormat.YV12; parameters.setPreviewFormat(format); camera.setParameters(parameters); @@ -685,14 +711,14 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba throw new RuntimeException("camera already set."); this.camera = camera; - int newframeSize = - width * height * ImageFormat.getBitsPerPixel(format) / 8; + int newFrameSize = CaptureFormat.frameSize(width, height, format); + int numberOfEnquedCameraBuffers = 0; - if (newframeSize != frameSize) { + if (newFrameSize != frameSize) { // Create new frames and add to the camera. // The old frames will be released when frames are returned. for (int i = 0; i < numCaptureBuffers; ++i) { - Frame frame = new Frame(newframeSize); + Frame frame = new Frame(newFrameSize); cameraFrames.add(frame); this.camera.addCallbackBuffer(frame.data()); } @@ -706,7 +732,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba } } } - frameSize = newframeSize; + frameSize = newFrameSize; Log.d(TAG, "queueCameraBuffers enqued " + numberOfEnquedCameraBuffers + " buffers of size " + frameSize + "."); }