summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartlomiej Moskal <bartlomiej.moskal@qt.io>2023-08-09 12:24:28 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-08-11 16:30:36 +0000
commitd562f0716a2777c676716dc5cad2ea739a58a2dc (patch)
tree4ef779a288ea57322272d520bbb4035cdf519ab3
parentdc55e7cddf89d170eb02084c8c5a4eb53feb8ad4 (diff)
Android-backend: Save photos from bytes array using QFile
Since Qt6, Android-backend started to use QImageWriter for saving pictures on device. This makes photos do not contain Exif data. Exif data were lost during QVideoFrame creation. Also QImageWriter does not support it at all (QTBUG-28330). To bring those metadata back, Android-backend will save photos from whole bytes array using QFile. Task-number: QTBUG-113020 Change-Id: I7e5625a2943ee7edef24cbab223c617e317606b1 Reviewed-by: Artem Dyomin <artem.dyomin@qt.io> (cherry picked from commit af2321f6bf8e404f8bbd8eb1391e16a9da2b0a9a) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java27
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp54
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h7
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp6
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h2
5 files changed, 55 insertions, 41 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
index cb0fcfa0e..3e35eb416 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
@@ -6,10 +6,14 @@ package org.qtproject.qt.android.multimedia;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.util.Log;
import java.lang.Math;
+import java.io.ByteArrayOutputStream;
public class QtCameraListener implements Camera.ShutterCallback,
Camera.PictureCallback,
@@ -29,6 +33,7 @@ public class QtCameraListener implements Camera.ShutterCallback,
private Camera.Size m_previewSize = null;
private int m_previewFormat = ImageFormat.NV21; // Default preview format on all devices
private int m_previewBytesPerLine = -1;
+ private int m_rotation = 0;
private QtCameraListener(int id)
{
@@ -81,6 +86,11 @@ public class QtCameraListener implements Camera.ShutterCallback,
camera.setPreviewCallbackWithBuffer(null);
}
+ public void setPhotoRotation(int rotation)
+ {
+ m_rotation = rotation;
+ }
+
public void setupPreviewCallback(Camera camera)
{
// Clear previous callback (also clears added buffers)
@@ -164,7 +174,22 @@ public class QtCameraListener implements Camera.ShutterCallback,
@Override
public void onPictureTaken(byte[] data, Camera camera)
{
- notifyPictureCaptured(m_cameraId, data);
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ Camera.getCameraInfo(m_cameraId, info);
+ Bitmap source = BitmapFactory.decodeByteArray(data, 0, data.length);
+ Matrix matrix = new Matrix();
+ matrix.postRotate(m_rotation);
+ if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
+ matrix.postScale(-1, 1, source.getWidth() / 2.0f, source.getHeight() / 2.0f);
+ }
+ Bitmap rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
+ source.getHeight(), matrix, true);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
+ byte[] byteArray = outputStream.toByteArray();
+ rotatedBitmap.recycle();
+ source.recycle();
+ notifyPictureCaptured(m_cameraId, byteArray);
}
@Override
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
index 8de5bb3a7..9c5cf319a 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
@@ -661,28 +661,16 @@ void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
m_videoFrameCallbackMutex.unlock();
}
-void QAndroidCameraSession::onCameraPictureCaptured(const QVideoFrame &frame)
-{
- // Frame needs to be correctly rotated before image proccessing. We are using
- // the same rotation angle that was used for preview with setDisplayOrientation
- auto rotation = QVideoFrame::Rotation0;
- switch (currentCameraRotation()) {
- case 90:
- rotation = QVideoFrame::Rotation90;
- break;
- case 180:
- rotation = QVideoFrame::Rotation180;
- break;
- case 270:
- rotation = QVideoFrame::Rotation270;
- break;
+void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size,int bytesPerLine)
+{
+ if (m_imageCaptureToBuffer) {
+ processCapturedImageToBuffer(m_currentImageCaptureId, bytes, format, size, bytesPerLine);
+ } else {
+ // Loading and saving the captured image can be slow, do it in a separate thread
+ (void)QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
+ m_currentImageCaptureId, bytes, m_currentImageCaptureFileName);
}
- const_cast<QVideoFrame&>(frame).setRotationAngle(rotation);
-
- // Loading and saving the captured image can be slow, do it in a separate thread
- (void)QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
- m_currentImageCaptureId, frame, m_imageCaptureToBuffer,
- m_currentImageCaptureFileName);
// Preview needs to be restarted after taking a picture
if (m_camera)
@@ -720,37 +708,37 @@ void QAndroidCameraSession::onCameraPreviewStopped()
setReadyForCapture(false);
}
-void QAndroidCameraSession::processCapturedImage(int id, const QVideoFrame &frame,
- bool captureToBuffer, const QString &fileName)
+void QAndroidCameraSession::processCapturedImage(int id, const QByteArray &bytes, const QString &fileName)
{
- if (captureToBuffer) {
- emit imageAvailable(id, frame);
- return;
- }
-
const QString actualFileName = QMediaStorageLocation::generateFileName(
fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
- QImageWriter writer(actualFileName);
-
- if (!writer.canWrite()) {
+ QFile writer(actualFileName);
+ if (!writer.open(QIODeviceBase::WriteOnly)) {
const QString errorMessage = tr("File is not available: %1").arg(writer.errorString());
emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
return;
}
- const bool written = writer.write(frame.toImage());
- if (!written) {
+ if (writer.write(bytes) < 0) {
const QString errorMessage = tr("Could not save to file: %1").arg(writer.errorString());
emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
return;
}
+ writer.close();
if (fileName.isEmpty() || QFileInfo(fileName).isRelative())
AndroidMultimediaUtils::registerMediaFile(actualFileName);
emit imageSaved(id, actualFileName);
}
+void QAndroidCameraSession::processCapturedImageToBuffer(int id, const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine)
+{
+ QVideoFrame frame(new QMemoryVideoBuffer(bytes, bytesPerLine), QVideoFrameFormat(size, format));
+ emit imageAvailable(id, frame);
+}
+
void QAndroidCameraSession::onVideoOutputReady(bool ready)
{
if (ready && m_active)
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
index 94c45f7d5..3b56d9c3b 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
@@ -102,7 +102,7 @@ private Q_SLOTS:
void onCameraTakePictureFailed();
void onCameraPictureExposed();
- void onCameraPictureCaptured(const QVideoFrame &frame);
+ void onCameraPictureCaptured(const QByteArray &bytes, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void onLastPreviewFrameFetched(const QVideoFrame &frame);
void onNewPreviewFrame(const QVideoFrame &frame);
void onCameraPreviewStarted();
@@ -121,8 +121,9 @@ private:
void applyImageSettings();
void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
- void processCapturedImage(int id, const QVideoFrame &frame, bool captureToBuffer,
- const QString &fileName);
+ void processCapturedImage(int id, const QByteArray &bytes, const QString &fileName);
+ void processCapturedImageToBuffer(int id, const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void setActiveHelper(bool active);
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
index df2b5c407..268434217 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
@@ -125,10 +125,9 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
bytesPerLine = -1;
}
- QVideoFrame frame(new QMemoryVideoBuffer(bytes, bytesPerLine),
- QVideoFrameFormat(pictureSize, qt_pixelFormatFromAndroidImageFormat(format)));
+ auto pictureFormat = qt_pixelFormatFromAndroidImageFormat(format);
- emit camera->pictureCaptured(frame);
+ emit camera->pictureCaptured(bytes, pictureFormat, pictureSize, bytesPerLine);
}
static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
@@ -1205,6 +1204,7 @@ bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
void AndroidCameraPrivate::setDisplayOrientation(int degrees)
{
m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
+ m_cameraListener.callMethod<void>("setPhotoRotation", "(I)V", degrees);
}
bool AndroidCameraPrivate::isZoomSupported()
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h b/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
index 580b33120..8375cf3b1 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
@@ -189,7 +189,7 @@ Q_SIGNALS:
void takePictureFailed();
void pictureExposed();
- void pictureCaptured(const QVideoFrame &frame);
+ void pictureCaptured(const QByteArray &frame, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void lastPreviewFrameFetched(const QVideoFrame &frame);
void newPreviewFrame(const QVideoFrame &frame);