Supporting formats of non-multiple of 16 widths on Android.

This is an updated version of perkj's issue (https://webrtc-codereview.appspot.com/44129004/) which was reverted due to libjingle_peerconnection_android_unittest crashing on Nexus 9. It crashed because there was old test code still assuming the width was multiple of 16 (which was only a problem on devices with non-16 widths).

BUG=4522
R=glaznev@webrtc.org, magjed@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9029}
This commit is contained in:
Henrik Boström 2015-04-17 17:31:53 +02:00
parent a51e8f490c
commit 09a9ea8886
3 changed files with 53 additions and 22 deletions

View File

@ -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();

View File

@ -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<uint8_t*>(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

View File

@ -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<Camera.Size> 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 + ".");
}