summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/android')
-rw-r--r--src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp156
-rw-r--r--src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h12
-rw-r--r--src/multimedia/platform/android/audio/qandroidaudiosink.cpp8
-rw-r--r--src/multimedia/platform/android/audio/qandroidaudiosource.cpp2
-rw-r--r--src/multimedia/platform/android/audio/qopenslesengine.cpp52
-rw-r--r--src/multimedia/platform/android/audio/qopenslesengine_p.h3
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput.cpp37
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput_p.h4
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamera.cpp10
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamera_p.h1
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp25
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h2
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp47
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h3
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp4
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp60
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h4
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp11
-rw-r--r--src/multimedia/platform/android/qandroidformatsinfo.cpp12
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidcamera.cpp6
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp44
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h2
22 files changed, 312 insertions, 193 deletions
diff --git a/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp b/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp
index 7e3fe9a01..2a35a06eb 100644
--- a/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp
+++ b/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp
@@ -53,8 +53,7 @@
QT_BEGIN_NAMESPACE
-static const char tempFile[] = "encoded.tmp";
-static const char tempPath[] = "/storage/emulated/0/data/local/tmp/audiodecoder/";
+static const char tempFile[] = "encoded.wav";
constexpr int dequeueTimeout = 5000;
Q_LOGGING_CATEGORY(adLogger, "QAndroidAudioDecoder")
@@ -92,6 +91,20 @@ void Decoder::stop()
void Decoder::setSource(const QUrl &source)
{
+ const QJniObject path = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getMimeType",
+ "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;",
+ QNativeInterface::QAndroidApplication::context(),
+ QJniObject::fromString(source.path()).object());
+
+ const QString mime = path.isValid() ? path.toString() : "";
+
+ if (!mime.isEmpty() && !mime.contains("audio", Qt::CaseInsensitive)) {
+ m_formatError = tr("Cannot set source, invalid mime type for the provided source.");
+ return;
+ }
+
if (!m_extractor)
m_extractor = AMediaExtractor_new();
@@ -108,9 +121,9 @@ void Decoder::setSource(const QUrl &source)
}
if (fd < 0) {
- emit error(QAudioDecoder::ResourceError, tr("Invalid fileDescriptor for source."));
- return;
- }
+ emit error(QAudioDecoder::ResourceError, tr("Invalid fileDescriptor for source."));
+ return;
+ }
const int size = QFile(source.toString()).size();
media_status_t status = AMediaExtractor_setDataSourceFd(m_extractor, fd, 0,
size > 0 ? size : LONG_MAX);
@@ -121,7 +134,7 @@ void Decoder::setSource(const QUrl &source)
AMediaExtractor_delete(m_extractor);
m_extractor = nullptr;
}
- emit error(QAudioDecoder::ResourceError, tr("Setting source for Audio Decoder failed."));
+ m_formatError = tr("Setting source for Audio Decoder failed.");
}
}
@@ -162,6 +175,11 @@ void Decoder::createDecoder()
void Decoder::doDecode()
{
+ if (!m_formatError.isEmpty()) {
+ emit error(QAudioDecoder::FormatError, m_formatError);
+ return;
+ }
+
if (!m_extractor) {
emit error(QAudioDecoder::ResourceError, tr("Cannot decode, source not set."));
return;
@@ -190,6 +208,7 @@ void Decoder::doDecode()
AMediaExtractor_selectTrack(m_extractor, 0);
+ emit decodingChanged(true);
m_inputEOS = false;
while (!m_inputEOS) {
// handle input buffer
@@ -212,6 +231,15 @@ void Decoder::doDecode()
// handle output buffer
AMediaCodecBufferInfo info;
ssize_t idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
+
+ while (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
+ || idx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
+ qCWarning(adLogger) << "dequeueOutputBuffer() status: outputFormat changed";
+
+ idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
+ }
+
if (idx >= 0) {
if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)
break;
@@ -222,30 +250,22 @@ void Decoder::doDecode()
&bufferSize);
const QByteArray data((const char*)(bufferData + info.offset), info.size);
auto audioBuffer = QAudioBuffer(data, m_outputFormat, presentationTimeUs);
- if (presentationTimeUs > 0)
+ if (presentationTimeUs >= 0)
emit positionChanged(std::move(audioBuffer), presentationTimeUs / 1000);
+
AMediaCodec_releaseOutputBuffer(m_codec, idx, false);
}
+ } else if (idx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ qCWarning(adLogger) << "dequeueOutputBuffer() status: try again later";
+ break;
} else {
- // The outputIndex doubles as a status return if its value is < 0
- switch (idx) {
- case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
- qCWarning(adLogger) << "dequeueOutputBuffer() status: try again later";
- break;
- case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
- qCWarning(adLogger) << "dequeueOutputBuffer() status: output buffers changed";
- break;
- case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
- m_format = AMediaCodec_getOutputFormat(m_codec);
- qCWarning(adLogger) << "dequeueOutputBuffer() status: outputFormat changed";
- break;
- }
+ qCWarning(adLogger) <<
+ "AMediaCodec_dequeueOutputBuffer() status: invalid buffer idx " << idx;
}
} else {
qCWarning(adLogger) << "dequeueInputBuffer() status: invalid buffer idx " << bufferIdx;
}
}
-
emit finished();
}
@@ -257,12 +277,16 @@ QAndroidAudioDecoder::QAndroidAudioDecoder(QAudioDecoder *parent)
connect(m_decoder, &Decoder::durationChanged, this, &QAndroidAudioDecoder::durationChanged);
connect(m_decoder, &Decoder::error, this, &QAndroidAudioDecoder::error);
connect(m_decoder, &Decoder::finished, this, &QAndroidAudioDecoder::finished);
+ connect(m_decoder, &Decoder::decodingChanged, this, &QPlatformAudioDecoder::setIsDecoding);
+ connect(this, &QAndroidAudioDecoder::setSourceUrl, m_decoder, & Decoder::setSource);
}
QAndroidAudioDecoder::~QAndroidAudioDecoder()
{
- m_decoder->thread()->exit();
- m_decoder->deleteLater();
+ m_decoder->thread()->quit();
+ m_decoder->thread()->wait();
+ delete m_threadDecoder;
+ delete m_decoder;
}
void QAndroidAudioDecoder::setSource(const QUrl &fileName)
@@ -278,7 +302,7 @@ void QAndroidAudioDecoder::setSource(const QUrl &fileName)
if (m_source != fileName) {
m_source = fileName;
- m_decoder->setSource(m_source);
+ emit setSourceUrl(m_source);
sourceChanged();
}
}
@@ -304,38 +328,45 @@ void QAndroidAudioDecoder::start()
if (isDecoding())
return;
- setIsDecoding(true);
m_position = -1;
- m_threadDecoder = new QThread(this);
- m_decoder->moveToThread(m_threadDecoder);
- m_threadDecoder->start();
+ if (m_device && (!m_device->isOpen() || !m_device->isReadable())) {
+ emit error(QAudioDecoder::ResourceError,
+ QString::fromUtf8("Unable to read from the specified device"));
+ return;
+ }
+
+ if (!m_threadDecoder) {
+ m_threadDecoder = new QThread(this);
+ m_decoder->moveToThread(m_threadDecoder);
+ m_threadDecoder->start();
+ }
+
decode();
}
void QAndroidAudioDecoder::stop()
{
- if (!isDecoding())
+ if (!isDecoding() && m_position < 0 && m_duration < 0)
return;
m_decoder->stop();
-
- if (m_threadDecoder && m_threadDecoder->isRunning())
- m_threadDecoder->exit();
-
- QMutexLocker locker(&m_buffersMutex);
- m_position = -1;
m_audioBuffer.clear();
- locker.unlock();
+ m_position = -1;
+ m_duration = -1;
setIsDecoding(false);
+
+ emit bufferAvailableChanged(false);
+ emit QPlatformAudioDecoder::positionChanged(m_position);
}
QAudioBuffer QAndroidAudioDecoder::read()
{
- QMutexLocker locker(&m_buffersMutex);
- if (m_buffersAvailable && !m_audioBuffer.isEmpty()) {
- --m_buffersAvailable;
- return m_audioBuffer.takeFirst();
+ if (!m_audioBuffer.isEmpty()) {
+ QPair<QAudioBuffer, int> buffer = m_audioBuffer.takeFirst();
+ m_position = buffer.second;
+ emit QPlatformAudioDecoder::positionChanged(buffer.second);
+ return buffer.first;
}
// no buffers available
@@ -344,38 +375,29 @@ QAudioBuffer QAndroidAudioDecoder::read()
bool QAndroidAudioDecoder::bufferAvailable() const
{
- QMutexLocker locker(&m_buffersMutex);
- return m_buffersAvailable;
+ return m_audioBuffer.size() > 0;
}
qint64 QAndroidAudioDecoder::position() const
{
- QMutexLocker locker(&m_buffersMutex);
return m_position;
}
qint64 QAndroidAudioDecoder::duration() const
{
- QMutexLocker locker(&m_buffersMutex);
return m_duration;
}
void QAndroidAudioDecoder::positionChanged(QAudioBuffer audioBuffer, qint64 position)
{
- QMutexLocker locker(&m_buffersMutex);
- m_audioBuffer.append(audioBuffer);
+ m_audioBuffer.append(QPair<QAudioBuffer, int>(audioBuffer, position));
m_position = position;
- m_buffersAvailable++;
- locker.unlock();
emit bufferReady();
- emit QPlatformAudioDecoder::positionChanged(position);
}
void QAndroidAudioDecoder::durationChanged(qint64 duration)
{
- QMutexLocker locker(&m_buffersMutex);
m_duration = duration;
- locker.unlock();
emit QPlatformAudioDecoder::durationChanged(duration);
}
@@ -387,9 +409,13 @@ void QAndroidAudioDecoder::error(const QAudioDecoder::Error err, const QString &
void QAndroidAudioDecoder::finished()
{
- stop();
+ emit bufferAvailableChanged(m_audioBuffer.size() > 0);
+
+ if (m_duration != -1)
+ emit durationChanged(m_duration);
+
// remove temp file when decoding is finished
- QFile(QString::fromUtf8(tempPath).append(QString::fromUtf8(tempFile))).remove();
+ QFile(QString(QDir::tempPath()).append(QString::fromUtf8(tempFile))).remove();
emit QPlatformAudioDecoder::finished();
}
@@ -415,22 +441,22 @@ void QAndroidAudioDecoder::decode()
bool QAndroidAudioDecoder::createTempFile()
{
- QFile file = QFile(QString::fromUtf8(tempPath).append(QString::fromUtf8(tempFile)));
- if (!QDir().mkpath(QString::fromUtf8(tempPath)) || !file.open(QIODevice::WriteOnly)) {
- emit error(QAudioDecoder::ResourceError,
- QString::fromUtf8("Error while creating or opening tmp file"));
- return false;
- }
+ QFile file = QFile(QDir::tempPath().append(QString::fromUtf8(tempFile)), this);
- QDataStream out;
- out.setDevice(&file);
- out << m_deviceBuffer;
- file.close();
+ bool success = file.open(QIODevice::QIODevice::ReadWrite);
+ if (!success)
+ emit error(QAudioDecoder::ResourceError, tr("Error while opening tmp file"));
+ success &= (file.write(m_deviceBuffer) == m_deviceBuffer.size());
+ if (!success)
+ emit error(QAudioDecoder::ResourceError, tr("Error while writing data to tmp file"));
+
+ file.close();
m_deviceBuffer.clear();
- m_decoder->setSource(file.fileName());
+ if (success)
+ m_decoder->setSource(file.fileName());
- return true;
+ return success;
}
void QAndroidAudioDecoder::readDevice() {
diff --git a/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h b/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h
index efb7cdc24..3707f4c50 100644
--- a/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h
+++ b/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h
@@ -53,7 +53,6 @@
#include "private/qplatformaudiodecoder_p.h"
#include <QtCore/qurl.h>
-#include <QtCore/qmutex.h>
#include <QThread>
#include "media/NdkMediaCodec.h"
@@ -81,6 +80,7 @@ signals:
void durationChanged(const qint64 duration);
void error(const QAudioDecoder::Error error, const QString &errorString);
void finished();
+ void decodingChanged(bool decoding);
private:
void createDecoder();
@@ -90,6 +90,7 @@ private:
AMediaFormat *m_format = nullptr;
QAudioFormat m_outputFormat;
+ QString m_formatError;
bool m_inputEOS;
};
@@ -119,6 +120,9 @@ public:
qint64 position() const override;
qint64 duration() const override;
+signals:
+ void setSourceUrl(const QUrl &source);
+
private slots:
void positionChanged(QAudioBuffer audioBuffer, qint64 position);
void durationChanged(qint64 duration);
@@ -134,18 +138,16 @@ private:
QIODevice *m_device = nullptr;
Decoder *m_decoder;
- QList<QAudioBuffer> m_audioBuffer;
+ QList<QPair<QAudioBuffer, int>> m_audioBuffer;
QUrl m_source;
- mutable QMutex m_buffersMutex;
qint64 m_position = -1;
qint64 m_duration = -1;
long long m_presentationTimeUs = 0;
- int m_buffersAvailable = 0;
QByteArray m_deviceBuffer;
- QThread *m_threadDecoder;
+ QThread *m_threadDecoder = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp b/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
index 1a0b622a3..d7a67f207 100644
--- a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
+++ b/src/multimedia/platform/android/audio/qandroidaudiosink.cpp
@@ -328,6 +328,9 @@ void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex)
m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
+
+ if (m_audioSource->atEnd())
+ setState(QAudio::IdleState);
}
void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
@@ -353,6 +356,9 @@ bool QAndroidAudioSink::preparePlayer()
else
return true;
+ if (!QOpenSLESEngine::setAudioOutput(m_deviceName))
+ qWarning() << "Unable to setup Audio Output Device";
+
SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
if (!engine) {
qWarning() << "No engine";
@@ -361,7 +367,7 @@ bool QAndroidAudioSink::preparePlayer()
}
SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT };
- SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
+ SLAndroidDataFormat_PCM_EX pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat };
diff --git a/src/multimedia/platform/android/audio/qandroidaudiosource.cpp b/src/multimedia/platform/android/audio/qandroidaudiosource.cpp
index c7eaf57ad..2f8d52830 100644
--- a/src/multimedia/platform/android/audio/qandroidaudiosource.cpp
+++ b/src/multimedia/platform/android/audio/qandroidaudiosource.cpp
@@ -220,7 +220,7 @@ bool QAndroidAudioSource::startRecording()
SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
#endif
- SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
+ SLAndroidDataFormat_PCM_EX format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
SLDataSink audioSnk = { &loc_bq, &format_pcm };
// create audio recorder
diff --git a/src/multimedia/platform/android/audio/qopenslesengine.cpp b/src/multimedia/platform/android/audio/qopenslesengine.cpp
index 7d207a369..6032c06cc 100644
--- a/src/multimedia/platform/android/audio/qopenslesengine.cpp
+++ b/src/multimedia/platform/android/audio/qopenslesengine.cpp
@@ -51,6 +51,8 @@
#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; }
+#define SL_ANDROID_PCM_REPRESENTATION_INVALID 0
+
Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine);
QOpenSLESEngine::QOpenSLESEngine()
@@ -81,12 +83,12 @@ QOpenSLESEngine *QOpenSLESEngine::instance()
return openslesEngine();
}
-SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format)
+SLAndroidDataFormat_PCM_EX QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format)
{
- SLDataFormat_PCM format_pcm;
- format_pcm.formatType = SL_DATAFORMAT_PCM;
+ SLAndroidDataFormat_PCM_EX format_pcm;
+ format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
format_pcm.numChannels = format.channelCount();
- format_pcm.samplesPerSec = format.sampleRate() * 1000;
+ format_pcm.sampleRate = format.sampleRate() * 1000;
format_pcm.bitsPerSample = format.bytesPerSample() * 8;
format_pcm.containerSize = format.bytesPerSample() * 8;
format_pcm.channelMask = (format.channelCount() == 1 ?
@@ -95,8 +97,25 @@ SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &f
format_pcm.endianness = (QSysInfo::ByteOrder == QSysInfo::LittleEndian ?
SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN);
- return format_pcm;
+ switch (format.sampleFormat()) {
+ case QAudioFormat::SampleFormat::UInt8:
+ format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
+ break;
+ case QAudioFormat::SampleFormat::Int16:
+ case QAudioFormat::SampleFormat::Int32:
+ format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
+ break;
+ case QAudioFormat::SampleFormat::Float:
+ format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
+ break;
+ case QAudioFormat::SampleFormat::NSampleFormats:
+ case QAudioFormat::SampleFormat::Unknown:
+ format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_INVALID;
+ break;
+ }
+
+ return format_pcm;
}
QList<QAudioDevice> QOpenSLESEngine::availableDevices(QAudioDevice::Mode mode)
@@ -128,15 +147,34 @@ QList<QAudioDevice> QOpenSLESEngine::availableDevices(QAudioDevice::Mode mode)
return devices;
}
+bool QOpenSLESEngine::setAudioOutput(const QByteArray &deviceId)
+{
+ return QJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "setAudioOutput",
+ "(I)Z",
+ deviceId.toInt());
+}
+
static bool hasRecordPermission()
{
const auto recordPerm = QtAndroidPrivate::checkPermission(QtAndroidPrivate::Microphone);
return recordPerm.result() == QtAndroidPrivate::Authorized;
}
+static bool requestPermissions()
+{
+ const auto recordPerm = QtAndroidPrivate::requestPermission(QtAndroidPrivate::Microphone);
+ return recordPerm.result() == QtAndroidPrivate::Authorized;
+}
+
QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) const
{
- if (mode == QAudioDevice::Input && hasRecordPermission()) {
+ bool hasRecordPermissions = hasRecordPermission();
+ if (!hasRecordPermissions)
+ hasRecordPermissions = requestPermissions();
+
+ if (mode == QAudioDevice::Input && hasRecordPermissions) {
if (!m_checkedInputFormats)
const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats();
return m_supportedInputChannelCounts;
@@ -302,9 +340,9 @@ void QOpenSLESEngine::checkSupportedInputFormats()
defaultFormat.sampleRate = SL_SAMPLINGRATE_44_1;
defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
- defaultFormat.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
defaultFormat.channelMask = SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(SL_SPEAKER_FRONT_CENTER);
defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
+ defaultFormat.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
const SLuint32 rates[13] = { SL_SAMPLINGRATE_8,
SL_SAMPLINGRATE_11_025,
diff --git a/src/multimedia/platform/android/audio/qopenslesengine_p.h b/src/multimedia/platform/android/audio/qopenslesengine_p.h
index 36e994fb2..0f9781bd5 100644
--- a/src/multimedia/platform/android/audio/qopenslesengine_p.h
+++ b/src/multimedia/platform/android/audio/qopenslesengine_p.h
@@ -72,9 +72,10 @@ public:
SLEngineItf slEngine() const { return m_engine; }
- static SLDataFormat_PCM audioFormatToSLFormatPCM(const QAudioFormat &format);
+ static SLAndroidDataFormat_PCM_EX audioFormatToSLFormatPCM(const QAudioFormat &format);
static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode);
+ static bool setAudioOutput(const QByteArray &deviceId);
QList<int> supportedChannelCounts(QAudioDevice::Mode mode) const;
QList<int> supportedSampleRates(QAudioDevice::Mode mode) const;
diff --git a/src/multimedia/platform/android/common/qandroidvideooutput.cpp b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
index 6508cb554..f3a8bff2e 100644
--- a/src/multimedia/platform/android/common/qandroidvideooutput.cpp
+++ b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
@@ -123,10 +123,12 @@ static QMatrix4x4 extTransformMatrix(AndroidSurfaceTexture *surfaceTexture)
quint64 AndroidTextureVideoBuffer::textureHandle(int plane) const
{
- if (plane != 0 || !rhi || !m_output->m_nativeSize.isValid())
+ if (plane != 0 || !rhi || !m_output->m_nativeSize.isValid() || !m_output->m_readbackRhi
+ || !m_output->m_surfaceTexture)
return 0;
- m_output->ensureExternalTexture(rhi);
+ m_output->m_readbackRhi->makeThreadLocalNativeContextCurrent();
+ m_output->ensureExternalTexture(m_output->m_readbackRhi);
m_output->m_surfaceTexture->updateTexImage();
m_externalMatrix = extTransformMatrix(m_output->m_surfaceTexture);
return m_output->m_externalTex->nativeTexture().object;
@@ -233,15 +235,13 @@ void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
if (m_nativeSize == size)
return;
- stop();
-
m_nativeSize = size;
}
void QAndroidTextureVideoOutput::start()
{
m_started = true;
- renderAndReadbackFrame();
+ QMetaObject::invokeMethod(this, "onFrameAvailable", Qt::QueuedConnection);
}
void QAndroidTextureVideoOutput::stop()
@@ -304,6 +304,22 @@ void QAndroidTextureVideoOutput::onFrameAvailable()
if (!(m_nativeSize.isValid() && m_sink) || !(m_started || m_renderFrame))
return;
+ const bool needsToBeInOpenGLThread =
+ !m_readbackRhi || !m_readbackTex || !m_readbackSrb || !m_readbackPs;
+
+ const bool movedToOpenGLThread = needsToBeInOpenGLThread && moveToOpenGLContextThread();
+
+ if (movedToOpenGLThread || QThread::currentThread() != m_thread) {
+ // the render thread may get blocked waiting for events, force refresh until get back to
+ // original thread.
+ QMetaObject::invokeMethod(this, "onFrameAvailable", Qt::QueuedConnection);
+
+ if (!needsToBeInOpenGLThread) {
+ parent()->moveToThread(m_thread);
+ moveToThread(m_thread);
+ }
+ }
+
m_renderFrame = false;
QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
@@ -313,11 +329,6 @@ void QAndroidTextureVideoOutput::onFrameAvailable()
: QVideoFrameFormat::Format_RGBA8888;
QVideoFrame frame(buffer, QVideoFrameFormat(m_nativeSize, format));
m_sink->platformVideoSink()->setVideoFrame(frame);
-
- QMetaObject::invokeMethod(m_surfaceTexture
- , "frameAvailable"
- , Qt::QueuedConnection
- );
}
static const float g_quad[] = {
@@ -343,12 +354,6 @@ bool QAndroidTextureVideoOutput::renderAndReadbackFrame()
if (!m_nativeSize.isValid() || !m_surfaceTexture)
return false;
- if (moveToOpenGLContextThread()) {
- // just moved to another thread, must close the execution of this method
- QMetaObject::invokeMethod(this, "onFrameAvailable", Qt::QueuedConnection);
- return false;
- }
-
if (!m_readbackRhi) {
QRhi *sinkRhi = m_sink ? m_sink->rhi() : nullptr;
if (sinkRhi && sinkRhi->backend() == QRhi::OpenGLES2) {
diff --git a/src/multimedia/platform/android/common/qandroidvideooutput_p.h b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
index 473c58552..d7eedc985 100644
--- a/src/multimedia/platform/android/common/qandroidvideooutput_p.h
+++ b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
@@ -82,6 +82,7 @@ public:
virtual void start() { }
virtual void stop() { }
virtual void reset() { }
+ virtual QSize getVideoSize() const { return QSize(0, 0); }
Q_SIGNALS:
void readyChanged(bool);
@@ -122,6 +123,7 @@ public:
void stop() override;
void reset() override;
void renderFrame();
+ QSize getVideoSize() const override { return m_nativeSize; }
void setSubtitle(const QString &subtitle);
private Q_SLOTS:
@@ -166,6 +168,8 @@ private:
QString m_subtitleText;
QPixmap m_subtitlePixmap;
+ QThread *m_thread = QThread::currentThread();
+
GraphicsResourceDeleter *m_graphicsDeleter = nullptr;
friend class AndroidTextureVideoBuffer;
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp
index 3bcc93564..0db9b7bbc 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp
@@ -60,8 +60,11 @@ QAndroidCamera::~QAndroidCamera()
void QAndroidCamera::setActive(bool active)
{
- if (m_cameraSession)
+ if (m_cameraSession) {
m_cameraSession->setActive(active);
+ } else {
+ isPendingSetActive = active;
+ }
}
bool QAndroidCamera::isActive() const
@@ -135,6 +138,11 @@ void QAndroidCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
connect(m_cameraSession, &QAndroidCameraSession::activeChanged, this, &QAndroidCamera::activeChanged);
connect(m_cameraSession, &QAndroidCameraSession::error, this, &QAndroidCamera::error);
connect(m_cameraSession, &QAndroidCameraSession::opened, this, &QAndroidCamera::onCameraOpened);
+
+ if (isPendingSetActive) {
+ setActive(true);
+ isPendingSetActive = false;
+ }
}
void QAndroidCamera::setFocusMode(QCamera::FocusMode mode)
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h
index e97368698..45e97a4fa 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h
@@ -121,6 +121,7 @@ private:
bool isFlashSupported = false;
bool isFlashAutoSupported = false;
bool isTorchSupported = false;
+ bool isPendingSetActive = false;
QCameraDevice m_cameraDev;
QMap<QCamera::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
index 5df959341..19db3ec73 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
@@ -269,6 +269,8 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
// -- adjust resolution
QSize adjustedViewfinderResolution;
+ const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
+
const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
if (validCaptureSize
&& m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
@@ -280,8 +282,6 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
if (validCaptureSize)
captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
- const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
-
if (validCaptureSize) {
// search for viewfinder resolution with the same aspect ratio
qreal minAspectDiff = 1;
@@ -326,24 +326,31 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
// -- Set values on camera
// fix the resolution of output based on the orientation
- QSize outputResolution = adjustedViewfinderResolution;
+ QSize cameraOutputResolution = adjustedViewfinderResolution;
+ QSize videoOutputResolution = adjustedViewfinderResolution;
+ QSize currentVideoOutputResolution = m_videoOutput ? m_videoOutput->getVideoSize() : QSize(0, 0);
const int rotation = currentCameraRotation();
- if (rotation == 90 || rotation == 270)
- outputResolution.transpose();
+ // only transpose if it's valid for the preview
+ if (rotation == 90 || rotation == 270) {
+ videoOutputResolution.transpose();
+ if (previewSizes.contains(cameraOutputResolution.transposed()))
+ cameraOutputResolution.transpose();
+ }
- if (currentViewfinderResolution != outputResolution
+ if (currentViewfinderResolution != cameraOutputResolution
+ || (m_videoOutput && currentVideoOutputResolution != videoOutputResolution)
|| currentPreviewFormat != adjustedPreviewFormat || currentFpsRange.min != adjustedFps.min
|| currentFpsRange.max != adjustedFps.max) {
if (m_videoOutput) {
- m_videoOutput->setVideoSize(outputResolution);
+ m_videoOutput->setVideoSize(videoOutputResolution);
}
// if preview is started, we have to stop it first before changing its size
if (m_previewStarted && restartPreview)
m_camera->stopPreview();
- m_camera->setPreviewSize(outputResolution);
+ m_camera->setPreviewSize(cameraOutputResolution);
m_camera->setPreviewFormat(adjustedPreviewFormat);
m_camera->setPreviewFpsRange(adjustedFps);
@@ -409,6 +416,7 @@ bool QAndroidCameraSession::startPreview()
m_camera->startPreview();
m_previewStarted = true;
+ m_videoOutput->start();
return true;
}
@@ -612,7 +620,6 @@ int QAndroidCameraSession::captureImage()
m_currentImageCaptureId = newImageCaptureId;
- applyImageSettings();
applyResolution(m_actualImageSettings.resolution());
m_camera->takePicture();
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
index fdafe3520..2fdd9d33b 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
@@ -183,7 +183,7 @@ private:
QImageEncoderSettings m_requestedImageSettings;
QImageEncoderSettings m_actualImageSettings;
AndroidCamera::FpsRange m_requestedFpsRange;
- AndroidCamera::ImageFormat m_requestedPixelFromat;
+ AndroidCamera::ImageFormat m_requestedPixelFromat = AndroidCamera::ImageFormat::NV21;
bool m_readyForCapture;
int m_currentImageCaptureId;
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
index d274fc9e9..ba296e4f7 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
@@ -41,6 +41,8 @@
#include "androidcamera_p.h"
#include "qandroidcamerasession_p.h"
+#include "qaudioinput.h"
+#include "qaudiooutput.h"
#include "androidmediaplayer_p.h"
#include "androidmultimediautils_p.h"
#include "qandroidmultimediautils_p.h"
@@ -74,6 +76,8 @@ QAndroidCaptureSession::~QAndroidCaptureSession()
{
stop();
m_mediaRecorder = nullptr;
+ if (m_audioInput && m_audioOutput)
+ AndroidMediaPlayer::stopSoundStreaming();
}
void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSession)
@@ -97,7 +101,23 @@ void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSessi
void QAndroidCaptureSession::setAudioInput(QPlatformAudioInput *input)
{
+ if (m_audioInput == input)
+ return;
+
+ if (m_audioInput) {
+ disconnect(m_audioInputChanged);
+ }
+
m_audioInput = input;
+
+ if (m_audioInput) {
+ m_audioInputChanged = connect(m_audioInput->q, &QAudioInput::deviceChanged, this, [this]() {
+ if (m_state == QMediaRecorder::RecordingState)
+ m_mediaRecorder->setAudioInput(m_audioInput->device.id());
+ updateStreamingState();
+ });
+ }
+ updateStreamingState();
}
void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
@@ -105,10 +125,30 @@ void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
if (m_audioOutput == output)
return;
+ if (m_audioOutput)
+ disconnect(m_audioOutputChanged);
+
m_audioOutput = output;
- if (m_audioOutput)
+ if (m_audioOutput) {
+ m_audioOutputChanged = connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
+ [this] () {
+ AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
+ updateStreamingState();
+ });
AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
+ }
+ updateStreamingState();
+}
+
+void QAndroidCaptureSession::updateStreamingState()
+{
+ if (m_audioInput && m_audioOutput) {
+ AndroidMediaPlayer::startSoundStreaming(m_audioInput->device.id().toInt(),
+ m_audioOutput->device.id().toInt());
+ } else {
+ AndroidMediaPlayer::stopSoundStreaming();
+ }
}
QMediaRecorder::RecorderState QAndroidCaptureSession::state() const
@@ -160,7 +200,6 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
// Set audio/video sources
if (validCameraSession) {
m_cameraSession->camera()->stopPreviewSynchronous();
- m_cameraSession->applyResolution(settings.videoResolution(), false);
m_cameraSession->camera()->unlock();
m_mediaRecorder->setCamera(m_cameraSession->camera());
@@ -434,6 +473,10 @@ void QAndroidCaptureSession::onCameraOpened()
std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
+
+ QMediaEncoderSettings defaultSettings;
+ applySettings(defaultSettings);
+ m_cameraSession->applyResolution(defaultSettings.videoResolution());
}
QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id)
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
index e91e5b210..c3b4926cb 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
@@ -153,6 +153,7 @@ private:
CaptureProfile getProfile(int id);
void restartViewfinder();
+ void updateStreamingState();
QAndroidMediaEncoder *m_mediaEncoder = nullptr;
std::shared_ptr<AndroidMediaRecorder> m_mediaRecorder;
@@ -179,6 +180,8 @@ private:
QList<QSize> m_supportedResolutions;
QList<qreal> m_supportedFramerates;
+ QMetaObject::Connection m_audioInputChanged;
+ QMetaObject::Connection m_audioOutputChanged;
QMetaObject::Connection m_connOpenCamera;
QMetaObject::Connection m_connActiveChangedCamera;
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp
index ddc690d77..ce7135466 100644
--- a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp
@@ -83,10 +83,8 @@ void QAndroidMediaCaptureSession::setCamera(QPlatformCamera *camera)
m_cameraControl->setCaptureSession(nullptr);
m_cameraControl = control;
- if (m_cameraControl) {
+ if (m_cameraControl)
m_cameraControl->setCaptureSession(this);
- m_cameraControl->setActive(true);
- }
emit cameraChanged();
}
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp
index 01a57c298..cbcc34ca3 100644
--- a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp
@@ -84,6 +84,8 @@ QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent)
mMediaPlayer(new AndroidMediaPlayer),
mState(AndroidMediaPlayer::Uninitialized)
{
+ // Set seekable to True by default. It changes if MEDIA_INFO_NOT_SEEKABLE is received
+ seekableChanged(true);
connect(mMediaPlayer, &AndroidMediaPlayer::bufferingChanged, this,
&QAndroidMediaPlayer::onBufferingChanged);
connect(mMediaPlayer, &AndroidMediaPlayer::info, this, &QAndroidMediaPlayer::onInfo);
@@ -249,43 +251,24 @@ void QAndroidMediaPlayer::updateAvailablePlaybackRanges()
qreal QAndroidMediaPlayer::playbackRate() const
{
- if (mHasPendingPlaybackRate ||
- (mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- return mPendingPlaybackRate;
- }
-
- return mMediaPlayer->playbackRate();
+ return mCurrentPlaybackRate;
}
void QAndroidMediaPlayer::setPlaybackRate(qreal rate)
{
- if ((mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- if (mPendingPlaybackRate != rate) {
- mPendingPlaybackRate = rate;
+ if (mState != AndroidMediaPlayer::Started) {
+ // If video isn't playing, changing speed rate may start it automatically
+ // It need to be postponed
+ if (mCurrentPlaybackRate != rate) {
+ mCurrentPlaybackRate = rate;
mHasPendingPlaybackRate = true;
Q_EMIT playbackRateChanged(rate);
}
return;
}
- bool succeeded = mMediaPlayer->setPlaybackRate(rate);
-
- if (mHasPendingPlaybackRate) {
- mHasPendingPlaybackRate = false;
- mPendingPlaybackRate = qreal(1.0);
- if (!succeeded)
- Q_EMIT playbackRateChanged(playbackRate());
- } else if (succeeded) {
+ if (mMediaPlayer->setPlaybackRate(rate)) {
+ mCurrentPlaybackRate = rate;
Q_EMIT playbackRateChanged(rate);
}
}
@@ -416,6 +399,14 @@ void QAndroidMediaPlayer::play()
updateAudioDevice();
+ if (mHasPendingPlaybackRate) {
+ mHasPendingPlaybackRate = false;
+ if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
+ return;
+ mCurrentPlaybackRate = mMediaPlayer->playbackRate();
+ Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
+ }
+
mMediaPlayer->play();
}
@@ -462,17 +453,16 @@ void QAndroidMediaPlayer::stop()
return;
}
+ if (mCurrentPlaybackRate != 1.)
+ // Playback rate need to by reapplied
+ mHasPendingPlaybackRate = true;
+
if (mVideoOutput)
mVideoOutput->stop();
mMediaPlayer->stop();
}
-bool QAndroidMediaPlayer::isSeekable() const
-{
- return true;
-}
-
void QAndroidMediaPlayer::onInfo(qint32 what, qint32 extra)
{
StateChangeNotifier notifier(this);
@@ -552,7 +542,9 @@ void QAndroidMediaPlayer::onError(qint32 what, qint32 extra)
setMediaStatus(QMediaPlayer::InvalidMedia);
break;
case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
- errorString += QLatin1String(" (Unknown error/Insufficient resources)");
+ errorString += mMediaContent.scheme() == QLatin1String("rtsp")
+ ? QLatin1String(" (Unknown error/Insufficient resources or RTSP may not be supported)")
+ : QLatin1String(" (Unknown error/Insufficient resources)");
error = QMediaPlayer::ResourceError;
break;
}
@@ -981,8 +973,6 @@ void QAndroidMediaPlayer::flushPendingStates()
setVolume(mPendingVolume);
if (mPendingMute != -1)
setMuted((mPendingMute == 1));
- if (mHasPendingPlaybackRate)
- setPlaybackRate(mPendingPlaybackRate);
switch (newState) {
case QMediaPlayer::PlayingState:
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h
index b8e187a08..486b0ddb3 100644
--- a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h
@@ -96,8 +96,6 @@ public:
void pause() override;
void stop() override;
- bool isSeekable() const override;
-
int trackCount(TrackType trackType) override;
QMediaMetaData trackMetaData(TrackType trackType, int streamNumber) override;
int activeTrack(TrackType trackType) override;
@@ -137,7 +135,7 @@ private:
int mPendingMute = -1;
bool mReloadingMedia = false;
int mActiveStateChangeNotifiers = 0;
- qreal mPendingPlaybackRate = 1.;
+ qreal mCurrentPlaybackRate = 1.;
bool mHasPendingPlaybackRate = false; // we need this because the rate can theoretically be negative
QMap<TrackType, QList<QAndroidMetaData>> mTracksMetadata;
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp
index 4765fa0ad..93d3b246d 100644
--- a/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qlist.h>
#include <QtConcurrent/qtconcurrentrun.h>
#include <QLoggingCategory>
+#include <private/qiso639_2_p.h>
QT_BEGIN_NAMESPACE
@@ -170,15 +171,7 @@ QLocale::Language getLocaleLanguage(const QString &language)
if (language == QLatin1String("und") || language == QStringLiteral("mis"))
return QLocale::AnyLanguage;
- QLocale locale(language);
-
- if (locale == QLocale::c()) {
- qCWarning(lcaMetadata) << "Could not parse language:" << language
- << ". It is not a valid Unicode CLDR language code.";
- return QLocale::AnyLanguage;
- }
-
- return locale.language();
+ return QtMultimediaPrivate::fromIso639(language.toStdString().c_str());
}
QAndroidMetaData::QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber,
diff --git a/src/multimedia/platform/android/qandroidformatsinfo.cpp b/src/multimedia/platform/android/qandroidformatsinfo.cpp
index 584c7a122..e32479e28 100644
--- a/src/multimedia/platform/android/qandroidformatsinfo.cpp
+++ b/src/multimedia/platform/android/qandroidformatsinfo.cpp
@@ -108,7 +108,6 @@ QAndroidFormatInfo::QAndroidFormatInfo()
{
const QMediaFormat::AudioCodec aac = hasEncoder(QMediaFormat::AudioCodec::AAC);
const QMediaFormat::AudioCodec mp3 = hasEncoder(QMediaFormat::AudioCodec::MP3);
- const QMediaFormat::AudioCodec flac = hasEncoder(QMediaFormat::AudioCodec::FLAC);
const QMediaFormat::AudioCodec opus = hasEncoder(QMediaFormat::AudioCodec::Opus);
const QMediaFormat::AudioCodec vorbis = hasEncoder(QMediaFormat::AudioCodec::Vorbis);
@@ -123,11 +122,12 @@ QAndroidFormatInfo::QAndroidFormatInfo()
encoders = {
{ QMediaFormat::AAC, {aac}, {} },
{ QMediaFormat::MP3, {mp3}, {} },
- { QMediaFormat::FLAC, {flac}, {} },
- { QMediaFormat::Mpeg4Audio, {mp3, aac, flac, vorbis}, {} },
- { QMediaFormat::MPEG4, {mp3, aac, flac, vorbis}, {h264, h265, av1} },
- { QMediaFormat::Ogg, {opus, vorbis, flac}, {} },
- { QMediaFormat::Matroska, {mp3, opus, flac}, {vp8, vp9, h264, h265, av1} },
+ // FLAC encoder is not supported by the MediaRecorder used for recording
+ // { QMediaFormat::FLAC, {flac}, {} },
+ { QMediaFormat::Mpeg4Audio, {mp3, aac, vorbis}, {} },
+ { QMediaFormat::MPEG4, {mp3, aac, vorbis}, {h264, h265, av1} },
+ { QMediaFormat::Ogg, {opus, vorbis}, {} },
+ { QMediaFormat::Matroska, {mp3, opus}, {vp8, vp9, h264, h265, av1} },
// NOTE: WebM seems to be documented to supported with VP8 encoder,
// but the Camera API doesn't work with it, keep it commented for now.
// { QMediaFormat::WebM, {vorbis, opus}, {vp8, vp9} }
diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
index 80830565e..70a8e52ba 100644
--- a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
+++ b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
@@ -836,6 +836,12 @@ void AndroidCamera::getCameraInfo(int id, QCameraDevicePrivate *info)
default:
break;
}
+ // Add a number to allow correct access to cameras on systems with two
+ // (and more) front/back cameras
+ if (id > 1) {
+ info->id.append(QByteArray::number(id));
+ info->description.append(QString(" %1").arg(id));
+ }
}
QVideoFrameFormat::PixelFormat AndroidCamera::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
index 1378cfbeb..1bdc517d6 100644
--- a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
@@ -288,6 +288,21 @@ void AndroidMediaPlayer::unblockAudio()
mAudioBlocked = false;
}
+void AndroidMediaPlayer::startSoundStreaming(const int inputId, const int outputId)
+{
+ QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "startSoundStreaming",
+ "(II)V",
+ inputId,
+ outputId);
+}
+
+void AndroidMediaPlayer::stopSoundStreaming()
+{
+ QJniObject::callStaticMethod<void>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", "stopSoundStreaming");
+}
+
bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
{
if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
@@ -296,34 +311,7 @@ bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
return false;
}
- QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle",
- "()Landroid/media/MediaPlayer;");
- if (player.isValid()) {
- QJniObject playbackParams = player.callObjectMethod("getPlaybackParams",
- "()Landroid/media/PlaybackParams;");
- if (playbackParams.isValid()) {
- playbackParams.callObjectMethod("setSpeed", "(F)Landroid/media/PlaybackParams;",
- jfloat(rate));
- // pitch can only be > 0
- if (!qFuzzyIsNull(rate))
- playbackParams.callObjectMethod("setPitch", "(F)Landroid/media/PlaybackParams;",
- jfloat(qAbs(rate)));
-
- QJniEnvironment env;
- auto methodId = env->GetMethodID(player.objectClass(), "setPlaybackParams",
- "(Landroid/media/PlaybackParams;)V");
- env->CallVoidMethod(player.object(), methodId, playbackParams.object());
-
- if (env.checkAndClearExceptions()) {
- qWarning() << "Invalid playback rate" << rate;
- return false;
- } else {
- return true;
- }
- }
- }
-
- return false;
+ return mMediaPlayer.callMethod<jboolean>("setPlaybackRate", "(F)V", jfloat(rate));
}
void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture)
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
index d5cf07f9c..83fb212c7 100644
--- a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
@@ -136,6 +136,8 @@ public:
void setDataSource(const QNetworkRequest &request);
void prepareAsync();
void setVolume(int volume);
+ static void startSoundStreaming(const int inputId, const int outputId);
+ static void stopSoundStreaming();
bool setPlaybackRate(qreal rate);
void setDisplay(AndroidSurfaceTexture *surfaceTexture);
static bool setAudioOutput(const QByteArray &deviceId);