8#include <QReadWriteLock>
10#include <QtCore/qcoreapplication.h>
11#include <qloggingcategory.h>
15Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers)
16Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
25 QWriteLocker locker(rwLock);
26 auto context = QNativeInterface::QAndroidApplication::context();
27 const jlong id =
reinterpret_cast<jlong>(
this);
28 mMediaPlayer = QJniObject(QtAndroidMediaPlayerClassName,
29 "(Landroid/content/Context;J)V",
32 mediaPlayers->append(
this);
37 QWriteLocker locker(rwLock);
38 const int i = mediaPlayers->indexOf(
this);
40 mediaPlayers->remove(i);
45 mMediaPlayer.callMethod<
void>(
"release");
50 mMediaPlayer.callMethod<
void>(
"reset");
55 return mMediaPlayer.callMethod<jint>(
"getCurrentPosition");
60 return mMediaPlayer.callMethod<jint>(
"getDuration");
65 return mMediaPlayer.callMethod<jboolean>(
"isPlaying");
70 return mMediaPlayer.callMethod<jint>(
"getVolume");
75 return mMediaPlayer.callMethod<jboolean>(
"isMuted");
82 if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
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())
104 return mMediaPlayer.callObjectMethod(
"display",
"()Landroid/view/SurfaceHolder;").object();
109 const QLatin1String unknownMimeType(
"application/octet-stream");
110 const QLatin1String undefinedLanguage(
"und");
112 if (!androidTrackInfo.isValid())
117 auto methodId = env->GetMethodID(androidTrackInfo.objectClass(),
"getType",
"()I");
118 const jint type = env->CallIntMethod(androidTrackInfo.object(), methodId);
119 if (env.checkAndClearExceptions())
123 if (type < 0 || type > 5) {
130 auto languageObject = androidTrackInfo.callObjectMethod(
"getLanguage",
"()Ljava/lang/String;");
131 QString language = languageObject.isValid() ? languageObject.toString() : undefinedLanguage;
133 auto mimeTypeObject = androidTrackInfo.callObjectMethod(
"getMime",
"()Ljava/lang/String;");
134 QString mimeType = mimeTypeObject.isValid() ? mimeTypeObject.toString() : unknownMimeType;
136 return { streamNumber, trackType, language, mimeType };
141 auto androidTracksInfoObject = mMediaPlayer.callObjectMethod(
143 "()[Lorg/qtproject/qt/android/multimedia/QtAndroidMediaPlayer$TrackInfo;");
145 if (!androidTracksInfoObject.isValid())
148 auto androidTracksInfo = androidTracksInfoObject.object<jobjectArray>();
149 if (!androidTracksInfo)
152 QJniEnvironment environment;
153 auto numberofTracks = environment->GetArrayLength(androidTracksInfo);
155 QList<AndroidMediaPlayer::TrackInfo> tracksInformation;
157 for (
int index = 0; index < numberofTracks; index++) {
158 auto androidTrackInformation = environment->GetObjectArrayElement(androidTracksInfo, index);
160 if (environment.checkAndClearExceptions()) {
164 auto trackInfo = convertTrackInfo(index, androidTrackInformation);
165 tracksInformation.insert(index, trackInfo);
167 environment->DeleteLocalRef(androidTrackInformation);
170 return tracksInformation;
175 int type =
static_cast<
int>(androidTrackType);
176 return mMediaPlayer.callMethod<jint>(
"getSelectedTrack",
"(I)I", type);
181 mMediaPlayer.callMethod<
void>(
"deselectTrack",
"(I)V", trackNumber);
186 mMediaPlayer.callMethod<
void>(
"selectTrack",
"(I)V", trackNumber);
191 mMediaPlayer.callMethod<
void>(
"start");
196 mMediaPlayer.callMethod<
void>(
"pause");
201 mMediaPlayer.callMethod<
void>(
"stop");
206 mMediaPlayer.callMethod<
void>(
"seekTo",
"(I)V", jint(msec));
214 mMediaPlayer.callMethod<
void>(
"mute",
"(Z)V", jboolean(mute));
219 QJniObject string = QJniObject::fromString(request.url().toString(QUrl::FullyEncoded));
221 mMediaPlayer.callMethod<
void>(
"initHeaders",
"()V");
222 for (
auto &header : request.rawHeaderList()) {
223 auto value = request.rawHeader(header);
224 mMediaPlayer.callMethod<
void>(
"setHeader",
"(Ljava/lang/String;Ljava/lang/String;)V",
225 QJniObject::fromString(QLatin1String(header)).object(),
226 QJniObject::fromString(QLatin1String(value)).object());
229 mMediaPlayer.callMethod<
void>(
"setDataSource",
"(Ljava/lang/String;)V", string.object());
234 mMediaPlayer.callMethod<
void>(
"prepareAsync");
242 mMediaPlayer.callMethod<
void>(
"setVolume",
"(I)V", jint(volume));
247 mAudioBlocked =
true;
252 mAudioBlocked =
false;
257 QJniObject::callStaticMethod<
void>(
"org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
258 "startSoundStreaming",
265 QJniObject::callStaticMethod<
void>(
266 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
"stopSoundStreaming");
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";
277 return mMediaPlayer.callMethod<jboolean>(
"setPlaybackRate", jfloat(rate));
282 mMediaPlayer.callMethod<
void>(
"setDisplay",
283 "(Landroid/view/SurfaceHolder;)V",
284 surfaceTexture ? surfaceTexture->surfaceHolder() : 0);
289 const bool ret = QJniObject::callStaticMethod<jboolean>(
290 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
291 "prepareAudioOutput",
296 qCWarning(lcAudio) <<
"Output device not set";
302void AndroidMediaPlayer::setAudioRole(QAudio::Role role)
306 case QAudio::MusicRole:
307 r = QLatin1String(
"CONTENT_TYPE_MUSIC");
309 case QAudio::VideoRole:
310 r = QLatin1String(
"CONTENT_TYPE_MOVIE");
312 case QAudio::VoiceCommunicationRole:
313 r = QLatin1String(
"USAGE_VOICE_COMMUNICATION");
315 case QAudio::AlarmRole:
316 r = QLatin1String(
"USAGE_ALARM");
318 case QAudio::NotificationRole:
319 r = QLatin1String(
"USAGE_NOTIFICATION");
321 case QAudio::RingtoneRole:
322 r = QLatin1String(
"USAGE_NOTIFICATION_RINGTONE");
324 case QAudio::AccessibilityRole:
325 r = QLatin1String(
"USAGE_ASSISTANCE_ACCESSIBILITY");
327 case QAudio::SonificationRole:
328 r = QLatin1String(
"CONTENT_TYPE_SONIFICATION");
330 case QAudio::GameRole:
331 r = QLatin1String(
"USAGE_GAME");
340 if (r == QLatin1String(
"CONTENT_TYPE_MOVIE"))
342 else if (r == QLatin1String(
"CONTENT_TYPE_MUSIC"))
344 else if (r == QLatin1String(
"CONTENT_TYPE_SONIFICATION"))
346 else if (r == QLatin1String(
"CONTENT_TYPE_SPEECH"))
348 else if (r == QLatin1String(
"USAGE_ALARM"))
350 else if (r == QLatin1String(
"USAGE_ASSISTANCE_ACCESSIBILITY"))
352 else if (r == QLatin1String(
"USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"))
354 else if (r == QLatin1String(
"USAGE_ASSISTANCE_SONIFICATION"))
356 else if (r == QLatin1String(
"USAGE_ASSISTANT"))
358 else if (r == QLatin1String(
"USAGE_GAME"))
360 else if (r == QLatin1String(
"USAGE_MEDIA"))
362 else if (r == QLatin1String(
"USAGE_NOTIFICATION"))
364 else if (r == QLatin1String(
"USAGE_NOTIFICATION_COMMUNICATION_DELAYED"))
366 else if (r == QLatin1String(
"USAGE_NOTIFICATION_COMMUNICATION_INSTANT"))
368 else if (r == QLatin1String(
"USAGE_NOTIFICATION_COMMUNICATION_REQUEST"))
370 else if (r == QLatin1String(
"USAGE_NOTIFICATION_EVENT"))
372 else if (r == QLatin1String(
"USAGE_NOTIFICATION_RINGTONE"))
374 else if (r == QLatin1String(
"USAGE_VOICE_COMMUNICATION"))
376 else if (r == QLatin1String(
"USAGE_VOICE_COMMUNICATION_SIGNALLING"))
379 mMediaPlayer.callMethod<
void>(
"setAudioAttributes",
"(II)V", jint(type), jint(usage));
383static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
387 QReadLocker locker(rwLock);
388 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
389 if (Q_UNLIKELY(i == -1))
392 Q_EMIT (*mediaPlayers)[i]->error(what, extra);
399 QReadLocker locker(rwLock);
400 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
401 if (Q_UNLIKELY(i == -1))
404 Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
411 QReadLocker locker(rwLock);
412 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
413 if (Q_UNLIKELY(i == -1))
416 Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
423 QReadLocker locker(rwLock);
424 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
425 if (Q_UNLIKELY(i == -1))
428 Q_EMIT (*mediaPlayers)[i]->durationChanged(duration);
431static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
435 QReadLocker locker(rwLock);
436 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
437 if (Q_UNLIKELY(i == -1))
440 Q_EMIT (*mediaPlayers)[i]->info(what, extra);
447 QReadLocker locker(rwLock);
448 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
449 if (Q_UNLIKELY(i == -1))
452 Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
463 QReadLocker locker(rwLock);
464 const int i = mediaPlayers->indexOf(
reinterpret_cast<AndroidMediaPlayer *>(id));
465 if (Q_UNLIKELY(i == -1))
468 Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
474 if (!mediaplayer || !mediaPlayers->contains(mediaplayer))
485 QReadLocker locker(rwLock);
486 auto mediaplayer = getMediaPlayer(ptr);
490 emit mediaplayer->tracksInfoChanged();
500 QReadLocker locker(rwLock);
502 auto mediaplayer = getMediaPlayer(ptr);
506 QString subtitleText;
507 if (timedText !=
nullptr)
508 subtitleText = QString::fromUtf8(env->GetStringUTFChars(timedText, 0));
510 emit mediaplayer->timedTextChanged(subtitleText);
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) }
529 const int size = std::size(methods);
530 return QJniEnvironment().registerNativeMethods(QtAndroidMediaPlayerClassName, methods, size);
535#include "moc_androidmediaplayer_p.cpp"
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)