The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1856 lines
84KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2018 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. //==============================================================================
  18. // This byte-code is generated from:
  19. //
  20. // native/java/com/roli/juce/MediaControllerCallback.java
  21. // native/java/com/roli/juce/MediaSessionCallback.java
  22. // native/java/com/roli/juce/SystemVolumeObserver.java
  23. //
  24. // files with min sdk version 21
  25. // See juce_core/native/java/README.txt on how to generate this byte-code.
  26. static const unsigned char MediaSessionByteCode[] =
  27. {31,139,8,8,235,44,227,91,0,3,77,101,100,105,97,83,101,115,115,105,111,110,46,100,101,120,0,149,152,127,108,28,71,21,199,
  28. 223,236,253,244,157,125,183,119,254,21,55,110,226,52,206,15,151,218,119,36,129,58,57,215,248,7,54,113,56,59,110,206,118,
  29. 131,41,13,107,223,214,222,228,188,123,236,238,185,177,80,69,85,165,82,145,250,7,34,32,85,72,69,65,2,137,72,1,84,250,15,
  30. 63,42,26,9,81,33,129,10,149,42,17,4,130,34,1,165,168,18,63,148,72,21,164,130,239,204,206,158,247,46,14,42,151,124,246,
  31. 189,121,239,205,236,155,55,51,231,219,45,235,23,19,249,163,31,162,82,113,228,219,95,188,252,253,171,127,206,39,23,143,
  32. 254,97,228,234,196,107,173,223,121,242,213,183,126,120,41,67,84,37,162,139,75,199,178,36,63,215,85,162,85,242,236,221,
  33. 224,54,243,228,89,133,40,2,89,9,17,29,134,252,18,36,254,211,27,97,162,249,86,162,99,112,62,22,39,210,64,25,84,128,13,62,
  34. 7,158,6,207,128,23,192,117,240,27,112,11,244,180,16,13,131,243,224,10,248,5,248,15,56,152,32,58,14,230,64,25,60,11,94,4,
  35. 191,2,239,129,222,36,81,30,140,131,18,248,50,120,9,188,6,222,6,239,128,191,131,91,224,95,128,144,95,24,180,128,20,232,0,
  36. 187,192,189,160,15,244,131,33,240,16,208,193,6,184,4,158,7,95,5,87,193,183,192,75,224,85,240,22,136,181,17,61,0,102,193,
  37. 18,48,192,103,193,243,224,26,248,17,248,61,120,27,188,7,98,41,162,46,112,63,120,8,76,131,57,160,129,45,240,121,112,25,
  38. 188,0,94,4,175,128,215,193,31,193,59,224,31,224,93,144,73,35,111,48,0,78,128,105,48,11,74,224,83,224,34,120,50,237,173,
  39. 85,12,160,204,132,146,18,202,70,178,20,132,116,8,67,17,194,8,91,128,248,226,183,131,14,208,9,186,228,218,239,2,61,224,30,
  40. 208,11,246,129,7,64,20,40,114,191,112,61,20,208,219,165,190,79,142,197,63,247,73,253,58,18,218,47,245,159,66,239,151,250,
  41. 47,3,250,141,128,254,38,244,3,82,255,11,244,67,82,191,9,253,160,212,111,7,244,112,124,91,111,13,232,29,208,7,164,222,27,
  42. 176,31,142,123,123,153,235,249,128,125,24,250,253,82,31,131,254,1,169,159,132,62,40,235,51,31,208,207,198,121,61,147,20,
  43. 150,53,61,6,84,41,51,196,104,72,214,153,183,25,254,29,17,245,75,211,89,33,147,244,105,89,195,163,196,215,77,21,181,141,
  44. 193,162,136,181,243,100,18,215,115,98,252,176,104,167,17,113,92,200,40,157,16,50,78,5,233,31,17,178,141,166,132,108,165,
  45. 25,33,83,52,43,100,150,230,132,12,209,195,34,79,111,60,46,71,165,252,136,144,45,52,38,100,132,198,165,125,66,200,16,125,
  46. 76,200,78,58,41,219,167,164,252,184,144,25,42,202,246,105,217,111,94,182,207,72,89,146,246,5,217,94,20,245,73,136,60,50,
  47. 176,79,10,217,65,211,66,182,211,178,216,87,93,244,168,172,47,35,111,111,243,207,30,240,77,52,6,84,175,173,74,127,139,244,
  48. 239,149,242,19,210,159,145,254,136,180,247,73,249,13,233,231,190,30,232,97,228,197,245,75,105,111,95,87,213,8,218,203,89,
  49. 134,58,242,138,241,181,126,78,250,74,125,140,170,99,45,164,124,112,219,119,185,193,151,104,240,125,165,193,151,20,62,69,
  50. 158,168,175,165,189,28,82,98,79,48,177,47,174,5,227,243,173,164,176,84,61,191,239,214,243,139,138,252,20,244,100,194,67,
  51. 244,131,134,177,188,241,95,73,123,103,55,171,166,235,227,255,164,62,190,130,241,219,197,248,190,239,103,13,190,14,225,
  52. 139,192,195,235,247,122,208,183,208,41,230,225,223,251,215,129,123,71,101,252,239,252,248,60,226,23,187,40,52,190,29,255,
  53. 167,64,188,111,251,107,67,254,222,183,204,223,118,168,207,173,134,28,187,69,142,81,185,175,255,157,246,246,66,150,170,
  54. 125,220,242,201,253,33,90,238,15,139,254,17,57,106,68,245,190,3,77,53,134,94,173,116,15,203,147,217,119,8,123,41,65,195,
  55. 216,105,37,244,172,230,251,229,220,189,53,76,169,222,119,220,112,184,157,22,242,33,244,229,217,181,66,134,132,92,65,146,
  56. 217,172,57,198,72,61,146,218,49,194,68,182,121,58,252,79,170,127,24,109,239,97,254,247,212,111,43,13,50,42,101,92,202,
  57. 132,172,76,187,248,110,87,228,56,254,30,103,228,213,134,145,247,183,128,145,247,183,128,247,73,226,76,249,49,109,178,47,
  58. 31,133,183,59,164,189,75,218,187,16,205,219,97,105,111,39,118,152,216,8,69,71,12,211,112,71,41,60,90,24,88,162,228,228,
  59. 233,185,133,169,185,133,115,139,103,102,136,157,34,86,164,174,162,102,150,109,203,40,231,180,106,53,55,190,234,26,155,
  60. 134,187,85,160,125,117,251,170,101,186,186,233,230,38,61,121,70,119,172,202,166,110,23,168,103,231,144,139,110,129,118,
  61. 221,225,154,17,162,64,247,213,61,101,205,213,86,52,71,247,7,62,189,226,232,182,24,248,222,122,204,134,94,54,180,220,44,
  62. 191,206,234,174,198,187,20,40,223,228,118,116,199,49,44,211,11,227,131,217,86,165,162,219,253,147,90,165,178,162,173,94,
  63. 40,208,177,247,217,99,190,162,109,241,30,51,230,227,86,129,6,255,87,175,146,215,8,220,100,232,253,132,63,92,211,107,250,
  64. 140,171,111,20,232,192,93,226,253,36,74,174,230,234,5,202,212,195,76,221,205,45,218,70,129,218,235,38,203,201,77,212,204,
  65. 114,5,113,29,65,227,73,141,27,237,96,185,171,182,181,105,148,117,59,87,210,93,215,48,215,156,254,210,150,35,18,57,84,92,
  66. 181,54,114,40,129,145,59,95,91,213,155,171,178,61,195,254,157,2,229,204,238,30,229,221,102,201,170,212,54,244,224,26,151,
  67. 181,202,166,113,33,167,153,166,133,153,242,153,151,140,53,83,115,107,54,102,147,45,158,215,54,181,92,69,51,215,114,37,
  68. 215,70,190,5,74,123,182,154,107,84,114,69,195,113,73,109,50,20,104,172,201,50,242,255,173,201,104,129,122,239,50,251,33,
  69. 62,48,245,236,52,101,233,218,105,158,158,139,45,145,178,116,138,66,75,167,196,165,72,97,92,138,176,21,209,44,114,91,145,
  70. 219,138,203,220,182,140,38,20,182,76,10,68,92,147,39,146,218,124,109,193,90,116,116,58,40,15,214,137,28,38,229,45,104,
  71. 206,17,25,228,54,69,10,231,54,106,142,177,74,81,156,71,91,115,168,115,77,119,199,171,213,138,177,42,106,45,79,42,101,97,
  72. 110,58,216,20,94,183,80,220,22,126,245,238,165,24,101,10,27,56,19,148,17,101,156,168,185,174,101,122,39,154,246,110,52,
  73. 22,108,188,86,54,44,126,128,38,215,177,120,122,153,246,52,5,248,7,217,247,239,111,242,55,236,127,63,168,249,46,114,9,62,
  74. 170,59,48,108,33,32,38,2,102,202,50,69,233,159,215,106,200,95,109,48,97,120,218,221,108,153,182,173,141,89,57,66,54,232,
  75. 44,233,250,133,5,171,113,136,146,107,85,101,70,190,37,176,246,126,202,241,13,57,81,202,90,230,29,85,137,99,13,132,74,109,
  76. 150,57,173,57,238,180,101,63,161,217,101,30,60,187,93,228,169,77,94,227,12,183,53,86,45,230,79,46,42,167,148,185,115,34,
  77. 93,158,233,142,98,166,44,83,236,249,64,46,103,244,39,12,83,104,114,194,89,174,53,213,184,21,182,11,70,117,193,154,227,91,
  78. 71,245,91,243,182,190,105,88,53,135,167,224,89,234,39,138,103,39,170,21,170,90,14,69,62,195,237,212,109,235,107,56,155,
  79. 56,91,141,95,252,148,112,244,202,227,178,42,208,221,41,83,91,169,224,190,105,103,221,170,85,202,19,186,111,72,56,174,102,
  80. 187,206,35,134,187,78,17,135,79,141,194,238,186,225,80,220,181,188,111,11,234,169,153,119,187,77,168,102,27,20,217,212,
  81. 42,200,37,203,62,28,75,141,12,82,15,27,142,165,150,105,47,155,226,226,0,123,140,139,1,70,97,118,182,115,15,30,14,135,136,
  82. 27,58,100,244,131,252,121,113,132,29,199,117,183,112,236,19,215,67,202,232,17,46,199,184,123,144,61,194,245,73,174,79,
  83. 243,203,12,123,16,215,163,34,50,165,228,49,82,113,112,144,246,42,203,43,177,212,143,151,169,139,61,26,75,13,50,53,211,75,
  84. 74,150,173,244,70,186,169,123,119,55,235,86,187,241,59,42,18,97,74,252,169,167,194,47,39,216,211,10,5,105,99,55,18,140,
  85. 221,4,95,72,50,118,13,92,199,47,129,104,82,81,178,136,255,109,178,57,158,179,139,189,139,184,231,90,25,187,2,94,6,55,192,
  86. 77,240,108,27,99,95,7,223,3,63,7,111,182,145,162,40,33,133,41,251,49,218,237,54,222,251,0,187,146,98,244,70,138,212,166,
  87. 223,69,92,250,239,38,248,111,18,255,253,68,136,182,223,81,132,105,251,61,5,151,254,187,10,255,57,149,191,175,8,169,222,
  88. 88,226,55,86,159,247,108,55,2,61,218,231,217,249,243,4,83,189,103,89,254,204,170,244,121,247,229,239,55,66,50,158,63,23,
  89. 132,251,182,159,23,248,131,4,31,95,60,135,200,241,249,187,148,255,2,145,38,223,176,132,17,0,0};
  90. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  91. METHOD (getPlaybackInfo, "getPlaybackInfo", "()Landroid/media/session/MediaController$PlaybackInfo;") \
  92. METHOD (getPlaybackState, "getPlaybackState", "()Landroid/media/session/PlaybackState;") \
  93. METHOD (getTransportControls, "getTransportControls", "()Landroid/media/session/MediaController$TransportControls;") \
  94. METHOD (registerCallback, "registerCallback", "(Landroid/media/session/MediaController$Callback;)V") \
  95. METHOD (setVolumeTo, "setVolumeTo", "(II)V") \
  96. METHOD (unregisterCallback, "unregisterCallback", "(Landroid/media/session/MediaController$Callback;)V")
  97. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaController, "android/media/session/MediaController", 21)
  98. #undef JNI_CLASS_MEMBERS
  99. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  100. METHOD (getAudioAttributes, "getAudioAttributes", "()Landroid/media/AudioAttributes;") \
  101. METHOD (getCurrentVolume, "getCurrentVolume", "()I") \
  102. METHOD (getMaxVolume, "getMaxVolume", "()I")
  103. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaControllerPlaybackInfo, "android/media/session/MediaController$PlaybackInfo", 21)
  104. #undef JNI_CLASS_MEMBERS
  105. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  106. METHOD (pause, "pause", "()V") \
  107. METHOD (play, "play", "()V") \
  108. METHOD (playFromMediaId, "playFromMediaId", "(Ljava/lang/String;Landroid/os/Bundle;)V") \
  109. METHOD (seekTo, "seekTo", "(J)V") \
  110. METHOD (stop, "stop", "()V")
  111. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaControllerTransportControls, "android/media/session/MediaController$TransportControls", 21)
  112. #undef JNI_CLASS_MEMBERS
  113. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  114. METHOD (constructor, "<init>", "()V") \
  115. METHOD (getCurrentPosition, "getCurrentPosition", "()I") \
  116. METHOD (getDuration, "getDuration", "()I") \
  117. METHOD (getPlaybackParams, "getPlaybackParams", "()Landroid/media/PlaybackParams;") \
  118. METHOD (getVideoHeight, "getVideoHeight", "()I") \
  119. METHOD (getVideoWidth, "getVideoWidth", "()I") \
  120. METHOD (isPlaying, "isPlaying", "()Z") \
  121. METHOD (pause, "pause", "()V") \
  122. METHOD (prepareAsync, "prepareAsync", "()V") \
  123. METHOD (release, "release", "()V") \
  124. METHOD (seekTo, "seekTo", "(I)V") \
  125. METHOD (setAudioAttributes, "setAudioAttributes", "(Landroid/media/AudioAttributes;)V") \
  126. METHOD (setDataSource, "setDataSource", "(Landroid/content/Context;Landroid/net/Uri;)V") \
  127. METHOD (setDisplay, "setDisplay", "(Landroid/view/SurfaceHolder;)V") \
  128. METHOD (setOnBufferingUpdateListener, "setOnBufferingUpdateListener", "(Landroid/media/MediaPlayer$OnBufferingUpdateListener;)V") \
  129. METHOD (setOnCompletionListener, "setOnCompletionListener", "(Landroid/media/MediaPlayer$OnCompletionListener;)V") \
  130. METHOD (setOnErrorListener, "setOnErrorListener", "(Landroid/media/MediaPlayer$OnErrorListener;)V") \
  131. METHOD (setOnInfoListener, "setOnInfoListener", "(Landroid/media/MediaPlayer$OnInfoListener;)V") \
  132. METHOD (setOnPreparedListener, "setOnPreparedListener", "(Landroid/media/MediaPlayer$OnPreparedListener;)V") \
  133. METHOD (setOnSeekCompleteListener, "setOnSeekCompleteListener", "(Landroid/media/MediaPlayer$OnSeekCompleteListener;)V") \
  134. METHOD (setPlaybackParams, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V") \
  135. METHOD (setVolume, "setVolume", "(FF)V") \
  136. METHOD (start, "start", "()V") \
  137. METHOD (stop, "stop", "()V")
  138. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaPlayer, "android/media/MediaPlayer", 21)
  139. #undef JNI_CLASS_MEMBERS
  140. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  141. METHOD (constructor, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V") \
  142. METHOD (getController, "getController", "()Landroid/media/session/MediaController;") \
  143. METHOD (release, "release", "()V") \
  144. METHOD (setActive, "setActive", "(Z)V") \
  145. METHOD (setCallback, "setCallback", "(Landroid/media/session/MediaSession$Callback;)V") \
  146. METHOD (setFlags, "setFlags", "(I)V") \
  147. METHOD (setMediaButtonReceiver, "setMediaButtonReceiver", "(Landroid/app/PendingIntent;)V") \
  148. METHOD (setMetadata, "setMetadata", "(Landroid/media/MediaMetadata;)V") \
  149. METHOD (setPlaybackState, "setPlaybackState", "(Landroid/media/session/PlaybackState;)V") \
  150. METHOD (setPlaybackToLocal, "setPlaybackToLocal", "(Landroid/media/AudioAttributes;)V")
  151. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSession, "android/media/session/MediaSession", 21)
  152. #undef JNI_CLASS_MEMBERS
  153. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  154. METHOD (build, "build", "()Landroid/media/MediaMetadata;") \
  155. METHOD (constructor, "<init>", "()V") \
  156. METHOD (putLong, "putLong", "(Ljava/lang/String;J)Landroid/media/MediaMetadata$Builder;")
  157. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaMetadataBuilder, "android/media/MediaMetadata$Builder", 21)
  158. #undef JNI_CLASS_MEMBERS
  159. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  160. METHOD (getSpeed, "getSpeed", "()F") \
  161. METHOD (setSpeed, "setSpeed", "(F)Landroid/media/PlaybackParams;")
  162. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidPlaybackParams, "android/media/PlaybackParams", 21)
  163. #undef JNI_CLASS_MEMBERS
  164. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  165. METHOD (getActions, "getActions", "()J") \
  166. METHOD (getErrorMessage, "getErrorMessage", "()Ljava/lang/CharSequence;") \
  167. METHOD (getPlaybackSpeed, "getPlaybackSpeed", "()F") \
  168. METHOD (getPosition, "getPosition", "()J") \
  169. METHOD (getState, "getState", "()I")
  170. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidPlaybackState, "android/media/session/PlaybackState", 21)
  171. #undef JNI_CLASS_MEMBERS
  172. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  173. METHOD (build, "build", "()Landroid/media/session/PlaybackState;") \
  174. METHOD (constructor, "<init>", "()V") \
  175. METHOD (setActions, "setActions", "(J)Landroid/media/session/PlaybackState$Builder;") \
  176. METHOD (setErrorMessage, "setErrorMessage", "(Ljava/lang/CharSequence;)Landroid/media/session/PlaybackState$Builder;") \
  177. METHOD (setState, "setState", "(IJF)Landroid/media/session/PlaybackState$Builder;")
  178. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidPlaybackStateBuilder, "android/media/session/PlaybackState$Builder", 21)
  179. #undef JNI_CLASS_MEMBERS
  180. //==============================================================================
  181. class MediaPlayerListener : public AndroidInterfaceImplementer
  182. {
  183. public:
  184. struct Owner
  185. {
  186. virtual ~Owner() {}
  187. virtual void onPrepared (LocalRef<jobject>& mediaPlayer) = 0;
  188. virtual void onBufferingUpdate (LocalRef<jobject>& mediaPlayer, int progress) = 0;
  189. virtual void onSeekComplete (LocalRef<jobject>& mediaPlayer) = 0;
  190. virtual void onCompletion (LocalRef<jobject>& mediaPlayer) = 0;
  191. virtual bool onInfo (LocalRef<jobject>& mediaPlayer, int what, int extra) = 0;
  192. virtual bool onError (LocalRef<jobject>& mediaPlayer, int what, int extra) = 0;
  193. };
  194. MediaPlayerListener (Owner& ownerToUse) : owner (ownerToUse) {}
  195. private:
  196. Owner& owner;
  197. jobject invoke (jobject proxy, jobject method, jobjectArray args) override
  198. {
  199. auto* env = getEnv();
  200. auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
  201. int numArgs = args != nullptr ? env->GetArrayLength (args) : 0;
  202. if (methodName == "onPrepared" && numArgs == 1)
  203. {
  204. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  205. owner.onPrepared (mediaPlayer);
  206. return nullptr;
  207. }
  208. if (methodName == "onCompletion" && numArgs == 1)
  209. {
  210. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  211. owner.onCompletion (mediaPlayer);
  212. return nullptr;
  213. }
  214. if (methodName == "onInfo" && numArgs == 3)
  215. {
  216. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  217. auto what = LocalRef<jobject> (env->GetObjectArrayElement (args, 1));
  218. auto extra = LocalRef<jobject> (env->GetObjectArrayElement (args, 2));
  219. auto whatInt = (int) env->CallIntMethod (what, JavaInteger.intValue);
  220. auto extraInt = (int) env->CallIntMethod (extra, JavaInteger.intValue);
  221. auto res = owner.onInfo (mediaPlayer, whatInt, extraInt);
  222. return env->CallStaticObjectMethod (JavaBoolean, JavaBoolean.valueOf, (jboolean) res);
  223. }
  224. if (methodName == "onError" && numArgs == 3)
  225. {
  226. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  227. auto what = LocalRef<jobject> (env->GetObjectArrayElement (args, 1));
  228. auto extra = LocalRef<jobject> (env->GetObjectArrayElement (args, 2));
  229. auto whatInt = (int) env->CallIntMethod (what, JavaInteger.intValue);
  230. auto extraInt = (int) env->CallIntMethod (extra, JavaInteger.intValue);
  231. auto res = owner.onError (mediaPlayer, whatInt, extraInt);
  232. return env->CallStaticObjectMethod (JavaBoolean, JavaBoolean.valueOf, (jboolean) res);
  233. }
  234. if (methodName == "onSeekComplete" && numArgs == 1)
  235. {
  236. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  237. owner.onSeekComplete (mediaPlayer);
  238. return nullptr;
  239. }
  240. if (methodName == "onBufferingUpdate" && numArgs == 2)
  241. {
  242. auto mediaPlayer = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  243. auto progress = LocalRef<jobject> (env->GetObjectArrayElement (args, 1));
  244. auto progressInt = (int) env->CallIntMethod (progress, JavaInteger.intValue);
  245. owner.onBufferingUpdate (mediaPlayer, progressInt);
  246. return nullptr;
  247. }
  248. return AndroidInterfaceImplementer::invoke (proxy, method, args);
  249. }
  250. };
  251. //==============================================================================
  252. class AudioManagerOnAudioFocusChangeListener : public AndroidInterfaceImplementer
  253. {
  254. public:
  255. struct Owner
  256. {
  257. virtual ~Owner() {}
  258. virtual void onAudioFocusChange (int changeType) = 0;
  259. };
  260. AudioManagerOnAudioFocusChangeListener (Owner& ownerToUse) : owner (ownerToUse) {}
  261. private:
  262. Owner& owner;
  263. jobject invoke (jobject proxy, jobject method, jobjectArray args) override
  264. {
  265. auto* env = getEnv();
  266. auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
  267. int numArgs = args != nullptr ? env->GetArrayLength (args) : 0;
  268. if (methodName == "onAudioFocusChange" && numArgs == 1)
  269. {
  270. auto changeType = LocalRef<jobject> (env->GetObjectArrayElement (args, 0));
  271. auto changeTypeInt = (int) env->CallIntMethod (changeType, JavaInteger.intValue);
  272. owner.onAudioFocusChange (changeTypeInt);
  273. return nullptr;
  274. }
  275. return AndroidInterfaceImplementer::invoke (proxy, method, args);
  276. }
  277. };
  278. //==============================================================================
  279. struct VideoComponent::Pimpl
  280. : public AndroidViewComponent, private ActivityLifecycleCallbacks, private SurfaceHolderCallback
  281. {
  282. Pimpl (VideoComponent& ownerToUse, bool)
  283. : owner (ownerToUse),
  284. mediaSession (*this)
  285. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  286. , systemVolumeListener (*this)
  287. #endif
  288. {
  289. // Video requires SDK version 21 or higher
  290. jassert (getAndroidSDKVersion() >= 21);
  291. setVisible (true);
  292. auto* env = getEnv();
  293. LocalRef<jobject> appContext (getAppContext());
  294. if (appContext != nullptr)
  295. {
  296. ActivityLifecycleCallbacks* callbacks = dynamic_cast<ActivityLifecycleCallbacks*> (this);
  297. activityLifeListener = GlobalRef (CreateJavaInterface (callbacks, "android/app/Application$ActivityLifecycleCallbacks"));
  298. env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, activityLifeListener.get());
  299. }
  300. {
  301. LocalRef<jobject> surfaceView (env->NewObject (AndroidSurfaceView, AndroidSurfaceView.constructor, getAppContext().get()));
  302. LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), AndroidSurfaceView.getHolder));
  303. SurfaceHolderCallback* callbacks = dynamic_cast<SurfaceHolderCallback*> (this);
  304. surfaceHolderCallback = GlobalRef (CreateJavaInterface (callbacks, "android/view/SurfaceHolder$Callback"));
  305. env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, surfaceHolderCallback.get());
  306. setView (surfaceView.get());
  307. }
  308. }
  309. ~Pimpl()
  310. {
  311. auto* env = getEnv();
  312. if (surfaceHolderCallback != nullptr)
  313. {
  314. jobject view = reinterpret_cast<jobject> (getView());
  315. if (view != nullptr)
  316. {
  317. LocalRef <jobject> holder (env->CallObjectMethod (view, AndroidSurfaceView.getHolder));
  318. env->CallVoidMethod (holder, AndroidSurfaceHolder.removeCallback, surfaceHolderCallback.get());
  319. SurfaceHolderCallback::clear();
  320. surfaceHolderCallback.clear();
  321. }
  322. }
  323. if (activityLifeListener != nullptr)
  324. {
  325. env->CallVoidMethod (getAppContext().get(), AndroidApplication.unregisterActivityLifecycleCallbacks, activityLifeListener.get());
  326. ActivityLifecycleCallbacks::clear();
  327. activityLifeListener.clear();
  328. }
  329. }
  330. void loadAsync (const URL& url, std::function<void(const URL&, Result)> callback)
  331. {
  332. close();
  333. wasOpen = false;
  334. if (url.isEmpty())
  335. {
  336. jassertfalse;
  337. return;
  338. }
  339. if (! url.isLocalFile())
  340. {
  341. if (! isPermissionDeclaredInManifest ("android.permission.INTERNET"))
  342. {
  343. // In order to access videos from the Internet, the Internet permission has to be specified in
  344. // Android Manifest.
  345. jassertfalse;
  346. return;
  347. }
  348. }
  349. currentURL = url;
  350. jassert (callback != nullptr);
  351. loadFinishedCallback = std::move (callback);
  352. static constexpr jint visible = 0;
  353. getEnv()->CallVoidMethod ((jobject) getView(), AndroidView.setVisibility, visible);
  354. mediaSession.load (url);
  355. }
  356. void close()
  357. {
  358. if (! isOpen())
  359. return;
  360. mediaSession.closeVideo();
  361. static constexpr jint invisible = 4;
  362. getEnv()->CallVoidMethod ((jobject) getView(), AndroidView.setVisibility, invisible);
  363. }
  364. bool isOpen() const noexcept { return mediaSession.isVideoOpen(); }
  365. bool isPlaying() const noexcept { return mediaSession.isPlaying(); }
  366. void play() { mediaSession.play(); }
  367. void stop() { mediaSession.stop(); }
  368. void setPosition (double newPosition) { mediaSession.setPosition (newPosition); }
  369. double getPosition() const { return mediaSession.getPosition(); }
  370. void setSpeed (double newSpeed) { mediaSession.setSpeed (newSpeed); }
  371. double getSpeed() const { return mediaSession.getSpeed(); }
  372. Rectangle<int> getNativeSize() const { return mediaSession.getNativeSize(); }
  373. double getDuration() const { return mediaSession.getDuration(); }
  374. void setVolume (float newVolume) { mediaSession.setVolume (newVolume); }
  375. float getVolume() const { return mediaSession.getVolume(); }
  376. File currentFile;
  377. URL currentURL;
  378. private:
  379. //==============================================================================
  380. class MediaSession : private AudioManagerOnAudioFocusChangeListener::Owner
  381. {
  382. public:
  383. MediaSession (Pimpl& ownerToUse)
  384. : owner (ownerToUse),
  385. sdkVersion (getAndroidSDKVersion()),
  386. audioAttributes (getAudioAttributes()),
  387. nativeMediaSession (LocalRef<jobject> (getEnv()->NewObject (AndroidMediaSession,
  388. AndroidMediaSession.constructor,
  389. getAppContext().get(),
  390. javaString ("JuceVideoMediaSession").get()))),
  391. mediaSessionCallback (createCallbackObject()),
  392. playbackStateBuilder (LocalRef<jobject> (getEnv()->NewObject (AndroidPlaybackStateBuilder,
  393. AndroidPlaybackStateBuilder.constructor))),
  394. controller (*this, LocalRef<jobject> (getEnv()->CallObjectMethod (nativeMediaSession,
  395. AndroidMediaSession.getController))),
  396. player (*this),
  397. audioManager (LocalRef<jobject> (getEnv()->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService,
  398. javaString ("audio").get()))),
  399. audioFocusChangeListener (*this),
  400. nativeAudioFocusChangeListener (GlobalRef (CreateJavaInterface (&audioFocusChangeListener,
  401. "android/media/AudioManager$OnAudioFocusChangeListener"))),
  402. audioFocusRequest (createAudioFocusRequestIfNecessary (sdkVersion, audioAttributes,
  403. nativeAudioFocusChangeListener))
  404. {
  405. auto* env = getEnv();
  406. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setPlaybackToLocal, audioAttributes.get());
  407. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setMediaButtonReceiver, nullptr);
  408. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setCallback, mediaSessionCallback.get());
  409. }
  410. ~MediaSession()
  411. {
  412. auto* env = getEnv();
  413. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setCallback, nullptr);
  414. controller.stop();
  415. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.release);
  416. }
  417. bool isVideoOpen() const { return player.isVideoOpen(); }
  418. bool isPlaying() const { return player.isPlaying(); }
  419. void load (const URL& url) { controller.load (url); }
  420. void closeVideo()
  421. {
  422. resetState();
  423. controller.closeVideo();
  424. }
  425. void setDisplay (const LocalRef<jobject>& surfaceHolder) { player.setDisplay (surfaceHolder); }
  426. void play() { controller.play(); }
  427. void stop() { controller.stop(); }
  428. void setPosition (double newPosition) { controller.setPosition (newPosition); }
  429. double getPosition() const { return controller.getPosition(); }
  430. void setSpeed (double newSpeed)
  431. {
  432. playSpeedMult = newSpeed;
  433. // Calling non 0.0 speed on a paused player would start it...
  434. if (player.isPlaying())
  435. {
  436. player.setPlaySpeed (playSpeedMult);
  437. updatePlaybackState();
  438. }
  439. }
  440. double getSpeed() const { return controller.getPlaySpeed(); }
  441. Rectangle<int> getNativeSize() const { return player.getVideoNativeSize(); }
  442. double getDuration() const { return player.getVideoDuration() / 1000.0; }
  443. void setVolume (float newVolume)
  444. {
  445. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  446. controller.setVolume (newVolume);
  447. #else
  448. player.setAudioVolume (newVolume);
  449. #endif
  450. }
  451. float getVolume() const
  452. {
  453. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  454. return controller.getVolume();
  455. #else
  456. return player.getAudioVolume();
  457. #endif
  458. }
  459. void storeState()
  460. {
  461. storedPlaybackState.clear();
  462. storedPlaybackState = GlobalRef (getCurrentPlaybackState());
  463. }
  464. void restoreState()
  465. {
  466. if (storedPlaybackState.get() == nullptr)
  467. return;
  468. auto* env = getEnv();
  469. auto pos = env->CallLongMethod (storedPlaybackState, AndroidPlaybackState.getPosition);
  470. setPosition (pos / 1000.0);
  471. setSpeed (playSpeedMult);
  472. auto state = env->CallIntMethod (storedPlaybackState, AndroidPlaybackState.getState);
  473. if (state != PlaybackState::STATE_NONE && state != PlaybackState::STATE_STOPPED
  474. && state != PlaybackState::STATE_PAUSED && state != PlaybackState::STATE_ERROR)
  475. {
  476. play();
  477. }
  478. }
  479. private:
  480. struct PlaybackState
  481. {
  482. enum
  483. {
  484. STATE_NONE = 0,
  485. STATE_STOPPED = 1,
  486. STATE_PAUSED = 2,
  487. STATE_PLAYING = 3,
  488. STATE_FAST_FORWARDING = 4,
  489. STATE_REWINDING = 5,
  490. STATE_BUFFERING = 6,
  491. STATE_ERROR = 7,
  492. STATE_CONNECTING = 8,
  493. STATE_SKIPPING_TO_PREVIOUS = 9,
  494. STATE_SKIPPING_TO_NEXT = 10,
  495. STATE_SKIPPING_TO_QUEUE_ITEM = 11,
  496. };
  497. enum
  498. {
  499. ACTION_PAUSE = 0x2,
  500. ACTION_PLAY = 0x4,
  501. ACTION_PLAY_FROM_MEDIA_ID = 0x8000,
  502. ACTION_PLAY_PAUSE = 0x200,
  503. ACTION_SEEK_TO = 0x100,
  504. ACTION_STOP = 0x1,
  505. };
  506. };
  507. //==============================================================================
  508. class Controller
  509. {
  510. public:
  511. Controller (MediaSession& ownerToUse, const LocalRef<jobject>& nativeControllerToUse)
  512. : owner (ownerToUse),
  513. nativeController (GlobalRef (nativeControllerToUse)),
  514. controllerTransportControls (LocalRef<jobject> (getEnv()->CallObjectMethod (nativeControllerToUse,
  515. AndroidMediaController.getTransportControls))),
  516. controllerCallback (createControllerCallbacks())
  517. {
  518. auto* env = getEnv();
  519. env->CallVoidMethod (nativeController, AndroidMediaController.registerCallback, controllerCallback.get());
  520. }
  521. ~Controller()
  522. {
  523. auto* env = getEnv();
  524. env->CallVoidMethod (nativeController, AndroidMediaController.unregisterCallback, controllerCallback.get());
  525. }
  526. void load (const URL& url)
  527. {
  528. // NB: would use playFromUri, but it was only introduced in API 23...
  529. getEnv()->CallVoidMethod (controllerTransportControls, AndroidMediaControllerTransportControls.playFromMediaId,
  530. javaString (url.toString (true)).get(), nullptr);
  531. }
  532. void closeVideo()
  533. {
  534. getEnv()->CallVoidMethod (controllerTransportControls, AndroidMediaControllerTransportControls.stop);
  535. }
  536. void play()
  537. {
  538. getEnv()->CallVoidMethod (controllerTransportControls, AndroidMediaControllerTransportControls.play);
  539. }
  540. void stop()
  541. {
  542. // NB: calling pause, rather than stop, because after calling stop, we would have to call load() again.
  543. getEnv()->CallVoidMethod (controllerTransportControls, AndroidMediaControllerTransportControls.pause);
  544. }
  545. void setPosition (double newPosition)
  546. {
  547. auto seekPos = static_cast<jlong> (newPosition * 1000);
  548. getEnv()->CallVoidMethod (controllerTransportControls, AndroidMediaControllerTransportControls.seekTo, seekPos);
  549. }
  550. double getPosition() const
  551. {
  552. auto* env = getEnv();
  553. auto playbackState = LocalRef<jobject> (env->CallObjectMethod (nativeController, AndroidMediaController.getPlaybackState));
  554. if (playbackState != nullptr)
  555. return env->CallLongMethod (playbackState, AndroidPlaybackState.getPosition) / 1000.0;
  556. return 0.0;
  557. }
  558. double getPlaySpeed() const
  559. {
  560. auto* env = getEnv();
  561. auto playbackState = LocalRef<jobject> (env->CallObjectMethod (nativeController, AndroidMediaController.getPlaybackState));
  562. if (playbackState != nullptr)
  563. return (double) env->CallFloatMethod (playbackState, AndroidPlaybackState.getPlaybackSpeed);
  564. return 1.0;
  565. }
  566. void setVolume (float newVolume)
  567. {
  568. auto* env = getEnv();
  569. auto playbackInfo = LocalRef<jobject> (env->CallObjectMethod (nativeController, AndroidMediaController.getPlaybackInfo));
  570. auto maxVolume = env->CallIntMethod (playbackInfo, AndroidMediaControllerPlaybackInfo.getMaxVolume);
  571. auto targetVolume = jmin (jint (maxVolume * newVolume), maxVolume);
  572. static constexpr jint flagShowUI = 1;
  573. env->CallVoidMethod (nativeController, AndroidMediaController.setVolumeTo, targetVolume, flagShowUI);
  574. }
  575. float getVolume() const
  576. {
  577. auto* env = getEnv();
  578. auto playbackInfo = LocalRef<jobject> (env->CallObjectMethod (nativeController, AndroidMediaController.getPlaybackInfo));
  579. auto maxVolume = (int) (env->CallIntMethod (playbackInfo, AndroidMediaControllerPlaybackInfo.getMaxVolume));
  580. auto curVolume = (int) (env->CallIntMethod (playbackInfo, AndroidMediaControllerPlaybackInfo.getCurrentVolume));
  581. return static_cast<float> (curVolume) / maxVolume;
  582. }
  583. private:
  584. MediaSession& owner;
  585. GlobalRef nativeController;
  586. GlobalRef controllerTransportControls;
  587. GlobalRef controllerCallback;
  588. bool wasPlaying = false;
  589. bool wasPaused = true;
  590. //==============================================================================
  591. void stateChanged (jobject playbackState)
  592. {
  593. JUCE_VIDEO_LOG ("MediaSessionController::playbackStateChanged()");
  594. if (playbackState == nullptr)
  595. return;
  596. auto state = getEnv()->CallIntMethod (playbackState, AndroidPlaybackState.getState);
  597. static constexpr jint statePaused = 2;
  598. static constexpr jint statePlaying = 3;
  599. if (wasPlaying == false && state == statePlaying)
  600. owner.playbackStarted();
  601. else if (wasPaused == false && state == statePaused)
  602. owner.playbackStopped();
  603. wasPlaying = state == statePlaying;
  604. wasPaused = state == statePaused;
  605. }
  606. //==============================================================================
  607. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  608. METHOD (constructor, "<init>", "(J)V") \
  609. CALLBACK (audioInfoChanged, "mediaControllerAudioInfoChanged", "(JLandroid/media/session/MediaController$PlaybackInfo;)V") \
  610. CALLBACK (metadataChanged, "mediaControllerMetadataChanged", "(JLandroid/media/MediaMetadata;)V") \
  611. CALLBACK (playbackStateChanged, "mediaControllerPlaybackStateChanged", "(JLandroid/media/session/PlaybackState;)V") \
  612. CALLBACK (sessionDestroyed, "mediaControllerSessionDestroyed", "(J)V")
  613. DECLARE_JNI_CLASS_WITH_BYTECODE (AndroidMediaControllerCallback, "com/roli/juce/MediaControllerCallback", 21, MediaSessionByteCode, sizeof(MediaSessionByteCode))
  614. #undef JNI_CLASS_MEMBERS
  615. LocalRef<jobject> createControllerCallbacks()
  616. {
  617. return LocalRef<jobject> (getEnv()->NewObject (AndroidMediaControllerCallback,
  618. AndroidMediaControllerCallback.constructor,
  619. reinterpret_cast<jlong> (this)));
  620. }
  621. //==============================================================================
  622. // MediaSessionController callbacks
  623. static void audioInfoChanged (JNIEnv*, jobject, jlong host, jobject playbackInfo)
  624. {
  625. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host))
  626. {
  627. ignoreUnused (playbackInfo);
  628. JUCE_VIDEO_LOG ("MediaSessionController::audioInfoChanged()");
  629. }
  630. }
  631. static void metadataChanged (JNIEnv*, jobject, jlong host, jobject metadata)
  632. {
  633. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host))
  634. {
  635. ignoreUnused (metadata);
  636. JUCE_VIDEO_LOG ("MediaSessionController::metadataChanged()");
  637. }
  638. }
  639. static void playbackStateChanged (JNIEnv*, jobject, jlong host, jobject state)
  640. {
  641. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host))
  642. myself->stateChanged (state);
  643. }
  644. static void sessionDestroyed (JNIEnv*, jobject, jlong host)
  645. {
  646. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession::Controller*> (host))
  647. JUCE_VIDEO_LOG ("MediaSessionController::sessionDestroyed()");
  648. }
  649. };
  650. //==============================================================================
  651. class Player : private MediaPlayerListener::Owner
  652. {
  653. public:
  654. Player (MediaSession& ownerToUse)
  655. : owner (ownerToUse),
  656. mediaPlayerListener (*this),
  657. nativeMediaPlayerListener (GlobalRef (CreateJavaInterface (&mediaPlayerListener,
  658. getNativeMediaPlayerListenerInterfaces())))
  659. {}
  660. void setDisplay (const LocalRef<jobject>& surfaceHolder)
  661. {
  662. if (surfaceHolder == nullptr)
  663. {
  664. videoSurfaceHolder.clear();
  665. if (nativeMediaPlayer.get() != nullptr)
  666. getEnv()->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setDisplay, nullptr);
  667. return;
  668. }
  669. videoSurfaceHolder = GlobalRef (surfaceHolder);
  670. if (nativeMediaPlayer.get() != nullptr)
  671. getEnv()->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setDisplay, videoSurfaceHolder.get());
  672. }
  673. void load (const LocalRef<jstring>& mediaId, const LocalRef<jobject>& extras)
  674. {
  675. ignoreUnused (extras);
  676. closeVideo();
  677. auto* env = getEnv();
  678. nativeMediaPlayer = GlobalRef (LocalRef<jobject> (env->NewObject (AndroidMediaPlayer, AndroidMediaPlayer.constructor)));
  679. currentState = State::idle;
  680. auto uri = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidUri, AndroidUri.parse, mediaId.get()));
  681. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setDataSource, getAppContext().get(), uri.get());
  682. if (jniCheckHasExceptionOccurredAndClear())
  683. {
  684. owner.errorOccurred ("Could not find video under path provided (" + juceString (mediaId) + ")");
  685. return;
  686. }
  687. currentState = State::initialised;
  688. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnBufferingUpdateListener, nativeMediaPlayerListener.get());
  689. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnCompletionListener, nativeMediaPlayerListener.get());
  690. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnErrorListener, nativeMediaPlayerListener.get());
  691. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnInfoListener, nativeMediaPlayerListener.get());
  692. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnPreparedListener, nativeMediaPlayerListener.get());
  693. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setOnSeekCompleteListener, nativeMediaPlayerListener.get());
  694. if (videoSurfaceHolder != nullptr)
  695. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setDisplay, videoSurfaceHolder.get());
  696. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.prepareAsync);
  697. currentState = State::preparing;
  698. }
  699. void closeVideo()
  700. {
  701. if (nativeMediaPlayer.get() == nullptr)
  702. return;
  703. auto* env = getEnv();
  704. if (getCurrentStateInfo().canCallStop)
  705. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.stop);
  706. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.release);
  707. nativeMediaPlayer.clear();
  708. currentState = State::end;
  709. }
  710. bool isVideoOpen() const noexcept
  711. {
  712. return currentState == State::prepared || currentState == State::started
  713. || currentState == State::paused || currentState == State::complete;
  714. }
  715. int getPlaybackStateFlag() const noexcept { return getCurrentStateInfo().playbackStateFlag; }
  716. int getAllowedActions() const noexcept { return getCurrentStateInfo().allowedActions; }
  717. jlong getVideoDuration() const
  718. {
  719. if (! getCurrentStateInfo().canCallGetVideoDuration)
  720. return 0;
  721. return getEnv()->CallIntMethod (nativeMediaPlayer, AndroidMediaPlayer.getDuration);
  722. }
  723. Rectangle<int> getVideoNativeSize() const
  724. {
  725. if (! getCurrentStateInfo().canCallGetVideoHeight)
  726. {
  727. jassertfalse;
  728. return {};
  729. }
  730. auto* env = getEnv();
  731. auto width = (int) env->CallIntMethod (nativeMediaPlayer, AndroidMediaPlayer.getVideoWidth);
  732. auto height = (int) env->CallIntMethod (nativeMediaPlayer, AndroidMediaPlayer.getVideoHeight);
  733. return Rectangle<int> (0, 0, width, height);
  734. }
  735. void play()
  736. {
  737. if (! getCurrentStateInfo().canCallStart)
  738. {
  739. jassertfalse;
  740. return;
  741. }
  742. auto* env = getEnv();
  743. // Perform a potentially pending volume setting
  744. if (lastAudioVolume != std::numeric_limits<float>::min())
  745. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setVolume, (jfloat) lastAudioVolume, (jfloat) lastAudioVolume);
  746. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.start);
  747. currentState = State::started;
  748. }
  749. void pause()
  750. {
  751. if (! getCurrentStateInfo().canCallPause)
  752. {
  753. jassertfalse;
  754. return;
  755. }
  756. getEnv()->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.pause);
  757. currentState = State::paused;
  758. }
  759. bool isPlaying() const
  760. {
  761. return getCurrentStateInfo().isPlaying;
  762. }
  763. void setPlayPosition (jint newPositionMs)
  764. {
  765. if (! getCurrentStateInfo().canCallSeekTo)
  766. {
  767. jassertfalse;
  768. return;
  769. }
  770. getEnv()->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.seekTo, (jint) newPositionMs);
  771. }
  772. jint getPlayPosition() const
  773. {
  774. if (! getCurrentStateInfo().canCallGetCurrentPosition)
  775. return 0.0;
  776. return getEnv()->CallIntMethod (nativeMediaPlayer, AndroidMediaPlayer.getCurrentPosition);
  777. }
  778. void setPlaySpeed (double newSpeed)
  779. {
  780. if (! getCurrentStateInfo().canCallSetPlaybackParams)
  781. {
  782. jassertfalse;
  783. return;
  784. }
  785. auto* env = getEnv();
  786. auto playbackParams = LocalRef<jobject> (env->CallObjectMethod (nativeMediaPlayer, AndroidMediaPlayer.getPlaybackParams));
  787. LocalRef<jobject> (env->CallObjectMethod (playbackParams, AndroidPlaybackParams.setSpeed, (jfloat) newSpeed));
  788. env->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setPlaybackParams, playbackParams.get());
  789. if (jniCheckHasExceptionOccurredAndClear())
  790. {
  791. // MediaPlayer can't handle speed provided!
  792. jassertfalse;
  793. }
  794. }
  795. double getPlaySpeed() const
  796. {
  797. if (! getCurrentStateInfo().canCallGetPlaybackParams)
  798. return 0.0;
  799. auto* env = getEnv();
  800. auto playbackParams = LocalRef<jobject> (env->CallObjectMethod (nativeMediaPlayer, AndroidMediaPlayer.getPlaybackParams));
  801. return (double) env->CallFloatMethod (playbackParams, AndroidPlaybackParams.getSpeed);
  802. }
  803. void setAudioVolume (float newVolume)
  804. {
  805. if (! getCurrentStateInfo().canCallSetVolume)
  806. {
  807. jassertfalse;
  808. return;
  809. }
  810. lastAudioVolume = jlimit (0.0f, 1.0f, newVolume);
  811. if (nativeMediaPlayer.get() != nullptr)
  812. getEnv()->CallVoidMethod (nativeMediaPlayer, AndroidMediaPlayer.setVolume, (jfloat) lastAudioVolume, (jfloat) lastAudioVolume);
  813. }
  814. float getAudioVolume() const
  815. {
  816. // There is NO getVolume() in MediaPlayer, so the value returned here can be incorrect!
  817. return lastAudioVolume;
  818. }
  819. private:
  820. //=============================================================================
  821. struct StateInfo
  822. {
  823. int playbackStateFlag = 0, allowedActions = 0;
  824. bool isPlaying, canCallGetCurrentPosition, canCallGetVideoDuration,
  825. canCallGetVideoHeight, canCallGetVideoWidth, canCallGetPlaybackParams,
  826. canCallPause, canCallPrepare, canCallSeekTo, canCallSetAudioAttributes,
  827. canCallSetDataSource, canCallSetPlaybackParams, canCallSetVolume,
  828. canCallStart, canCallStop;
  829. };
  830. enum class State
  831. {
  832. idle, initialised, preparing, prepared, started, paused, stopped, complete, error, end
  833. };
  834. static constexpr StateInfo stateInfos[] = {
  835. /* idle */
  836. {PlaybackState::STATE_NONE, PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  837. false, true, false, true, true, false, false, false, false, true,
  838. true, false, true, false, false},
  839. /* initialised */
  840. {PlaybackState::STATE_NONE, 0, // NB: could use action prepare, but that's API 24 onwards only
  841. false, true, false, true, true, true, false, true, false, true,
  842. false, true, true, false, false},
  843. /* preparing */
  844. {PlaybackState::STATE_BUFFERING, 0,
  845. false, false, false, false, false, true, false, false, false, false,
  846. false, false, false, false, false},
  847. /* prepared */
  848. {PlaybackState::STATE_PAUSED,
  849. PlaybackState::ACTION_PLAY | PlaybackState::ACTION_PLAY_PAUSE | PlaybackState::ACTION_PLAY_FROM_MEDIA_ID | PlaybackState::ACTION_STOP | PlaybackState::ACTION_SEEK_TO,
  850. false, true, true, true, true, true, false, false, true, true,
  851. false, true, true, true, true},
  852. /* started */
  853. {PlaybackState::STATE_PLAYING,
  854. PlaybackState::ACTION_PAUSE | PlaybackState::ACTION_PLAY_PAUSE | PlaybackState::ACTION_SEEK_TO | PlaybackState::ACTION_STOP | PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  855. true, true, true, true, true, true, true, false, true, true,
  856. false, true, true, true, true},
  857. /* paused */
  858. {PlaybackState::STATE_PAUSED,
  859. PlaybackState::ACTION_PLAY | PlaybackState::ACTION_PLAY_PAUSE | PlaybackState::ACTION_SEEK_TO | PlaybackState::ACTION_STOP | PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  860. false, true, true, true, true, true, true, false, true, true,
  861. false, true, true, true, true},
  862. /* stopped */
  863. {PlaybackState::STATE_STOPPED,
  864. PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  865. false, true, true, true, true, true, false, true, false, true,
  866. false, false, true, false, true},
  867. /* complete */
  868. {PlaybackState::STATE_PAUSED,
  869. PlaybackState::ACTION_SEEK_TO | PlaybackState::ACTION_STOP | PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  870. false, true, true, true, true, true, true, false, true, true,
  871. false, true, true, true, true},
  872. /* error */
  873. {PlaybackState::STATE_ERROR,
  874. PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  875. false, false, false, false, false, false, false, false, false, false,
  876. false, false, false, false, false},
  877. /* end */
  878. {PlaybackState::STATE_NONE,
  879. PlaybackState::ACTION_PLAY_FROM_MEDIA_ID,
  880. false, false, false, false, false, false, false, false, false, false,
  881. false, false, false, false, false}
  882. };
  883. StateInfo getCurrentStateInfo() const noexcept { return stateInfos[static_cast<int> (currentState)]; }
  884. //==============================================================================
  885. MediaSession& owner;
  886. GlobalRef nativeMediaPlayer;
  887. MediaPlayerListener mediaPlayerListener;
  888. GlobalRef nativeMediaPlayerListener;
  889. float lastAudioVolume = std::numeric_limits<float>::min();
  890. GlobalRef videoSurfaceHolder;
  891. State currentState = State::idle;
  892. //==============================================================================
  893. void onPrepared (LocalRef<jobject>& mediaPlayer) override
  894. {
  895. JUCE_VIDEO_LOG ("MediaPlayer::onPrepared()");
  896. ignoreUnused (mediaPlayer);
  897. currentState = State::prepared;
  898. owner.playerPrepared();
  899. }
  900. void onBufferingUpdate (LocalRef<jobject>& mediaPlayer, int progress) override
  901. {
  902. ignoreUnused (mediaPlayer);
  903. owner.playerBufferingUpdated (progress);
  904. }
  905. void onSeekComplete (LocalRef<jobject>& mediaPlayer) override
  906. {
  907. JUCE_VIDEO_LOG ("MediaPlayer::onSeekComplete()");
  908. ignoreUnused (mediaPlayer);
  909. owner.playerSeekCompleted();
  910. }
  911. void onCompletion (LocalRef<jobject>& mediaPlayer) override
  912. {
  913. JUCE_VIDEO_LOG ("MediaPlayer::onCompletion()");
  914. ignoreUnused (mediaPlayer);
  915. currentState = State::complete;
  916. owner.playerPlaybackCompleted();
  917. }
  918. enum
  919. {
  920. MEDIA_INFO_UNKNOWN = 1,
  921. MEDIA_INFO_VIDEO_RENDERING_START = 3,
  922. MEDIA_INFO_VIDEO_TRACK_LAGGING = 700,
  923. MEDIA_INFO_BUFFERING_START = 701,
  924. MEDIA_INFO_BUFFERING_END = 702,
  925. MEDIA_INFO_NETWORK_BANDWIDTH = 703,
  926. MEDIA_INFO_BAD_INTERLEAVING = 800,
  927. MEDIA_INFO_NOT_SEEKABLE = 801,
  928. MEDIA_INFO_METADATA_UPDATE = 802,
  929. MEDIA_INFO_AUDIO_NOT_PLAYING = 804,
  930. MEDIA_INFO_VIDEO_NOT_PLAYING = 805,
  931. MEDIA_INFO_UNSUPPORTED_SUBTITE = 901,
  932. MEDIA_INFO_SUBTITLE_TIMED_OUT = 902
  933. };
  934. bool onInfo (LocalRef<jobject>& mediaPlayer, int what, int extra) override
  935. {
  936. JUCE_VIDEO_LOG ("MediaPlayer::onInfo(), infoCode: " + String (what) + " (" + infoCodeToString (what) + ")"
  937. + ", extraCode: " + String (extra));
  938. ignoreUnused (mediaPlayer, extra);
  939. if (what == MEDIA_INFO_BUFFERING_START)
  940. owner.playerBufferingStarted();
  941. else if (what == MEDIA_INFO_BUFFERING_END)
  942. owner.playerBufferingEnded();
  943. return true;
  944. }
  945. static String infoCodeToString (int code)
  946. {
  947. switch (code)
  948. {
  949. case MEDIA_INFO_UNKNOWN: return "Unknown";
  950. case MEDIA_INFO_VIDEO_RENDERING_START: return "Rendering start";
  951. case MEDIA_INFO_VIDEO_TRACK_LAGGING: return "Video track lagging";
  952. case MEDIA_INFO_BUFFERING_START: return "Buffering start";
  953. case MEDIA_INFO_BUFFERING_END: return "Buffering end";
  954. case MEDIA_INFO_NETWORK_BANDWIDTH: return "Network bandwidth info available";
  955. case MEDIA_INFO_BAD_INTERLEAVING: return "Bad interleaving";
  956. case MEDIA_INFO_NOT_SEEKABLE: return "Video not seekable";
  957. case MEDIA_INFO_METADATA_UPDATE: return "Metadata updated";
  958. case MEDIA_INFO_AUDIO_NOT_PLAYING: return "Audio not playing";
  959. case MEDIA_INFO_VIDEO_NOT_PLAYING: return "Video not playing";
  960. case MEDIA_INFO_UNSUPPORTED_SUBTITE: return "Unsupported subtitle";
  961. case MEDIA_INFO_SUBTITLE_TIMED_OUT: return "Subtitle timed out";
  962. default: return "";
  963. }
  964. }
  965. bool onError (LocalRef<jobject>& mediaPlayer, int what, int extra) override
  966. {
  967. auto errorMessage = errorCodeToString (what);
  968. auto extraMessage = errorCodeToString (extra);
  969. if (extraMessage.isNotEmpty())
  970. errorMessage << ", " << extraMessage;
  971. JUCE_VIDEO_LOG ("MediaPlayer::onError(), errorCode: " + String (what) + " (" + errorMessage + ")"
  972. + ", extraCode: " + String (extra) + " (" + extraMessage + ")");
  973. ignoreUnused (mediaPlayer);
  974. currentState = State::error;
  975. owner.errorOccurred (errorMessage);
  976. return true;
  977. }
  978. static String errorCodeToString (int code)
  979. {
  980. enum
  981. {
  982. MEDIA_ERROR_UNSUPPORTED = -1010,
  983. MEDIA_ERROR_MALFORMED = -1007,
  984. MEDIA_ERROR_IO = -1004,
  985. MEDIA_ERROR_TIMED_OUT = -110,
  986. MEDIA_ERROR_UNKNOWN = 1,
  987. MEDIA_ERROR_SERVER_DIED = 100,
  988. MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200
  989. };
  990. switch (code)
  991. {
  992. case MEDIA_ERROR_UNSUPPORTED: return "Unsupported bitstream";
  993. case MEDIA_ERROR_MALFORMED: return "Malformed bitstream";
  994. case MEDIA_ERROR_IO: return "File/Network I/O error";
  995. case MEDIA_ERROR_TIMED_OUT: return "Timed out";
  996. case MEDIA_ERROR_UNKNOWN: return "Unknown error";
  997. case MEDIA_ERROR_SERVER_DIED: return "Media server died (playback restart required)";
  998. case MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: return "Video container not valid for progressive playback";
  999. default: return "";
  1000. }
  1001. }
  1002. //==============================================================================
  1003. static StringArray getNativeMediaPlayerListenerInterfaces()
  1004. {
  1005. #define IFPREFIX "android/media/MediaPlayer$"
  1006. return { IFPREFIX "OnCompletionListener", IFPREFIX "OnErrorListener",
  1007. IFPREFIX "OnInfoListener", IFPREFIX "OnPreparedListener",
  1008. IFPREFIX "OnBufferingUpdateListener", IFPREFIX "OnSeekCompleteListener"
  1009. };
  1010. #undef IFPREFIX
  1011. }
  1012. };
  1013. //==============================================================================
  1014. Pimpl& owner;
  1015. int sdkVersion;
  1016. GlobalRef audioAttributes;
  1017. GlobalRef nativeMediaSession;
  1018. GlobalRef mediaSessionCallback;
  1019. GlobalRef playbackStateBuilder;
  1020. Controller controller;
  1021. Player player;
  1022. GlobalRef audioManager;
  1023. AudioManagerOnAudioFocusChangeListener audioFocusChangeListener;
  1024. GlobalRef nativeAudioFocusChangeListener;
  1025. GlobalRef audioFocusRequest;
  1026. GlobalRef storedPlaybackState;
  1027. bool pendingSeekRequest = false;
  1028. bool playerBufferingInProgress = false;
  1029. bool usesBuffering = false;
  1030. SparseSet<int> bufferedRegions;
  1031. double playSpeedMult = 1.0;
  1032. bool hasAudioFocus = false;
  1033. //==============================================================================
  1034. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  1035. METHOD (constructor, "<init>", "(J)V") \
  1036. CALLBACK (pauseCallback, "mediaSessionPause", "(J)V") \
  1037. CALLBACK (playCallback, "mediaSessionPlay", "(J)V") \
  1038. CALLBACK (playFromMediaIdCallback, "mediaSessionPlayFromMediaId", "(JLjava/lang/String;Landroid/os/Bundle;)V") \
  1039. CALLBACK (seekToCallback, "mediaSessionSeekTo", "(JJ)V") \
  1040. CALLBACK (stopCallback, "mediaSessionStop", "(J)V")
  1041. DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidMediaSessionCallback, "com/roli/juce/MediaSessionCallback", 21)
  1042. #undef JNI_CLASS_MEMBERS
  1043. LocalRef<jobject> createCallbackObject()
  1044. {
  1045. return LocalRef<jobject> (getEnv()->NewObject (AndroidMediaSessionCallback,
  1046. AndroidMediaSessionCallback.constructor,
  1047. reinterpret_cast<jlong> (this)));
  1048. }
  1049. //==============================================================================
  1050. // MediaSession callbacks
  1051. static void pauseCallback (JNIEnv*, jobject, jlong host)
  1052. {
  1053. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host))
  1054. {
  1055. JUCE_VIDEO_LOG ("MediaSession::pauseCallback()");
  1056. myself->player.pause();
  1057. myself->updatePlaybackState();
  1058. myself->abandonAudioFocus();
  1059. }
  1060. }
  1061. static void playCallback (JNIEnv*, jobject, jlong host)
  1062. {
  1063. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host))
  1064. {
  1065. JUCE_VIDEO_LOG ("MediaSession::playCallback()");
  1066. myself->requestAudioFocus();
  1067. if (! myself->hasAudioFocus)
  1068. {
  1069. myself->errorOccurred ("Application has been denied audio focus. Try again later.");
  1070. return;
  1071. }
  1072. getEnv()->CallVoidMethod (myself->nativeMediaSession, AndroidMediaSession.setActive, true);
  1073. myself->player.play();
  1074. myself->setSpeed (myself->playSpeedMult);
  1075. myself->updatePlaybackState();
  1076. }
  1077. }
  1078. static void playFromMediaIdCallback (JNIEnv* env, jobject, jlong host, jstring mediaId, jobject extras)
  1079. {
  1080. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host))
  1081. {
  1082. JUCE_VIDEO_LOG ("MediaSession::playFromMediaIdCallback()");
  1083. myself->player.load (LocalRef<jstring> ((jstring) env->NewLocalRef(mediaId)), LocalRef<jobject> (env->NewLocalRef(extras)));
  1084. myself->updatePlaybackState();
  1085. }
  1086. }
  1087. static void seekToCallback (JNIEnv* /*env*/, jobject, jlong host, jlong pos)
  1088. {
  1089. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host))
  1090. {
  1091. JUCE_VIDEO_LOG ("MediaSession::seekToCallback()");
  1092. myself->pendingSeekRequest = true;
  1093. myself->player.setPlayPosition ((jint) pos);
  1094. myself->updatePlaybackState();
  1095. }
  1096. }
  1097. static void stopCallback(JNIEnv* env, jobject, jlong host)
  1098. {
  1099. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::MediaSession*> (host))
  1100. {
  1101. JUCE_VIDEO_LOG ("MediaSession::stopCallback()");
  1102. env->CallVoidMethod (myself->nativeMediaSession, AndroidMediaSession.setActive, false);
  1103. myself->player.closeVideo();
  1104. myself->updatePlaybackState();
  1105. myself->abandonAudioFocus();
  1106. myself->owner.closeVideoFinished();
  1107. }
  1108. }
  1109. //==============================================================================
  1110. bool isSeekInProgress() const noexcept
  1111. {
  1112. if (pendingSeekRequest)
  1113. return true;
  1114. if (! usesBuffering)
  1115. return false;
  1116. // NB: player sometimes notifies us about buffering, but only for regions that
  1117. // were previously buffered already. For buffering happening for the first time,
  1118. // we don't get such notification...
  1119. if (playerBufferingInProgress)
  1120. return true;
  1121. auto playPos = player.getPlayPosition();
  1122. auto durationMs = player.getVideoDuration();
  1123. int playPosPercent = 100 * playPos / static_cast<double> (durationMs);
  1124. // NB: assuming the playback will start roughly when there is 5% of content loaded...
  1125. return ! bufferedRegions.containsRange (Range<int> (playPosPercent, jmin (101, playPosPercent + 5)));
  1126. }
  1127. void updatePlaybackState()
  1128. {
  1129. getEnv()->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setPlaybackState, getCurrentPlaybackState().get());
  1130. }
  1131. LocalRef<jobject> getCurrentPlaybackState()
  1132. {
  1133. static constexpr int bufferingState = 6;
  1134. auto playbackStateFlag = isSeekInProgress() ? bufferingState : player.getPlaybackStateFlag();
  1135. auto playPos = player.getPlayPosition();
  1136. auto playSpeed = player.getPlaySpeed();
  1137. auto allowedActions = player.getAllowedActions();
  1138. auto* env = getEnv();
  1139. LocalRef<jobject> (env->CallObjectMethod (playbackStateBuilder, AndroidPlaybackStateBuilder.setState,
  1140. (jint) playbackStateFlag, (jlong) playPos, (jfloat) playSpeed));
  1141. LocalRef<jobject> (env->CallObjectMethod (playbackStateBuilder, AndroidPlaybackStateBuilder.setActions, (jint) allowedActions));
  1142. return LocalRef<jobject> (env->CallObjectMethod (playbackStateBuilder, AndroidPlaybackStateBuilder.build));
  1143. }
  1144. //==============================================================================
  1145. void playerPrepared()
  1146. {
  1147. resetState();
  1148. updateMetadata();
  1149. owner.loadFinished();
  1150. }
  1151. void playerBufferingStarted() { playerBufferingInProgress = true; }
  1152. void playerBufferingEnded() { playerBufferingInProgress = false; }
  1153. void playerBufferingUpdated (int progress)
  1154. {
  1155. usesBuffering = true;
  1156. updatePlaybackState();
  1157. auto playPos = player.getPlayPosition();
  1158. auto durationMs = player.getVideoDuration();
  1159. int playPosPercent = 100 * playPos / static_cast<double> (durationMs);
  1160. bufferedRegions.addRange (Range<int> (playPosPercent, progress + 1));
  1161. String ranges;
  1162. for (auto& r : bufferedRegions.getRanges())
  1163. ranges << "[" << r.getStart() << "%, " << r.getEnd() - 1 << "%] ";
  1164. JUCE_VIDEO_LOG ("Buffering status update, seek pos: " + String (playPosPercent) + "%, buffered regions: " + ranges);
  1165. }
  1166. void playerSeekCompleted()
  1167. {
  1168. pendingSeekRequest = false;
  1169. updatePlaybackState();
  1170. }
  1171. void playerPlaybackCompleted()
  1172. {
  1173. player.pause();
  1174. abandonAudioFocus();
  1175. pendingSeekRequest = true;
  1176. player.setPlayPosition (0);
  1177. updatePlaybackState();
  1178. }
  1179. void updateMetadata()
  1180. {
  1181. auto* env = getEnv();
  1182. auto metadataBuilder = LocalRef<jobject> (env->NewObject (AndroidMediaMetadataBuilder,
  1183. AndroidMediaMetadataBuilder.constructor));
  1184. auto durationMs = player.getVideoDuration();
  1185. auto jDurationKey = javaString ("android.media.metadata.DURATION");
  1186. LocalRef<jobject> (env->CallObjectMethod (metadataBuilder,
  1187. AndroidMediaMetadataBuilder.putLong,
  1188. jDurationKey.get(),
  1189. (jlong) durationMs));
  1190. auto jNumTracksKey = javaString ("android.media.metadata.NUM_TRACKS");
  1191. LocalRef<jobject> (env->CallObjectMethod (metadataBuilder,
  1192. AndroidMediaMetadataBuilder.putLong,
  1193. jNumTracksKey.get(),
  1194. (jlong) 1));
  1195. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setMetadata,
  1196. env->CallObjectMethod (metadataBuilder, AndroidMediaMetadataBuilder.build));
  1197. }
  1198. void errorOccurred (const String& errorMessage)
  1199. {
  1200. auto* env = getEnv();
  1201. // Propagate error to session controller(s) and ...
  1202. LocalRef<jobject> (env->CallObjectMethod (playbackStateBuilder, AndroidPlaybackStateBuilder.setErrorMessage,
  1203. javaString (errorMessage).get()));
  1204. auto state = LocalRef<jobject> (env->CallObjectMethod (playbackStateBuilder, AndroidPlaybackStateBuilder.build));
  1205. env->CallVoidMethod (nativeMediaSession, AndroidMediaSession.setPlaybackState, state.get());
  1206. // ...also notify JUCE side client
  1207. owner.errorOccurred (errorMessage);
  1208. }
  1209. //==============================================================================
  1210. static LocalRef<jobject> createAudioFocusRequestIfNecessary (int sdkVersion, const GlobalRef& audioAttributes,
  1211. const GlobalRef& nativeAudioFocusChangeListener)
  1212. {
  1213. if (sdkVersion < 26)
  1214. return LocalRef<jobject>();
  1215. auto* env = getEnv();
  1216. auto requestBuilderClass = LocalRef<jclass> (env->FindClass ("android/media/AudioFocusRequest$Builder"));
  1217. static jmethodID constructor = env->GetMethodID (requestBuilderClass, "<init>", "(I)V");
  1218. static jmethodID buildMethod = env->GetMethodID (requestBuilderClass, "build", "()Landroid/media/AudioFocusRequest;");
  1219. static jmethodID setAudioAttributesMethod = env->GetMethodID (requestBuilderClass, "setAudioAttributes",
  1220. "(Landroid/media/AudioAttributes;)Landroid/media/AudioFocusRequest$Builder;");
  1221. static jmethodID setOnAudioFocusChangeListenerMethod = env->GetMethodID (requestBuilderClass, "setOnAudioFocusChangeListener",
  1222. "(Landroid/media/AudioManager$OnAudioFocusChangeListener;)Landroid/media/AudioFocusRequest$Builder;");
  1223. static constexpr jint audioFocusGain = 1;
  1224. auto requestBuilder = LocalRef<jobject> (env->NewObject (requestBuilderClass, constructor, audioFocusGain));
  1225. LocalRef<jobject> (env->CallObjectMethod (requestBuilder, setAudioAttributesMethod, audioAttributes.get()));
  1226. LocalRef<jobject> (env->CallObjectMethod (requestBuilder, setOnAudioFocusChangeListenerMethod, nativeAudioFocusChangeListener.get()));
  1227. return LocalRef<jobject> (env->CallObjectMethod (requestBuilder, buildMethod));
  1228. }
  1229. void requestAudioFocus()
  1230. {
  1231. static constexpr jint audioFocusGain = 1;
  1232. static constexpr jint streamMusic = 3;
  1233. static constexpr jint audioFocusRequestGranted = 1;
  1234. jint result = audioFocusRequestGranted;
  1235. if (sdkVersion >= 26)
  1236. {
  1237. static jmethodID requestAudioFocusMethod = getEnv()->GetMethodID (AndroidAudioManager, "requestAudioFocus",
  1238. "(Landroid/media/AudioFocusRequest;)I");
  1239. result = getEnv()->CallIntMethod (audioManager, requestAudioFocusMethod, audioFocusRequest.get());
  1240. }
  1241. else
  1242. {
  1243. result = getEnv()->CallIntMethod (audioManager, AndroidAudioManager.requestAudioFocus,
  1244. nativeAudioFocusChangeListener.get(), streamMusic, audioFocusGain);
  1245. }
  1246. hasAudioFocus = result == audioFocusRequestGranted;
  1247. }
  1248. void abandonAudioFocus()
  1249. {
  1250. if (! hasAudioFocus)
  1251. return;
  1252. static constexpr jint audioFocusRequestGranted = 1;
  1253. jint result = audioFocusRequestGranted;
  1254. if (sdkVersion >= 26)
  1255. {
  1256. static jmethodID abandonAudioFocusMethod = getEnv()->GetMethodID (AndroidAudioManager, "abandonAudioFocusRequest",
  1257. "(Landroid/media/AudioFocusRequest;)I");
  1258. result = getEnv()->CallIntMethod (audioManager, abandonAudioFocusMethod, audioFocusRequest.get());
  1259. }
  1260. else
  1261. {
  1262. result = getEnv()->CallIntMethod (audioManager, AndroidAudioManager.abandonAudioFocus,
  1263. nativeAudioFocusChangeListener.get());
  1264. }
  1265. // NB: granted in this case means "granted to change the focus to abandoned"...
  1266. hasAudioFocus = result != audioFocusRequestGranted;
  1267. }
  1268. void onAudioFocusChange (int changeType) override
  1269. {
  1270. static constexpr jint audioFocusGain = 1;
  1271. if (changeType == audioFocusGain)
  1272. JUCE_VIDEO_LOG ("Audio focus gained");
  1273. else
  1274. JUCE_VIDEO_LOG ("Audio focus lost");
  1275. if (changeType != audioFocusGain)
  1276. {
  1277. if (isPlaying())
  1278. {
  1279. JUCE_VIDEO_LOG ("Received a request to abandon audio focus. Stopping playback...");
  1280. stop();
  1281. }
  1282. abandonAudioFocus();
  1283. }
  1284. }
  1285. //==============================================================================
  1286. void playbackStarted()
  1287. {
  1288. owner.playbackStarted();
  1289. }
  1290. void playbackStopped()
  1291. {
  1292. owner.playbackStopped();
  1293. }
  1294. //==============================================================================
  1295. void resetState()
  1296. {
  1297. usesBuffering = false;
  1298. bufferedRegions.clear();
  1299. playerBufferingInProgress = false;
  1300. pendingSeekRequest = false;
  1301. playSpeedMult = 1.0;
  1302. hasAudioFocus = false;
  1303. }
  1304. //==============================================================================
  1305. static LocalRef<jobject> getAudioAttributes()
  1306. {
  1307. // Video requires SDK version 21 or higher
  1308. jassert (getAndroidSDKVersion() >= 21);
  1309. auto* env = getEnv();
  1310. auto audioAttribsBuilder = LocalRef<jobject> (env->NewObject (AndroidAudioAttributesBuilder,
  1311. AndroidAudioAttributesBuilder.constructor));
  1312. static constexpr jint contentTypeMovie = 3;
  1313. static constexpr jint usageMedia = 1;
  1314. LocalRef<jobject> (env->CallObjectMethod (audioAttribsBuilder, AndroidAudioAttributesBuilder.setContentType, contentTypeMovie));
  1315. LocalRef<jobject> (env->CallObjectMethod (audioAttribsBuilder, AndroidAudioAttributesBuilder.setUsage, usageMedia));
  1316. return LocalRef<jobject> (env->CallObjectMethod (audioAttribsBuilder, AndroidAudioAttributesBuilder.build));
  1317. }
  1318. };
  1319. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  1320. //==============================================================================
  1321. class SystemVolumeListener
  1322. {
  1323. public:
  1324. SystemVolumeListener (Pimpl& ownerToUse)
  1325. : owner (ownerToUse),
  1326. nativeObserver (createCallbackObject())
  1327. {
  1328. setEnabled (true);
  1329. }
  1330. ~SystemVolumeListener()
  1331. {
  1332. setEnabled (false);
  1333. }
  1334. private:
  1335. Pimpl& owner;
  1336. GlobalRef nativeObserver;
  1337. //==============================================================================
  1338. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
  1339. METHOD (constructor, "<init>", "(Landroid/app/Activity;J)V") \
  1340. METHOD (setEnabled, "setEnabled", "(Z)V") \
  1341. CALLBACK (systemVolumeChangedCallback, "mediaSessionSystemVolumeChanged", "(J)V")
  1342. DECLARE_JNI_CLASS_WITH_MIN_SDK (SystemVolumeObserver, "com/roli/juce/SystemVolumeObserver", 21)
  1343. #undef JNI_CLASS_MEMBERS
  1344. LocalRef<jobject> createCallbackObject()
  1345. {
  1346. return LocalRef<jobject> (getEnv()->NewObject (SystemVolumeObserver,
  1347. SystemVolumeObserver.constructor,
  1348. getCurrentActivity().get(),
  1349. reinterpret_cast<jlong> (this)));
  1350. }
  1351. public:
  1352. void setEnabled (bool shouldBeEnabled)
  1353. {
  1354. getEnv()->CallVoidMethod (nativeObserver, SystemVolumeObserver.setEnabled, shouldBeEnabled);
  1355. // Send first notification instantly to ensure sync.
  1356. if (shouldBeEnabled)
  1357. systemVolumeChanged();
  1358. }
  1359. private:
  1360. //==============================================================================
  1361. void systemVolumeChanged()
  1362. {
  1363. WeakReference<SystemVolumeListener> weakThis (this);
  1364. MessageManager::callAsync ([weakThis]() mutable
  1365. {
  1366. if (weakThis == nullptr)
  1367. return;
  1368. if (weakThis->owner.owner.onGlobalMediaVolumeChanged != nullptr)
  1369. weakThis->owner.owner.onGlobalMediaVolumeChanged();
  1370. });
  1371. }
  1372. //==============================================================================
  1373. static void systemVolumeChangedCallback (JNIEnv*, jobject, jlong host)
  1374. {
  1375. if (auto* myself = reinterpret_cast<VideoComponent::Pimpl::SystemVolumeListener*> (host))
  1376. myself->systemVolumeChanged();
  1377. }
  1378. JUCE_DECLARE_WEAK_REFERENCEABLE (SystemVolumeListener)
  1379. };
  1380. //==============================================================================
  1381. VideoComponent& owner;
  1382. MediaSession mediaSession;
  1383. GlobalRef activityLifeListener;
  1384. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  1385. SystemVolumeListener systemVolumeListener;
  1386. #endif
  1387. GlobalRef surfaceHolderCallback;
  1388. std::function<void(const URL&, Result)> loadFinishedCallback;
  1389. bool wasOpen = false;
  1390. //==============================================================================
  1391. void loadFinished()
  1392. {
  1393. owner.resized();
  1394. if (loadFinishedCallback != nullptr)
  1395. {
  1396. loadFinishedCallback (currentURL, Result::ok());
  1397. loadFinishedCallback = nullptr;
  1398. }
  1399. }
  1400. void closeVideoFinished()
  1401. {
  1402. owner.resized();
  1403. }
  1404. void errorOccurred (const String& errorMessage)
  1405. {
  1406. if (owner.onErrorOccurred != nullptr)
  1407. owner.onErrorOccurred (errorMessage);
  1408. }
  1409. void playbackStarted()
  1410. {
  1411. if (owner.onPlaybackStarted != nullptr)
  1412. owner.onPlaybackStarted();
  1413. }
  1414. void playbackStopped()
  1415. {
  1416. if (owner.onPlaybackStopped != nullptr)
  1417. owner.onPlaybackStopped();
  1418. }
  1419. //==============================================================================
  1420. void surfaceChanged (LocalRef<jobject> holder, int /*format*/, int /*width*/, int /*height*/) override
  1421. {
  1422. mediaSession.setDisplay (holder);
  1423. }
  1424. void surfaceDestroyed (LocalRef<jobject> /*holder*/) override
  1425. {
  1426. mediaSession.setDisplay (LocalRef<jobject>());
  1427. }
  1428. void surfaceCreated (LocalRef<jobject> /*holder*/) override
  1429. {
  1430. }
  1431. //==============================================================================
  1432. void onActivityPaused (jobject) override
  1433. {
  1434. wasOpen = isOpen();
  1435. if (! wasOpen)
  1436. return;
  1437. JUCE_VIDEO_LOG ("App paused, releasing media player...");
  1438. mediaSession.storeState();
  1439. mediaSession.closeVideo();
  1440. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  1441. systemVolumeListener.setEnabled (false);
  1442. #endif
  1443. }
  1444. void onActivityResumed (jobject) override
  1445. {
  1446. if (! wasOpen)
  1447. return;
  1448. JUCE_VIDEO_LOG ("App resumed, restoring media player...");
  1449. loadAsync (currentURL, [this](const URL&, Result r)
  1450. {
  1451. if (r.wasOk())
  1452. mediaSession.restoreState();
  1453. });
  1454. #if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
  1455. systemVolumeListener.setEnabled (true);
  1456. #endif
  1457. }
  1458. //==============================================================================
  1459. #endif
  1460. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  1461. };
  1462. //==============================================================================
  1463. constexpr VideoComponent::Pimpl::MediaSession::Player::StateInfo VideoComponent::Pimpl::MediaSession::Player::stateInfos[];
  1464. //==============================================================================
  1465. VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback_Class VideoComponent::Pimpl::MediaSession::AndroidMediaSessionCallback;
  1466. VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback_Class VideoComponent::Pimpl::MediaSession::Controller::AndroidMediaControllerCallback;
  1467. VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver_Class VideoComponent::Pimpl::SystemVolumeListener::SystemVolumeObserver;