• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(VIDEO)
29 #include "MediaPlayerPrivateQuickTimeWin.h"
30 
31 #include "Cookie.h"
32 #include "CookieJar.h"
33 #include "Frame.h"
34 #include "FrameView.h"
35 #include "GraphicsContext.h"
36 #include "KURL.h"
37 #include "MediaPlayerPrivateTaskTimer.h"
38 #include "QTMovieTask.h"
39 #include "ScrollView.h"
40 #include "SoftLinking.h"
41 #include "TimeRanges.h"
42 #include "Timer.h"
43 #include <CoreGraphics/CGColorSpace.h>
44 #include <CoreGraphics/CGContext.h>
45 #include <CoreGraphics/CGImage.h>
46 #include <Wininet.h>
47 #include <wtf/CurrentTime.h>
48 #include <wtf/HashSet.h>
49 #include <wtf/MathExtras.h>
50 #include <wtf/StdLibExtras.h>
51 #include <wtf/text/StringBuilder.h>
52 #include <wtf/text/StringHash.h>
53 
54 #if USE(ACCELERATED_COMPOSITING)
55 #include "GraphicsLayerCACF.h"
56 #include "PlatformCALayer.h"
57 #endif
58 
59 #if DRAW_FRAME_RATE
60 #include "Document.h"
61 #include "Font.h"
62 #include "RenderObject.h"
63 #include "RenderStyle.h"
64 #include "Windows.h"
65 #endif
66 
67 using namespace std;
68 
69 namespace WebCore {
70 
71 SOFT_LINK_LIBRARY(Wininet)
72 SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved))
73 
create(MediaPlayer * player)74 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
75 {
76     return new MediaPlayerPrivate(player);
77 }
78 
registerMediaEngine(MediaEngineRegistrar registrar)79 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
80 {
81     if (isAvailable())
82         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
83 }
84 
MediaPlayerPrivate(MediaPlayer * player)85 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
86     : m_player(player)
87     , m_seekTo(-1)
88     , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired)
89     , m_networkState(MediaPlayer::Empty)
90     , m_readyState(MediaPlayer::HaveNothing)
91     , m_enabledTrackCount(0)
92     , m_totalTrackCount(0)
93     , m_hasUnsupportedTracks(false)
94     , m_startedPlaying(false)
95     , m_isStreaming(false)
96     , m_visible(false)
97     , m_newFrameAvailable(false)
98 #if DRAW_FRAME_RATE
99     , m_frameCountWhilePlaying(0)
100     , m_timeStartedPlaying(0)
101     , m_timeStoppedPlaying(0)
102 #endif
103 {
104 }
105 
~MediaPlayerPrivate()106 MediaPlayerPrivate::~MediaPlayerPrivate()
107 {
108     tearDownVideoRendering();
109     m_qtGWorld->setMovie(0);
110 }
111 
supportsFullscreen() const112 bool MediaPlayerPrivate::supportsFullscreen() const
113 {
114     return true;
115 }
116 
platformMedia() const117 PlatformMedia MediaPlayerPrivate::platformMedia() const
118 {
119     PlatformMedia p;
120     p.type = PlatformMedia::QTMovieGWorldType;
121     p.media.qtMovieGWorld = m_qtGWorld.get();
122     return p;
123 }
124 
125 #if USE(ACCELERATED_COMPOSITING)
platformLayer() const126 PlatformLayer* MediaPlayerPrivate::platformLayer() const
127 {
128     return m_qtVideoLayer ? m_qtVideoLayer->platformLayer() : 0;
129 }
130 #endif
131 
rfc2616DateStringFromTime(CFAbsoluteTime time)132 String MediaPlayerPrivate::rfc2616DateStringFromTime(CFAbsoluteTime time)
133 {
134     static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
135     static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
136     static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT");
137     static CFTimeZoneRef gmtTimeZone;
138     if (!gmtTimeZone)
139         gmtTimeZone = CFTimeZoneCopyDefault();
140 
141     CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone);
142     if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits))
143         return String();
144 
145     time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone);
146     SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0);
147 
148     RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day,
149         monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second));
150     return dateCFString.get();
151 }
152 
addCookieParam(StringBuilder & cookieBuilder,const String & name,const String & value)153 static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value)
154 {
155     if (name.isEmpty())
156         return;
157 
158     // If this isn't the first parameter added, terminate the previous one.
159     if (cookieBuilder.length())
160         cookieBuilder.append("; ");
161 
162     // Add parameter name, and value if there is one.
163     cookieBuilder.append(name);
164     if (!value.isEmpty()) {
165         cookieBuilder.append('=');
166         cookieBuilder.append(value);
167     }
168 }
169 
170 
setUpCookiesForQuickTime(const String & url)171 void MediaPlayerPrivate::setUpCookiesForQuickTime(const String& url)
172 {
173     // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will
174     // use WinINet to download the movie, so we need to copy any cookies needed to
175     // download the movie into WinInet before asking QuickTime to open it.
176     Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
177     if (!frame || !frame->page() || !frame->page()->cookieEnabled())
178         return;
179 
180     KURL movieURL = KURL(KURL(), url);
181     Vector<Cookie> documentCookies;
182     if (!getRawCookies(frame->document(), movieURL, documentCookies))
183         return;
184 
185     for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) {
186         const Cookie& cookie = documentCookies[ndx];
187 
188         if (cookie.name.isEmpty())
189             continue;
190 
191         // Build up the cookie string with as much information as we can get so WinINet
192         // knows what to do with it.
193         StringBuilder cookieBuilder;
194         addCookieParam(cookieBuilder, cookie.name, cookie.value);
195         addCookieParam(cookieBuilder, "path", cookie.path);
196         if (cookie.expires)
197             addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires));
198         if (cookie.httpOnly)
199             addCookieParam(cookieBuilder, "httpOnly", String());
200         cookieBuilder.append(';');
201 
202         String cookieURL;
203         if (!cookie.domain.isEmpty()) {
204             StringBuilder urlBuilder;
205 
206             urlBuilder.append(movieURL.protocol());
207             urlBuilder.append("://");
208             if (cookie.domain[0] == '.')
209                 urlBuilder.append(cookie.domain.substring(1));
210             else
211                 urlBuilder.append(cookie.domain);
212             if (cookie.path.length() > 1)
213                 urlBuilder.append(cookie.path);
214 
215             cookieURL = urlBuilder.toString();
216         } else
217             cookieURL = movieURL;
218 
219         InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0);
220     }
221 }
222 
load(const String & url)223 void MediaPlayerPrivate::load(const String& url)
224 {
225     if (!QTMovie::initializeQuickTime()) {
226         // FIXME: is this the right error to return?
227         m_networkState = MediaPlayer::DecodeError;
228         m_player->networkStateChanged();
229         return;
230     }
231 
232     // Initialize the task timer.
233     MediaPlayerPrivateTaskTimer::initialize();
234 
235     if (m_networkState != MediaPlayer::Loading) {
236         m_networkState = MediaPlayer::Loading;
237         m_player->networkStateChanged();
238     }
239     if (m_readyState != MediaPlayer::HaveNothing) {
240         m_readyState = MediaPlayer::HaveNothing;
241         m_player->readyStateChanged();
242     }
243     cancelSeek();
244 
245     setUpCookiesForQuickTime(url);
246 
247     m_qtMovie = adoptRef(new QTMovie(this));
248     m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch());
249     m_qtMovie->setVolume(m_player->volume());
250 
251     m_qtGWorld = adoptRef(new QTMovieGWorld(this));
252     m_qtGWorld->setMovie(m_qtMovie.get());
253     m_qtGWorld->setVisible(m_player->visible());
254 }
255 
play()256 void MediaPlayerPrivate::play()
257 {
258     if (!m_qtMovie)
259         return;
260     m_startedPlaying = true;
261 #if DRAW_FRAME_RATE
262     m_frameCountWhilePlaying = 0;
263 #endif
264 
265     m_qtMovie->play();
266 }
267 
pause()268 void MediaPlayerPrivate::pause()
269 {
270     if (!m_qtMovie)
271         return;
272     m_startedPlaying = false;
273 #if DRAW_FRAME_RATE
274     m_timeStoppedPlaying = WTF::currentTime();
275 #endif
276     m_qtMovie->pause();
277 }
278 
duration() const279 float MediaPlayerPrivate::duration() const
280 {
281     if (!m_qtMovie)
282         return 0;
283     return m_qtMovie->duration();
284 }
285 
currentTime() const286 float MediaPlayerPrivate::currentTime() const
287 {
288     if (!m_qtMovie)
289         return 0;
290     return m_qtMovie->currentTime();
291 }
292 
seek(float time)293 void MediaPlayerPrivate::seek(float time)
294 {
295     cancelSeek();
296 
297     if (!m_qtMovie)
298         return;
299 
300     if (time > duration())
301         time = duration();
302 
303     m_seekTo = time;
304     if (maxTimeLoaded() >= m_seekTo)
305         doSeek();
306     else
307         m_seekTimer.start(0, 0.5f);
308 }
309 
doSeek()310 void MediaPlayerPrivate::doSeek()
311 {
312     float oldRate = m_qtMovie->rate();
313     if (oldRate)
314         m_qtMovie->setRate(0);
315     m_qtMovie->setCurrentTime(m_seekTo);
316     float timeAfterSeek = currentTime();
317     // restore playback only if not at end, othewise QTMovie will loop
318     if (oldRate && timeAfterSeek < duration())
319         m_qtMovie->setRate(oldRate);
320     cancelSeek();
321 }
322 
cancelSeek()323 void MediaPlayerPrivate::cancelSeek()
324 {
325     m_seekTo = -1;
326     m_seekTimer.stop();
327 }
328 
seekTimerFired(Timer<MediaPlayerPrivate> *)329 void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*)
330 {
331     if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
332         cancelSeek();
333         updateStates();
334         m_player->timeChanged();
335         return;
336     }
337 
338     if (maxTimeLoaded() >= m_seekTo)
339         doSeek();
340     else {
341         MediaPlayer::NetworkState state = networkState();
342         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
343             cancelSeek();
344             updateStates();
345             m_player->timeChanged();
346         }
347     }
348 }
349 
paused() const350 bool MediaPlayerPrivate::paused() const
351 {
352     if (!m_qtMovie)
353         return true;
354     return (!m_qtMovie->rate());
355 }
356 
seeking() const357 bool MediaPlayerPrivate::seeking() const
358 {
359     if (!m_qtMovie)
360         return false;
361     return m_seekTo >= 0;
362 }
363 
naturalSize() const364 IntSize MediaPlayerPrivate::naturalSize() const
365 {
366     if (!m_qtMovie)
367         return IntSize();
368     int width;
369     int height;
370     m_qtMovie->getNaturalSize(width, height);
371     return IntSize(width, height);
372 }
373 
hasVideo() const374 bool MediaPlayerPrivate::hasVideo() const
375 {
376     if (!m_qtMovie)
377         return false;
378     return m_qtMovie->hasVideo();
379 }
380 
hasAudio() const381 bool MediaPlayerPrivate::hasAudio() const
382 {
383     if (!m_qtMovie)
384         return false;
385     return m_qtMovie->hasAudio();
386 }
387 
setVolume(float volume)388 void MediaPlayerPrivate::setVolume(float volume)
389 {
390     if (!m_qtMovie)
391         return;
392     m_qtMovie->setVolume(volume);
393 }
394 
setRate(float rate)395 void MediaPlayerPrivate::setRate(float rate)
396 {
397     if (!m_qtMovie)
398         return;
399     m_qtMovie->setRate(rate);
400 }
401 
setPreservesPitch(bool preservesPitch)402 void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch)
403 {
404     if (!m_qtMovie)
405         return;
406     m_qtMovie->setPreservesPitch(preservesPitch);
407 }
408 
hasClosedCaptions() const409 bool MediaPlayerPrivate::hasClosedCaptions() const
410 {
411     if (!m_qtMovie)
412         return false;
413     return m_qtMovie->hasClosedCaptions();
414 }
415 
setClosedCaptionsVisible(bool visible)416 void MediaPlayerPrivate::setClosedCaptionsVisible(bool visible)
417 {
418     if (!m_qtMovie)
419         return;
420     m_qtMovie->setClosedCaptionsVisible(visible);
421 }
422 
buffered() const423 PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
424 {
425     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
426     float loaded = maxTimeLoaded();
427     // rtsp streams are not buffered
428     if (!m_isStreaming && loaded > 0)
429         timeRanges->add(0, loaded);
430     return timeRanges.release();
431 }
432 
maxTimeSeekable() const433 float MediaPlayerPrivate::maxTimeSeekable() const
434 {
435     // infinite duration means live stream
436     return !isfinite(duration()) ? 0 : maxTimeLoaded();
437 }
438 
maxTimeLoaded() const439 float MediaPlayerPrivate::maxTimeLoaded() const
440 {
441     if (!m_qtMovie)
442         return 0;
443     return m_qtMovie->maxTimeLoaded();
444 }
445 
bytesLoaded() const446 unsigned MediaPlayerPrivate::bytesLoaded() const
447 {
448     if (!m_qtMovie)
449         return 0;
450     float dur = duration();
451     float maxTime = maxTimeLoaded();
452     if (!dur)
453         return 0;
454     return totalBytes() * maxTime / dur;
455 }
456 
totalBytes() const457 unsigned MediaPlayerPrivate::totalBytes() const
458 {
459     if (!m_qtMovie)
460         return 0;
461     return m_qtMovie->dataSize();
462 }
463 
cancelLoad()464 void MediaPlayerPrivate::cancelLoad()
465 {
466     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
467         return;
468 
469     tearDownVideoRendering();
470 
471     // Cancel the load by destroying the movie.
472     m_qtMovie.clear();
473 
474     updateStates();
475 }
476 
updateStates()477 void MediaPlayerPrivate::updateStates()
478 {
479     MediaPlayer::NetworkState oldNetworkState = m_networkState;
480     MediaPlayer::ReadyState oldReadyState = m_readyState;
481 
482     long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError;
483 
484     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
485         m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
486         if (m_player->inMediaDocument()) {
487             if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
488                 // This is a type of media that we do not handle directly with a <video>
489                 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the
490                 // MediaPlayerClient that we won't support it.
491                 sawUnsupportedTracks();
492                 return;
493             }
494         } else if (!m_enabledTrackCount)
495             loadState = QTMovieLoadStateError;
496     }
497 
498     // "Loaded" is reserved for fully buffered movies, never the case when streaming
499     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
500         m_networkState = MediaPlayer::Loaded;
501         m_readyState = MediaPlayer::HaveEnoughData;
502     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
503         m_readyState = MediaPlayer::HaveEnoughData;
504     } else if (loadState >= QTMovieLoadStatePlayable) {
505         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
506         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
507     } else if (loadState >= QTMovieLoadStateLoaded) {
508         m_readyState = MediaPlayer::HaveMetadata;
509     } else if (loadState > QTMovieLoadStateError) {
510         m_networkState = MediaPlayer::Loading;
511         m_readyState = MediaPlayer::HaveNothing;
512     } else {
513         if (m_player->inMediaDocument()) {
514             // Something went wrong in the loading of media within a standalone file.
515             // This can occur with chained ref movies that eventually resolve to a
516             // file we don't support.
517             sawUnsupportedTracks();
518             return;
519         }
520 
521         float loaded = maxTimeLoaded();
522         if (!loaded)
523             m_readyState = MediaPlayer::HaveNothing;
524 
525         if (!m_enabledTrackCount)
526             m_networkState = MediaPlayer::FormatError;
527         else {
528             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
529             if (loaded > 0)
530                 m_networkState = MediaPlayer::DecodeError;
531             else
532                 m_readyState = MediaPlayer::HaveNothing;
533         }
534     }
535 
536     if (isReadyForRendering() && !hasSetUpVideoRendering())
537         setUpVideoRendering();
538 
539     if (seeking())
540         m_readyState = MediaPlayer::HaveNothing;
541 
542     if (m_networkState != oldNetworkState)
543         m_player->networkStateChanged();
544     if (m_readyState != oldReadyState)
545         m_player->readyStateChanged();
546 }
547 
isReadyForRendering() const548 bool MediaPlayerPrivate::isReadyForRendering() const
549 {
550     return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
551 }
552 
sawUnsupportedTracks()553 void MediaPlayerPrivate::sawUnsupportedTracks()
554 {
555     m_qtMovie->setDisabled(true);
556     m_hasUnsupportedTracks = true;
557     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
558 }
559 
didEnd()560 void MediaPlayerPrivate::didEnd()
561 {
562     if (m_hasUnsupportedTracks)
563         return;
564 
565     m_startedPlaying = false;
566 #if DRAW_FRAME_RATE
567     m_timeStoppedPlaying = WTF::currentTime();
568 #endif
569     updateStates();
570     m_player->timeChanged();
571 }
572 
setSize(const IntSize & size)573 void MediaPlayerPrivate::setSize(const IntSize& size)
574 {
575     if (m_hasUnsupportedTracks || !m_qtMovie || m_size == size)
576         return;
577     m_size = size;
578     m_qtGWorld->setSize(size.width(), size.height());
579 }
580 
setVisible(bool visible)581 void MediaPlayerPrivate::setVisible(bool visible)
582 {
583     if (m_hasUnsupportedTracks || !m_qtMovie || m_visible == visible)
584         return;
585 
586     m_qtGWorld->setVisible(visible);
587     m_visible = visible;
588     if (m_visible) {
589         if (isReadyForRendering())
590             setUpVideoRendering();
591     } else
592         tearDownVideoRendering();
593 }
594 
paint(GraphicsContext * p,const IntRect & r)595 void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r)
596 {
597 #if USE(ACCELERATED_COMPOSITING)
598     if (m_qtVideoLayer)
599         return;
600 #endif
601     if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks)
602         return;
603 
604     bool usingTempBitmap = false;
605     OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
606     // FIXME: use LocalWindowsContext.
607     HDC hdc = p->getWindowsContext(r);
608     if (!hdc) {
609         // The graphics context doesn't have an associated HDC so create a temporary
610         // bitmap where QTMovieGWorld can draw the frame and we can copy it.
611         usingTempBitmap = true;
612         bitmap.set(p->createWindowsBitmap(r.size()));
613         hdc = bitmap->hdc();
614 
615         // FIXME: is this necessary??
616         XFORM xform;
617         xform.eM11 = 1.0f;
618         xform.eM12 = 0.0f;
619         xform.eM21 = 0.0f;
620         xform.eM22 = 1.0f;
621         xform.eDx = -r.x();
622         xform.eDy = -r.y();
623         SetWorldTransform(hdc, &xform);
624     }
625 
626     m_qtGWorld->paint(hdc, r.x(), r.y());
627     if (usingTempBitmap)
628         p->drawWindowsBitmap(bitmap.get(), r.location());
629     else
630         p->releaseWindowsContext(hdc, r);
631 
632     paintCompleted(*p, r);
633 }
634 
paintCompleted(GraphicsContext & context,const IntRect & rect)635 void MediaPlayerPrivate::paintCompleted(GraphicsContext& context, const IntRect& rect)
636 {
637     m_newFrameAvailable = false;
638 
639 #if DRAW_FRAME_RATE
640     if (m_frameCountWhilePlaying > 10) {
641         double interval =  m_startedPlaying ? WTF::currentTime() - m_timeStartedPlaying : m_timeStoppedPlaying - m_timeStartedPlaying;
642         double frameRate = (m_frameCountWhilePlaying - 1) / interval;
643         CGContextRef cgContext = context.platformContext();
644         CGRect drawRect = rect;
645 
646         char text[8];
647         _snprintf(text, sizeof(text), "%1.2f", frameRate);
648 
649         static const int fontSize = 25;
650         static const int fontCharWidth = 12;
651         static const int boxHeight = 25;
652         static const int boxBorderWidth = 4;
653         drawRect.size.width = boxBorderWidth * 2 + fontCharWidth * strlen(text);
654         drawRect.size.height = boxHeight;
655 
656         CGContextSaveGState(cgContext);
657 #if USE(ACCELERATED_COMPOSITING)
658         if (m_qtVideoLayer)
659             CGContextScaleCTM(cgContext, 1, -1);
660         CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, m_qtVideoLayer ? -rect.height() : 0);
661 #else
662         CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, 0);
663 #endif
664         static const CGFloat backgroundColor[4] = { 0.98, 0.98, 0.82, 0.8 };
665         CGContextSetFillColor(cgContext, backgroundColor);
666         CGContextFillRect(cgContext, drawRect);
667 
668         static const CGFloat textColor[4] = { 0, 0, 0, 1 };
669         CGContextSetFillColor(cgContext, textColor);
670         CGContextSetTextMatrix(cgContext, CGAffineTransformMakeScale(1, -1));
671         CGContextSelectFont(cgContext, "Helvetica", fontSize, kCGEncodingMacRoman);
672 
673         CGContextShowTextAtPoint(cgContext, drawRect.origin.x + boxBorderWidth, drawRect.origin.y + boxHeight - boxBorderWidth, text, strlen(text));
674 
675         CGContextRestoreGState(cgContext);
676     }
677 #endif
678 }
679 
mimeTypeCache()680 static HashSet<String> mimeTypeCache()
681 {
682     DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
683     static bool typeListInitialized = false;
684 
685     if (!typeListInitialized) {
686         unsigned count = QTMovie::countSupportedTypes();
687         for (unsigned n = 0; n < count; n++) {
688             const UChar* character;
689             unsigned len;
690             QTMovie::getSupportedType(n, character, len);
691             if (len)
692                 typeCache.add(String(character, len));
693         }
694 
695         typeListInitialized = true;
696     }
697 
698     return typeCache;
699 }
700 
getSupportedTypes(HashSet<String> & types)701 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
702 {
703     types = mimeTypeCache();
704 }
705 
isAvailable()706 bool MediaPlayerPrivate::isAvailable()
707 {
708     return QTMovie::initializeQuickTime();
709 }
710 
supportsType(const String & type,const String & codecs)711 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
712 {
713     // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
714     //  extended MIME type
715     return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
716 }
717 
movieEnded(QTMovie * movie)718 void MediaPlayerPrivate::movieEnded(QTMovie* movie)
719 {
720     if (m_hasUnsupportedTracks)
721         return;
722 
723     ASSERT(m_qtMovie.get() == movie);
724     didEnd();
725 }
726 
movieLoadStateChanged(QTMovie * movie)727 void MediaPlayerPrivate::movieLoadStateChanged(QTMovie* movie)
728 {
729     if (m_hasUnsupportedTracks)
730         return;
731 
732     ASSERT(m_qtMovie.get() == movie);
733     updateStates();
734 }
735 
movieTimeChanged(QTMovie * movie)736 void MediaPlayerPrivate::movieTimeChanged(QTMovie* movie)
737 {
738     if (m_hasUnsupportedTracks)
739         return;
740 
741     ASSERT(m_qtMovie.get() == movie);
742     updateStates();
743     m_player->timeChanged();
744 }
745 
movieNewImageAvailable(QTMovieGWorld * movie)746 void MediaPlayerPrivate::movieNewImageAvailable(QTMovieGWorld* movie)
747 {
748     if (m_hasUnsupportedTracks)
749         return;
750 
751     ASSERT(m_qtGWorld.get() == movie);
752 #if DRAW_FRAME_RATE
753     if (m_startedPlaying) {
754         m_frameCountWhilePlaying++;
755         // To eliminate preroll costs from our calculation, our frame rate calculation excludes
756         // the first frame drawn after playback starts.
757         if (m_frameCountWhilePlaying == 1)
758             m_timeStartedPlaying = WTF::currentTime();
759     }
760 #endif
761 
762     m_newFrameAvailable = true;
763 
764 #if USE(ACCELERATED_COMPOSITING)
765     if (m_qtVideoLayer)
766         m_qtVideoLayer->setNeedsDisplay();
767     else
768 #endif
769         m_player->repaint();
770 }
771 
hasSingleSecurityOrigin() const772 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
773 {
774     // We tell quicktime to disallow resources that come from different origins
775     // so we all media is single origin.
776     return true;
777 }
778 
currentRenderingMode() const779 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const
780 {
781     if (!m_qtMovie)
782         return MediaRenderingNone;
783 
784 #if USE(ACCELERATED_COMPOSITING)
785     if (m_qtVideoLayer)
786         return MediaRenderingMovieLayer;
787 #endif
788 
789     return MediaRenderingSoftwareRenderer;
790 }
791 
preferredRenderingMode() const792 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const
793 {
794     if (!m_player->frameView() || !m_qtMovie)
795         return MediaRenderingNone;
796 
797 #if USE(ACCELERATED_COMPOSITING)
798     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
799         return MediaRenderingMovieLayer;
800 #endif
801 
802     return MediaRenderingSoftwareRenderer;
803 }
804 
setUpVideoRendering()805 void MediaPlayerPrivate::setUpVideoRendering()
806 {
807     MediaRenderingMode currentMode = currentRenderingMode();
808     MediaRenderingMode preferredMode = preferredRenderingMode();
809 
810 #if !USE(ACCELERATED_COMPOSITING)
811     ASSERT(preferredMode != MediaRenderingMovieLayer);
812 #endif
813 
814     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
815         return;
816 
817     if (currentMode != MediaRenderingNone)
818         tearDownVideoRendering();
819 
820     if (preferredMode == MediaRenderingMovieLayer)
821         createLayerForMovie();
822 
823 #if USE(ACCELERATED_COMPOSITING)
824     if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
825         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
826 #endif
827 }
828 
tearDownVideoRendering()829 void MediaPlayerPrivate::tearDownVideoRendering()
830 {
831 #if USE(ACCELERATED_COMPOSITING)
832     if (m_qtVideoLayer)
833         destroyLayerForMovie();
834 #endif
835 }
836 
hasSetUpVideoRendering() const837 bool MediaPlayerPrivate::hasSetUpVideoRendering() const
838 {
839 #if USE(ACCELERATED_COMPOSITING)
840     return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer;
841 #else
842     return true;
843 #endif
844 }
845 
846 #if USE(ACCELERATED_COMPOSITING)
847 
848 // Up-call from compositing layer drawing callback.
paintContents(const GraphicsLayer *,GraphicsContext & context,GraphicsLayerPaintingPhase,const IntRect &)849 void MediaPlayerPrivate::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect&)
850 {
851      if (m_hasUnsupportedTracks)
852          return;
853 
854     ASSERT(supportsAcceleratedRendering());
855 
856     // No reason to replace the current layer image unless we have something new to show.
857     if (!m_newFrameAvailable)
858         return;
859 
860     static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
861     void* buffer;
862     unsigned bitsPerPixel;
863     unsigned rowBytes;
864     unsigned width;
865     unsigned height;
866 
867     m_qtGWorld->getCurrentFrameInfo(buffer, bitsPerPixel, rowBytes, width, height);
868     if (!buffer)
869         return;
870 
871     RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(buffer), rowBytes * height, kCFAllocatorNull));
872     RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
873     RetainPtr<CGImageRef> frameImage(AdoptCF, CGImageCreate(width, height, 8, bitsPerPixel, rowBytes, colorSpace,
874         kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault));
875     if (!frameImage)
876         return;
877 
878     IntRect rect(0, 0, m_size.width(), m_size.height());
879     CGContextDrawImage(context.platformContext(), rect, frameImage.get());
880     paintCompleted(context, rect);
881 }
882 #endif
883 
createLayerForMovie()884 void MediaPlayerPrivate::createLayerForMovie()
885 {
886 #if USE(ACCELERATED_COMPOSITING)
887     ASSERT(supportsAcceleratedRendering());
888 
889     if (!m_qtMovie || m_qtVideoLayer)
890         return;
891 
892     // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used
893     // as a wrapper for a PlatformCALayer which gets inserted as the content layer of the video
894     // renderer's GraphicsLayer.
895     m_qtVideoLayer.set(new GraphicsLayerCACF(this));
896     if (!m_qtVideoLayer)
897         return;
898 
899     // Mark the layer as drawing itself, anchored in the top left, and bottom-up.
900     m_qtVideoLayer->setDrawsContent(true);
901     m_qtVideoLayer->setAnchorPoint(FloatPoint3D());
902     m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp);
903 #ifndef NDEBUG
904     m_qtVideoLayer->setName("Video layer");
905 #endif
906     // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
907 #endif
908 }
909 
destroyLayerForMovie()910 void MediaPlayerPrivate::destroyLayerForMovie()
911 {
912 #if USE(ACCELERATED_COMPOSITING)
913     if (!m_qtVideoLayer)
914         return;
915     m_qtVideoLayer = 0;
916 #endif
917 }
918 
919 #if USE(ACCELERATED_COMPOSITING)
supportsAcceleratedRendering() const920 bool MediaPlayerPrivate::supportsAcceleratedRendering() const
921 {
922     return isReadyForRendering();
923 }
924 
acceleratedRenderingStateChanged()925 void MediaPlayerPrivate::acceleratedRenderingStateChanged()
926 {
927     // Set up or change the rendering path if necessary.
928     setUpVideoRendering();
929 }
930 
931 #endif
932 
933 
934 }
935 
936 #endif
937