5#include <QtCore/qcoreapplication.h>
6#include <QtCore/qjniobject.h>
7#include <QtCore/qjnienvironment.h>
8#include <QtCore/private/qandroidextras_p.h>
9#include <qloggingcategory.h>
24using namespace Qt::Literals;
27 : m_format(AMediaFormat_new())
33 AMediaCodec_delete(m_codec);
38 AMediaExtractor_delete(m_extractor);
39 m_extractor =
nullptr;
43 AMediaFormat_delete(m_format);
53 const media_status_t err = AMediaCodec_stop(m_codec);
55 qCWarning(adLogger) <<
"stop() error: " << err;
58void Decoder::setSource(
const QUrl &source)
60 const QJniObject path = QJniObject::callStaticObjectMethod(
61 "org/qtproject/qt/android/multimedia/QtMultimediaUtils",
63 "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;",
64 QNativeInterface::QAndroidApplication::context().object(),
65 QJniObject::fromString(source.path()).object());
67 const QString mime = path.isValid() ? path.toString() : u""_s;
69 if (!mime.isEmpty() && !mime.contains(u"audio"_s, Qt::CaseInsensitive)) {
70 m_formatError = tr(
"Cannot set source, invalid mime type for the source provided.");
75 m_extractor = AMediaExtractor_new();
77 QFile file(source.path());
78 if (!file.open(QFile::ReadOnly)) {
79 emit error(QAudioDecoder::ResourceError, tr(
"Cannot open the file"));
83 const int fd = file.handle();
86 emit error(QAudioDecoder::ResourceError, tr(
"Invalid fileDescriptor for source."));
89 const int size = file.size();
90 media_status_t status = AMediaExtractor_setDataSourceFd(m_extractor, fd, 0,
91 size > 0 ? size : LONG_MAX);
94 if (status != AMEDIA_OK) {
96 AMediaExtractor_delete(m_extractor);
97 m_extractor =
nullptr;
99 m_formatError = tr(
"Setting source for Audio Decoder failed.");
103void Decoder::createDecoder()
106 m_format = AMediaExtractor_getTrackFormat(m_extractor, 0);
109 if (!AMediaFormat_getString(m_format, AMEDIAFORMAT_KEY_MIME, &mime)) {
111 AMediaExtractor_delete(m_extractor);
112 m_extractor =
nullptr;
114 emit error(QAudioDecoder::FormatError, tr(
"Format not supported by Audio Decoder."));
121 AMediaFormat_getInt64(m_format, AMEDIAFORMAT_KEY_DURATION, &durationUs);
122 emit durationChanged(durationUs / 1000);
125 if (!m_outputFormat.isValid()) {
127 AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
128 m_outputFormat.setSampleRate(sampleRate);
129 int32_t channelCount;
130 AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
131 m_outputFormat.setChannelCount(channelCount);
132 m_outputFormat.setSampleFormat(QAudioFormat::Int16);
135 m_codec = AMediaCodec_createDecoderByType(mime);
138void Decoder::doDecode()
140 if (!m_formatError.isEmpty()) {
141 emit error(QAudioDecoder::FormatError, m_formatError);
146 emit error(QAudioDecoder::ResourceError, tr(
"Cannot decode, source not set."));
153 emit error(QAudioDecoder::ResourceError, tr(
"Audio Decoder could not be created."));
157 media_status_t status = AMediaCodec_configure(m_codec, m_format,
nullptr ,
160 if (status != AMEDIA_OK) {
161 emit error(QAudioDecoder::ResourceError, tr(
"Audio Decoder failed configuration."));
165 status = AMediaCodec_start(m_codec);
166 if (status != AMEDIA_OK) {
167 emit error(QAudioDecoder::ResourceError, tr(
"Audio Decoder failed to start."));
171 AMediaExtractor_selectTrack(m_extractor, 0);
173 emit decodingChanged(
true);
175 while (!m_inputEOS) {
177 const ssize_t bufferIdx = AMediaCodec_dequeueInputBuffer(m_codec, dequeueTimeout);
179 if (bufferIdx >= 0) {
180 size_t bufferSize = {};
181 uint8_t *buffer = AMediaCodec_getInputBuffer(m_codec, bufferIdx, &bufferSize);
182 const int sample = AMediaExtractor_readSampleData(m_extractor, buffer, bufferSize);
188 const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(m_extractor);
189 AMediaCodec_queueInputBuffer(m_codec, bufferIdx, 0, sample, presentationTimeUs,
190 m_inputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
191 AMediaExtractor_advance(m_extractor);
194 AMediaCodecBufferInfo info;
195 ssize_t idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
197 while (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
198 || idx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
199 if (idx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
200 qCWarning(adLogger) <<
"dequeueOutputBuffer() status: outputFormat changed";
202 idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout);
206 if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)
211 const uint8_t *bufferData = AMediaCodec_getOutputBuffer(m_codec, idx,
213 const QByteArray data((
const char*)(bufferData + info.offset), info.size);
214 auto audioBuffer = QAudioBuffer(data, m_outputFormat, presentationTimeUs);
215 if (presentationTimeUs >= 0)
216 emit positionChanged(std::move(audioBuffer), presentationTimeUs / 1000);
218 AMediaCodec_releaseOutputBuffer(m_codec, idx,
false);
220 }
else if (idx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
221 qCWarning(adLogger) <<
"dequeueOutputBuffer() status: try again later";
225 "AMediaCodec_dequeueOutputBuffer() status: invalid buffer idx " << idx;
228 qCWarning(adLogger) <<
"dequeueInputBuffer() status: invalid buffer idx " << bufferIdx;
235 : QPlatformAudioDecoder(parent),
236 m_decoder(
new Decoder())
238 connect(m_decoder, &Decoder::positionChanged,
this, &QAndroidAudioDecoder::positionChanged);
239 connect(m_decoder, &Decoder::durationChanged,
this, &QAndroidAudioDecoder::durationChanged);
240 connect(m_decoder, &Decoder::error,
this, &QAndroidAudioDecoder::error);
242 connect(m_decoder, &Decoder::decodingChanged,
this, &QPlatformAudioDecoder::setIsDecoding);
243 connect(
this, &QAndroidAudioDecoder::setSourceUrl, m_decoder, & Decoder::setSource);
248 m_decoder->thread()->quit();
249 m_decoder->thread()->wait();
250 delete m_threadDecoder;
256 if (!requestPermissions())
263 error(QAudioDecoder::NoError, QStringLiteral(
""));
265 if (m_source != fileName) {
267 emit setSourceUrl(m_source);
278 if (m_device != device) {
281 if (!requestPermissions())
295 if (m_device && (!m_device->isOpen() || !m_device->isReadable())) {
296 emit error(QAudioDecoder::ResourceError,
297 QString::fromUtf8(
"Unable to read from the specified device"));
301 if (!m_threadDecoder) {
302 m_threadDecoder =
new QThread(
this);
303 m_decoder->moveToThread(m_threadDecoder);
304 m_threadDecoder->start();
312 if (!isDecoding() && m_position < 0 && m_duration < 0)
316 m_audioBuffer.clear();
319 setIsDecoding(
false);
321 emit bufferAvailableChanged(
false);
322 emit QPlatformAudioDecoder::positionChanged(m_position);
327 if (!m_audioBuffer.isEmpty()) {
328 std::pair<QAudioBuffer,
int> buffer = m_audioBuffer.takeFirst();
329 m_position = buffer.second;
330 emit QPlatformAudioDecoder::positionChanged(buffer.second);
340 return m_audioBuffer.size() > 0;
355 m_audioBuffer.append(std::pair<QAudioBuffer,
int>(audioBuffer, position));
356 m_position = position;
362 m_duration = duration;
363 emit QPlatformAudioDecoder::durationChanged(duration);
369 emit QPlatformAudioDecoder::error(err, errorString);
374 emit bufferAvailableChanged(m_audioBuffer.size() > 0);
376 if (m_duration != -1)
377 emit durationChanged(m_duration);
380 QFile(QString(QDir::tempPath()).append(QString::fromUtf8(tempFile))).remove();
381 emit QPlatformAudioDecoder::finished();
386 const auto writeRes = QtAndroidPrivate::requestPermission(QStringLiteral(
"android.permission.WRITE_EXTERNAL_STORAGE"));
387 if (writeRes.result() == QtAndroidPrivate::Authorized)
396 connect(m_device, &QIODevice::readyRead,
this, &QAndroidAudioDecoder::readDevice);
397 if (m_device->bytesAvailable())
400 QTimer::singleShot(0, m_decoder, &Decoder::doDecode);
406 QFile file = QFile(QDir::tempPath().append(QString::fromUtf8(tempFile)),
this);
408 bool success = file.open(QIODevice::QIODevice::ReadWrite);
410 emit error(QAudioDecoder::ResourceError, tr(
"Error opening temporary file: %1").arg(file.errorString()));
412 success &= (file.write(m_deviceBuffer) == m_deviceBuffer.size());
414 emit error(QAudioDecoder::ResourceError, tr(
"Error while writing data to temporary file"));
417 m_deviceBuffer.clear();
419 m_decoder->setSource(file.fileName());
425 m_deviceBuffer.append(m_device->readAll());
426 if (m_device->atEnd()) {
427 disconnect(m_device, &QIODevice::readyRead,
this, &QAndroidAudioDecoder::readDevice);
428 if (!createTempFile()) {
429 m_deviceBuffer.clear();
433 QTimer::singleShot(0, m_decoder, &Decoder::doDecode);
439#include "moc_qandroidaudiodecoder_p.cpp"
bool bufferAvailable() const override
virtual ~QAndroidAudioDecoder()
QAudioBuffer read() override
qint64 duration() const override
qint64 position() const override
void setSourceDevice(QIODevice *device) override
void setSource(const QUrl &fileName) override
static QT_BEGIN_NAMESPACE const char tempFile[]
constexpr int dequeueTimeout
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)