1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.media; |
6 | |
7 | import android.content.Context; |
8 | import android.media.MediaPlayer; |
9 | import android.net.Uri; |
10 | import android.text.TextUtils; |
11 | import android.util.Log; |
12 | import android.view.Surface; |
13 | |
14 | import org.chromium.base.CalledByNative; |
15 | import org.chromium.base.JNINamespace; |
16 | |
17 | import java.io.IOException; |
18 | import java.io.Serializable; |
19 | import java.lang.reflect.InvocationTargetException; |
20 | import java.lang.reflect.Method; |
21 | import java.util.HashMap; |
22 | import java.util.Map; |
23 | |
24 | // A wrapper around android.media.MediaPlayer that allows the native code to use it. |
25 | // See media/base/android/media_player_bridge.cc for the corresponding native code. |
26 | @JNINamespace("media") |
27 | public class MediaPlayerBridge { |
28 | |
29 | private static final String TAG = "MediaPlayerBridge"; |
30 | |
31 | // Local player to forward this to. We don't initialize it here since the subclass might not |
32 | // want it. |
33 | private MediaPlayer mPlayer; |
34 | |
35 | @CalledByNative |
36 | private static MediaPlayerBridge create() { |
37 | return new MediaPlayerBridge(); |
38 | } |
39 | |
40 | protected MediaPlayer getLocalPlayer() { |
41 | if (mPlayer == null) { |
42 | mPlayer = new MediaPlayer(); |
43 | } |
44 | return mPlayer; |
45 | } |
46 | |
47 | @CalledByNative |
48 | protected void setSurface(Surface surface) { |
49 | getLocalPlayer().setSurface(surface); |
50 | } |
51 | |
52 | @CalledByNative |
53 | protected void prepareAsync() throws IllegalStateException { |
54 | getLocalPlayer().prepareAsync(); |
55 | } |
56 | |
57 | @CalledByNative |
58 | protected boolean isPlaying() { |
59 | return getLocalPlayer().isPlaying(); |
60 | } |
61 | |
62 | @CalledByNative |
63 | protected int getVideoWidth() { |
64 | return getLocalPlayer().getVideoWidth(); |
65 | } |
66 | |
67 | @CalledByNative |
68 | protected int getVideoHeight() { |
69 | return getLocalPlayer().getVideoHeight(); |
70 | } |
71 | |
72 | @CalledByNative |
73 | protected int getCurrentPosition() { |
74 | return getLocalPlayer().getCurrentPosition(); |
75 | } |
76 | |
77 | @CalledByNative |
78 | protected int getDuration() { |
79 | return getLocalPlayer().getDuration(); |
80 | } |
81 | |
82 | @CalledByNative |
83 | protected void release() { |
84 | getLocalPlayer().release(); |
85 | } |
86 | |
87 | @CalledByNative |
88 | protected void setVolume(double volume) { |
89 | getLocalPlayer().setVolume((float) volume, (float) volume); |
90 | } |
91 | |
92 | @CalledByNative |
93 | protected void start() { |
94 | getLocalPlayer().start(); |
95 | } |
96 | |
97 | @CalledByNative |
98 | protected void pause() { |
99 | getLocalPlayer().pause(); |
100 | } |
101 | |
102 | @CalledByNative |
103 | protected void seekTo(int msec) throws IllegalStateException { |
104 | getLocalPlayer().seekTo(msec); |
105 | } |
106 | |
107 | @CalledByNative |
108 | protected boolean setDataSource( |
109 | Context context, String url, String cookies, boolean hideUrlLog) { |
110 | Uri uri = Uri.parse(url); |
111 | HashMap<String, String> headersMap = new HashMap<String, String>(); |
112 | if (hideUrlLog) |
113 | headersMap.put("x-hide-urls-from-log", "true"); |
114 | if (!TextUtils.isEmpty(cookies)) |
115 | headersMap.put("Cookie", cookies); |
116 | try { |
117 | getLocalPlayer().setDataSource(context, uri, headersMap); |
118 | return true; |
119 | } catch (Exception e) { |
120 | return false; |
121 | } |
122 | } |
123 | |
124 | protected void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) { |
125 | getLocalPlayer().setOnBufferingUpdateListener(listener); |
126 | } |
127 | |
128 | protected void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) { |
129 | getLocalPlayer().setOnCompletionListener(listener); |
130 | } |
131 | |
132 | protected void setOnErrorListener(MediaPlayer.OnErrorListener listener) { |
133 | getLocalPlayer().setOnErrorListener(listener); |
134 | } |
135 | |
136 | protected void setOnPreparedListener(MediaPlayer.OnPreparedListener listener) { |
137 | getLocalPlayer().setOnPreparedListener(listener); |
138 | } |
139 | |
140 | protected void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener) { |
141 | getLocalPlayer().setOnSeekCompleteListener(listener); |
142 | } |
143 | |
144 | protected void setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener) { |
145 | getLocalPlayer().setOnVideoSizeChangedListener(listener); |
146 | } |
147 | |
148 | private static class AllowedOperations { |
149 | private final boolean mCanPause; |
150 | private final boolean mCanSeekForward; |
151 | private final boolean mCanSeekBackward; |
152 | |
153 | private AllowedOperations(boolean canPause, boolean canSeekForward, |
154 | boolean canSeekBackward) { |
155 | mCanPause = canPause; |
156 | mCanSeekForward = canSeekForward; |
157 | mCanSeekBackward = canSeekBackward; |
158 | } |
159 | |
160 | @CalledByNative("AllowedOperations") |
161 | private boolean canPause() { return mCanPause; } |
162 | |
163 | @CalledByNative("AllowedOperations") |
164 | private boolean canSeekForward() { return mCanSeekForward; } |
165 | |
166 | @CalledByNative("AllowedOperations") |
167 | private boolean canSeekBackward() { return mCanSeekBackward; } |
168 | } |
169 | |
170 | /** |
171 | * Returns an AllowedOperations object to show all the operations that are |
172 | * allowed on the media player. |
173 | */ |
174 | @CalledByNative |
175 | private static AllowedOperations getAllowedOperations(MediaPlayer player) { |
176 | boolean canPause = true; |
177 | boolean canSeekForward = true; |
178 | boolean canSeekBackward = true; |
179 | try { |
180 | Method getMetadata = player.getClass().getDeclaredMethod( |
181 | "getMetadata", boolean.class, boolean.class); |
182 | getMetadata.setAccessible(true); |
183 | Object data = getMetadata.invoke(player, false, false); |
184 | if (data != null) { |
185 | Class<?> metadataClass = data.getClass(); |
186 | Method hasMethod = metadataClass.getDeclaredMethod("has", int.class); |
187 | Method getBooleanMethod = metadataClass.getDeclaredMethod("getBoolean", int.class); |
188 | |
189 | int pause = (Integer) metadataClass.getField("PAUSE_AVAILABLE").get(null); |
190 | int seekForward = |
191 | (Integer) metadataClass.getField("SEEK_FORWARD_AVAILABLE").get(null); |
192 | int seekBackward = |
193 | (Integer) metadataClass.getField("SEEK_BACKWARD_AVAILABLE").get(null); |
194 | hasMethod.setAccessible(true); |
195 | getBooleanMethod.setAccessible(true); |
196 | canPause = !((Boolean) hasMethod.invoke(data, pause)) |
197 | || ((Boolean) getBooleanMethod.invoke(data, pause)); |
198 | canSeekForward = !((Boolean) hasMethod.invoke(data, seekForward)) |
199 | || ((Boolean) getBooleanMethod.invoke(data, seekForward)); |
200 | canSeekBackward = !((Boolean) hasMethod.invoke(data, seekBackward)) |
201 | || ((Boolean) getBooleanMethod.invoke(data, seekBackward)); |
202 | } |
203 | } catch (NoSuchMethodException e) { |
204 | Log.e(TAG, "Cannot find getMetadata() method: " + e); |
205 | } catch (InvocationTargetException e) { |
206 | Log.e(TAG, "Cannot invoke MediaPlayer.getMetadata() method: " + e); |
207 | } catch (IllegalAccessException e) { |
208 | Log.e(TAG, "Cannot access metadata: " + e); |
209 | } catch (NoSuchFieldException e) { |
210 | Log.e(TAG, "Cannot find matching fields in Metadata class: " + e); |
211 | } |
212 | return new AllowedOperations(canPause, canSeekForward, canSeekBackward); |
213 | } |
214 | } |