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
androidmediaplayer.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
6
7#include <QList>
8#include <QReadWriteLock>
9#include <QString>
10#include <QtCore/qcoreapplication.h>
11#include <qloggingcategory.h>
12
13static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer";
14typedef QList<AndroidMediaPlayer *> MediaPlayerList;
17
19
20static Q_LOGGING_CATEGORY(lcAudio, "qt.multimedia.audio")
21
23 : QObject()
24{
25 QWriteLocker locker(rwLock);
26 auto context = QNativeInterface::QAndroidApplication::context();
27 const jlong id = reinterpret_cast<jlong>(this);
29 "(Landroid/content/Context;J)V",
30 context.object(),
31 id);
32 mediaPlayers->append(this);
33}
34
36{
37 QWriteLocker locker(rwLock);
38 const int i = mediaPlayers->indexOf(this);
39 Q_ASSERT(i != -1);
40 mediaPlayers->remove(i);
41}
42
44{
45 mMediaPlayer.callMethod<void>("release");
46}
47
49{
50 mMediaPlayer.callMethod<void>("reset");
51}
52
54{
55 return mMediaPlayer.callMethod<jint>("getCurrentPosition");
56}
57
59{
60 return mMediaPlayer.callMethod<jint>("getDuration");
61}
62
64{
65 return mMediaPlayer.callMethod<jboolean>("isPlaying");
66}
67
69{
70 return mMediaPlayer.callMethod<jint>("getVolume");
71}
72
74{
75 return mMediaPlayer.callMethod<jboolean>("isMuted");
76}
77
79{
80 qreal rate(1.0);
81
82 if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
83 return rate;
84
85 QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle",
86 "()Landroid/media/MediaPlayer;");
87 if (player.isValid()) {
88 QJniObject playbackParams = player.callObjectMethod("getPlaybackParams",
89 "()Landroid/media/PlaybackParams;");
90 if (playbackParams.isValid()) {
92 auto methodId = env->GetMethodID(playbackParams.objectClass(), "getSpeed", "()F");
93 const qreal speed = env->CallFloatMethod(playbackParams.object(), methodId);
94 if (!env.checkAndClearExceptions())
95 rate = speed;
96 }
97 }
98
99 return rate;
100}
101
103{
104 return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object();
105}
106
108{
109 const QLatin1String unknownMimeType("application/octet-stream");
110 const QLatin1String undefinedLanguage("und");
111
112 if (!androidTrackInfo.isValid())
113 return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
114 unknownMimeType };
115
116 QJniEnvironment env;
117 auto methodId = env->GetMethodID(androidTrackInfo.objectClass(), "getType", "()I");
118 const jint type = env->CallIntMethod(androidTrackInfo.object(), methodId);
119 if (env.checkAndClearExceptions())
120 return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
121 unknownMimeType };
122
123 if (type < 0 || type > 5) {
124 return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage,
125 unknownMimeType };
126 }
127
129
130 auto languageObject = androidTrackInfo.callObjectMethod("getLanguage", "()Ljava/lang/String;");
131 QString language = languageObject.isValid() ? languageObject.toString() : undefinedLanguage;
132
133 auto mimeTypeObject = androidTrackInfo.callObjectMethod("getMime", "()Ljava/lang/String;");
134 QString mimeType = mimeTypeObject.isValid() ? mimeTypeObject.toString() : unknownMimeType;
135
136 return { streamNumber, trackType, language, mimeType };
137}
138
139QList<AndroidMediaPlayer::TrackInfo> AndroidMediaPlayer::tracksInfo()
140{
141 auto androidTracksInfoObject = mMediaPlayer.callObjectMethod(
142 "getAllTrackInfo",
143 "()[Lorg/qtproject/qt/android/multimedia/QtAndroidMediaPlayer$TrackInfo;");
144
145 if (!androidTracksInfoObject.isValid())
146 return QList<AndroidMediaPlayer::TrackInfo>();
147
148 auto androidTracksInfo = androidTracksInfoObject.object<jobjectArray>();
149 if (!androidTracksInfo)
150 return QList<AndroidMediaPlayer::TrackInfo>();
151
152 QJniEnvironment environment;
153 auto numberofTracks = environment->GetArrayLength(androidTracksInfo);
154
155 QList<AndroidMediaPlayer::TrackInfo> tracksInformation;
156
157 for (int index = 0; index < numberofTracks; index++) {
158 auto androidTrackInformation = environment->GetObjectArrayElement(androidTracksInfo, index);
159
160 if (environment.checkAndClearExceptions()) {
161 continue;
162 }
163
164 auto trackInfo = convertTrackInfo(index, androidTrackInformation);
165 tracksInformation.insert(index, trackInfo);
166
167 environment->DeleteLocalRef(androidTrackInformation);
168 }
169
170 return tracksInformation;
171}
172
174{
175 int type = static_cast<int>(androidTrackType);
176 return mMediaPlayer.callMethod<jint>("getSelectedTrack", "(I)I", type);
177}
178
180{
181 mMediaPlayer.callMethod<void>("deselectTrack", "(I)V", trackNumber);
182}
183
185{
186 mMediaPlayer.callMethod<void>("selectTrack", "(I)V", trackNumber);
187}
188
190{
191 mMediaPlayer.callMethod<void>("start");
192}
193
195{
196 mMediaPlayer.callMethod<void>("pause");
197}
198
200{
201 mMediaPlayer.callMethod<void>("stop");
202}
203
205{
206 mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec));
207}
208
210{
211 if (mAudioBlocked)
212 return;
213
214 mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute));
215}
216
218{
219 QJniObject string = QJniObject::fromString(request.url().toString(QUrl::FullyEncoded));
220
221 mMediaPlayer.callMethod<void>("initHeaders", "()V");
222 for (auto &header : request.rawHeaderList()) {
224 mMediaPlayer.callMethod<void>("setHeader", "(Ljava/lang/String;Ljava/lang/String;)V",
225 QJniObject::fromString(QLatin1String(header)).object(),
226 QJniObject::fromString(QLatin1String(value)).object());
227 }
228
229 mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object());
230}
231
233{
234 mMediaPlayer.callMethod<void>("prepareAsync");
235}
236
238{
239 if (mAudioBlocked)
240 return;
241
242 mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume));
243}
244
246{
247 mAudioBlocked = true;
248}
249
251{
252 mAudioBlocked = false;
253}
254
255void AndroidMediaPlayer::startSoundStreaming(const int inputId, const int outputId)
256{
257 QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
258 "startSoundStreaming",
259 inputId,
260 outputId);
261}
262
264{
265 QJniObject::callStaticMethod<void>(
266 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", "stopSoundStreaming");
267}
268
270{
271 if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
272 qWarning() << "Setting the playback rate on a media player requires"
273 << "Android 6.0 (API level 23) or later";
274 return false;
275 }
276
277 return mMediaPlayer.callMethod<jboolean>("setPlaybackRate", jfloat(rate));
278}
279
281{
282 mMediaPlayer.callMethod<void>("setDisplay",
283 "(Landroid/view/SurfaceHolder;)V",
284 surfaceTexture ? surfaceTexture->surfaceHolder() : 0);
285}
286
288{
289 const bool ret = QJniObject::callStaticMethod<jboolean>(
290 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
291 "setAudioOutput",
292 "(I)Z",
293 deviceId.toInt());
294
295 if (!ret)
296 qCWarning(lcAudio) << "Output device not set";
297
298 return ret;
299}
300
301#if 0
302void AndroidMediaPlayer::setAudioRole(QAudio::Role role)
303{
304 QString r;
305 switch (role) {
306 case QAudio::MusicRole:
307 r = QLatin1String("CONTENT_TYPE_MUSIC");
308 break;
309 case QAudio::VideoRole:
310 r = QLatin1String("CONTENT_TYPE_MOVIE");
311 break;
312 case QAudio::VoiceCommunicationRole:
313 r = QLatin1String("USAGE_VOICE_COMMUNICATION");
314 break;
315 case QAudio::AlarmRole:
316 r = QLatin1String("USAGE_ALARM");
317 break;
318 case QAudio::NotificationRole:
319 r = QLatin1String("USAGE_NOTIFICATION");
320 break;
321 case QAudio::RingtoneRole:
322 r = QLatin1String("USAGE_NOTIFICATION_RINGTONE");
323 break;
324 case QAudio::AccessibilityRole:
325 r = QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY");
326 break;
327 case QAudio::SonificationRole:
328 r = QLatin1String("CONTENT_TYPE_SONIFICATION");
329 break;
330 case QAudio::GameRole:
331 r = QLatin1String("USAGE_GAME");
332 break;
333 default:
334 return;
335 }
336
337 int type = 0; // CONTENT_TYPE_UNKNOWN
338 int usage = 0; // USAGE_UNKNOWN
339
340 if (r == QLatin1String("CONTENT_TYPE_MOVIE"))
341 type = 3;
342 else if (r == QLatin1String("CONTENT_TYPE_MUSIC"))
343 type = 2;
344 else if (r == QLatin1String("CONTENT_TYPE_SONIFICATION"))
345 type = 4;
346 else if (r == QLatin1String("CONTENT_TYPE_SPEECH"))
347 type = 1;
348 else if (r == QLatin1String("USAGE_ALARM"))
349 usage = 4;
350 else if (r == QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY"))
351 usage = 11;
352 else if (r == QLatin1String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"))
353 usage = 12;
354 else if (r == QLatin1String("USAGE_ASSISTANCE_SONIFICATION"))
355 usage = 13;
356 else if (r == QLatin1String("USAGE_ASSISTANT"))
357 usage = 16;
358 else if (r == QLatin1String("USAGE_GAME"))
359 usage = 14;
360 else if (r == QLatin1String("USAGE_MEDIA"))
361 usage = 1;
362 else if (r == QLatin1String("USAGE_NOTIFICATION"))
363 usage = 5;
364 else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"))
365 usage = 9;
366 else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"))
367 usage = 8;
368 else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"))
369 usage = 7;
370 else if (r == QLatin1String("USAGE_NOTIFICATION_EVENT"))
371 usage = 10;
372 else if (r == QLatin1String("USAGE_NOTIFICATION_RINGTONE"))
373 usage = 6;
374 else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION"))
375 usage = 2;
376 else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION_SIGNALLING"))
377 usage = 3;
378
379 mMediaPlayer.callMethod<void>("setAudioAttributes", "(II)V", jint(type), jint(usage));
380}
381#endif
382
383static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
384{
385 Q_UNUSED(env);
386 Q_UNUSED(thiz);
387 QReadLocker locker(rwLock);
388 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
389 if (Q_UNLIKELY(i == -1))
390 return;
391
392 Q_EMIT (*mediaPlayers)[i]->error(what, extra);
393}
394
395static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
396{
397 Q_UNUSED(env);
398 Q_UNUSED(thiz);
399 QReadLocker locker(rwLock);
400 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
401 if (Q_UNLIKELY(i == -1))
402 return;
403
404 Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
405}
406
407static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
408{
409 Q_UNUSED(env);
410 Q_UNUSED(thiz);
411 QReadLocker locker(rwLock);
412 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
413 if (Q_UNLIKELY(i == -1))
414 return;
415
416 Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
417}
418
419static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
420{
421 Q_UNUSED(env);
422 Q_UNUSED(thiz);
423 QReadLocker locker(rwLock);
424 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
425 if (Q_UNLIKELY(i == -1))
426 return;
427
428 Q_EMIT (*mediaPlayers)[i]->durationChanged(duration);
429}
430
431static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
432{
433 Q_UNUSED(env);
434 Q_UNUSED(thiz);
435 QReadLocker locker(rwLock);
436 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
437 if (Q_UNLIKELY(i == -1))
438 return;
439
440 Q_EMIT (*mediaPlayers)[i]->info(what, extra);
441}
442
443static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
444{
445 Q_UNUSED(env);
446 Q_UNUSED(thiz);
447 QReadLocker locker(rwLock);
448 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
449 if (Q_UNLIKELY(i == -1))
450 return;
451
452 Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
453}
454
455static void onVideoSizeChangedNative(JNIEnv *env,
456 jobject thiz,
457 jint width,
458 jint height,
459 jlong id)
460{
461 Q_UNUSED(env);
462 Q_UNUSED(thiz);
463 QReadLocker locker(rwLock);
464 const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
465 if (Q_UNLIKELY(i == -1))
466 return;
467
468 Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
469}
470
472{
473 auto mediaplayer = reinterpret_cast<AndroidMediaPlayer *>(ptr);
474 if (!mediaplayer || !mediaPlayers->contains(mediaplayer))
475 return nullptr;
476
477 return mediaplayer;
478}
479
480static void onTrackInfoChangedNative(JNIEnv *env, jobject thiz, jlong ptr)
481{
482 Q_UNUSED(env);
483 Q_UNUSED(thiz);
484
485 QReadLocker locker(rwLock);
486 auto mediaplayer = getMediaPlayer(ptr);
487 if (!mediaplayer)
488 return;
489
490 emit mediaplayer->tracksInfoChanged();
491}
492
493static void onTimedTextChangedNative(JNIEnv *env, jobject thiz, jstring timedText, jint time,
494 jlong ptr)
495{
496 Q_UNUSED(env);
497 Q_UNUSED(thiz);
498 Q_UNUSED(time);
499
500 QReadLocker locker(rwLock);
501
502 auto mediaplayer = getMediaPlayer(ptr);
503 if (!mediaplayer)
504 return;
505
506 QString subtitleText;
507 if (timedText != nullptr)
508 subtitleText = QString::fromUtf8(env->GetStringUTFChars(timedText, 0));
509
510 emit mediaplayer->timedTextChanged(subtitleText);
511}
512
514{
515 static const JNINativeMethod methods[] = {
516 { "onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative) },
517 { "onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative) },
518 { "onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative) },
519 { "onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative) },
520 { "onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative) },
521 { "onVideoSizeChangedNative", "(IIJ)V",
522 reinterpret_cast<void *>(onVideoSizeChangedNative) },
523 { "onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative) },
524 { "onTrackInfoChangedNative", "(J)V", reinterpret_cast<void *>(onTrackInfoChangedNative) },
525 { "onTimedTextChangedNative", "(Ljava/lang/String;IJ)V",
526 reinterpret_cast<void *>(onTimedTextChangedNative) }
527 };
528
529 const int size = std::size(methods);
530 return QJniEnvironment().registerNativeMethods(QtAndroidMediaPlayerClassName, methods, size);
531}
532
534
535#include "moc_androidmediaplayer_p.cpp"
static JNINativeMethod methods[]
static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
static void onTrackInfoChangedNative(JNIEnv *env, jobject thiz, jlong ptr)
static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
static void onVideoSizeChangedNative(JNIEnv *env, jobject thiz, jint width, jint height, jlong id)
static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
static const char QtAndroidMediaPlayerClassName[]
AndroidMediaPlayer::TrackInfo convertTrackInfo(int streamNumber, QJniObject androidTrackInfo)
static AndroidMediaPlayer * getMediaPlayer(jlong ptr)
static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
QList< AndroidMediaPlayer * > MediaPlayerList
static void onTimedTextChangedNative(JNIEnv *env, jobject thiz, jstring timedText, jint time, jlong ptr)
QMediaPlayer player
Definition audio.cpp:213
void seekTo(qint32 msec)
void deselectTrack(int trackNumber)
static bool registerNativeMethods()
void selectTrack(int trackNumber)
QList< TrackInfo > tracksInfo()
void setDataSource(const QNetworkRequest &request)
static void stopSoundStreaming()
void setDisplay(AndroidSurfaceTexture *surfaceTexture)
static bool setAudioOutput(const QByteArray &deviceId)
bool setPlaybackRate(qreal rate)
int activeTrack(TrackType trackType)
static void startSoundStreaming(const int inputId, const int outputId)
void setVolume(int volume)
\inmodule QtCore
Definition qbytearray.h:57
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
\inmodule QtCore
\inmodule QtCore
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
QByteArray rawHeader(QAnyStringView headerName) const
Returns the raw form of header headerName.
QList< QByteArray > rawHeaderList() const
Returns a list of all raw headers that are set in this network request.
QUrl url() const
Returns the URL this network request is referring to.
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
@ FullyEncoded
Definition qurl.h:129
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
\inmodule QtCore
else opt state
[0]
Combined button and popup list for selecting options.
static void * context
#define Q_UNLIKELY(x)
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * mimeType
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
return ret
static ControlElement< T > * ptr(QWidget *widget)
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLint GLsizei width
GLenum type
GLuint GLenum * rate
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_EMIT
#define emit
#define Q_UNUSED(x)
int qint32
Definition qtypes.h:49
double qreal
Definition qtypes.h:187
QNetworkRequest request(url)