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
QtAndroidMediaPlayer.java
Go to the documentation of this file.
1// Copyright (C) 2016 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
4package org.qtproject.qt.android.multimedia;
5
6import java.io.IOException;
7import java.lang.String;
8import java.util.HashMap;
9import java.io.FileInputStream;
10
11// API is level is < 9 unless marked otherwise.
12import android.content.Context;
13import android.media.MediaPlayer;
14import android.media.MediaFormat;
15import android.media.PlaybackParams;
16import android.media.AudioAttributes;
17import android.media.TimedText;
18import android.net.Uri;
19import android.util.Log;
20import java.io.FileDescriptor;
21import android.content.res.AssetFileDescriptor;
22import android.content.res.AssetManager;
23import android.view.SurfaceHolder;
24
25import java.util.concurrent.Executors;
26import java.util.concurrent.ScheduledExecutorService;
27import java.util.concurrent.ScheduledFuture;
28import java.util.concurrent.TimeUnit;
29
31{
32 // Native callback functions for MediaPlayer
33 native public void onErrorNative(int what, int extra, long id);
34 native public void onBufferingUpdateNative(int percent, long id);
35 native public void onProgressUpdateNative(int progress, long id);
36 native public void onDurationChangedNative(int duration, long id);
37 native public void onInfoNative(int what, int extra, long id);
38 native public void onVideoSizeChangedNative(int width, int height, long id);
39 native public void onStateChangedNative(int state, long id);
40
41 native public void onTrackInfoChangedNative(long id);
42 native public void onTimedTextChangedNative(String text, int time, long id);
43
44 private MediaPlayer mMediaPlayer = null;
45 private AudioAttributes mAudioAttributes = null;
46 private HashMap<String, String> mHeaders = null;
47 private Uri mUri = null;
48 private final long mID;
49 private final Context mContext;
50 private boolean mMuted = false;
51 private int mVolume = 100;
52 private static final String TAG = "Qt MediaPlayer";
53 private SurfaceHolder mSurfaceHolder = null;
54 private ScheduledExecutorService mProgressScheduler = null;
55 private ScheduledFuture mProgressWatcherHandle = null;
56
57 private class State {
58 final static int Uninitialized = 0x1 /* End */;
59 final static int Idle = 0x2;
60 final static int Preparing = 0x4;
61 final static int Prepared = 0x8;
62 final static int Initialized = 0x10;
63 final static int Started = 0x20;
64 final static int Stopped = 0x40;
65 final static int Paused = 0x80;
66 final static int PlaybackCompleted = 0x100;
67 final static int Error = 0x200;
68 }
69
70 public class TrackInfo
71 {
72 private int type;
73 private String mime, language;
74
75 TrackInfo(int type, String mime, String language)
76 {
77 this.type = type;
78 this.mime = mime;
79 this.language = language;
80 }
81
82 int getType() { return this.type; }
83 String getMime() { return this.mime; }
84 String getLanguage() { return this.language; }
85 }
86
87 private volatile int mState = State.Uninitialized;
88
92 private class MediaPlayerErrorListener
93 implements MediaPlayer.OnErrorListener
94 {
95 @Override
96 public boolean onError(final MediaPlayer mp,
97 final int what,
98 final int extra)
99 {
100 setState(State.Error);
101 onErrorNative(what, extra, mID);
102 return true;
103 }
104 }
105
109 private class MediaPlayerBufferingListener
110 implements MediaPlayer.OnBufferingUpdateListener
111 {
112 private int mBufferPercent = -1;
113 @Override
114 public void onBufferingUpdate(final android.media.MediaPlayer mp,
115 final int percent)
116 {
117 // Avoid updates when percent is unchanged.
118 // E.g., we keep getting updates when percent == 100
119 if (mBufferPercent == percent)
120 return;
121
122 onBufferingUpdateNative((mBufferPercent = percent), mID);
123 }
124
125 }
126
130 private class MediaPlayerCompletionListener
131 implements MediaPlayer.OnCompletionListener
132 {
133 @Override
134 public void onCompletion(final MediaPlayer mp)
135 {
136 setState(State.PlaybackCompleted);
137 }
138
139 }
140
144 private class MediaPlayerInfoListener
145 implements MediaPlayer.OnInfoListener
146 {
147 @Override
148 public boolean onInfo(final MediaPlayer mp,
149 final int what,
150 final int extra)
151 {
152 onInfoNative(what, extra, mID);
153 return true;
154 }
155
156 }
157
161 private class MediaPlayerPreparedListener
162 implements MediaPlayer.OnPreparedListener
163 {
164
165 @Override
166 public void onPrepared(final MediaPlayer mp)
167 {
168 setState(State.Prepared);
171 }
172
173 }
174
178 private class MediaPlayerSeekCompleteListener
179 implements MediaPlayer.OnSeekCompleteListener
180 {
181
182 @Override
183 public void onSeekComplete(final MediaPlayer mp)
184 {
186 }
187
188 }
189
193 private class MediaPlayerVideoSizeChangedListener
194 implements MediaPlayer.OnVideoSizeChangedListener
195 {
196
197 @Override
198 public void onVideoSizeChanged(final MediaPlayer mp,
199 final int width,
200 final int height)
201 {
203 }
204
205 }
206
207 private class MediaPlayerTimedTextListener implements MediaPlayer.OnTimedTextListener
208 {
209 @Override public void onTimedText(MediaPlayer mp, TimedText text)
210 {
211 onTimedTextChangedNative(text.getText(), mp.getCurrentPosition(), mID);
212 }
213 }
214
215 public QtAndroidMediaPlayer(final Context context, final long id)
216 {
217 mID = id;
218 mContext = context;
219 }
220
221 public MediaPlayer getMediaPlayerHandle()
222 {
223 return mMediaPlayer;
224 }
225
226 private void setState(int state)
227 {
228 if (mState == state)
229 return;
230
231 mState = state;
232
233 onStateChangedNative(mState, mID);
234 }
235
236 private void init()
237 {
238 if (mMediaPlayer != null)
239 return;
240
241 mMediaPlayer = new MediaPlayer();
242 setState(State.Idle);
243 // Make sure the new media player has the volume that was set on the QMediaPlayer
244 setVolumeHelper(mMuted ? 0 : mVolume);
245 setAudioAttributes(mMediaPlayer, mAudioAttributes);
246
247 mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayerBufferingListener());
248 mMediaPlayer.setOnCompletionListener(new MediaPlayerCompletionListener());
249 mMediaPlayer.setOnInfoListener(new MediaPlayerInfoListener());
250 mMediaPlayer.setOnSeekCompleteListener(new MediaPlayerSeekCompleteListener());
251 mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayerVideoSizeChangedListener());
252 mMediaPlayer.setOnErrorListener(new MediaPlayerErrorListener());
253 mMediaPlayer.setOnPreparedListener(new MediaPlayerPreparedListener());
254 mMediaPlayer.setOnTimedTextListener(new MediaPlayerTimedTextListener());
255 // Report playback position since there is no default listner for that in MediaPlayer
256 mProgressScheduler = Executors.newScheduledThreadPool(1);
257 }
258
260 {
261 // if it was shutdown, request new thread
262 if (mProgressScheduler.isTerminated() || mProgressScheduler == null)
263 mProgressScheduler = Executors.newScheduledThreadPool(1);
264
265 mProgressWatcherHandle = mProgressScheduler.scheduleAtFixedRate(new Runnable() {
266 @Override
267 public void run() {
268 if (isPlaying())
270 }
271 }, 10, 100, TimeUnit.MILLISECONDS);
272 }
273
275 {
276 if (mProgressScheduler != null)
277 mProgressScheduler.shutdown();
278 }
279
280 public void start()
281 {
282 if ((mState & (State.Prepared
283 | State.Started
284 | State.Paused
285 | State.PlaybackCompleted)) == 0) {
286 return;
287 }
288
289 try {
290 mMediaPlayer.start();
291 setState(State.Started);
293 } catch (final IllegalStateException exception) {
294 Log.w(TAG, exception);
295 }
296 }
297
298 public void pause()
299 {
300 if ((mState & (State.Started | State.Paused | State.PlaybackCompleted)) == 0)
301 return;
302
303 try {
304 mMediaPlayer.pause();
305 setState(State.Paused);
306 } catch (final IllegalStateException exception) {
307 Log.w(TAG, exception);
308 }
309 }
310
311
312 public void stop()
313 {
314 if ((mState & (State.Prepared
315 | State.Started
316 | State.Stopped
317 | State.Paused
318 | State.PlaybackCompleted)) == 0) {
319 return;
320 }
321
322 try {
323 mMediaPlayer.stop();
324 setState(State.Stopped);
326 } catch (final IllegalStateException exception) {
327 Log.w(TAG, exception);
328 }
329 }
330
331
332 public void seekTo(final int msec)
333 {
334 if ((mState & (State.Prepared
335 | State.Started
336 | State.Paused
337 | State.PlaybackCompleted)) == 0) {
338 return;
339 }
340
341 try {
342 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
343 // seekTo to closest frame of the provided msec is only available for devices
344 // with api level over 26
345 mMediaPlayer.seekTo(msec, MediaPlayer.SEEK_CLOSEST);
346 } else {
347 mMediaPlayer.seekTo(msec);
348 }
349 } catch (final IllegalStateException exception) {
350 Log.w(TAG, exception);
351 }
352 }
353
354 public boolean isPlaying()
355 {
356 boolean playing = false;
357 if ((mState & (State.Idle
358 | State.Initialized
359 | State.Prepared
360 | State.Started
361 | State.Paused
362 | State.Stopped
363 | State.PlaybackCompleted)) == 0) {
364 return playing;
365 }
366
367 try {
368 playing = mMediaPlayer.isPlaying();
369 } catch (final IllegalStateException exception) {
370 Log.w(TAG, exception);
371 }
372
373 return playing;
374 }
375
376 public void prepareAsync()
377 {
378 if ((mState & (State.Initialized | State.Stopped)) == 0)
379 return;
380
381 try {
382 mMediaPlayer.prepareAsync();
383 setState(State.Preparing);
384 } catch (final IllegalStateException exception) {
385 Log.w(TAG, exception);
386 }
387 }
388
389 public void initHeaders()
390 {
391 mHeaders = new HashMap<String, String>();
392 }
393
394 public void setHeader(final String header, final String value)
395 {
396 mHeaders.put(header, value);
397 }
398
399 public void setDataSource(final String path)
400 {
401 if (mState == State.Uninitialized)
402 init();
403
404 if (mState != State.Idle)
405 reset();
406
407 // mediaplayer can only setDataSource if it is on State.Idle
408 if (mState != State.Idle) {
409 Log.w(TAG, "Trying to set data source of a media player that is not idle!");
410 return;
411 }
412
413 if (mSurfaceHolder != null)
414 mMediaPlayer.setDisplay(mSurfaceHolder);
415
416 AssetFileDescriptor afd = null;
417 FileInputStream fis = null;
418 try {
419 mUri = Uri.parse(path);
420 if (mUri.getScheme().compareTo("assets") == 0) {
421 final String asset = mUri.getPath().substring(1 /* Remove first '/' */);
422 final AssetManager am = mContext.getAssets();
423 afd = am.openFd(asset);
424 final long offset = afd.getStartOffset();
425 final long length = afd.getLength();
426 FileDescriptor fd = afd.getFileDescriptor();
427 mMediaPlayer.setDataSource(fd, offset, length);
428 } else if (mUri.getScheme().compareTo("file") == 0) {
429 fis = new FileInputStream(mUri.getPath());
430 FileDescriptor fd = fis.getFD();
431 mMediaPlayer.setDataSource(fd);
432 } else if (mUri.getScheme().compareTo("content") == 0) {
433 mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
434 } else {
435 mMediaPlayer.setDataSource(path);
436 }
437 setState(State.Initialized);
438 } catch (final Exception exception) {
439 Log.w(TAG, exception);
440 } finally {
441 try {
442 if (afd != null)
443 afd.close();
444 if (fis != null)
445 fis.close();
446 } catch (final IOException ioe) { /* Ignore... */ }
447
448 if ((mState & State.Initialized) == 0) {
449 setState(State.Error);
450 onErrorNative(MediaPlayer.MEDIA_ERROR_UNKNOWN,
451 -1004 /*MEDIA_ERROR_IO*/,
452 mID);
453 return;
454 }
455 }
456 }
457
458 private boolean isMediaPlayerPrepared()
459 {
460 int preparedState = (State.Prepared | State.Started | State.Paused | State.Stopped
461 | State.PlaybackCompleted);
462 return ((mState & preparedState) != 0);
463 }
464
466 {
467 if (!isMediaPlayerPrepared()) {
468 Log.w(TAG, "Trying to get track info of a media player that is not prepared!");
469 return new TrackInfo[0];
470 }
471
472 MediaPlayer.TrackInfo[] tracks = new MediaPlayer.TrackInfo[0];
473
474 try {
475 // media player will ignore if this a out bounds index.
476 tracks = mMediaPlayer.getTrackInfo();
477 } catch (final IllegalStateException exception) {
478 Log.w(TAG, exception);
479 }
480
481 int numberOfTracks = tracks.length;
482 TrackInfo[] qtTracksInfo = new TrackInfo[numberOfTracks];
483
484 for (int index = 0; index < numberOfTracks; index++) {
485
486 MediaPlayer.TrackInfo track = tracks[index];
487
488 int type = track.getTrackType();
489 String mimeType = getMimeType(track);
490 String language = track.getLanguage();
491
492 qtTracksInfo[index] = new TrackInfo(type, mimeType, language);
493 }
494
495 return qtTracksInfo;
496 }
497
498 private String getMimeType(MediaPlayer.TrackInfo trackInfo)
499 {
500 // The "octet-stream" subtype is used to indicate that a body contains arbitrary binary
501 // data.
502 String defaultMimeType = "application/octet-stream";
503
504 String mimeType = defaultMimeType;
505
506 MediaFormat mediaFormat = trackInfo.getFormat();
507 if (mediaFormat != null) {
508 mimeType = mediaFormat.getString(MediaFormat.KEY_MIME, defaultMimeType);
509 }
510
511 return mimeType;
512 }
513
514 public void selectTrack(int index)
515 {
516 if (!isMediaPlayerPrepared()) {
517 Log.d(TAG, "Trying to select a track of a media player that is not prepared!");
518 return;
519 }
520 try {
521 // media player will ignore if this a out bounds index.
522 mMediaPlayer.selectTrack(index);
523 } catch (final IllegalStateException exception) {
524 Log.w(TAG, exception);
525 }
526 }
527
528 public void deselectTrack(int index)
529 {
530 if (!isMediaPlayerPrepared()) {
531 Log.d(TAG, "Trying to deselect track of a media player that is not prepared!");
532 return;
533 }
534
535 try {
536 // media player will ignore if this a out bounds index.
537 mMediaPlayer.deselectTrack(index);
538 } catch (final IllegalStateException exception) {
539 Log.w(TAG, exception);
540 }
541 }
542
543 public int getSelectedTrack(int type)
544 {
545
546 int InvalidTrack = -1;
547 if (!isMediaPlayerPrepared()) {
548 Log.d(TAG, "Trying to get the selected track of a media player that is not prepared!");
549 return InvalidTrack;
550 }
551
552 boolean isVideoTrackType = (type == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO);
553 boolean isAudioTrackType = (type == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO);
554 boolean isTimedTextTrackType = (type == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
555 boolean isSubtitleTrackType = (type == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE);
556
557 if (!(isVideoTrackType || isAudioTrackType || isSubtitleTrackType
558 || isTimedTextTrackType)) {
559 Log.w(TAG,
560 "Trying to get a selected track of a invalid type"
561 + " Only Video,Audio, TimedText and Subtitle tracks are selectable.");
562 return InvalidTrack;
563 }
564
565 try {
566 return mMediaPlayer.getSelectedTrack(type);
567 } catch (final IllegalStateException exception) {
568 Log.w(TAG, exception);
569 }
570
571 return InvalidTrack;
572 }
573
575 {
576 int currentPosition = 0;
577 if ((mState & (State.Idle
578 | State.Initialized
579 | State.Prepared
580 | State.Started
581 | State.Paused
582 | State.Stopped
583 | State.PlaybackCompleted)) == 0) {
584 return currentPosition;
585 }
586
587 try {
588 currentPosition = mMediaPlayer.getCurrentPosition();
589 } catch (final IllegalStateException exception) {
590 Log.w(TAG, exception);
591 }
592
593 return currentPosition;
594 }
595
596
597 public int getDuration()
598 {
599 int duration = 0;
600 if ((mState & (State.Prepared
601 | State.Started
602 | State.Paused
603 | State.Stopped
604 | State.PlaybackCompleted)) == 0) {
605 return duration;
606 }
607
608 try {
609 duration = mMediaPlayer.getDuration();
610 } catch (final IllegalStateException exception) {
611 Log.w(TAG, exception);
612 }
613
614 return duration;
615 }
616
617 public void setVolume(int volume)
618 {
619 if (volume < 0)
620 volume = 0;
621
622 if (volume > 100)
623 volume = 100;
624
625 mVolume = volume;
626
627 if (!mMuted)
628 setVolumeHelper(mVolume);
629 }
630
631 private void setVolumeHelper(int volume)
632 {
633 if ((mState & (State.Idle
634 | State.Initialized
635 | State.Stopped
636 | State.Prepared
637 | State.Started
638 | State.Paused
639 | State.PlaybackCompleted)) == 0) {
640 return;
641 }
642
643 try {
644 float newVolume = (float)volume / 100;
645 mMediaPlayer.setVolume(newVolume, newVolume);
646 } catch (final IllegalStateException exception) {
647 Log.w(TAG, exception);
648 }
649 }
650
651 public SurfaceHolder display()
652 {
653 return mSurfaceHolder;
654 }
655
656 public void setDisplay(SurfaceHolder sh)
657 {
658 mSurfaceHolder = sh;
659
660 if ((mState & State.Uninitialized) != 0)
661 return;
662
663 mMediaPlayer.setDisplay(mSurfaceHolder);
664 }
665
666
667 public int getVolume()
668 {
669 return mVolume;
670 }
671
672 public void mute(final boolean mute)
673 {
674 mMuted = mute;
675 setVolumeHelper(mute ? 0 : mVolume);
676 }
677
678 public boolean isMuted()
679 {
680 return mMuted;
681 }
682
683 public void reset()
684 {
685 if (mState == State.Uninitialized) {
686 return;
687 }
688
689 mMediaPlayer.reset();
690 setState(State.Idle);
692 }
693
694 public void release()
695 {
696 if (mMediaPlayer != null) {
697 mMediaPlayer.reset();
698 mMediaPlayer.release();
699 mMediaPlayer = null;
700 }
701
702 setState(State.Uninitialized);
704 }
705
706 public void setAudioAttributes(int type, int usage)
707 {
708 mAudioAttributes = new AudioAttributes.Builder()
709 .setUsage(usage)
710 .setContentType(type)
711 .build();
712
713 setAudioAttributes(mMediaPlayer, mAudioAttributes);
714 }
715
716 static private void setAudioAttributes(MediaPlayer player, AudioAttributes attr)
717 {
718 if (player == null || attr == null)
719 return;
720
721 try {
722 player.setAudioAttributes(attr);
723 } catch (final IllegalArgumentException exception) {
724 Log.w(TAG, exception);
725 }
726 }
727
728 public boolean setPlaybackRate(float rate)
729 {
730 PlaybackParams playbackParams = mMediaPlayer.getPlaybackParams();
731 playbackParams.setSpeed(rate);
732 // According to discussion under the patch from QTBUG-61115: At least with DirectShow
733 // and GStreamer, it changes both speed and pitch. (...) need to be consistent
734 if (rate != 0.0)
735 playbackParams.setPitch(Math.abs(rate));
736
737 try {
738 mMediaPlayer.setPlaybackParams(playbackParams);
739 } catch (IllegalStateException | IllegalArgumentException e) {
740 Log.e(TAG, "Cannot set playback rate " + rate + " :" + e.toString());
741 return false;
742 }
743
744 if ((mState & State.Started) == 0 && mMediaPlayer.isPlaying()) {
745 setState(State.Started);
747 }
748
749 return true;
750 }
751}
QMediaPlayer player
Definition audio.cpp:213
\inmodule QtGui
native void onBufferingUpdateNative(int percent, long id)
native void onVideoSizeChangedNative(int width, int height, long id)
void setHeader(final String header, final String value)
native void onDurationChangedNative(int duration, long id)
native void onInfoNative(int what, int extra, long id)
native void onProgressUpdateNative(int progress, long id)
native void onTimedTextChangedNative(String text, int time, long id)
native void onStateChangedNative(int state, long id)
native void onErrorNative(int what, int extra, long id)
QString text
else opt state
[0]
static void * context
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * mimeType
#define TAG(x)
GLint GLsizei GLsizei height
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLint GLsizei width
GLenum type
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLuint GLenum * rate
GLsizei const GLchar *const * path
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
application x qt windows mime
[2]