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