Android: fix camera viewfinder orientation.

The camera sensor on Android devices might be in a different orientation
than the device natural orientation. There is no API in Qt to know about
the camera orientation, so correcting the viewfinder orientation is not
possible without making guesses. This patch makes sure the viewfinder
orientation always matches the device natural orientation. For example,
if the camera is mounted in landscape, and the device natural
orientation is portrait, the viewfinder will automatically be rotated 90
degrees counter-clockwise.

Task-number: QTBUG-35086
Change-Id: Ia890872971d72657debe709f61edba27d09dec65
Reviewed-by: Christian Stromme <christian.stromme@digia.com>
This commit is contained in:
Yoann Lopes
2013-11-29 17:16:44 +01:00
committed by The Qt Project
parent 99bebdbb7d
commit 13d7f835fa
4 changed files with 50 additions and 6 deletions

View File

@@ -92,6 +92,7 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
, m_selectedCamera(0) , m_selectedCamera(0)
, m_camera(0) , m_camera(0)
, m_nativeOrientation(0) , m_nativeOrientation(0)
, m_previewOrientation(0)
, m_videoOutput(0) , m_videoOutput(0)
, m_captureMode(QCamera::CaptureViewfinder) , m_captureMode(QCamera::CaptureViewfinder)
, m_state(QCamera::UnloadedState) , m_state(QCamera::UnloadedState)
@@ -183,10 +184,20 @@ bool QAndroidCameraSession::open()
connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed())); connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray))); connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray)));
connect(m_camera, SIGNAL(previewFrameAvailable(QByteArray)), this, SLOT(onCameraPreviewFrameAvailable(QByteArray))); connect(m_camera, SIGNAL(previewFrameAvailable(QByteArray)), this, SLOT(onCameraPreviewFrameAvailable(QByteArray)));
m_nativeOrientation = m_camera->getNativeOrientation(); m_nativeOrientation = m_camera->getNativeOrientation();
// Preview orientation will always match the device natural orientation
if (m_camera->getFacing() == JCamera::CameraFacingFront)
m_previewOrientation = 360 - m_nativeOrientation;
else
m_previewOrientation = m_nativeOrientation;
m_status = QCamera::LoadedStatus; m_status = QCamera::LoadedStatus;
if (m_camera->getPreviewFormat() != JCamera::NV21) if (m_camera->getPreviewFormat() != JCamera::NV21)
m_camera->setPreviewFormat(JCamera::NV21); m_camera->setPreviewFormat(JCamera::NV21);
emit opened(); emit opened();
} else { } else {
m_status = QCamera::UnavailableStatus; m_status = QCamera::UnavailableStatus;
@@ -257,8 +268,16 @@ void QAndroidCameraSession::adjustViewfinderSize(const QSize &captureSize, bool
} }
if (m_camera->previewSize() != viewfinderResolution) { if (m_camera->previewSize() != viewfinderResolution) {
if (m_videoOutput) if (m_videoOutput) {
m_videoOutput->setVideoSize(viewfinderResolution); QSize size = viewfinderResolution;
// If the preview orientation is not the defaut one (0 or 180 degrees),
// we have to invert the output aspect ratio.
if (m_previewOrientation % 180)
size.transpose();
m_videoOutput->setVideoSize(size);
}
// if preview is started, we have to stop it first before changing its size // if preview is started, we have to stop it first before changing its size
if (m_previewStarted && restartPreview) if (m_previewStarted && restartPreview)
@@ -282,6 +301,7 @@ void QAndroidCameraSession::startPreview()
applyImageSettings(); applyImageSettings();
adjustViewfinderSize(m_imageSettings.resolution()); adjustViewfinderSize(m_imageSettings.resolution());
m_camera->setDisplayOrientation(m_previewOrientation);
if (m_videoOutput && m_videoOutput->isReady()) if (m_videoOutput && m_videoOutput->isReady())
onVideoOutputReady(true); onVideoOutputReady(true);
@@ -558,10 +578,11 @@ void QAndroidCameraSession::onCameraPreviewFrameAvailable(const QByteArray &data
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
m_currentImageCaptureId, m_currentImageCaptureId,
data); data,
m_camera->getRotation());
} }
void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data) void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int rotation)
{ {
QSize frameSize = m_camera->previewSize(); QSize frameSize = m_camera->previewSize();
QImage preview(frameSize, QImage::Format_ARGB32); QImage preview(frameSize, QImage::Format_ARGB32);
@@ -570,11 +591,17 @@ void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data)
frameSize.width(), frameSize.width(),
frameSize.height()); frameSize.height());
QTransform transform;
// Preview display of front-facing cameras is flipped horizontally, but the frame data // Preview display of front-facing cameras is flipped horizontally, but the frame data
// we get here is not. Flip it ourselves if the camera is front-facing to match what the user // we get here is not. Flip it ourselves if the camera is front-facing to match what the user
// sees on the viewfinder. // sees on the viewfinder.
if (m_camera->getFacing() == JCamera::CameraFacingFront) if (m_camera->getFacing() == JCamera::CameraFacingFront)
preview = preview.transformed(QTransform().scale(-1, 1)); transform.scale(-1, 1);
transform.rotate(rotation);
preview = preview.transformed(transform);
emit imageCaptured(id, preview); emit imageCaptured(id, preview);
} }

