| /* |
| * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(VIDEO) |
| #include "MediaPlayerPrivateBlackBerry.h" |
| |
| #include "AuthenticationChallengeManager.h" |
| #include "CookieManager.h" |
| #include "Credential.h" |
| #include "CredentialStorage.h" |
| #include "ExceptionCodePlaceholder.h" |
| #include "Frame.h" |
| #include "FrameView.h" |
| #include "GraphicsContext.h" |
| #include "HTMLNames.h" |
| #include "HostWindow.h" |
| #include "MediaStreamDescriptor.h" |
| #include "MediaStreamRegistry.h" |
| #include "NotImplemented.h" |
| #include "PlatformContextSkia.h" |
| #include "ProtectionSpace.h" |
| #include "RenderBox.h" |
| #include "TimeRanges.h" |
| #include "WebPageClient.h" |
| |
| #include <BlackBerryPlatformDeviceInfo.h> |
| #include <BlackBerryPlatformPrimitives.h> |
| #include <BlackBerryPlatformSettings.h> |
| #include <FrameLoaderClientBlackBerry.h> |
| #include <set> |
| #include <wtf/text/CString.h> |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| #include "NativeImageSkia.h" |
| #include "VideoLayerWebKitThread.h" |
| #include <GLES2/gl2.h> |
| #endif |
| |
| using namespace std; |
| using namespace BlackBerry::Platform; |
| |
| namespace WebCore { |
| |
| // Static callback functions for factory |
| PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivate::create(MediaPlayer* player) |
| { |
| return adoptPtr(new MediaPlayerPrivate(player)); |
| } |
| |
| void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) |
| { |
| registrar(create, getSupportedTypes, supportsType, 0, 0, 0); |
| } |
| |
| void MediaPlayerPrivate::getSupportedTypes(HashSet<WTF::String>& types) |
| { |
| set<BlackBerry::Platform::String> supported = PlatformPlayer::allSupportedMimeTypes(); |
| set<BlackBerry::Platform::String>::iterator i = supported.begin(); |
| for (; i != supported.end(); i++) |
| types.add(*i); |
| } |
| |
| MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const WTF::String& type, const WTF::String& codecs, const KURL& url) |
| { |
| bool isRTSP = url.protocolIs("rtsp"); |
| |
| if (!isRTSP && (type.isNull() || type.isEmpty())) { |
| LOG(Media, "MediaPlayer does not support type; type is null or empty."); |
| return MediaPlayer::IsNotSupported; |
| } |
| |
| // spec says we should not return "probably" if the codecs string is empty |
| if (isRTSP || PlatformPlayer::mimeTypeSupported(type.ascii().data())) { |
| LOG(Media, "MediaPlayer supports type %s.", isRTSP ? "rtsp" : type.ascii().data()); |
| return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; |
| } |
| LOG(Media, "MediaPlayer does not support type %s.", type.ascii().data()); |
| return MediaPlayer::IsNotSupported; |
| } |
| |
| void MediaPlayerPrivate::notifyAppActivatedEvent(bool activated) |
| { |
| PlatformPlayer::notifyAppActivatedEvent(activated); |
| } |
| |
| void MediaPlayerPrivate::setCertificatePath(const WTF::String& caPath) |
| { |
| PlatformPlayer::setCertificatePath(caPath); |
| } |
| |
| MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) |
| : m_webCorePlayer(player) |
| , m_platformPlayer(0) |
| , m_networkState(MediaPlayer::Empty) |
| , m_readyState(MediaPlayer::HaveNothing) |
| , m_fullscreenWebPageClient(0) |
| #if USE(ACCELERATED_COMPOSITING) |
| , m_bufferingTimer(this, &MediaPlayerPrivate::bufferingTimerFired) |
| , m_showBufferingImage(false) |
| , m_mediaIsBuffering(false) |
| #endif |
| , m_userDrivenSeekTimer(this, &MediaPlayerPrivate::userDrivenSeekTimerFired) |
| , m_lastSeekTime(0) |
| , m_lastLoadingTime(0) |
| , m_lastSeekTimePending(false) |
| , m_isAuthenticationChallenging(false) |
| , m_waitMetadataTimer(this, &MediaPlayerPrivate::waitMetadataTimerFired) |
| , m_waitMetadataPopDialogCounter(0) |
| { |
| } |
| |
| MediaPlayerPrivate::~MediaPlayerPrivate() |
| { |
| if (m_isAuthenticationChallenging) |
| AuthenticationChallengeManager::instance()->cancelAuthenticationChallenge(this); |
| |
| if (isFullscreen()) |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerExitFullscreen(); |
| #if USE(ACCELERATED_COMPOSITING) |
| // Remove media player from platform layer. |
| if (m_platformLayer) |
| static_cast<VideoLayerWebKitThread*>(m_platformLayer.get())->setMediaPlayer(0); |
| #endif |
| |
| if (m_platformPlayer) { |
| if (m_platformPlayer->dialogState() == PlatformPlayer::DialogShown) { |
| m_platformPlayer->setDialogState(PlatformPlayer::MediaPlayerPrivateDestroyed); |
| m_platformPlayer->stop(); |
| } else |
| deleteGuardedObject(m_platformPlayer); |
| } |
| } |
| |
| void MediaPlayerPrivate::load(const WTF::String& url) |
| { |
| WTF::String modifiedUrl(url); |
| |
| if (modifiedUrl.startsWith("local://")) { |
| KURL kurl = KURL(KURL(), modifiedUrl); |
| kurl.setProtocol("file"); |
| WTF::String tempPath(BlackBerry::Platform::Settings::instance()->applicationLocalDirectory().c_str()); |
| tempPath.append(kurl.path()); |
| kurl.setPath(tempPath); |
| modifiedUrl = kurl.string(); |
| } |
| if (modifiedUrl.startsWith("file://")) { |
| // The QNX Multimedia Framework cannot handle filenames containing URL escape sequences. |
| modifiedUrl = decodeURLEscapeSequences(modifiedUrl); |
| } |
| |
| void* tabId = m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient(); |
| int playerID = m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->playerID(); |
| bool isVideo = m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsVideo(); |
| |
| deleteGuardedObject(m_platformPlayer); |
| #if USE(ACCELERATED_COMPOSITING) |
| m_platformPlayer = PlatformPlayer::create(this, tabId, isVideo, true, modifiedUrl.utf8().data()); |
| #else |
| m_platformPlayer = PlatformPlayer::create(this, tabId, isVideo, false, modifiedUrl.utf8().data()); |
| #endif |
| |
| WTF::String cookiePairs; |
| if (!url.isEmpty()) |
| cookiePairs = cookieManager().getCookie(KURL(ParsedURLString, url.utf8().data()), WithHttpOnlyCookies); |
| if (!cookiePairs.isEmpty() && cookiePairs.utf8().data()) |
| m_platformPlayer->load(playerID, modifiedUrl.utf8().data(), m_webCorePlayer->userAgent().utf8().data(), cookiePairs.utf8().data()); |
| else |
| m_platformPlayer->load(playerID, modifiedUrl.utf8().data(), m_webCorePlayer->userAgent().utf8().data(), 0); |
| } |
| |
| void MediaPlayerPrivate::cancelLoad() |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->cancelLoad(); |
| } |
| |
| void MediaPlayerPrivate::prepareToPlay() |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->prepareToPlay(); |
| } |
| |
| void MediaPlayerPrivate::play() |
| { |
| if (m_platformPlayer) { |
| m_platformPlayer->play(); |
| if (m_platformPlayer->isMetadataReady()) |
| conditionallyGoFullscreenAfterPlay(); |
| } |
| } |
| |
| void MediaPlayerPrivate::pause() |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->pause(); |
| } |
| |
| bool MediaPlayerPrivate::supportsFullscreen() const |
| { |
| return true; |
| } |
| |
| IntSize MediaPlayerPrivate::naturalSize() const |
| { |
| if (!m_platformPlayer) |
| return IntSize(); |
| |
| // Cannot return empty size, otherwise paint() will never get called. |
| // Also, the values here will affect the aspect ratio of the output rectangle that will |
| // be used for renderering the video, so we must take PAR into account. |
| // Now, hope that metadata has been provided before this gets called if this is a video. |
| double pixelWidth = static_cast<double>(m_platformPlayer->pixelWidth()); |
| double pixelHeight = static_cast<double>(m_platformPlayer->pixelHeight()); |
| if (!m_platformPlayer->pixelWidth() || !m_platformPlayer->pixelHeight()) |
| pixelWidth = pixelHeight = 1.0; |
| |
| // Use floating point arithmetic to eliminate the chance of integer |
| // overflow. PAR numbers can be 5 digits or more. |
| double adjustedSourceWidth = static_cast<double>(m_platformPlayer->sourceWidth()) * pixelWidth / pixelHeight; |
| return IntSize(static_cast<int>(adjustedSourceWidth + 0.5), m_platformPlayer->sourceHeight()); |
| } |
| |
| bool MediaPlayerPrivate::hasVideo() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->hasVideo(); |
| return false; |
| } |
| |
| bool MediaPlayerPrivate::hasAudio() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->hasAudio(); |
| return false; |
| } |
| |
| void MediaPlayerPrivate::setVisible(bool) |
| { |
| notImplemented(); |
| } |
| |
| float MediaPlayerPrivate::duration() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->duration(); |
| return 0.0f; |
| } |
| |
| static const double SeekSubmissionDelay = 0.1; // Reasonable throttling value. |
| static const double ShortMediaThreshold = SeekSubmissionDelay * 2.0; |
| |
| float MediaPlayerPrivate::currentTime() const |
| { |
| if (!m_platformPlayer) |
| return 0.0f; |
| |
| // For very short media on the order of SeekSubmissionDelay we get |
| // unwanted repeats if we don't return the most up-to-date currentTime(). |
| bool shortMedia = m_platformPlayer->duration() < ShortMediaThreshold; |
| return m_userDrivenSeekTimer.isActive() && !shortMedia ? m_lastSeekTime: m_platformPlayer->currentTime(); |
| } |
| |
| void MediaPlayerPrivate::seek(float time) |
| { |
| if (!m_platformPlayer) |
| return; |
| |
| m_lastSeekTime = time; |
| m_lastSeekTimePending = true; |
| if (!m_userDrivenSeekTimer.isActive()) |
| userDrivenSeekTimerFired(0); |
| } |
| |
| void MediaPlayerPrivate::userDrivenSeekTimerFired(Timer<MediaPlayerPrivate>*) |
| { |
| if (m_lastSeekTimePending) { |
| m_platformPlayer->seek(m_lastSeekTime); |
| m_lastSeekTimePending = false; |
| m_userDrivenSeekTimer.startOneShot(SeekSubmissionDelay); |
| } |
| } |
| |
| bool MediaPlayerPrivate::seeking() const |
| { |
| return false; |
| } |
| |
| void MediaPlayerPrivate::setRate(float rate) |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->setRate(rate); |
| } |
| |
| bool MediaPlayerPrivate::paused() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->paused(); |
| return false; |
| } |
| |
| void MediaPlayerPrivate::setVolume(float volume) |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->setVolume(volume); |
| } |
| |
| void MediaPlayerPrivate::setMuted(bool muted) |
| { |
| if (m_platformPlayer) |
| m_platformPlayer->setMuted(muted); |
| } |
| |
| bool MediaPlayerPrivate::muted() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->muted(); |
| return false; |
| } |
| |
| MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const |
| { |
| return m_networkState; |
| } |
| |
| MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const |
| { |
| return m_readyState; |
| } |
| |
| float MediaPlayerPrivate::maxTimeSeekable() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->maxTimeSeekable(); |
| return 0.0f; |
| } |
| |
| PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const |
| { |
| if (!m_platformPlayer) |
| return TimeRanges::create(); |
| |
| RefPtr<TimeRanges> timeRanges = TimeRanges::create(); |
| if (float bufferLoaded = m_platformPlayer->bufferLoaded()) |
| timeRanges->add(0, bufferLoaded); |
| return timeRanges.release(); |
| } |
| |
| bool MediaPlayerPrivate::didLoadingProgress() const |
| { |
| if (!m_platformPlayer) |
| return false; |
| |
| float bufferLoaded = m_platformPlayer->bufferLoaded(); |
| if (bufferLoaded == m_lastLoadingTime) |
| return false; |
| |
| m_lastLoadingTime = bufferLoaded; |
| return true; |
| } |
| |
| void MediaPlayerPrivate::setSize(const IntSize&) |
| { |
| notImplemented(); |
| } |
| |
| void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) |
| { |
| if (!m_platformPlayer) |
| return; |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| if (supportsAcceleratedRendering()) { |
| // Only process paint calls coming via the accelerated compositing code |
| // path, where we get called with a null graphics context. See |
| // LayerCompositingThread::drawTextures(). Ignore calls from the regular |
| // rendering path. |
| if (!context) |
| m_platformPlayer->notifyOutputUpdate(BlackBerry::Platform::IntRect(rect.x(), rect.y(), rect.width(), rect.height())); |
| |
| return; |
| } |
| #endif |
| |
| paintCurrentFrameInContext(context, rect); |
| } |
| |
| void MediaPlayerPrivate::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect) |
| { |
| if (!hasVideo() || context->paintingDisabled() || !m_webCorePlayer->visible()) |
| return; |
| |
| PlatformGraphicsContext* graphics = context->platformContext(); |
| ASSERT(graphics); |
| |
| BlackBerry::Platform::IntRect platformRect(rect.x(), rect.y(), rect.width(), rect.height()); |
| IntRect clippedRect = m_webCorePlayer->mediaPlayerClient()->mediaPlayerWindowClipRect(); |
| BlackBerry::Platform::IntRect platformWindowClipRect(clippedRect.x(), clippedRect.y(), clippedRect.width(), clippedRect.height()); |
| m_platformPlayer->paint(graphics->canvas(), platformRect, platformWindowClipRect); |
| } |
| |
| bool MediaPlayerPrivate::hasAvailableVideoFrame() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->hasAvailableVideoFrame(); |
| return false; |
| } |
| |
| bool MediaPlayerPrivate::hasSingleSecurityOrigin() const |
| { |
| return true; |
| } |
| |
| MediaPlayer::MovieLoadType MediaPlayerPrivate::movieLoadType() const |
| { |
| if (m_platformPlayer) |
| return static_cast<MediaPlayer::MovieLoadType>(m_platformPlayer->movieLoadType()); |
| return MediaPlayer::Unknown; |
| } |
| |
| void MediaPlayerPrivate::resizeSourceDimensions() |
| { |
| if (!m_webCorePlayer) |
| return; |
| |
| if (!m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsVideo()) |
| return; |
| |
| // If we have an HTMLVideoElement but the source has no video, then we need to resize the media element. |
| if (!hasVideo() && PlatformPlayer::MediaOK == m_platformPlayer->error()) { |
| LayoutRect rect = m_webCorePlayer->mediaPlayerClient()->mediaPlayerContentBoxRect(); |
| |
| static const int playbookMinAudioElementWidth = 300; |
| static const int playbookMinAudioElementHeight = 32; |
| // If the rect dimensions are less than the allowed minimum, use the minimum instead. |
| int newWidth = max(rect.width().toInt(), playbookMinAudioElementWidth); |
| int newHeight = max(rect.height().toInt(), playbookMinAudioElementHeight); |
| |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerSetSize(IntSize(newWidth, newHeight)); |
| } |
| |
| // If we don't know what the width and height of the video source is, then we need to set it to something sane. |
| if (m_platformPlayer->sourceWidth() && m_platformPlayer->sourceHeight()) |
| return; |
| LayoutRect rect = m_webCorePlayer->mediaPlayerClient()->mediaPlayerContentBoxRect(); |
| m_platformPlayer->setSourceDimension(rect.width().toUnsigned(), rect.height().toUnsigned()); |
| } |
| |
| void MediaPlayerPrivate::setFullscreenWebPageClient(BlackBerry::WebKit::WebPageClient* client) |
| { |
| if (m_fullscreenWebPageClient == client) |
| return; |
| |
| m_fullscreenWebPageClient = client; |
| m_platformPlayer->toggleFullscreen(client); |
| |
| // The following repaint is needed especially if video is paused and |
| // fullscreen is exiting, so that a MediaPlayerPrivate::paint() is |
| // triggered and the code in outputUpdate() sets the correct window |
| // rectangle. |
| if (!client) |
| m_webCorePlayer->repaint(); |
| } |
| |
| BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::getWindow() |
| { |
| return m_platformPlayer->getWindow(); |
| } |
| |
| BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::getPeerWindow(const char* uniqueID) const |
| { |
| return m_platformPlayer->getPeerWindow(uniqueID); |
| } |
| |
| BlackBerry::Platform::IntRect MediaPlayerPrivate::getWindowScreenRect() const |
| { |
| unsigned x, y, width, height; |
| m_platformPlayer->getWindowPosition(x, y, width, height); |
| return BlackBerry::Platform::IntRect(x, y, width, height); |
| } |
| |
| const char* MediaPlayerPrivate::mmrContextName() |
| { |
| return m_platformPlayer->mmrContextName(); |
| } |
| |
| float MediaPlayerPrivate::percentLoaded() |
| { |
| if (!m_platformPlayer->duration()) |
| return 0; |
| |
| float buffered = 0; |
| RefPtr<TimeRanges> timeRanges = this->buffered(); |
| for (unsigned i = 0; i < timeRanges->length(); ++i) { |
| float start = timeRanges->start(i, IGNORE_EXCEPTION); |
| float end = timeRanges->end(i, IGNORE_EXCEPTION); |
| buffered += end - start; |
| } |
| |
| float loaded = buffered / m_platformPlayer->duration(); |
| return loaded; |
| } |
| |
| unsigned MediaPlayerPrivate::sourceWidth() |
| { |
| return m_platformPlayer->sourceWidth(); |
| } |
| |
| unsigned MediaPlayerPrivate::sourceHeight() |
| { |
| return m_platformPlayer->sourceHeight(); |
| } |
| |
| void MediaPlayerPrivate::setAllowPPSVolumeUpdates(bool allow) |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->setAllowPPSVolumeUpdates(allow); |
| } |
| |
| void MediaPlayerPrivate::updateStates() |
| { |
| MediaPlayer::NetworkState oldNetworkState = m_networkState; |
| MediaPlayer::ReadyState oldReadyState = m_readyState; |
| |
| PlatformPlayer::Error currentError = m_platformPlayer->error(); |
| |
| if (currentError != PlatformPlayer::MediaOK) { |
| m_readyState = MediaPlayer::HaveNothing; |
| if (currentError == PlatformPlayer::MediaDecodeError) |
| m_networkState = MediaPlayer::DecodeError; |
| else if (currentError == PlatformPlayer::MediaMetaDataError |
| || currentError == PlatformPlayer::MediaAudioReceiveError |
| || currentError == PlatformPlayer::MediaVideoReceiveError) |
| m_networkState = MediaPlayer::NetworkError; |
| } else { |
| switch (m_platformPlayer->mediaState()) { |
| case PlatformPlayer::MMRPlayStateIdle: |
| m_networkState = MediaPlayer::Idle; |
| break; |
| case PlatformPlayer::MMRPlayStatePlaying: |
| m_networkState = MediaPlayer::Loading; |
| break; |
| case PlatformPlayer::MMRPlayStateStopped: |
| m_networkState = MediaPlayer::Idle; |
| break; |
| case PlatformPlayer::MMRPlayStateUnknown: |
| default: |
| break; |
| } |
| |
| switch (m_platformPlayer->state()) { |
| case PlatformPlayer::MP_STATE_IDLE: |
| #if USE(ACCELERATED_COMPOSITING) |
| setBuffering(false); |
| m_mediaIsBuffering = false; |
| // Remove media player from platform layer (remove hole punch rect). |
| if (m_platformLayer) { |
| static_cast<VideoLayerWebKitThread*>(m_platformLayer.get())->setMediaPlayer(0); |
| m_platformLayer.clear(); |
| } |
| #endif |
| if (isFullscreen()) |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerExitFullscreen(); |
| break; |
| case PlatformPlayer::MP_STATE_ACTIVE: |
| #if USE(ACCELERATED_COMPOSITING) |
| m_showBufferingImage = false; |
| m_mediaIsBuffering = false; |
| // Create platform layer for video (create hole punch rect). |
| if (!m_platformLayer && supportsAcceleratedRendering()) |
| m_platformLayer = VideoLayerWebKitThread::create(m_webCorePlayer); |
| #endif |
| break; |
| case PlatformPlayer::MP_STATE_UNSUPPORTED: |
| break; |
| default: |
| break; |
| } |
| if ((duration() || movieLoadType() == MediaPlayer::LiveStream) |
| && m_readyState != MediaPlayer::HaveEnoughData) |
| m_readyState = MediaPlayer::HaveEnoughData; |
| } |
| |
| if (m_readyState != oldReadyState) |
| m_webCorePlayer->readyStateChanged(); |
| if (m_networkState != oldNetworkState) |
| m_webCorePlayer->networkStateChanged(); |
| } |
| |
| // IPlatformPlayerListener callbacks implementation |
| void MediaPlayerPrivate::onStateChanged(PlatformPlayer::MpState) |
| { |
| updateStates(); |
| } |
| |
| void MediaPlayerPrivate::onMediaStatusChanged(PlatformPlayer::MMRPlayState) |
| { |
| updateStates(); |
| } |
| |
| void MediaPlayerPrivate::onError(PlatformPlayer::Error type) |
| { |
| updateStates(); |
| } |
| |
| void MediaPlayerPrivate::onDurationChanged(float duration) |
| { |
| updateStates(); |
| m_webCorePlayer->durationChanged(); |
| } |
| |
| void MediaPlayerPrivate::onTimeChanged(float) |
| { |
| m_webCorePlayer->timeChanged(); |
| } |
| |
| void MediaPlayerPrivate::onPauseStateChanged() |
| { |
| if (!isFullscreen()) |
| return; |
| |
| // Paused state change not due to local controller. |
| if (m_platformPlayer->isPaused()) |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerPause(); |
| else { |
| // The HMI fullscreen widget has resumed play. Check if the |
| // pause timeout occurred. |
| m_platformPlayer->processPauseTimeoutIfNecessary(); |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerPlay(); |
| } |
| } |
| |
| void MediaPlayerPrivate::onRateChanged(float) |
| { |
| m_webCorePlayer->rateChanged(); |
| } |
| |
| void MediaPlayerPrivate::onVolumeChanged(float volume) |
| { |
| m_webCorePlayer->volumeChanged(volume); |
| } |
| |
| void MediaPlayerPrivate::onRepaint() |
| { |
| m_webCorePlayer->repaint(); |
| } |
| |
| void MediaPlayerPrivate::onSizeChanged() |
| { |
| resizeSourceDimensions(); |
| if (hasVideo()) |
| m_webCorePlayer->sizeChanged(); |
| } |
| |
| void MediaPlayerPrivate::onPlayNotified() |
| { |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerPlay(); |
| } |
| |
| void MediaPlayerPrivate::onPauseNotified() |
| { |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerPause(); |
| } |
| |
| static const int popupDialogInterval = 10; |
| static const double checkMetadataReadyInterval = 0.5; |
| void MediaPlayerPrivate::onWaitMetadataNotified(bool hasFinished, int timeWaited) |
| { |
| if (!hasFinished) { |
| if (!m_waitMetadataTimer.isActive()) { |
| // Make sure to popup dialog every 10 seconds after metadata start to load. |
| // This should be set only once at the first time when user press the play button. |
| m_waitMetadataPopDialogCounter = static_cast<int>(timeWaited / checkMetadataReadyInterval); |
| m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); |
| } |
| } else if (m_waitMetadataTimer.isActive()) { |
| m_waitMetadataTimer.stop(); |
| m_waitMetadataPopDialogCounter = 0; |
| } |
| } |
| |
| void MediaPlayerPrivate::waitMetadataTimerFired(Timer<MediaPlayerPrivate>*) |
| { |
| if (m_platformPlayer->isMetadataReady()) { |
| conditionallyGoFullscreenAfterPlay(); |
| m_waitMetadataPopDialogCounter = 0; |
| m_platformPlayer->playWithMetadataReady(); |
| return; |
| } |
| |
| static const int hitTimeToPopupDialog = static_cast<int>(popupDialogInterval / checkMetadataReadyInterval); |
| m_waitMetadataPopDialogCounter++; |
| if (m_waitMetadataPopDialogCounter < hitTimeToPopupDialog) { |
| m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); |
| return; |
| } |
| m_waitMetadataPopDialogCounter = 0; |
| |
| PlatformPlayer::DialogResult wait = m_platformPlayer->showErrorDialog(PlatformPlayer::MediaMetaDataTimeoutError); |
| if (wait == PlatformPlayer::DialogEmergencyExit) |
| return; |
| if (wait == PlatformPlayer::DialogResponse0) |
| onPauseNotified(); |
| else { |
| if (m_platformPlayer->isMetadataReady()) { |
| m_platformPlayer->playWithMetadataReady(); |
| conditionallyGoFullscreenAfterPlay(); |
| } else |
| m_waitMetadataTimer.startOneShot(checkMetadataReadyInterval); |
| } |
| } |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| void MediaPlayerPrivate::onBuffering(bool flag) |
| { |
| setBuffering(flag); |
| } |
| #endif |
| |
| static ProtectionSpace generateProtectionSpaceFromMMRAuthChallenge(const MMRAuthChallenge& authChallenge) |
| { |
| KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); |
| ASSERT(url.isValid()); |
| |
| return ProtectionSpace(url.host(), url.port(), |
| static_cast<ProtectionSpaceServerType>(authChallenge.serverType()), |
| authChallenge.realm().c_str(), |
| static_cast<ProtectionSpaceAuthenticationScheme>(authChallenge.authScheme())); |
| } |
| |
| void MediaPlayerPrivate::onAuthenticationNeeded(MMRAuthChallenge& authChallenge) |
| { |
| KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); |
| if (!url.isValid()) |
| return; |
| |
| ProtectionSpace protectionSpace = generateProtectionSpaceFromMMRAuthChallenge(authChallenge); |
| Credential credential = CredentialStorage::get(protectionSpace); |
| if (!credential.isEmpty()) { |
| notifyChallengeResult(url, protectionSpace, AuthenticationChallengeSuccess, credential); |
| return; |
| } |
| |
| if (!frameView() || !frameView()->hostWindow()) |
| return; |
| |
| m_isAuthenticationChallenging = true; |
| AuthenticationChallengeManager::instance()->authenticationChallenge(url, protectionSpace, credential, |
| this, frameView()->hostWindow()->platformPageClient()); |
| } |
| |
| void MediaPlayerPrivate::notifyChallengeResult(const KURL& url, const ProtectionSpace& protectionSpace, AuthenticationChallengeResult result, const Credential& credential) |
| { |
| m_isAuthenticationChallenging = false; |
| |
| if (result != AuthenticationChallengeSuccess || !url.isValid()) |
| return; |
| |
| m_platformPlayer->reloadWithCredential(credential.user().utf8(String::StrictConversion).data(), |
| credential.password().utf8(String::StrictConversion).data(), |
| static_cast<MMRAuthChallenge::CredentialPersistence>(credential.persistence())); |
| } |
| |
| void MediaPlayerPrivate::onAuthenticationAccepted(const MMRAuthChallenge& authChallenge) const |
| { |
| KURL url(ParsedURLString, WTF::String(authChallenge.url().c_str())); |
| if (!url.isValid()) |
| return; |
| |
| ProtectionSpace protectionSpace = generateProtectionSpaceFromMMRAuthChallenge(authChallenge); |
| Credential savedCredential = CredentialStorage::get(protectionSpace); |
| if (savedCredential.isEmpty()) |
| CredentialStorage::set(Credential(authChallenge.username().c_str(), authChallenge.password().c_str(), static_cast<CredentialPersistence>(authChallenge.persistence())), protectionSpace, url); |
| } |
| |
| int MediaPlayerPrivate::onShowErrorDialog(PlatformPlayer::Error type) |
| { |
| using namespace BlackBerry::WebKit; |
| |
| WebPageClient::AlertType atype; |
| switch (type) { |
| case PlatformPlayer::MediaOK: |
| atype = WebPageClient::MediaOK; |
| break; |
| case PlatformPlayer::MediaDecodeError: |
| atype = WebPageClient::MediaDecodeError; |
| break; |
| case PlatformPlayer::MediaMetaDataError: |
| atype = WebPageClient::MediaMetaDataError; |
| break; |
| case PlatformPlayer::MediaMetaDataTimeoutError: |
| atype = WebPageClient::MediaMetaDataTimeoutError; |
| break; |
| case PlatformPlayer::MediaNoMetaDataError: |
| atype = WebPageClient::MediaNoMetaDataError; |
| break; |
| case PlatformPlayer::MediaVideoReceiveError: |
| atype = WebPageClient::MediaVideoReceiveError; |
| break; |
| case PlatformPlayer::MediaAudioReceiveError: |
| atype = WebPageClient::MediaAudioReceiveError; |
| break; |
| case PlatformPlayer::MediaInvalidError: |
| atype = WebPageClient::MediaInvalidError; |
| break; |
| default: |
| LOG(Media, "Alert type does not exist."); |
| return -1; |
| } |
| return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->showAlertDialog(atype); |
| } |
| |
| static WebMediaStreamSource toWebMediaStreamSource(MediaStreamSource* src) |
| { |
| return WebMediaStreamSource(src->id().utf8().data(), static_cast<WebMediaStreamSource::Type>(src->type()), src->name().utf8().data()); |
| } |
| |
| static WebMediaStreamDescriptor toWebMediaStreamDescriptor(MediaStreamDescriptor* d) |
| { |
| vector<WebMediaStreamSource> audioSources; |
| for (size_t i = 0; i < d->numberOfAudioComponents(); i++) |
| audioSources.push_back(toWebMediaStreamSource(d->audioComponent(i)->source())); |
| |
| vector<WebMediaStreamSource> videoSources; |
| for (size_t i = 0; i < d->numberOfVideoComponents(); i++) |
| videoSources.push_back(toWebMediaStreamSource(d->videoComponent(i)->source())); |
| |
| return WebMediaStreamDescriptor(d->label().utf8().data(), audioSources, videoSources); |
| } |
| |
| WebMediaStreamDescriptor MediaPlayerPrivate::lookupMediaStream(const BlackBerry::Platform::String& url) |
| { |
| MediaStreamDescriptor* descriptor = MediaStreamRegistry::registry().lookupMediaStreamDescriptor(WTF::String::fromUTF8(url.c_str())); |
| if (!descriptor) |
| return WebMediaStreamDescriptor(); |
| |
| return toWebMediaStreamDescriptor(descriptor); |
| } |
| |
| BlackBerry::Platform::Graphics::Window* MediaPlayerPrivate::platformWindow() |
| { |
| return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->platformWindow(); |
| } |
| |
| bool MediaPlayerPrivate::isFullscreen() const |
| { |
| return m_fullscreenWebPageClient; |
| } |
| |
| bool MediaPlayerPrivate::isElementPaused() const |
| { |
| return m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsPaused(); |
| } |
| |
| bool MediaPlayerPrivate::isTabVisible() const |
| { |
| return m_webCorePlayer->mediaPlayerClient()->mediaPlayerHostWindow()->platformPageClient()->isVisible(); |
| } |
| |
| bool MediaPlayerPrivate::supportsAcceleratedRendering() const |
| { |
| if (m_platformPlayer) |
| return m_platformPlayer->supportsAcceleratedRendering(); |
| return false; |
| } |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| static const double BufferingAnimationDelay = 1.0 / 24; |
| static char* s_bufferingImageData = 0; |
| static int s_bufferingImageWidth = 0; |
| static int s_bufferingImageHeight = 0; |
| |
| PlatformMedia MediaPlayerPrivate::platformMedia() const |
| { |
| PlatformMedia pm; |
| pm.type = PlatformMedia::QNXMediaPlayerType; |
| pm.media.qnxMediaPlayer = const_cast<MediaPlayerPrivate*>(this); |
| return pm; |
| } |
| |
| PlatformLayer* MediaPlayerPrivate::platformLayer() const |
| { |
| if (m_platformLayer) |
| return m_platformLayer.get(); |
| return 0; |
| } |
| |
| static void loadBufferingImageData() |
| { |
| static bool loaded = false; |
| if (!loaded) { |
| static Image* bufferingIcon = Image::loadPlatformResource("vidbuffer").leakRef(); |
| NativeImageSkia* nativeImage = bufferingIcon->nativeImageForCurrentFrame(); |
| if (!nativeImage) |
| return; |
| |
| if (!nativeImage->isDataComplete()) |
| return; |
| |
| loaded = true; |
| nativeImage->bitmap().lockPixels(); |
| |
| int bufSize = nativeImage->bitmap().width() * nativeImage->bitmap().height() * 4; |
| s_bufferingImageWidth = nativeImage->bitmap().width(); |
| s_bufferingImageHeight = nativeImage->bitmap().height(); |
| s_bufferingImageData = static_cast<char*>(malloc(bufSize)); |
| memcpy(s_bufferingImageData, nativeImage->bitmap().getPixels(), bufSize); |
| |
| nativeImage->bitmap().unlockPixels(); |
| bufferingIcon->deref(); |
| } |
| } |
| |
| void MediaPlayerPrivate::bufferingTimerFired(Timer<MediaPlayerPrivate>*) |
| { |
| if (m_showBufferingImage) { |
| if (!isFullscreen() && m_platformLayer) |
| m_platformLayer->setNeedsDisplay(); |
| m_bufferingTimer.startOneShot(BufferingAnimationDelay); |
| } |
| } |
| |
| void MediaPlayerPrivate::setBuffering(bool buffering) |
| { |
| if (!hasVideo()) |
| buffering = false; // Buffering animation not visible for audio. |
| if (buffering != m_showBufferingImage) { |
| m_showBufferingImage = buffering; |
| if (buffering) { |
| loadBufferingImageData(); |
| m_bufferingTimer.startOneShot(BufferingAnimationDelay); |
| } else |
| m_bufferingTimer.stop(); |
| |
| if (m_platformLayer) |
| m_platformLayer->setNeedsDisplay(); |
| } |
| } |
| |
| static unsigned allocateTextureId() |
| { |
| unsigned texid; |
| glGenTextures(1, &texid); |
| glBindTexture(GL_TEXTURE_2D, texid); |
| // Do basic linear filtering on resize. |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| // NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE. |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| return texid; |
| } |
| |
| void MediaPlayerPrivate::drawBufferingAnimation(const TransformationMatrix& matrix, int positionLocation, int texCoordLocation) |
| { |
| if (m_showBufferingImage && s_bufferingImageData && !isFullscreen()) { |
| TransformationMatrix renderMatrix = matrix; |
| |
| // Rotate the buffering indicator so that it takes 1 second to do 1 revolution. |
| timespec time; |
| clock_gettime(CLOCK_REALTIME, &time); |
| renderMatrix.rotate(time.tv_nsec / 1000000000.0 * 360.0); |
| |
| static bool initialized = false; |
| static unsigned texId = allocateTextureId(); |
| glBindTexture(GL_TEXTURE_2D, texId); |
| if (!initialized) { |
| initialized = true; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s_bufferingImageWidth, s_bufferingImageHeight, |
| 0, GL_RGBA, GL_UNSIGNED_BYTE, s_bufferingImageData); |
| free(s_bufferingImageData); |
| } |
| |
| float texcoords[] = { 0, 0, 0, 1, 1, 1, 1, 0 }; |
| FloatPoint vertices[4]; |
| float bx = s_bufferingImageWidth / 2.0; |
| float by = s_bufferingImageHeight / 2.0; |
| vertices[0] = renderMatrix.mapPoint(FloatPoint(-bx, -by)); |
| vertices[1] = renderMatrix.mapPoint(FloatPoint(-bx, by)); |
| vertices[2] = renderMatrix.mapPoint(FloatPoint(bx, by)); |
| vertices[3] = renderMatrix.mapPoint(FloatPoint(bx, -by)); |
| |
| glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, vertices); |
| glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords); |
| glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
| } |
| } |
| #endif |
| |
| void MediaPlayerPrivate::conditionallyGoFullscreenAfterPlay() |
| { |
| BlackBerry::Platform::DeviceInfo* info = BlackBerry::Platform::DeviceInfo::instance(); |
| if (hasVideo() && m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsFullscreenPermitted() && info->isMobile()) { |
| // This is a mobile device (small screen), not a tablet, so we |
| // enter fullscreen video on user-initiated plays. |
| bool nothingIsFullscreen = !m_webCorePlayer->mediaPlayerClient()->mediaPlayerIsFullscreen(); |
| #if ENABLE(FULLSCREEN_API) |
| if (m_webCorePlayer->mediaPlayerClient()->mediaPlayerOwningDocument()->webkitIsFullScreen()) |
| nothingIsFullscreen = false; |
| #endif |
| if (nothingIsFullscreen && currentTime() == 0.0f) { |
| // Only enter fullscreen when playing from the beginning. Doing |
| // so on every play is sure to annoy the user who does not want |
| // to watch the video fullscreen. Note that the following call |
| // will fail if we are not here due to a user gesture, as per the |
| // check in Document::requestFullScreenForElement() to prevent |
| // popups. |
| m_webCorePlayer->mediaPlayerClient()->mediaPlayerEnterFullscreen(); |
| } |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(VIDEO) |