4#include <QtFFmpegMediaPluginImpl/private/qandroidvideoframebuffer_p.h>
6#include <QtCore/qdebug.h>
7#include <QtCore/qjnitypes.h>
8#include <QtCore/qloggingcategory.h>
16bool isWorkaroundForEmulatorNeeded() {
17 const static bool workaroundForEmulator
18 = QtJniTypes::QtVideoDeviceManager::callStaticMethod<jboolean>(
"isEmulator");
19 return workaroundForEmulator;
23bool QAndroidVideoFrameBuffer::useCopiedData()
const
25 return m_policy == MemoryPolicy::Copy;
28bool QAndroidVideoFrameBuffer::parse(
const QJniObject &frame)
30 QJniEnvironment jniEnv;
35 const auto planes = frame.callMethod<QtJniTypes::ImagePlane[]>(
"getPlanes");
36 if (!planes.isValid())
39 const int numberPlanes = planes.size();
40 Q_ASSERT(numberPlanes <= MAX_PLANES);
43 int pixelStrides[MAX_PLANES];
44 int rowStrides[MAX_PLANES];
45 int bufferSize[MAX_PLANES];
46 char *buffer[MAX_PLANES];
48 auto resetPlane = [&](
int index) {
49 if (index < 0 || index > numberPlanes)
52 rowStrides[index] = 0;
53 pixelStrides[index] = 0;
54 bufferSize[index] = 0;
55 buffer[index] =
nullptr;
58 for (qsizetype index = 0; index < numberPlanes; ++index) {
59 auto plane = planes.at(index);
60 if (!plane.isValid()) {
65 rowStrides[index] = plane.callMethod<jint>(
"getRowStride");
66 pixelStrides[index] = plane.callMethod<jint>(
"getPixelStride");
68 auto byteBuffer = plane.callMethod<QtJniTypes::ByteBuffer>(
"getBuffer");
69 if (!byteBuffer.isValid()) {
76 buffer[index] =
static_cast<
char *>(jniEnv->GetDirectBufferAddress(byteBuffer.object()));
77 bufferSize[index] = byteBuffer.callMethod<jint>(
"remaining");
80 QVideoFrameFormat::PixelFormat calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
85 int format = frame.callMethod<jint>(
"getFormat");
86 AndroidImageFormat imageFormat = AndroidImageFormat(format);
88 switch (imageFormat) {
89 case AndroidImageFormat::RGBA_8888: {
91 calculedPixelFormat = numberPlanes != 1 ? QVideoFrameFormat::Format_Invalid :
92 QVideoFrameFormat::Format_RGBA8888;
95 case AndroidImageFormat::JPEG:
96 calculedPixelFormat = QVideoFrameFormat::Format_Jpeg;
98 case AndroidImageFormat::YUV_420_888:
99 if (numberPlanes < 3) {
101 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
104 if (pixelStrides[1] == 1)
105 calculedPixelFormat = QVideoFrameFormat::Format_YUV420P;
106 else if (pixelStrides[1] == 2) {
107 if (buffer[1] - buffer[2] == -1)
108 calculedPixelFormat = QVideoFrameFormat::Format_NV12;
109 else if (buffer[1] - buffer[2] == 1)
110 calculedPixelFormat = QVideoFrameFormat::Format_NV21;
113 case AndroidImageFormat::HEIC:
115 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
117 case AndroidImageFormat::RAW_PRIVATE:
118 case AndroidImageFormat::RAW_SENSOR:
120 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
122 case AndroidImageFormat::FLEX_RGBA_8888:
123 case AndroidImageFormat::FLEX_RGB_888:
126 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
128 case AndroidImageFormat::YUV_422_888:
129 case AndroidImageFormat::YUV_444_888:
130 case AndroidImageFormat::YCBCR_P010:
132 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
135 calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
139 if (calculedPixelFormat == QVideoFrameFormat::Format_Invalid) {
140 qCWarning(qLCAndroidCameraFrame) <<
"Cannot determine image format!";
144 auto copyPlane = [&](
int mapIndex,
int arrayIndex) {
145 if (arrayIndex >= numberPlanes)
148 m_mapData.bytesPerLine[mapIndex] = rowStrides[arrayIndex];
149 dataCleaner[mapIndex] = useCopiedData() ?
150 QByteArray(buffer[arrayIndex], bufferSize[arrayIndex])
151 : QByteArray::fromRawData(buffer[arrayIndex], bufferSize[arrayIndex]);
152 m_mapData.data[mapIndex] = (uchar *)dataCleaner[mapIndex].constData();
153 m_mapData.dataSize[mapIndex] = bufferSize[arrayIndex];
156 int width = frame.callMethod<jint>(
"getWidth");
157 int height = frame.callMethod<jint>(
"getHeight");
158 QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
160 switch (calculedPixelFormat) {
161 case QVideoFrameFormat::Format_RGBA8888:
162 m_mapData.planeCount = 1;
163 if (isWorkaroundForEmulatorNeeded())
164 m_policy = MemoryPolicy::Copy;
168 pixelFormat = QVideoFrameFormat::Format_RGBA8888;
170 case QVideoFrameFormat::Format_YUV420P:
171 m_mapData.planeCount = 3;
172 if (isWorkaroundForEmulatorNeeded())
173 m_policy = MemoryPolicy::Copy;
178 pixelFormat = QVideoFrameFormat::Format_YUV420P;
180 case QVideoFrameFormat::Format_NV12:
181 case QVideoFrameFormat::Format_NV21:
183 m_mapData.planeCount = 2;
193 const int indexOfFirstPlane = calculedPixelFormat == QVideoFrameFormat::Format_NV21 ?
195 m_mapData.bytesPerLine[1] = rowStrides[indexOfFirstPlane];
197 dataCleaner[1] = useCopiedData() ?
198 QByteArray(buffer[indexOfFirstPlane],
199 bufferSize[indexOfFirstPlane] + 1)
200 : QByteArray::fromRawData(buffer[indexOfFirstPlane],
201 bufferSize[indexOfFirstPlane] + 1);
202 m_mapData.data[1] = (uchar *)dataCleaner[1].constData();
203 m_mapData.dataSize[1] = bufferSize[indexOfFirstPlane] + 1;
205 pixelFormat = calculedPixelFormat;
207 case QVideoFrameFormat::Format_Jpeg:
208 qCWarning(qLCAndroidCameraFrame)
209 <<
"FFmpeg HW Mediacodec does not encode other than YCbCr formats";
211 m_policy = MemoryPolicy::Copy;
212 m_image = QImage::fromData((uchar *)buffer[0], bufferSize[0]);
213 m_mapData.bytesPerLine[0] = m_image.bytesPerLine();
214 dataCleaner[0] = QByteArray::fromRawData((
char*)m_image.bits(), m_image.sizeInBytes());
215 m_mapData.data[0] = (uchar *)dataCleaner[0].constData();
216 m_mapData.dataSize[0] = m_image.sizeInBytes();
217 pixelFormat = QVideoFrameFormat::pixelFormatFromImageFormat(m_image.format());
223 long timestamp = frame.callMethod<jlong>(
"getTimestamp");
224 m_timestamp = timestamp / 1000;
226 m_videoFrameFormat = QVideoFrameFormat(QSize(width, height), pixelFormat);
230QAndroidVideoFrameBuffer::QAndroidVideoFrameBuffer(QJniObject frame,
231 std::shared_ptr<FrameReleaseDelegate> frameReleaseDelegate,
233 QtVideo::Rotation rotation)
234 : m_frameReleaseDelegate(frameReleaseDelegate)
236 , m_parsed(parse(frame))
238 if (isParsed() && !useCopiedData()) {
240 QJniEnvironment jniEnv;
241 m_nativeFrame = jniEnv->NewGlobalRef(frame.object());
242 jniEnv.checkAndClearExceptions();
243 }
else if (frame.isValid()) {
244 frame.callMethod<
void>(
"close");
245 m_frameReleaseDelegate->onFrameReleased();
247 m_videoFrameFormat.setRotation(rotation);
250QAndroidVideoFrameBuffer::~QAndroidVideoFrameBuffer()
252 if (!isParsed() || useCopiedData())
255 QJniObject qFrame(m_nativeFrame);
256 if (qFrame.isValid()) {
257 qFrame.callMethod<
void>(
"close");
258 m_frameReleaseDelegate->onFrameReleased();
261 QJniEnvironment jniEnv;
263 jniEnv->DeleteGlobalRef(m_nativeFrame);
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")