1/* 2 * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19#include "config.h" 20 21#if ENABLE(VIDEO) 22#include "MediaPlayerPrivateBlackBerry.h" 23 24#include "CookieManager.h" 25#include "CredentialStorage.h" 26#include "DOMFileSystemBase.h" 27#include "HostWindow.h" 28#include "MediaStreamDescriptor.h" 29#include "MediaStreamRegistry.h" 30#include "SecurityOrigin.h" 31 32#include <BlackBerryPlatformDeviceInfo.h> 33#include <BlackBerryPlatformPrimitives.h> 34#include <BlackBerryPlatformSettings.h> 35#include <BlackBerryPlatformWebFileSystem.h> 36#include <FrameLoaderClientBlackBerry.h> 37 38#if USE(ACCELERATED_COMPOSITING) 39#include "VideoLayerWebKitThread.h" 40#include <BlackBerryPlatformGLES2Program.h> 41#include <GLES2/gl2.h> 42#endif 43 44#include <TiledImage.h> 45 46using namespace std; 47using namespace BlackBerry::Platform; 48 49namespace WebCore { 50 51// Static callback functions for factory 52PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivate::create(MediaPlayer* player) 53{ 54 return adoptPtr(new MediaPlayerPrivate(player)); 55} 56 57void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) 58{ 59 registrar(create, getSupportedTypes, supportsType, 0, 0, 0); 60} 61 62void MediaPlayerPrivate::getSupportedTypes(HashSet<WTF::String>& types) 63{ 64 set<BlackBerry::Platform::String> supported = PlatformPlayer::allSupportedMimeTypes(); 65 set<BlackBerry::Platform::String>::iterator i = supported.begin(); 66 for (; i != supported.end(); i++) 67 types.add(*i); 68} 69 70MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const WTF::String& type, const WTF::String& codecs, const KURL& url) 71{ 72 bool isRTSP = url.protocolIs("rtsp"); 73 74 if (!isRTSP && (type.isNull() || type.isEmpty())) { 75 LOG(Media, "MediaPlayer does not support type; type is null or empty."); 76 return MediaPlayer::IsNotSupported; 77 } 78 79 // spec says we should not return "probably" if the codecs string is empty 80 if (isRTSP || PlatformPlayer::mimeTypeSupported(type.ascii().data())) { 81 LOG(Media, "MediaPlayer supports type %s.", isRTSP ? "rtsp" : type.ascii().data()); 82 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; 83 } 84 LOG(Media, "MediaPlayer does not support type %s.", type.ascii().data()); 85 return MediaPlayer::IsNotSupported; 86} 87 88void MediaPlayerPrivate::notifyAppActivatedEvent(bool activated) 89{ 90 PlatformPlayer::notifyAppActivatedEvent(activated); 91} 92 93void MediaPlayerPrivate::setCertificatePath(const WTF::String& caPath) 94{ 95 PlatformPlayer::setCertificatePath(caPath); 96} 97 98MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) 99 : m_webCorePlayer(player) 100 , m_platformPlayer(0) 101 , m_networkState(MediaPlayer::Empty) 102 , m_readyState(MediaPlayer::HaveNothing) 103 , m_fullscreenWebPageClient(0) 104#if USE(ACCELERATED_COMPOSITING) 105 , m_bufferingTimer(this, &MediaPlayerPrivate::bufferingTimerFired) 106 , m_showBufferingImage(false) 107#endif 108 , m_userDrivenSeekTimer(this, &MediaPlayerPrivate::userDrivenSeekTimerFired) 109 , m_lastSeekTime(0) 110 , m_lastLoadingTime(0) 111 , m_lastSeekTimePending(false) 112 , m_isAuthenticationChallenging(false) 113 , m_waitMetadataTimer(this, &MediaPlayerPrivate::waitMetadataTimerFired) 114 , m_waitMetadataPopDialogCounter(0) 115{ 116} 117 118MediaPlayerPrivate::~MediaPlayerPrivate() 119{ 120 if (m_isAuthenticationChallenging) 121 AuthenticationChallengeManager::instance()->cancelAuthenticationChallenge(this); 122 123 if (isFullscreen()) 124 m_webCorePlayer->mediaPlayerClient()->mediaPlayerExitFullscreen(); 125#if USE(ACCELERATED_COMPOSITING) 126 // Remove media player from platform layer. 127 if (m_platformLayer) 128 static_cast<VideoLayerWebKitThread*>(m_platformLayer.get())->setMediaPlayer(0); 129#endif 130 131 if (m_platformPlayer) { 132 if (m_platformPlayer->dialogState() == PlatformPlayer::DialogShown) { 133 m_platformPlayer->setDialogState(PlatformPlayer::MediaPlayerPrivateDestroyed); 134 m_platformPlayer->stop(); 135 } else 136 deleteGuardedObject(m_platformPlayer); 137 } 138} 139 140void MediaPlayerPrivate::load(const WTF::String& url) 141{ 142 WTF::String modifiedUrl(url); 143 144 if (modifiedUrl.startsWith("local://")) { 145 KURL kurl = KURL(KURL(), modifiedUrl); 146 kurl.setProtocol("file"); 147 WTF::String tempPath(BlackBerry::Platform::Settings::instance()->applicationLocalDirectory().c_str()); 148 tempPath.append(kurl.path()); 149 kurl.setPath(tempPath); 150 modifiedUrl = kurl.string(); 151 } 152 // filesystem: URLs refer to entities in the Web File System (WFS) and are 153 // intended to be useable by HTML 5 media elements directly. Unfortunately 154 // the loader for our media player is implemented in a separate process and 155 // does not have access to WFS, so we translate to a file:// URL. Normally 156 // this would be a security violation, but since the MediaElement has 157 // already done a security check on the filesystem: URL as part of the 158 // media resource selection algorithm, we should be OK here. 159 if (modifiedUrl.startsWith("filesystem:")) { 160 KURL kurl = KURL(KURL(), modifiedUrl); 161 KURL mediaURL; 162 WTF::String fsPath; 163 FileSystemType fsType; 164 WebFileSystem::Type type; 165 166 // Extract the root and file paths from WFS 167 DOMFileSystemBase::crackFileSystemURL(kurl, fsType, fsPath); 168 if (fsType == FileSystemTypeTemporary) 169 type = WebFileSystem::Temporary; 170 else 171 type = WebFileSystem::Persistent; 172 WTF::String fsRoot = BlackBerry::Platform::WebFileSystem::rootPathForWebFileSystem(type); 173 174 // Build a BlackBerry::Platform::SecurityOrigin from the document's 175 // WebCore::SecurityOrigin and serialize it to build the last 176 // path component 177 WebCore::SecurityOrigin* wkOrigin = m_webCorePlayer->mediaPlayerClient()->mediaPlayerOwningDocument()->securityOrigin(); 178 BlackBerry::Platform::SecurityOrigin bbOrigin(wkOrigin->protocol(), wkOrigin->host(), wkOrigin->port()); 179 WTF::String secOrigin(bbOrigin.serialize('_')); 180 181 // Build a file:// URL from the path components and extract it to 182 // a string for further processing 183 mediaURL.setProtocol("file"); 184 mediaURL.setPath(fsRoot + "/" + secOrigin + "/" + fsPath); 185 modifiedUrl = mediaURL.string(); 186 } else if (modifiedUrl.startsWith("file://") || modifiedUrl.startsWith("data:")) { 187 // The QNX Multimedia Framework cannot handle filenames or data containing URL escape sequences. 188 modifiedUrl = decodeURLEscapeSequences(modifiedUrl); 189 } 190 191 void* tabId = m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient(); 192 int playerID = m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->playerID(); 193 bool isVideo = m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsVideo(); 194 195 deleteGuardedObject(m_platformPlayer); 196#if USE(ACCELERATED_COMPOSITING) 197 m_platformPlayer = PlatformPlayer::create(this, tabId, isVideo, true, modifiedUrl); 198#else 199 m_platformPlayer = PlatformPlayer::create(this, tabId, isVideo, false, modifiedUrl); 200#endif 201 202 WTF::String cookiePairs; 203 if (!url.isEmpty()) 204 cookiePairs = cookieManager().getCookie(KURL(ParsedURLString, url), WithHttpOnlyCookies); 205 m_platformPlayer->load(playerID, modifiedUrl, m_webCorePlayer->userAgent(), cookiePairs); 206} 207 208void MediaPlayerPrivate::cancelLoad() 209{ 210 if (m_platformPlayer) 211 m_platformPlayer->cancelLoad(); 212} 213 214void MediaPlayerPrivate::prepareToPlay() 215{ 216 if (m_platformPlayer) 217 m_platformPlayer->prepareToPlay(); 218} 219 220void MediaPlayerPrivate::play() 221{ 222 if (m_platformPlayer) 223 m_platformPlayer->play(); 224} 225 226void MediaPlayerPrivate::pause() 227{ 228 if (m_platformPlayer) 229 m_platformPlayer->pause(); 230} 231 232bool MediaPlayerPrivate::supportsFullscreen() const 233{ 234 return hasVideo(); 235} 236 237IntSize MediaPlayerPrivate::naturalSize() const 238{ 239 if (!m_platformPlayer) 240 return IntSize(); 241 242 // Cannot return empty size, otherwise paint() will never get called. 243 // Also, the values here will affect the aspect ratio of the output rectangle that will 244 // be used for renderering the video, so we must take PAR into account. 245 // Now, hope that metadata has been provided before this gets called if this is a video. 246 double pixelWidth = static_cast<double>(m_platformPlayer->pixelWidth()); 247 double pixelHeight = static_cast<double>(m_platformPlayer->pixelHeight()); 248 if (!m_platformPlayer->pixelWidth() || !m_platformPlayer->pixelHeight()) 249 pixelWidth = pixelHeight = 1.0; 250 251 // Use floating point arithmetic to eliminate the chance of integer 252 // overflow. PAR numbers can be 5 digits or more. 253 double adjustedSourceWidth = static_cast<double>(m_platformPlayer->sourceWidth()) * pixelWidth / pixelHeight; 254 return IntSize(static_cast<int>(adjustedSourceWidth + 0.5), m_platformPlayer->sourceHeight()); 255} 256 257bool MediaPlayerPrivate::hasVideo() const 258{ 259 if (m_platformPlayer) 260 return m_platformPlayer->hasVideo(); 261 return false; 262} 263 264bool MediaPlayerPrivate::hasAudio() const 265{ 266 if (m_platformPlayer) 267 return m_platformPlayer->hasAudio(); 268 return false; 269} 270 271void MediaPlayerPrivate::setVisible(bool) 272{ 273 notImplemented(); 274} 275 276float MediaPlayerPrivate::duration() const 277{ 278 if (m_platformPlayer) 279 return m_platformPlayer->duration(); 280 return 0.0f; 281} 282 283static const double SeekSubmissionDelay = 0.1; // Reasonable throttling value. 284static const double ShortMediaThreshold = SeekSubmissionDelay * 2.0; 285 286float MediaPlayerPrivate::currentTime() const 287{ 288 if (!m_platformPlayer) 289 return 0.0f; 290 291 // For very short media on the order of SeekSubmissionDelay we get 292 // unwanted repeats if we don't return the most up-to-date currentTime(). 293 bool shortMedia = m_platformPlayer->duration() < ShortMediaThreshold; 294 return m_userDrivenSeekTimer.isActive() && !shortMedia ? m_lastSeekTime: m_platformPlayer->currentTime(); 295} 296 297void MediaPlayerPrivate::seek(float time) 298{ 299 if (!m_platformPlayer) 300 return; 301 302 m_lastSeekTime = time; 303 m_lastSeekTimePending = true; 304 if (!m_userDrivenSeekTimer.isActive()) 305 userDrivenSeekTimerFired(0); 306} 307 308void MediaPlayerPrivate::userDrivenSeekTimerFired(Timer<MediaPlayerPrivate>*) 309{ 310 if (m_lastSeekTimePending) { 311 m_platformPlayer->seek(m_lastSeekTime); 312 m_lastSeekTimePending = false; 313 m_userDrivenSeekTimer.startOneShot(SeekSubmissionDelay); 314 } 315} 316 317bool MediaPlayerPrivate::seeking() const 318{ 319 return false; 320} 321 322void MediaPlayerPrivate::setRate(float rate) 323{ 324 if (m_platformPlayer) 325 m_platformPlayer->setRate(rate); 326} 327 328bool MediaPlayerPrivate::paused() const 329{ 330 if (m_platformPlayer) 331 return m_platformPlayer->paused(); 332 return false; 333} 334 335void MediaPlayerPrivate::setVolume(float volume) 336{ 337 if (m_platformPlayer) 338 m_platformPlayer->setVolume(volume); 339} 340 341void MediaPlayerPrivate::setMuted(bool muted) 342{ 343 if (m_platformPlayer) 344 m_platformPlayer->setMuted(muted); 345} 346 347bool MediaPlayerPrivate::muted() const 348{ 349 if (m_platformPlayer) 350 return m_platformPlayer->muted(); 351 return false; 352} 353 354MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const 355{ 356 return m_networkState; 357} 358 359MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const 360{ 361 return m_readyState; 362} 363 364float MediaPlayerPrivate::maxTimeSeekable() const 365{ 366 if (m_platformPlayer) 367 return m_platformPlayer->maxTimeSeekable(); 368 return 0.0f; 369} 370 371PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const 372{ 373 if (!m_platformPlayer) 374 return TimeRanges::create(); 375 376 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 377 if (float bufferLoaded = m_platformPlayer->bufferLoaded()) 378 timeRanges->add(0, bufferLoaded); 379 return timeRanges.release(); 380} 381 382bool MediaPlayerPrivate::didLoadingProgress() const 383{ 384 if (!m_platformPlayer) 385 return false; 386 387 float bufferLoaded = m_platformPlayer->bufferLoaded(); 388 if (bufferLoaded == m_lastLoadingTime) 389 return false; 390 391 m_lastLoadingTime = bufferLoaded; 392 return true; 393} 394 395void MediaPlayerPrivate::setSize(const IntSize&) 396{ 397 notImplemented(); 398} 399 400void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) 401{ 402 if (!m_platformPlayer) 403 return; 404 405#if USE(ACCELERATED_COMPOSITING) 406 if (supportsAcceleratedRendering()) { 407 // Only process paint calls coming via the accelerated compositing code 408 // path, where we get called with a null graphics context. See 409 // LayerCompositingThread::drawTextures(). Ignore calls from the regular 410 // rendering path. 411 if (!context) 412 m_platformPlayer->notifyOutputUpdate(BlackBerry::Platform::IntRect(rect.x(), rect.y(), rect.width(), rect.height())); 413 414 return; 415 } 416#endif 417 418 paintCurrentFrameInContext(context, rect); 419} 420 421void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect) 422{ 423 if (!hasVideo() || context->paintingDisabled() || !m_webCorePlayer->visible()) 424 return; 425 426 BlackBerry::Platform::Graphics::Drawable* dst = context->platformContext(); 427 428 BlackBerry::Platform::IntRect platformRect(rect.x(), rect.y(), rect.width(), rect.height()); 429 IntRect clippedRect = m_webCorePlayer->mediaPlayerClient()->mediaPlayerWindowClipRect(); 430 BlackBerry::Platform::IntRect platformWindowClipRect(clippedRect.x(), clippedRect.y(), clippedRect.width(), clippedRect.height()); 431 m_platformPlayer->paint(dst, platformRect, platformWindowClipRect); 432} 433 434bool MediaPlayerPrivate::hasAvailableVideoFrame() const 435{ 436 if (m_platformPlayer) 437 return m_platformPlayer->hasAvailableVideoFrame(); 438 return false; 439} 440 441bool MediaPlayerPrivate::hasSingleSecurityOrigin() const 442{ 443 return true; 444} 445 446MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const 447{ 448 if (m_platformPlayer) 449 return static_cast<MediaPlayer::MovieLoadType>(m_platformPlayer->movieLoadType()); 450 return MediaPlayer::Unknown; 451} 452 453void MediaPlayerPrivate::prepareForRendering() 454{ 455 if (m_platformPlayer) 456 m_platformPlayer->prepareForRendering(); 457} 458 459void MediaPlayerPrivate::resizeSourceDimensions() 460{ 461 if (!m_webCorePlayer) 462 return; 463 464 if (!m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsVideo()) 465 return; 466 467 // If we don't know what the width and height of the video source is, then we need to set it to something sane. 468 if (m_platformPlayer->sourceWidth() && m_platformPlayer->sourceHeight()) 469 return; 470 471 LayoutRect rect = m_webCorePlayer->mediaPlayerClient()->mediaPlayerContentBoxRect(); 472 m_platformPlayer->setSourceDimension(rect.width().toUnsigned(), rect.height().toUnsigned()); 473} 474 475void MediaPlayerPrivate::setFullscreenWebPageClient(BlackBerry::WebKit::WebPageClient* client) 476{ 477 if (m_fullscreenWebPageClient == client) 478 return; 479 480 m_fullscreenWebPageClient = client; 481 m_platformPlayer->toggleFullscreen(client); 482 483 // The following repaint is needed especially if video is paused and 484 // fullscreen is exiting, so that a MediaPlayerPrivate::paint() is 485 // triggered and the code in outputUpdate() sets the correct window 486 // rectangle. 487 if (!client) 488 m_webCorePlayer->repaint(); 489} 490 491BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::getWindow() 492{ 493 return m_platformPlayer->getWindow(); 494} 495 496BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::getPeerWindow(const char* uniqueID) const 497{ 498 return m_platformPlayer->getPeerWindow(uniqueID); 499} 500 501BlackBerry::Platform::IntRect MediaPlayerPrivate::getWindowScreenRect() const 502{ 503 unsigned x, y, width, height; 504 m_platformPlayer->getWindowPosition(x, y, width, height); 505 return BlackBerry::Platform::IntRect(x, y, width, height); 506} 507 508const char* MediaPlayerPrivate::mmrContextName() 509{ 510 return m_platformPlayer->mmrContextName(); 511} 512 513float MediaPlayerPrivate::percentLoaded() 514{ 515 if (!m_platformPlayer->duration()) 516 return 0; 517 518 float buffered = 0; 519 RefPtr<TimeRanges> timeRanges = this->buffered(); 520 for (unsigned i = 0; i < timeRanges->length(); ++i) { 521 ExceptionCode ignoredException; 522 float start = timeRanges->start(i, ignoredException); 523 float end = timeRanges->end(i, ignoredException); 524 buffered += end - start; 525 } 526 527 float loaded = buffered / m_platformPlayer->duration(); 528 return loaded; 529} 530 531unsigned MediaPlayerPrivate::sourceWidth() 532{ 533 return m_platformPlayer->sourceWidth(); 534} 535 536unsigned MediaPlayerPrivate::sourceHeight() 537{ 538 return m_platformPlayer->sourceHeight(); 539} 540 541void MediaPlayerPrivate::setAllowPPSVolumeUpdates(bool allow) 542{ 543 if (m_platformPlayer) 544 return m_platformPlayer->setAllowPPSVolumeUpdates(allow); 545} 546 547void MediaPlayerPrivate::updateStates() 548{ 549 MediaPlayer::NetworkState oldNetworkState = m_networkState; 550 MediaPlayer::ReadyState oldReadyState = m_readyState; 551 552 PlatformPlayer::Error currentError = m_platformPlayer->error(); 553 554 if (currentError != PlatformPlayer::MediaOK) { 555 m_readyState = MediaPlayer::HaveNothing; 556 if (currentError == PlatformPlayer::MediaDecodeError) 557 m_networkState = MediaPlayer::DecodeError; 558 else if (currentError == PlatformPlayer::MediaMetaDataError 559 || currentError == PlatformPlayer::MediaAudioReceiveError 560 || currentError == PlatformPlayer::MediaVideoReceiveError) 561 m_networkState = MediaPlayer::NetworkError; 562 } else { 563 switch (m_platformPlayer->mediaState()) { 564 case PlatformPlayer::MMRPlayStateIdle: 565 m_networkState = MediaPlayer::Idle; 566 break; 567 case PlatformPlayer::MMRPlayStatePlaying: 568 m_networkState = MediaPlayer::Loading; 569 break; 570 case PlatformPlayer::MMRPlayStateStopped: 571 m_networkState = MediaPlayer::Idle; 572 break; 573 case PlatformPlayer::MMRPlayStateUnknown: 574 default: 575 break; 576 } 577 578 switch (m_platformPlayer->state()) { 579 case PlatformPlayer::MP_STATE_IDLE: 580 if (isFullscreen()) 581 m_webCorePlayer->mediaPlayerClient()->mediaPlayerExitFullscreen(); 582 break; 583 case PlatformPlayer::MP_STATE_ACTIVE: 584 break; 585 case PlatformPlayer::MP_STATE_UNSUPPORTED: 586 break; 587 default: 588 break; 589 } 590 if ((duration() || movieLoadType() == MediaPlayer::LiveStream) 591 && m_readyState != MediaPlayer::HaveEnoughData) 592 m_readyState = MediaPlayer::HaveEnoughData; 593 } 594 595 if (m_readyState != oldReadyState) 596 m_webCorePlayer->readyStateChanged(); 597 if (m_networkState != oldNetworkState) 598 m_webCorePlayer->networkStateChanged(); 599} 600 601// IPlatformPlayerListener callbacks implementation 602void MediaPlayerPrivate::onStateChanged(PlatformPlayer::MpState) 603{ 604 updateStates(); 605} 606 607void MediaPlayerPrivate::onMediaStatusChanged(PlatformPlayer::MMRPlayState) 608{ 609 updateStates(); 610} 611 612void MediaPlayerPrivate::onError(PlatformPlayer::Error) 613{ 614 updateStates(); 615} 616 617void MediaPlayerPrivate::onDurationChanged(float) 618{ 619 updateStates(); 620 m_webCorePlayer->durationChanged(); 621} 622 623void MediaPlayerPrivate::onTimeChanged(float) 624{ 625 m_webCorePlayer->timeChanged(); 626} 627 628void MediaPlayerPrivate::onPauseStateChanged() 629{ 630 if (!isFullscreen()) 631 return; 632 633 // Paused state change not due to local controller. 634 if (m_platformPlayer->isPaused()) 635 m_webCorePlayer->mediaPlayerClient()->mediaPlayerPause(); 636 else { 637 // The HMI fullscreen widget has resumed play. Check if the 638 // pause timeout occurred. 639 m_platformPlayer->processPauseTimeoutIfNecessary(); 640 m_webCorePlayer->mediaPlayerClient()->mediaPlayerPlay(); 641 } 642} 643 644void MediaPlayerPrivate::onRateChanged(float) 645{ 646 m_webCorePlayer->rateChanged(); 647} 648 649void MediaPlayerPrivate::onVolumeChanged(float volume) 650{ 651 m_webCorePlayer->volumeChanged(volume); 652} 653 654void MediaPlayerPrivate::onRepaint() 655{ 656 m_webCorePlayer->repaint(); 657} 658 659void MediaPlayerPrivate::onSizeChanged() 660{ 661 resizeSourceDimensions(); 662 if (hasVideo()) 663 m_webCorePlayer->sizeChanged(); 664} 665 666void MediaPlayerPrivate::onPlayNotified() 667{ 668 m_webCorePlayer->mediaPlayerClient()->mediaPlayerPlay(); 669} 670 671void MediaPlayerPrivate::onPauseNotified() 672{ 673 m_webCorePlayer->mediaPlayerClient()->mediaPlayerPause(); 674} 675 676static const int popupDialogInterval = 10; 677static const double checkMetadataReadyInterval = 0.5; 678void MediaPlayerPrivate::onWaitMetadataNotified(bool hasFinished, int timeWaited) 679{ 680 if (!hasFinished) { 681 if (!m_waitMetadataTimer.isActive()) { 682 // Make sure to popup dialog every 10 seconds after metadata start to load. 683 // This should be set only once at the first time when user press the play button. 684 m_waitMetadataPopDialogCounter = static_cast<int>(timeWaited / checkMetadataReadyInterval); 685 m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); 686 } 687 } else if (m_waitMetadataTimer.isActive()) { 688 m_waitMetadataTimer.stop(); 689 m_waitMetadataPopDialogCounter = 0; 690 } 691} 692 693void MediaPlayerPrivate::waitMetadataTimerFired(Timer<MediaPlayerPrivate>*) 694{ 695 if (m_platformPlayer->isMetadataReady()) { 696 m_waitMetadataPopDialogCounter = 0; 697 m_platformPlayer->playWithMetadataReady(); 698 return; 699 } 700 701 static const int hitTimeToPopupDialog = static_cast<int>(popupDialogInterval / checkMetadataReadyInterval); 702 m_waitMetadataPopDialogCounter++; 703 if (m_waitMetadataPopDialogCounter < hitTimeToPopupDialog) { 704 m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); 705 return; 706 } 707 m_waitMetadataPopDialogCounter = 0; 708 709 PlatformPlayer::DialogResult wait = m_platformPlayer->showErrorDialog(PlatformPlayer::MediaMetaDataTimeoutError); 710 if (wait == PlatformPlayer::DialogEmergencyExit) 711 return; 712 if (wait == PlatformPlayer::DialogResponse0) 713 onPauseNotified(); 714 else { 715 if (m_platformPlayer->isMetadataReady()) 716 m_platformPlayer->playWithMetadataReady(); 717 else 718 m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); 719 } 720} 721 722#if USE(ACCELERATED_COMPOSITING) 723void MediaPlayerPrivate::onBuffering(bool flag) 724{ 725 setBuffering(flag); 726} 727#endif 728 729static ProtectionSpace generateProtectionSpaceFromMMRAuthChallenge(const MMRAuthChallenge& authChallenge) 730{ 731 KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); 732 ASSERT(url.isValid()); 733 734 return ProtectionSpace(url.host(), url.port(), 735 static_cast<ProtectionSpaceServerType>(authChallenge.serverType()), 736 authChallenge.realm().c_str(), 737 static_cast<ProtectionSpaceAuthenticationScheme>(authChallenge.authScheme())); 738} 739 740void MediaPlayerPrivate::onAuthenticationNeeded(MMRAuthChallenge& authChallenge) 741{ 742 KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); 743 if (!url.isValid()) 744 return; 745 746 ProtectionSpace protectionSpace = generateProtectionSpaceFromMMRAuthChallenge(authChallenge); 747 Credential credential = CredentialStorage::get(protectionSpace); 748 if (!credential.isEmpty()) { 749 notifyChallengeResult(url, protectionSpace, AuthenticationChallengeSuccess, credential); 750 return; 751 } 752 753 m_isAuthenticationChallenging = true; 754 AuthenticationChallengeManager::instance()->authenticationChallenge(url, protectionSpace, credential, 755 this, m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()); 756} 757 758void MediaPlayerPrivate::notifyChallengeResult(const KURL& url, const ProtectionSpace&, AuthenticationChallengeResult result, const Credential& credential) 759{ 760 m_isAuthenticationChallenging = false; 761 762 if (result != AuthenticationChallengeSuccess || !url.isValid()) 763 return; 764 765 m_platformPlayer->reloadWithCredential(credential.user(), credential.password(), static_cast<MMRAuthChallenge::CredentialPersistence>(credential.persistence())); 766} 767 768void MediaPlayerPrivate::onAuthenticationAccepted(const MMRAuthChallenge& authChallenge) const 769{ 770 KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); 771 if (!url.isValid()) 772 return; 773 774 ProtectionSpace protectionSpace = generateProtectionSpaceFromMMRAuthChallenge(authChallenge); 775 Credential savedCredential = CredentialStorage::get(protectionSpace); 776 if (savedCredential.isEmpty()) 777 CredentialStorage::set(Credential(authChallenge.username().c_str(), authChallenge.password().c_str(), static_cast<CredentialPersistence>(authChallenge.persistence())), protectionSpace, url); 778} 779 780int MediaPlayerPrivate::onShowErrorDialog(PlatformPlayer::Error type) 781{ 782 using namespace BlackBerry::WebKit; 783 784 WebPageClient::AlertType atype; 785 switch (type) { 786 case PlatformPlayer::MediaOK: 787 atype = WebPageClient::MediaOK; 788 break; 789 case PlatformPlayer::MediaDecodeError: 790 atype = WebPageClient::MediaDecodeError; 791 break; 792 case PlatformPlayer::MediaMetaDataError: 793 atype = WebPageClient::MediaMetaDataError; 794 break; 795 case PlatformPlayer::MediaMetaDataTimeoutError: 796 atype = WebPageClient::MediaMetaDataTimeoutError; 797 break; 798 case PlatformPlayer::MediaNoMetaDataError: 799 atype = WebPageClient::MediaNoMetaDataError; 800 break; 801 case PlatformPlayer::MediaVideoReceiveError: 802 atype = WebPageClient::MediaVideoReceiveError; 803 break; 804 case PlatformPlayer::MediaAudioReceiveError: 805 atype = WebPageClient::MediaAudioReceiveError; 806 break; 807 case PlatformPlayer::MediaInvalidError: 808 atype = WebPageClient::MediaInvalidError; 809 break; 810 default: 811 LOG(Media, "Alert type does not exist."); 812 return -1; 813 } 814 return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->showAlertDialog(atype); 815} 816 817static WebMediaStreamSource toWebMediaStreamSource(MediaStreamSource* src) 818{ 819 return WebMediaStreamSource(src->id(), static_cast<WebMediaStreamSource::Type>(src->type()), src->name()); 820} 821 822static WebMediaStreamDescriptor toWebMediaStreamDescriptor(MediaStreamDescriptor* d) 823{ 824 vector<WebMediaStreamSource> audioSources; 825 for (size_t i = 0; i < d->numberOfAudioComponents(); i++) 826 audioSources.push_back(toWebMediaStreamSource(d->audioComponent(i)->source())); 827 828 vector<WebMediaStreamSource> videoSources; 829 for (size_t i = 0; i < d->numberOfVideoComponents(); i++) 830 videoSources.push_back(toWebMediaStreamSource(d->videoComponent(i)->source())); 831 832 return WebMediaStreamDescriptor(d->id(), audioSources, videoSources); 833} 834 835WebMediaStreamDescriptor MediaPlayerPrivate::lookupMediaStream(const BlackBerry::Platform::String& url) 836{ 837 MediaStreamDescriptor* descriptor = MediaStreamRegistry::registry().lookupMediaStreamDescriptor(WTF::String::fromUTF8(url.c_str())); 838 if (!descriptor) 839 return WebMediaStreamDescriptor(); 840 841 return toWebMediaStreamDescriptor(descriptor); 842} 843 844BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::platformWindow() 845{ 846 return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->platformWindow(); 847} 848 849bool MediaPlayerPrivate::isProcessingUserGesture() const 850{ 851 return m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsProcessingUserGesture(); 852} 853 854bool MediaPlayerPrivate::isFullscreen() const 855{ 856 return m_fullscreenWebPageClient; 857} 858 859bool MediaPlayerPrivate::isElementPaused() const 860{ 861 return m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsPaused(); 862} 863 864bool MediaPlayerPrivate::isTabVisible() const 865{ 866 return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->isVisible(); 867} 868 869bool MediaPlayerPrivate::supportsAcceleratedRendering() const 870{ 871 if (m_platformPlayer) 872 return m_platformPlayer->supportsAcceleratedRendering(); 873 return false; 874} 875 876#if USE(ACCELERATED_COMPOSITING) 877static const double BufferingAnimationDelay = 1.0 / 24; 878static unsigned* s_bufferingImageData = 0; 879static IntSize s_bufferingImageSize; 880 881PlatformMedia MediaPlayerPrivate::platformMedia() const 882{ 883 PlatformMedia pm; 884 pm.type = PlatformMedia::QNXMediaPlayerType; 885 pm.media.qnxMediaPlayer = const_cast<MediaPlayerPrivate*>(this); 886 return pm; 887} 888 889PlatformLayer* MediaPlayerPrivate::platformLayer() const 890{ 891 if (m_platformLayer) 892 return m_platformLayer.get(); 893 return 0; 894} 895 896static void loadBufferingImageData() 897{ 898 static bool loaded = false; 899 if (!loaded) { 900 static Image* bufferingIcon = Image::loadPlatformResource("vidbuffer").leakRef(); 901 902 NativeImagePtr nativeImage = bufferingIcon->nativeImageForCurrentFrame(); 903 if (!nativeImage) 904 return; 905 906 loaded = true; 907 s_bufferingImageSize = bufferingIcon->size(); 908 int bufSize = bufferingIcon->decodedSize(); 909 s_bufferingImageData = static_cast<unsigned*>(malloc(bufSize)); 910 911 nativeImage->readPixels(s_bufferingImageData, s_bufferingImageSize.width() * s_bufferingImageSize.height()); 912 913 bufferingIcon->deref(); 914 } 915} 916 917void MediaPlayerPrivate::bufferingTimerFired(Timer<MediaPlayerPrivate>*) 918{ 919 if (m_showBufferingImage) { 920 if (!isFullscreen() && m_platformLayer) 921 m_platformLayer->setNeedsDisplay(); 922 m_bufferingTimer.startOneShot(BufferingAnimationDelay); 923 } 924} 925 926void MediaPlayerPrivate::setBuffering(bool buffering) 927{ 928 if (!m_webCorePlayer || !m_webCorePlayer->mediaPlayerClient() || !m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsVideo()) 929 buffering = false; // Buffering animation not visible for audio. 930 931 if (buffering != m_showBufferingImage) { 932 m_showBufferingImage = buffering; 933 if (buffering) { 934 loadBufferingImageData(); 935 m_bufferingTimer.startOneShot(BufferingAnimationDelay); 936 } else 937 m_bufferingTimer.stop(); 938 939 if (m_platformLayer) 940 m_platformLayer->setNeedsDisplay(); 941 } 942} 943 944static unsigned allocateTextureId() 945{ 946 unsigned texid; 947 glGenTextures(1, &texid); 948 glBindTexture(GL_TEXTURE_2D, texid); 949 // Do basic linear filtering on resize. 950 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 951 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 952 // NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE. 953 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 954 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 955 return texid; 956} 957 958void MediaPlayerPrivate::drawBufferingAnimation(const TransformationMatrix& matrix, const Graphics::GLES2Program& program) 959{ 960 if (m_showBufferingImage && s_bufferingImageData && !isFullscreen()) { 961 TransformationMatrix renderMatrix = matrix; 962 963 // Rotate the buffering indicator so that it takes 1 second to do 1 revolution. 964 timespec time; 965 clock_gettime(CLOCK_MONOTONIC, &time); 966 renderMatrix.rotate(time.tv_nsec / 1000000000.0 * 360.0); 967 968 static bool initialized = false; 969 static unsigned texId = allocateTextureId(); 970 glBindTexture(GL_TEXTURE_2D, texId); 971 if (!initialized) { 972 initialized = true; 973 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s_bufferingImageSize.width(), s_bufferingImageSize.height(), 974 0, GL_RGBA, GL_UNSIGNED_BYTE, s_bufferingImageData); 975 free(s_bufferingImageData); 976 } 977 978 float texcoords[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; 979 FloatRect bufferingImageRect(FloatPoint(-s_bufferingImageSize.width() / 2.0f, -s_bufferingImageSize.height() / 2.0f), s_bufferingImageSize); 980 FloatQuad transformedQuad = renderMatrix.mapQuad(bufferingImageRect); 981 982 glEnable(GL_BLEND); 983 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 984 glUniform1f(program.opacityLocation(), 1.0); 985 glVertexAttribPointer(program.positionLocation(), 2, GL_FLOAT, GL_FALSE, 0, &transformedQuad); 986 glVertexAttribPointer(program.texCoordLocation(), 2, GL_FLOAT, GL_FALSE, 0, texcoords); 987 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 988 } 989} 990#endif 991 992void MediaPlayerPrivate::onConditionallyEnterFullscreen() 993{ 994 Document* owningDocument = m_webCorePlayer->mediaPlayerClient()->mediaPlayerOwningDocument(); 995 BlackBerry::Platform::DeviceInfo* info = BlackBerry::Platform::DeviceInfo::instance(); 996 997 // Don't allow video in <embed> and <object> containers to go fullscreen 998 // on play because this does not currently work. Detect this by checking 999 // for MediaDocument with a parent document. 1000 if (owningDocument->isMediaDocument() && owningDocument->parentDocument()) 1001 return; 1002 1003 if (info->isMobile()) { 1004 // This is a mobile device (small screen), not a tablet, so we 1005 // enter fullscreen video on user-initiated plays. 1006 bool nothingIsFullscreen = !m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsFullscreen(); 1007#if ENABLE(FULLSCREEN_API) 1008 if (owningDocument->webkitIsFullScreen()) 1009 nothingIsFullscreen = false; 1010#endif 1011 if (nothingIsFullscreen) 1012 m_webCorePlayer->mediaPlayerClient()->mediaPlayerEnterFullscreen(); 1013 } 1014} 1015 1016void MediaPlayerPrivate::onExitFullscreen() 1017{ 1018 if (m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsFullscreen()) 1019 m_webCorePlayer->mediaPlayerClient()->mediaPlayerExitFullscreen(); 1020} 1021 1022void MediaPlayerPrivate::onCreateHolePunchRect() 1023{ 1024#if USE(ACCELERATED_COMPOSITING) 1025 // Create platform layer for video (create hole punch rect). 1026 if (!m_platformLayer && supportsAcceleratedRendering()) { 1027 m_showBufferingImage = false; 1028 m_platformLayer = VideoLayerWebKitThread::create(m_webCorePlayer); 1029 } 1030#endif 1031} 1032 1033void MediaPlayerPrivate::onDestroyHolePunchRect() 1034{ 1035#if USE(ACCELERATED_COMPOSITING) 1036 setBuffering(false); 1037 // Remove media player from platform layer (remove hole punch rect). 1038 if (m_platformLayer) { 1039 static_cast<VideoLayerWebKitThread*>(m_platformLayer.get())->setMediaPlayer(0); 1040 m_platformLayer.clear(); 1041 } 1042#endif 1043} 1044 1045} // namespace WebCore 1046 1047#endif // ENABLE(VIDEO) 1048