View File

@@ -124,7 +124,7 @@ private:
void stopPreview(); void stopPreview();
void applyImageSettings(); void applyImageSettings();
void processPreviewImage(int id, const QByteArray &data); void processPreviewImage(int id, const QByteArray &data, int rotation);
void processCapturedImage(int id, void processCapturedImage(int id,
const QByteArray &data, const QByteArray &data,
const QSize &resolution, const QSize &resolution,
@@ -134,6 +134,7 @@ private:
int m_selectedCamera; int m_selectedCamera;
JCamera *m_camera; JCamera *m_camera;
int m_nativeOrientation; int m_nativeOrientation;
int m_previewOrientation;
QAndroidVideoOutput *m_videoOutput; QAndroidVideoOutput *m_videoOutput;
QCamera::CaptureModes m_captureMode; QCamera::CaptureModes m_captureMode;

View File

@@ -118,6 +118,7 @@ JCamera::JCamera(int cameraId, jobject cam)
: QObject() : QObject()
, QJNIObjectPrivate(cam) , QJNIObjectPrivate(cam)
, m_cameraId(cameraId) , m_cameraId(cameraId)
, m_rotation(0)
, m_hasAPI14(false) , m_hasAPI14(false)
{ {
if (isValid()) { if (isValid()) {
@@ -205,6 +206,11 @@ int JCamera::getNativeOrientation()
return m_info.getField<jint>("orientation"); return m_info.getField<jint>("orientation");
} }
void JCamera::setDisplayOrientation(int degrees)
{
callMethod<void>("setDisplayOrientation", "(I)V", degrees);
}
QSize JCamera::getPreferredPreviewSizeForVideo() QSize JCamera::getPreferredPreviewSizeForVideo()
{ {
if (!m_parameters.isValid()) if (!m_parameters.isValid())
@@ -612,10 +618,16 @@ void JCamera::setRotation(int rotation)
if (!m_parameters.isValid()) if (!m_parameters.isValid())
return; return;
m_rotation = rotation;
m_parameters.callMethod<void>("setRotation", "(I)V", rotation); m_parameters.callMethod<void>("setRotation", "(I)V", rotation);
applyParameters(); applyParameters();
} }
int JCamera::getRotation() const
{
return m_rotation;
}
QList<QSize> JCamera::getSupportedPictureSizes() QList<QSize> JCamera::getSupportedPictureSizes()
{ {
QList<QSize> list; QList<QSize> list;

View File

@@ -82,6 +82,8 @@ public:
CameraFacing getFacing(); CameraFacing getFacing();
int getNativeOrientation(); int getNativeOrientation();
void setDisplayOrientation(int degrees);
QSize getPreferredPreviewSizeForVideo(); QSize getPreferredPreviewSizeForVideo();
QList<QSize> getSupportedPreviewSizes(); QList<QSize> getSupportedPreviewSizes();
@@ -136,6 +138,7 @@ public:
void setWhiteBalance(const QString &value); void setWhiteBalance(const QString &value);
void setRotation(int rotation); void setRotation(int rotation);
int getRotation() const;
QList<QSize> getSupportedPictureSizes(); QList<QSize> getSupportedPictureSizes();
void setPictureSize(const QSize &size); void setPictureSize(const QSize &size);
@@ -174,6 +177,7 @@ private:
QJNIObjectPrivate m_parameters; QJNIObjectPrivate m_parameters;
QSize m_previewSize; QSize m_previewSize;
int m_rotation;
bool m_hasAPI14; bool m_hasAPI14;
}; };