• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2009, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(VIDEO)
29 
30 #include "GraphicsContext.h"
31 #include "MediaPlayerPrivateAndroid.h"
32 #include "SkiaUtils.h"
33 #include "WebCoreJni.h"
34 #include "WebViewCore.h"
35 #include "jni_utility.h"
36 
37 #include <GraphicsJNI.h>
38 #include <JNIHelp.h>
39 #include <SkBitmap.h>
40 
41 using namespace android;
42 
43 namespace WebCore {
44 
45 static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
46 
47 struct MediaPlayerPrivate::JavaGlue
48 {
49     jobject   m_javaProxy;
50     jmethodID m_getInstance;
51     jmethodID m_play;
52     jmethodID m_teardown;
53     jmethodID m_loadPoster;
54     jmethodID m_seek;
55     jmethodID m_pause;
56 };
57 
~MediaPlayerPrivate()58 MediaPlayerPrivate::~MediaPlayerPrivate()
59 {
60     if (m_glue->m_javaProxy) {
61         JNIEnv* env = JSC::Bindings::getJNIEnv();
62         if (env) {
63             env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown);
64             env->DeleteGlobalRef(m_glue->m_javaProxy);
65         }
66     }
67 
68     delete m_glue;
69 }
70 
registerMediaEngine(MediaEngineRegistrar registrar)71 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
72 {
73     registrar(create, getSupportedTypes, supportsType);
74 }
75 
load(const String & url)76 void MediaPlayerPrivate::load(const String& url)
77 {
78     // Just save the URl.
79     m_url = url;
80 }
81 
cancelLoad()82 void MediaPlayerPrivate::cancelLoad()
83 {
84 }
85 
play()86 void MediaPlayerPrivate::play()
87 {
88     JNIEnv* env = JSC::Bindings::getJNIEnv();
89     if (!env || !m_glue->m_javaProxy || !m_url.length())
90         return;
91 
92     m_paused = false;
93     jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
94     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
95     env->DeleteLocalRef(jUrl);
96     checkException(env);
97 }
98 
pause()99 void MediaPlayerPrivate::pause()
100 {
101     JNIEnv* env = JSC::Bindings::getJNIEnv();
102     if (!env || !m_glue->m_javaProxy || !m_url.length())
103         return;
104 
105     m_paused = true;
106     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_pause);
107     checkException(env);
108 }
109 
naturalSize() const110 IntSize MediaPlayerPrivate::naturalSize() const
111 {
112     return m_naturalSize;
113 }
114 
hasVideo() const115 bool MediaPlayerPrivate::hasVideo() const
116 {
117     return false;
118 }
119 
setVisible(bool visible)120 void MediaPlayerPrivate::setVisible(bool visible)
121 {
122     m_isVisible = visible;
123     if (m_isVisible)
124         createJavaPlayerIfNeeded();
125 }
126 
duration() const127 float MediaPlayerPrivate::duration() const
128 {
129     return m_duration;
130 }
131 
currentTime() const132 float MediaPlayerPrivate::currentTime() const
133 {
134     return m_currentTime;
135 }
136 
seek(float time)137 void MediaPlayerPrivate::seek(float time)
138 {
139     JNIEnv* env = JSC::Bindings::getJNIEnv();
140     if (!env || !m_glue->m_javaProxy || !m_url.length())
141         return;
142 
143     m_currentTime = time;
144     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
145     checkException(env);
146 }
147 
seeking() const148 bool MediaPlayerPrivate::seeking() const
149 {
150     return false;
151 }
152 
setEndTime(float)153 void MediaPlayerPrivate::setEndTime(float)
154 {
155 }
156 
setRate(float)157 void MediaPlayerPrivate::setRate(float)
158 {
159 }
160 
paused() const161 bool MediaPlayerPrivate::paused() const
162 {
163     return m_paused;
164 }
165 
setVolume(float)166 void MediaPlayerPrivate::setVolume(float)
167 {
168 }
169 
networkState() const170 MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
171 {
172     return m_networkState;
173 }
174 
readyState() const175 MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
176 {
177     return m_readyState;
178 }
179 
maxTimeSeekable() const180 float MediaPlayerPrivate::maxTimeSeekable() const
181 {
182     return 0;
183 }
184 
maxTimeBuffered() const185 float MediaPlayerPrivate::maxTimeBuffered() const
186 {
187     return 0;
188 }
189 
dataRate() const190 int MediaPlayerPrivate::dataRate() const
191 {
192     return 0;
193 }
194 
totalBytes() const195 unsigned MediaPlayerPrivate::totalBytes() const
196 {
197     return 0;
198 }
199 
bytesLoaded() const200 unsigned MediaPlayerPrivate::bytesLoaded() const
201 {
202     return 0;
203 }
204 
setSize(const IntSize &)205 void MediaPlayerPrivate::setSize(const IntSize&)
206 {
207 }
208 
setPoster(const String & url)209 void MediaPlayerPrivate::setPoster(const String& url)
210 {
211     m_posterUrl = url;
212     JNIEnv* env = JSC::Bindings::getJNIEnv();
213     if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
214         return;
215     // Send the poster
216     jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
217     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
218     env->DeleteLocalRef(jUrl);
219 }
220 
prepareToPlay()221 void MediaPlayerPrivate::prepareToPlay() {
222     // We are about to start playing. Since our Java VideoView cannot
223     // buffer any data, we just simply transition to the HaveEnoughData
224     // state in here. This will allow the MediaPlayer to transition to
225     // the "play" state, at which point our VideoView will start downloading
226     // the content and start the playback.
227     m_networkState = MediaPlayer::Loaded;
228     m_player->networkStateChanged();
229     m_readyState = MediaPlayer::HaveEnoughData;
230     m_player->readyStateChanged();
231 }
232 
paint(GraphicsContext * ctxt,const IntRect & r)233 void MediaPlayerPrivate::paint(GraphicsContext* ctxt, const IntRect& r)
234 {
235     if (ctxt->paintingDisabled())
236         return;
237 
238     if (!m_isVisible)
239         return;
240 
241     if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
242         return;
243 
244     SkCanvas*   canvas = ctxt->platformContext()->mCanvas;
245     // We paint with the following rules in mind:
246     // - only downscale the poster, never upscale
247     // - maintain the natural aspect ratio of the poster
248     // - the poster should be centered in the target rect
249     float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
250     int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
251     int posterHeight = posterWidth / originalRatio;
252     int posterX = ((r.width() - posterWidth) / 2) + r.x();
253     int posterY = ((r.height() - posterHeight) / 2) + r.y();
254     IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
255     canvas->drawBitmapRect(*m_poster, 0, targetRect, 0);
256 }
257 
create(MediaPlayer * player)258 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
259 {
260     return new MediaPlayerPrivate(player);
261 }
262 
getSupportedTypes(HashSet<String> &)263 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&)
264 {
265 }
266 
supportsType(const String & type,const String & codecs)267 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
268 {
269     return MediaPlayer::IsNotSupported;
270 }
271 
MediaPlayerPrivate(MediaPlayer * player)272 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
273     : m_player(player),
274     m_glue(0),
275     m_duration(6000),
276     m_currentTime(0),
277     m_paused(true),
278     m_readyState(MediaPlayer::HaveNothing),
279     m_networkState(MediaPlayer::Empty),
280     m_poster(0),
281     m_naturalSize(100, 100),
282     m_naturalSizeUnknown(true),
283     m_isVisible(false)
284 {
285     JNIEnv* env = JSC::Bindings::getJNIEnv();
286     if (!env)
287         return;
288 
289     jclass clazz = env->FindClass(g_ProxyJavaClass);
290     if (!clazz)
291         return;
292 
293     m_glue = new JavaGlue;
294     m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
295     m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
296     m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
297     m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
298     m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
299     m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
300     m_glue->m_javaProxy = NULL;
301     env->DeleteLocalRef(clazz);
302     // An exception is raised if any of the above fails.
303     checkException(env);
304 }
305 
createJavaPlayerIfNeeded()306 void MediaPlayerPrivate::createJavaPlayerIfNeeded()
307 {
308     // Check if we have been already created.
309     if (m_glue->m_javaProxy)
310         return;
311 
312     FrameView* frameView = m_player->frameView();
313     if (!frameView)
314         return;
315 
316     JNIEnv* env = JSC::Bindings::getJNIEnv();
317     if (!env)
318         return;
319 
320     jclass clazz = env->FindClass(g_ProxyJavaClass);
321     if (!clazz)
322         return;
323 
324     WebViewCore* webViewCore =  WebViewCore::getWebViewCore(frameView);
325     ASSERT(webViewCore);
326 
327     // Get the HTML5VideoViewProxy instance
328     jobject obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
329     m_glue->m_javaProxy = env->NewGlobalRef(obj);
330     // Send the poster
331     jstring jUrl = 0;
332     if (m_posterUrl.length())
333         jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
334     // Sending a NULL jUrl allows the Java side to try to load the default poster.
335     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
336     if (jUrl)
337         env->DeleteLocalRef(jUrl);
338     // Clean up.
339     env->DeleteLocalRef(obj);
340     env->DeleteLocalRef(clazz);
341     checkException(env);
342 }
343 
onPrepared(int duration,int width,int height)344 void MediaPlayerPrivate::onPrepared(int duration, int width, int height) {
345     m_duration = duration / 1000.0f;
346     m_naturalSize = IntSize(width, height);
347     m_naturalSizeUnknown = false;
348     m_player->durationChanged();
349     m_player->sizeChanged();
350 }
351 
onEnded()352 void MediaPlayerPrivate::onEnded() {
353     m_paused = true;
354     m_currentTime = 0;
355     m_networkState = MediaPlayer::Idle;
356     m_readyState = MediaPlayer::HaveNothing;
357 }
358 
onPosterFetched(SkBitmap * poster)359 void MediaPlayerPrivate::onPosterFetched(SkBitmap* poster) {
360     m_poster = poster;
361     if (m_naturalSizeUnknown) {
362         // We had to fake the size at startup, or else our paint
363         // method would not be called. If we haven't yet received
364         // the onPrepared event, update the intrinsic size to the size
365         // of the poster. That will be overriden when onPrepare comes.
366         // In case of an error, we should report the poster size, rather
367         // than our initial fake value.
368         m_naturalSize = IntSize(poster->width(), poster->height());
369         m_player->sizeChanged();
370     }
371 }
372 
373 }
374 
375 namespace android {
376 
OnPrepared(JNIEnv * env,jobject obj,int duration,int width,int height,int pointer)377 static void OnPrepared(JNIEnv* env, jobject obj, int duration, int width, int height, int pointer) {
378     if (pointer) {
379         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
380         player->onPrepared(duration, width, height);
381     }
382 }
383 
OnEnded(JNIEnv * env,jobject obj,int pointer)384 static void OnEnded(JNIEnv* env, jobject obj, int pointer) {
385     if (pointer) {
386         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
387         player->onEnded();
388     }
389 }
390 
OnPosterFetched(JNIEnv * env,jobject obj,jobject poster,int pointer)391 static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer) {
392     if (!pointer || !poster)
393         return;
394 
395     WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
396     SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster);
397     if (!posterNative)
398         return;
399     player->onPosterFetched(posterNative);
400 }
401 
402 /*
403  * JNI registration
404  */
405 static JNINativeMethod g_MediaPlayerMethods[] = {
406     { "nativeOnPrepared", "(IIII)V",
407         (void*) OnPrepared },
408     { "nativeOnEnded", "(I)V",
409         (void*) OnEnded },
410     { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
411         (void*) OnPosterFetched },
412 };
413 
register_mediaplayer(JNIEnv * env)414 int register_mediaplayer(JNIEnv* env)
415 {
416     return jniRegisterNativeMethods(env, g_ProxyJavaClass,
417             g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
418 }
419 
420 }
421 #endif // VIDEO
422