Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qandroidcameraframe.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <jni.h>
6#include <QDebug>
7#include <QtCore/qjnitypes.h>
8#include <QtCore/QLoggingCategory>
9
10Q_DECLARE_JNI_CLASS(AndroidImageFormat, "android/graphics/ImageFormat");
11
12Q_DECLARE_JNI_CLASS(AndroidImage, "android/media/Image")
13Q_DECLARE_JNI_TYPE(AndroidImagePlaneArray, "[Landroid/media/Image$Plane;")
14Q_DECLARE_JNI_CLASS(JavaByteBuffer, "java/nio/ByteBuffer")
15
17static Q_LOGGING_CATEGORY(qLCAndroidCameraFrame, "qt.multimedia.ffmpeg.android.camera.frame");
18
19bool QAndroidCameraFrame::parse(const QJniObject &frame)
20{
21 QJniEnvironment jniEnv;
22
23 if (!frame.isValid())
24 return false;
25
26 auto planes = frame.callMethod<QtJniTypes::AndroidImagePlaneArray>("getPlanes");
27 if (!planes.isValid())
28 return false;
29
30 int numberPlanes = jniEnv->GetArrayLength(planes.object<jarray>());
31 // create and populate temporary array structure
32 int pixelStrides[numberPlanes];
33 int rowStrides[numberPlanes];
34 int bufferSize[numberPlanes];
35 uint8_t *buffer[numberPlanes];
36
37 auto resetPlane = [&](int index) {
38 if (index < 0 || index > numberPlanes)
39 return;
40
41 rowStrides[index] = 0;
42 pixelStrides[index] = 0;
43 bufferSize[index] = 0;
44 buffer[index] = nullptr;
45 };
46
47 for (int index = 0; index < numberPlanes; index++) {
48 QJniObject plane = jniEnv->GetObjectArrayElement(planes.object<jobjectArray>(), index);
49 if (jniEnv.checkAndClearExceptions() || !plane.isValid()) {
50 resetPlane(index);
51 continue;
52 }
53
54 rowStrides[index] = plane.callMethod<jint>("getRowStride");
55 pixelStrides[index] = plane.callMethod<jint>("getPixelStride");
56
57 auto byteBuffer = plane.callMethod<QtJniTypes::JavaByteBuffer>("getBuffer");
58 if (!byteBuffer.isValid()) {
59 resetPlane(index);
60 continue;
61 }
62
63 // Uses direct access which is garanteed by android to work with
64 // ImageReader bytebuffer
65 buffer[index] = static_cast<uint8_t *>(jniEnv->GetDirectBufferAddress(byteBuffer.object()));
66 bufferSize[index] = byteBuffer.callMethod<jint>("remaining");
67 }
68
70
71 // finding the image format
72 // the ImageFormats that can happen here are stated here:
73 // https://developer.android.com/reference/android/media/Image#getFormat()
74 int format = frame.callMethod<jint>("getFormat");
75 AndroidImageFormat imageFormat = AndroidImageFormat(format);
76
77 switch (imageFormat) {
78 case AndroidImageFormat::JPEG:
79 calculedPixelFormat = QVideoFrameFormat::Format_Jpeg;
80 break;
81 case AndroidImageFormat::YUV_420_888:
82 if (numberPlanes < 3) {
83 // something went wrong on parsing. YUV_420_888 format must always have 3 planes
84 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
85 break;
86 }
87 if (pixelStrides[1] == 1)
88 calculedPixelFormat = QVideoFrameFormat::Format_YUV420P;
89 else if (pixelStrides[1] == 2 && abs(buffer[1] - buffer[2]) == 1)
90 // this can be NV21, but it will converted below
91 calculedPixelFormat = QVideoFrameFormat::Format_NV12;
92 break;
93 case AndroidImageFormat::HEIC:
94 // QImage cannot parse HEIC
95 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
96 break;
97 case AndroidImageFormat::RAW_PRIVATE:
98 case AndroidImageFormat::RAW_SENSOR:
99 // we cannot know raw formats
100 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
101 break;
102 case AndroidImageFormat::FLEX_RGBA_8888:
103 case AndroidImageFormat::FLEX_RGB_888:
104 // these formats are only returned by Mediacodec.getOutputImage, they are not used as a
105 // Camera2 Image frame return
106 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
107 break;
108 case AndroidImageFormat::YUV_422_888:
109 case AndroidImageFormat::YUV_444_888:
110 case AndroidImageFormat::YCBCR_P010:
111 // not dealing with these formats, they require higher API levels than the current Qt min
112 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
113 break;
114 default:
115 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
116 break;
117 }
118
119 if (calculedPixelFormat == QVideoFrameFormat::Format_Invalid) {
120 qCWarning(qLCAndroidCameraFrame) << "Cannot determine image format!";
121 return false;
122 }
123
124 auto copyPlane = [&](int mapIndex, int arrayIndex) {
125 if (arrayIndex >= numberPlanes)
126 return;
127
128 m_planes[mapIndex].rowStride = rowStrides[arrayIndex];
129 m_planes[mapIndex].size = bufferSize[arrayIndex];
130 m_planes[mapIndex].data = buffer[arrayIndex];
131 };
132
133 switch (calculedPixelFormat) {
135 m_numberPlanes = 3;
136 copyPlane(0, 0);
137 copyPlane(1, 1);
138 copyPlane(2, 2);
139 m_pixelFormat = QVideoFrameFormat::Format_YUV420P;
140 break;
142 m_numberPlanes = 2;
143 copyPlane(0, 0);
144 copyPlane(1, 1);
145 m_pixelFormat = QVideoFrameFormat::Format_NV12;
146 break;
148 qCWarning(qLCAndroidCameraFrame)
149 << "FFmpeg HW Mediacodec does not encode other than YCbCr formats";
150 // we still parse it to preview the frame
151 m_image = QImage::fromData(buffer[0], bufferSize[0]);
152 m_planes[0].rowStride = m_image.bytesPerLine();
153 m_planes[0].size = m_image.sizeInBytes();
154 m_planes[0].data = m_image.bits();
155 m_pixelFormat = QVideoFrameFormat::pixelFormatFromImageFormat(m_image.format());
156 break;
157 default:
158 break;
159 }
160
161 long timestamp = frame.callMethod<jlong>("getTimestamp");
162 m_timestamp = timestamp / 1000;
163
164 int width = frame.callMethod<jint>("getWidth");
165 int height = frame.callMethod<jint>("getHeight");
166 m_size = QSize(width, height);
167
168 return true;
169}
170
172 : m_pixelFormat(QVideoFrameFormat::Format_Invalid), m_parsed(parse(frame))
173{
174 if (isParsed()) {
175 // holding the frame java object
176 QJniEnvironment jniEnv;
177 m_frame = jniEnv->NewGlobalRef(frame.object());
178 jniEnv.checkAndClearExceptions();
179 } else if (frame.isValid()) {
180 frame.callMethod<void>("close");
181 }
182}
183
185{
186 if (!isParsed()) // nothing to clean
187 return;
188
189 QJniObject qFrame(m_frame);
190 if (qFrame.isValid())
191 qFrame.callMethod<void>("close");
192
193 QJniEnvironment jniEnv;
194 if (m_frame)
195 jniEnv->DeleteGlobalRef(m_frame);
196}
197
Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
QAndroidCameraFrame(QJniObject frame)
static QImage fromData(QByteArrayView data, const char *format=nullptr)
Definition qimage.cpp:3841
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
static PixelFormat pixelFormatFromImageFormat(QImage::Format format)
Returns a video pixel format equivalent to an image format.
QCamera * camera
Definition camera.cpp:19
Combined button and popup list for selecting options.
Q_DECLARE_JNI_CLASS(AndroidImageFormat, "android/graphics/ImageFormat")
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
GLint GLsizei GLsizei height
GLuint index
[2]
GLenum GLuint buffer
GLint GLsizei width
GLint GLsizei GLsizei GLenum format
QFrame frame
[0]