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