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 THE COPYRIGHT OWNER 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 #include "MediaPlayerPrivateAndroid.h"
28
29 #if ENABLE(VIDEO)
30
31 #include "BaseLayerAndroid.h"
32 #include "GraphicsContext.h"
33 #include "HTMLMediaElement.h"
34 #include "SkiaUtils.h"
35 #include "TilesManager.h"
36 #include "VideoLayerAndroid.h"
37 #include "WebCoreJni.h"
38 #include "WebViewCore.h"
39 #include <GraphicsJNI.h>
40 #include <JNIHelp.h>
41 #include <JNIUtility.h>
42 #include <SkBitmap.h>
43 #include <gui/SurfaceTexture.h>
44
45 using namespace android;
46 // Forward decl
47 namespace android {
48 sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
49 };
50
51 namespace WebCore {
52
53 static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
54 static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio";
55
56 struct MediaPlayerPrivate::JavaGlue {
57 jobject m_javaProxy;
58 jmethodID m_play;
59 jmethodID m_enterFullscreenForVideoLayer;
60 jmethodID m_teardown;
61 jmethodID m_seek;
62 jmethodID m_pause;
63 // Audio
64 jmethodID m_newInstance;
65 jmethodID m_setDataSource;
66 jmethodID m_getMaxTimeSeekable;
67 // Video
68 jmethodID m_getInstance;
69 jmethodID m_loadPoster;
70 };
71
~MediaPlayerPrivate()72 MediaPlayerPrivate::~MediaPlayerPrivate()
73 {
74 TilesManager::instance()->videoLayerManager()->removeLayer(m_videoLayer->uniqueId());
75 // m_videoLayer is reference counted, unref is enough here.
76 m_videoLayer->unref();
77 if (m_glue->m_javaProxy) {
78 JNIEnv* env = JSC::Bindings::getJNIEnv();
79 if (env) {
80 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown);
81 env->DeleteGlobalRef(m_glue->m_javaProxy);
82 }
83 }
84 delete m_glue;
85 }
86
registerMediaEngine(MediaEngineRegistrar registrar)87 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
88 {
89 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
90 }
91
supportsType(const String & type,const String & codecs)92 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
93 {
94 if (WebViewCore::isSupportedMediaMimeType(type))
95 return MediaPlayer::MayBeSupported;
96 return MediaPlayer::IsNotSupported;
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 m_player->playbackStateChanged();
107 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_pause);
108 checkException(env);
109 }
110
setVisible(bool visible)111 void MediaPlayerPrivate::setVisible(bool visible)
112 {
113 m_isVisible = visible;
114 if (m_isVisible)
115 createJavaPlayerIfNeeded();
116 }
117
seek(float time)118 void MediaPlayerPrivate::seek(float time)
119 {
120 JNIEnv* env = JSC::Bindings::getJNIEnv();
121 if (!env || !m_url.length())
122 return;
123
124 if (m_glue->m_javaProxy) {
125 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
126 m_currentTime = time;
127 }
128 checkException(env);
129 }
130
prepareToPlay()131 void MediaPlayerPrivate::prepareToPlay()
132 {
133 // We are about to start playing. Since our Java VideoView cannot
134 // buffer any data, we just simply transition to the HaveEnoughData
135 // state in here. This will allow the MediaPlayer to transition to
136 // the "play" state, at which point our VideoView will start downloading
137 // the content and start the playback.
138 m_networkState = MediaPlayer::Loaded;
139 m_player->networkStateChanged();
140 m_readyState = MediaPlayer::HaveEnoughData;
141 m_player->readyStateChanged();
142 }
143
MediaPlayerPrivate(MediaPlayer * player)144 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
145 : m_player(player),
146 m_glue(0),
147 m_duration(1), // keep this minimal to avoid initial seek problem
148 m_currentTime(0),
149 m_paused(true),
150 m_readyState(MediaPlayer::HaveNothing),
151 m_networkState(MediaPlayer::Empty),
152 m_poster(0),
153 m_naturalSize(100, 100),
154 m_naturalSizeUnknown(true),
155 m_isVisible(false),
156 m_videoLayer(new VideoLayerAndroid())
157 {
158 }
159
onEnded()160 void MediaPlayerPrivate::onEnded()
161 {
162 m_currentTime = duration();
163 m_player->timeChanged();
164 m_paused = true;
165 m_player->playbackStateChanged();
166 m_networkState = MediaPlayer::Idle;
167 }
168
onRequestPlay()169 void MediaPlayerPrivate::onRequestPlay()
170 {
171 play();
172 }
173
onRestoreState()174 void MediaPlayerPrivate::onRestoreState()
175 {
176 if (!m_paused) {
177 //Kick off a JNI call to start the video.
178 play();
179 }
180 }
181
onPaused()182 void MediaPlayerPrivate::onPaused()
183 {
184 m_paused = true;
185 m_player->playbackStateChanged();
186 m_networkState = MediaPlayer::Idle;
187 m_player->playbackStateChanged();
188 }
189
onTimeupdate(int position)190 void MediaPlayerPrivate::onTimeupdate(int position)
191 {
192 m_currentTime = position / 1000.0f;
193 m_player->timeChanged();
194 }
195
onStopFullscreen(bool stillPlaying)196 void MediaPlayerPrivate::onStopFullscreen(bool stillPlaying)
197 {
198 if (m_player && m_player->mediaPlayerClient()) {
199 Document* doc = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
200 if (doc) {
201 HTMLMediaElement* element =
202 static_cast<HTMLMediaElement*>(doc->webkitCurrentFullScreenElement());
203 element->exitFullscreen();
204 doc->webkitDidExitFullScreenForElement(element);
205
206 if (stillPlaying)
207 element->play(true);
208 }
209 }
210 }
211
212 class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
213 public:
load(const String & url)214 void load(const String& url)
215 {
216 m_url = url;
217 // Cheat a bit here to make sure Window.onLoad event can be triggered
218 // at the right time instead of real video play time, since only full
219 // screen video play is supported in Java's VideoView.
220 // See also comments in prepareToPlay function.
221 m_networkState = MediaPlayer::Loading;
222 m_player->networkStateChanged();
223 m_readyState = MediaPlayer::HaveCurrentData;
224 m_player->readyStateChanged();
225 }
226
play()227 void play()
228 {
229 JNIEnv* env = JSC::Bindings::getJNIEnv();
230 if (!env || !m_url.length() || !m_glue->m_javaProxy)
231 return;
232
233 m_paused = false;
234 m_player->playbackStateChanged();
235
236 if (m_currentTime == duration())
237 m_currentTime = 0;
238
239 jstring jUrl = wtfStringToJstring(env, m_url);
240 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl,
241 static_cast<jint>(m_currentTime * 1000.0f),
242 m_videoLayer->uniqueId());
243 env->DeleteLocalRef(jUrl);
244
245 checkException(env);
246 }
247
enterFullscreenMode()248 void enterFullscreenMode()
249 {
250 JNIEnv* env = JSC::Bindings::getJNIEnv();
251 if (!env || !m_url.length() || !m_glue->m_javaProxy)
252 return;
253
254 jstring jUrl = wtfStringToJstring(env, m_url);
255 env->CallVoidMethod(m_glue->m_javaProxy,
256 m_glue->m_enterFullscreenForVideoLayer, jUrl,
257 m_videoLayer->uniqueId());
258 env->DeleteLocalRef(jUrl);
259
260 checkException(env);
261 }
262
canLoadPoster() const263 bool canLoadPoster() const { return true; }
setPoster(const String & url)264 void setPoster(const String& url)
265 {
266 if (m_posterUrl == url)
267 return;
268
269 m_posterUrl = url;
270 JNIEnv* env = JSC::Bindings::getJNIEnv();
271 if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
272 return;
273 // Send the poster
274 jstring jUrl = wtfStringToJstring(env, m_posterUrl);
275 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
276 env->DeleteLocalRef(jUrl);
277 }
paint(GraphicsContext * ctxt,const IntRect & r)278 void paint(GraphicsContext* ctxt, const IntRect& r)
279 {
280 if (ctxt->paintingDisabled())
281 return;
282
283 if (!m_isVisible)
284 return;
285
286 if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
287 return;
288
289 // We paint with the following rules in mind:
290 // - only downscale the poster, never upscale
291 // - maintain the natural aspect ratio of the poster
292 // - the poster should be centered in the target rect
293 float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
294 int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
295 int posterHeight = posterWidth / originalRatio;
296 int posterX = ((r.width() - posterWidth) / 2) + r.x();
297 int posterY = ((r.height() - posterHeight) / 2) + r.y();
298 IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
299 ctxt->platformContext()->drawBitmapRect(*m_poster, 0, targetRect);
300 }
301
onPosterFetched(SkBitmap * poster)302 void onPosterFetched(SkBitmap* poster)
303 {
304 m_poster = poster;
305 if (m_naturalSizeUnknown) {
306 // We had to fake the size at startup, or else our paint
307 // method would not be called. If we haven't yet received
308 // the onPrepared event, update the intrinsic size to the size
309 // of the poster. That will be overriden when onPrepare comes.
310 // In case of an error, we should report the poster size, rather
311 // than our initial fake value.
312 m_naturalSize = IntSize(poster->width(), poster->height());
313 m_player->sizeChanged();
314 }
315 // At this time, we know that the proxy has been setup. And it is the
316 // right time to trigger autoplay through the HTMLMediaElement state
317 // change. Since we are using the java MediaPlayer, so we have to
318 // pretend that the MediaPlayer has enough data.
319 m_readyState = MediaPlayer::HaveEnoughData;
320 m_player->readyStateChanged();
321
322 }
323
onPrepared(int duration,int width,int height)324 void onPrepared(int duration, int width, int height)
325 {
326 m_duration = duration / 1000.0f;
327 m_naturalSize = IntSize(width, height);
328 m_naturalSizeUnknown = false;
329 m_player->durationChanged();
330 m_player->sizeChanged();
331 TilesManager::instance()->videoLayerManager()->updateVideoLayerSize(
332 m_player->platformLayer()->uniqueId(), width * height,
333 width / (float)height);
334 }
335
hasAudio() const336 virtual bool hasAudio() const { return false; } // do not display the audio UI
hasVideo() const337 virtual bool hasVideo() const { return true; }
supportsFullscreen() const338 virtual bool supportsFullscreen() const { return true; }
339
MediaPlayerVideoPrivate(MediaPlayer * player)340 MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
341 {
342 JNIEnv* env = JSC::Bindings::getJNIEnv();
343 if (!env)
344 return;
345
346 jclass clazz = env->FindClass(g_ProxyJavaClass);
347
348 if (!clazz)
349 return;
350
351 m_glue = new JavaGlue;
352 m_glue->m_getInstance =
353 env->GetStaticMethodID(clazz, "getInstance",
354 "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
355 m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
356 m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;II)V");
357 m_glue->m_enterFullscreenForVideoLayer =
358 env->GetMethodID(clazz, "enterFullscreenForVideoLayer", "(Ljava/lang/String;I)V");
359
360 m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
361 m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
362 m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
363 m_glue->m_javaProxy = 0;
364 env->DeleteLocalRef(clazz);
365 // An exception is raised if any of the above fails.
366 checkException(env);
367 }
368
createJavaPlayerIfNeeded()369 void createJavaPlayerIfNeeded()
370 {
371 // Check if we have been already created.
372 if (m_glue->m_javaProxy)
373 return;
374
375 JNIEnv* env = JSC::Bindings::getJNIEnv();
376 if (!env)
377 return;
378
379 jclass clazz = env->FindClass(g_ProxyJavaClass);
380
381 if (!clazz)
382 return;
383
384 jobject obj = 0;
385
386 FrameView* frameView = m_player->frameView();
387 if (!frameView)
388 return;
389 AutoJObject javaObject = WebViewCore::getWebViewCore(frameView)->getJavaObject();
390 if (!javaObject.get())
391 return;
392
393 // Get the HTML5VideoViewProxy instance
394 obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, javaObject.get(), this);
395 m_glue->m_javaProxy = env->NewGlobalRef(obj);
396 // Send the poster
397 jstring jUrl = 0;
398 if (m_posterUrl.length())
399 jUrl = wtfStringToJstring(env, m_posterUrl);
400 // Sending a NULL jUrl allows the Java side to try to load the default poster.
401 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
402 if (jUrl)
403 env->DeleteLocalRef(jUrl);
404
405 // Clean up.
406 env->DeleteLocalRef(obj);
407 env->DeleteLocalRef(clazz);
408 checkException(env);
409 }
410
maxTimeSeekable() const411 float maxTimeSeekable() const
412 {
413 return m_duration;
414 }
415 };
416
417 class MediaPlayerAudioPrivate : public MediaPlayerPrivate {
418 public:
load(const String & url)419 void load(const String& url)
420 {
421 m_url = url;
422 JNIEnv* env = JSC::Bindings::getJNIEnv();
423 if (!env || !m_url.length())
424 return;
425
426 createJavaPlayerIfNeeded();
427
428 if (!m_glue->m_javaProxy)
429 return;
430
431 jstring jUrl = wtfStringToJstring(env, m_url);
432 // start loading the data asynchronously
433 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl);
434 env->DeleteLocalRef(jUrl);
435 checkException(env);
436 }
437
play()438 void play()
439 {
440 JNIEnv* env = JSC::Bindings::getJNIEnv();
441 if (!env || !m_url.length())
442 return;
443
444 createJavaPlayerIfNeeded();
445
446 if (!m_glue->m_javaProxy)
447 return;
448
449 m_paused = false;
450 m_player->playbackStateChanged();
451 env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play);
452 checkException(env);
453 }
454
hasAudio() const455 virtual bool hasAudio() const { return true; }
hasVideo() const456 virtual bool hasVideo() const { return false; }
supportsFullscreen() const457 virtual bool supportsFullscreen() const { return false; }
458
maxTimeSeekable() const459 float maxTimeSeekable() const
460 {
461 if (m_glue->m_javaProxy) {
462 JNIEnv* env = JSC::Bindings::getJNIEnv();
463 if (env) {
464 float maxTime = env->CallFloatMethod(m_glue->m_javaProxy,
465 m_glue->m_getMaxTimeSeekable);
466 checkException(env);
467 return maxTime;
468 }
469 }
470 return 0;
471 }
472
MediaPlayerAudioPrivate(MediaPlayer * player)473 MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
474 {
475 JNIEnv* env = JSC::Bindings::getJNIEnv();
476 if (!env)
477 return;
478
479 jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
480
481 if (!clazz)
482 return;
483
484 m_glue = new JavaGlue;
485 m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(Landroid/webkit/WebViewCore;I)V");
486 m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V");
487 m_glue->m_play = env->GetMethodID(clazz, "play", "()V");
488 m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F");
489 m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
490 m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
491 m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
492 m_glue->m_javaProxy = 0;
493 env->DeleteLocalRef(clazz);
494 // An exception is raised if any of the above fails.
495 checkException(env);
496 }
497
createJavaPlayerIfNeeded()498 void createJavaPlayerIfNeeded()
499 {
500 // Check if we have been already created.
501 if (m_glue->m_javaProxy)
502 return;
503
504 JNIEnv* env = JSC::Bindings::getJNIEnv();
505 if (!env)
506 return;
507
508 jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
509
510 if (!clazz)
511 return;
512
513 FrameView* frameView = m_player->mediaPlayerClient()->mediaPlayerOwningDocument()->view();
514 if (!frameView)
515 return;
516 AutoJObject javaObject = WebViewCore::getWebViewCore(frameView)->getJavaObject();
517 if (!javaObject.get())
518 return;
519
520 jobject obj = 0;
521
522 // Get the HTML5Audio instance
523 obj = env->NewObject(clazz, m_glue->m_newInstance, javaObject.get(), this);
524 m_glue->m_javaProxy = env->NewGlobalRef(obj);
525
526 // Clean up.
527 if (obj)
528 env->DeleteLocalRef(obj);
529 env->DeleteLocalRef(clazz);
530 checkException(env);
531 }
532
onPrepared(int duration,int width,int height)533 void onPrepared(int duration, int width, int height)
534 {
535 // Android media player gives us a duration of 0 for a live
536 // stream, so in that case set the real duration to infinity.
537 // We'll still be able to handle the case that we genuinely
538 // get an audio clip with a duration of 0s as we'll get the
539 // ended event when it stops playing.
540 if (duration > 0) {
541 m_duration = duration / 1000.0f;
542 } else {
543 m_duration = std::numeric_limits<float>::infinity();
544 }
545 m_player->durationChanged();
546 m_player->sizeChanged();
547 m_player->prepareToPlay();
548 }
549 };
550
create(MediaPlayer * player)551 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
552 {
553 if (player->mediaElementType() == MediaPlayer::Video)
554 return new MediaPlayerVideoPrivate(player);
555 return new MediaPlayerAudioPrivate(player);
556 }
557
558 }
559
560 namespace android {
561
OnPrepared(JNIEnv * env,jobject obj,int duration,int width,int height,int pointer)562 static void OnPrepared(JNIEnv* env, jobject obj, int duration, int width, int height, int pointer)
563 {
564 if (pointer) {
565 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
566 player->onPrepared(duration, width, height);
567 }
568 }
569
OnEnded(JNIEnv * env,jobject obj,int pointer)570 static void OnEnded(JNIEnv* env, jobject obj, int pointer)
571 {
572 if (pointer) {
573 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
574 player->onEnded();
575 }
576 }
577
OnRequestPlay(JNIEnv * env,jobject obj,int pointer)578 static void OnRequestPlay(JNIEnv* env, jobject obj, int pointer)
579 {
580 if (pointer) {
581 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
582 player->onRequestPlay();
583 }
584 }
585
OnPaused(JNIEnv * env,jobject obj,int pointer)586 static void OnPaused(JNIEnv* env, jobject obj, int pointer)
587 {
588 if (pointer) {
589 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
590 player->onPaused();
591 }
592 }
593
OnPosterFetched(JNIEnv * env,jobject obj,jobject poster,int pointer)594 static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer)
595 {
596 if (!pointer || !poster)
597 return;
598
599 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
600 SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster);
601 if (!posterNative)
602 return;
603 player->onPosterFetched(posterNative);
604 }
605
OnBuffering(JNIEnv * env,jobject obj,int percent,int pointer)606 static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer)
607 {
608 if (pointer) {
609 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
610 // TODO: player->onBuffering(percent);
611 }
612 }
613
OnTimeupdate(JNIEnv * env,jobject obj,int position,int pointer)614 static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer)
615 {
616 if (pointer) {
617 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
618 player->onTimeupdate(position);
619 }
620 }
621
OnRestoreState(JNIEnv * env,jobject obj,int pointer)622 static void OnRestoreState(JNIEnv* env, jobject obj, int pointer)
623 {
624 if (pointer) {
625 WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
626 player->onRestoreState();
627 }
628 }
629
630
631 // This is called on the UI thread only.
632 // The video layers are composited on the webkit thread and then copied over
633 // to the UI thread with the same ID. For rendering, we are only using the
634 // video layers on the UI thread. Therefore, on the UI thread, we have to use
635 // the videoLayerId from Java side to find the exact video layer in the tree
636 // to set the surface texture.
637 // Every time a play call into Java side, the videoLayerId will be sent and
638 // saved in Java side. Then every time setBaseLayer call, the saved
639 // videoLayerId will be passed to this function to find the Video Layer.
640 // Return value: true when the video layer is found.
SendSurfaceTexture(JNIEnv * env,jobject obj,jobject surfTex,int baseLayer,int videoLayerId,int textureName,int playerState)641 static bool SendSurfaceTexture(JNIEnv* env, jobject obj, jobject surfTex,
642 int baseLayer, int videoLayerId,
643 int textureName, int playerState) {
644 if (!surfTex)
645 return false;
646
647 sp<SurfaceTexture> texture = android::SurfaceTexture_getSurfaceTexture(env, surfTex);
648 if (!texture.get())
649 return false;
650
651 BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(baseLayer);
652 if (!layerImpl)
653 return false;
654
655 VideoLayerAndroid* videoLayer =
656 static_cast<VideoLayerAndroid*>(layerImpl->findById(videoLayerId));
657 if (!videoLayer)
658 return false;
659
660 // Set the SurfaceTexture to the layer we found
661 videoLayer->setSurfaceTexture(texture, textureName, static_cast<PlayerState>(playerState));
662 return true;
663 }
664
OnStopFullscreen(JNIEnv * env,jobject obj,int stillPlaying,int pointer)665 static void OnStopFullscreen(JNIEnv* env, jobject obj, int stillPlaying, int pointer)
666 {
667 if (pointer) {
668 WebCore::MediaPlayerPrivate* player =
669 reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
670 player->onStopFullscreen(stillPlaying);
671 }
672 }
673
674 /*
675 * JNI registration
676 */
677 static JNINativeMethod g_MediaPlayerMethods[] = {
678 { "nativeOnPrepared", "(IIII)V",
679 (void*) OnPrepared },
680 { "nativeOnEnded", "(I)V",
681 (void*) OnEnded },
682 { "nativeOnStopFullscreen", "(II)V",
683 (void*) OnStopFullscreen },
684 { "nativeOnPaused", "(I)V",
685 (void*) OnPaused },
686 { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
687 (void*) OnPosterFetched },
688 { "nativeOnRestoreState", "(I)V",
689 (void*) OnRestoreState },
690 { "nativeSendSurfaceTexture", "(Landroid/graphics/SurfaceTexture;IIII)Z",
691 (void*) SendSurfaceTexture },
692 { "nativeOnTimeupdate", "(II)V",
693 (void*) OnTimeupdate },
694 };
695
696 static JNINativeMethod g_MediaAudioPlayerMethods[] = {
697 { "nativeOnBuffering", "(II)V",
698 (void*) OnBuffering },
699 { "nativeOnEnded", "(I)V",
700 (void*) OnEnded },
701 { "nativeOnPrepared", "(IIII)V",
702 (void*) OnPrepared },
703 { "nativeOnRequestPlay", "(I)V",
704 (void*) OnRequestPlay },
705 { "nativeOnTimeupdate", "(II)V",
706 (void*) OnTimeupdate },
707 };
708
registerMediaPlayerVideo(JNIEnv * env)709 int registerMediaPlayerVideo(JNIEnv* env)
710 {
711 return jniRegisterNativeMethods(env, g_ProxyJavaClass,
712 g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
713 }
714
registerMediaPlayerAudio(JNIEnv * env)715 int registerMediaPlayerAudio(JNIEnv* env)
716 {
717 return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio,
718 g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods));
719 }
720
721 }
722 #endif // VIDEO
723