e1d457f13a3ec5aed4ad7332fdc06a6fa32fbefd
[WebKit-https.git] / WebCore / platform / graphics / win / MediaPlayerPrivateQuickTimeVisualContext.cpp
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 "MediaPlayerPrivateQuickTimeVisualContext.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 "QTCFDictionary.h"
39 #include "QTMovie.h"
40 #include "QTMovieTask.h"
41 #include "QTMovieVisualContext.h"
42 #include "ScrollView.h"
43 #include "SoftLinking.h"
44 #include "StringBuilder.h"
45 #include "StringHash.h"
46 #include "TimeRanges.h"
47 #include "Timer.h"
48 #include <CoreGraphics/CGContext.h>
49 #include <Wininet.h>
50 #include <wtf/CurrentTime.h>
51 #include <wtf/HashSet.h>
52 #include <wtf/MainThread.h>
53 #include <wtf/MathExtras.h>
54 #include <wtf/StdLibExtras.h>
55
56 #if USE(ACCELERATED_COMPOSITING)
57 #include "GraphicsLayerCACF.h"
58 #include "WKCACFLayer.h"
59 #endif
60
61 using namespace std;
62
63 const CFStringRef kMinQuartzCoreVersion = CFSTR("1.0.43.0");
64 const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.0.2");
65
66 namespace WebCore {
67
68 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer);
69
70 SOFT_LINK_LIBRARY(Wininet)
71 SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved))
72
73 // Interface declaration for MediaPlayerPrivateQuickTimeVisualContext's QTMovieClient aggregate
74 class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClient {
75 public:
76     MovieClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
77     virtual ~MovieClient() { m_parent = 0; }
78     virtual void movieEnded(QTMovie*);
79     virtual void movieLoadStateChanged(QTMovie*);
80     virtual void movieTimeChanged(QTMovie*);
81 private:
82     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
83 };
84
85 #if USE(ACCELERATED_COMPOSITING)
86 // Interface declaration for MediaPlayerPrivateQuickTimeVisualContext's GraphicsLayerClient aggregate
87 class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public GraphicsLayerClient {
88 public:
89     LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
90     virtual ~LayerClient() { m_parent = 0; }
91     virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip);
92     virtual void notifyAnimationStarted(const GraphicsLayer*, double time) { }
93     virtual void notifySyncRequired(const GraphicsLayer*) { }
94     virtual bool showDebugBorders() const { return false; }
95     virtual bool showRepaintCounter() const { return false; }
96 private:
97     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
98 };
99 #endif
100
101 class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTMovieVisualContextClient {
102 public:
103     VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
104     virtual ~VisualContextClient() { m_parent = 0; }
105     void imageAvailableForTime(const QTCVTimeStamp*);
106     static void retrieveCurrentImageProc(void*);
107 private:
108     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
109 };
110
111 MediaPlayerPrivateInterface* MediaPlayerPrivateQuickTimeVisualContext::create(MediaPlayer* player) 
112
113     return new MediaPlayerPrivateQuickTimeVisualContext(player);
114 }
115
116 void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRegistrar registrar)
117 {
118     if (isAvailable())
119         registrar(create, getSupportedTypes, supportsType);
120 }
121
122 MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualContext(MediaPlayer* player)
123     : m_player(player)
124     , m_seekTo(-1)
125     , m_seekTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired)
126     , m_visualContextTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired)
127     , m_networkState(MediaPlayer::Empty)
128     , m_readyState(MediaPlayer::HaveNothing)
129     , m_enabledTrackCount(0)
130     , m_totalTrackCount(0)
131     , m_hasUnsupportedTracks(false)
132     , m_startedPlaying(false)
133     , m_isStreaming(false)
134     , m_visible(false)
135     , m_newFrameAvailable(false)
136     , m_movieClient(new MediaPlayerPrivateQuickTimeVisualContext::MovieClient(this))
137 #if USE(ACCELERATED_COMPOSITING)
138     , m_layerClient(new MediaPlayerPrivateQuickTimeVisualContext::LayerClient(this))
139 #endif
140     , m_visualContextClient(new MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient(this))
141 {
142 }
143
144 MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext()
145 {
146     tearDownVideoRendering();
147     m_visualContext->setMovie(0);
148     cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this);
149 }
150
151 bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const
152 {
153     return false;
154 }
155
156 PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const
157 {
158     PlatformMedia p;
159     p.type = PlatformMedia::QTMovieVisualContextType;
160     p.media.qtMovieVisualContext = m_visualContext.get();
161     return p;
162 }
163
164 #if USE(ACCELERATED_COMPOSITING)
165 PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const
166 {
167     return m_qtVideoLayer ? m_qtVideoLayer->platformLayer() : 0;
168 }
169 #endif
170
171 String MediaPlayerPrivateQuickTimeVisualContext::rfc2616DateStringFromTime(CFAbsoluteTime time)
172 {
173     static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
174     static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
175     static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT");
176     static CFTimeZoneRef gmtTimeZone;
177     if (!gmtTimeZone)
178         gmtTimeZone = CFTimeZoneCopyDefault();
179
180     CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone); 
181     if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits))
182         return String();
183
184     time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone);
185     SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0);
186
187     RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day, 
188         monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second));
189     return dateCFString.get();
190 }
191
192 static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value)
193 {
194     if (name.isEmpty())
195         return;
196
197     // If this isn't the first parameter added, terminate the previous one.
198     if (cookieBuilder.length())
199         cookieBuilder.append("; ");
200
201     // Add parameter name, and value if there is one.
202     cookieBuilder.append(name);
203     if (!value.isEmpty()) {
204         cookieBuilder.append("=");
205         cookieBuilder.append(value);
206     }
207 }
208
209 void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const String& url)
210 {
211     // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will 
212     // use WinINet to download the movie, so we need to copy any cookies needed to
213     // download the movie into WinInet before asking QuickTime to open it.
214     Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
215     if (!frame || !frame->page() || !frame->page()->cookieEnabled())
216         return;
217
218     KURL movieURL = KURL(KURL(), url);
219     Vector<Cookie> documentCookies;
220     if (!getRawCookies(frame->document(), movieURL, documentCookies))
221         return;
222
223     for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) {
224         const Cookie& cookie = documentCookies[ndx];
225
226         if (cookie.name.isEmpty())
227             continue;
228
229         // Build up the cookie string with as much information as we can get so WinINet
230         // knows what to do with it.
231         StringBuilder cookieBuilder;
232         addCookieParam(cookieBuilder, cookie.name, cookie.value);
233         addCookieParam(cookieBuilder, "path", cookie.path);
234         if (cookie.expires) 
235             addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires));
236         if (cookie.httpOnly) 
237             addCookieParam(cookieBuilder, "httpOnly", String());
238         cookieBuilder.append(";");
239
240         String cookieURL;
241         if (!cookie.domain.isEmpty()) {
242             StringBuilder urlBuilder;
243
244             urlBuilder.append(movieURL.protocol());
245             urlBuilder.append("://");
246             if (cookie.domain[0] == '.')
247                 urlBuilder.append(cookie.domain.substring(1));
248             else
249                 urlBuilder.append(cookie.domain);
250             if (cookie.path.length() > 1)
251                 urlBuilder.append(cookie.path);
252
253             cookieURL = urlBuilder.toString();
254         } else
255             cookieURL = movieURL;
256
257         InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0);
258     }
259 }
260
261 void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url)
262 {
263     if (!QTMovie::initializeQuickTime()) {
264         // FIXME: is this the right error to return?
265         m_networkState = MediaPlayer::DecodeError; 
266         m_player->networkStateChanged();
267         return;
268     }
269
270     // Initialize the task timer.
271     MediaPlayerPrivateTaskTimer::initialize();
272
273     if (m_networkState != MediaPlayer::Loading) {
274         m_networkState = MediaPlayer::Loading;
275         m_player->networkStateChanged();
276     }
277     if (m_readyState != MediaPlayer::HaveNothing) {
278         m_readyState = MediaPlayer::HaveNothing;
279         m_player->readyStateChanged();
280     }
281     cancelSeek();
282
283     setUpCookiesForQuickTime(url);
284
285     m_movie = adoptRef(new QTMovie(m_movieClient.get()));
286     m_movie->load(url.characters(), url.length(), m_player->preservesPitch());
287     m_movie->setVolume(m_player->volume());
288
289     CFDictionaryRef options = QTMovieVisualContext::getCGImageOptions();
290     m_visualContext = adoptRef(new QTMovieVisualContext(m_visualContextClient.get(), options));
291     m_visualContext->setMovie(m_movie.get());
292 }
293
294 void MediaPlayerPrivateQuickTimeVisualContext::play()
295 {
296     if (!m_movie)
297         return;
298     m_startedPlaying = true;
299
300     m_movie->play();
301     m_visualContextTimer.startRepeating(1.0 / 30);
302 }
303
304 void MediaPlayerPrivateQuickTimeVisualContext::pause()
305 {
306     if (!m_movie)
307         return;
308     m_startedPlaying = false;
309
310     m_movie->pause();
311     m_visualContextTimer.stop();
312 }
313
314 float MediaPlayerPrivateQuickTimeVisualContext::duration() const
315 {
316     if (!m_movie)
317         return 0;
318     return m_movie->duration();
319 }
320
321 float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const
322 {
323     if (!m_movie)
324         return 0;
325     return m_movie->currentTime();
326 }
327
328 void MediaPlayerPrivateQuickTimeVisualContext::seek(float time)
329 {
330     cancelSeek();
331     
332     if (!m_movie)
333         return;
334     
335     if (time > duration())
336         time = duration();
337     
338     m_seekTo = time;
339     if (maxTimeLoaded() >= m_seekTo)
340         doSeek();
341     else 
342         m_seekTimer.start(0, 0.5f);
343 }
344     
345 void MediaPlayerPrivateQuickTimeVisualContext::doSeek() 
346 {
347     float oldRate = m_movie->rate();
348     if (oldRate)
349         m_movie->setRate(0);
350     m_movie->setCurrentTime(m_seekTo);
351     float timeAfterSeek = currentTime();
352     // restore playback only if not at end, othewise QTMovie will loop
353     if (oldRate && timeAfterSeek < duration())
354         m_movie->setRate(oldRate);
355     cancelSeek();
356 }
357
358 void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek()
359 {
360     m_seekTo = -1;
361     m_seekTimer.stop();
362 }
363
364 void MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
365 {        
366     if (!m_movie || !seeking() || currentTime() == m_seekTo) {
367         cancelSeek();
368         updateStates();
369         m_player->timeChanged(); 
370         return;
371     } 
372     
373     if (maxTimeLoaded() >= m_seekTo)
374         doSeek();
375     else {
376         MediaPlayer::NetworkState state = networkState();
377         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
378             cancelSeek();
379             updateStates();
380             m_player->timeChanged();
381         }
382     }
383 }
384
385 bool MediaPlayerPrivateQuickTimeVisualContext::paused() const
386 {
387     if (!m_movie)
388         return true;
389     return (!m_movie->rate());
390 }
391
392 bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const
393 {
394     if (!m_movie)
395         return false;
396     return m_seekTo >= 0;
397 }
398
399 IntSize MediaPlayerPrivateQuickTimeVisualContext::naturalSize() const
400 {
401     if (!m_movie)
402         return IntSize();
403     int width;
404     int height;
405     m_movie->getNaturalSize(width, height);
406     return IntSize(width, height);
407 }
408
409 bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const
410 {
411     if (!m_movie)
412         return false;
413     return m_movie->hasVideo();
414 }
415
416 bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const
417 {
418     if (!m_movie)
419         return false;
420     return m_movie->hasAudio();
421 }
422
423 void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume)
424 {
425     if (!m_movie)
426         return;
427     m_movie->setVolume(volume);
428 }
429
430 void MediaPlayerPrivateQuickTimeVisualContext::setRate(float rate)
431 {
432     if (!m_movie)
433         return;
434
435     // Do not call setRate(...) unless we have started playing; otherwise
436     // QuickTime's VisualContext can get wedged waiting for a rate change
437     // call which will never come.
438     if (m_startedPlaying)
439         m_movie->setRate(rate);
440 }
441
442 void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesPitch)
443 {
444     if (!m_movie)
445         return;
446     m_movie->setPreservesPitch(preservesPitch);
447 }
448
449 bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const
450 {
451     if (!m_movie)
452         return false;
453     return m_movie->hasClosedCaptions();
454 }
455
456 void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool visible)
457 {
458     if (!m_movie)
459         return;
460     m_movie->setClosedCaptionsVisible(visible);
461 }
462
463 PassRefPtr<TimeRanges> MediaPlayerPrivateQuickTimeVisualContext::buffered() const
464 {
465     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
466     float loaded = maxTimeLoaded();
467     // rtsp streams are not buffered
468     if (!m_isStreaming && loaded > 0)
469         timeRanges->add(0, loaded);
470     return timeRanges.release();
471 }
472
473 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const
474 {
475     // infinite duration means live stream
476     return !isfinite(duration()) ? 0 : maxTimeLoaded();
477 }
478
479 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const
480 {
481     if (!m_movie)
482         return 0;
483     return m_movie->maxTimeLoaded(); 
484 }
485
486 unsigned MediaPlayerPrivateQuickTimeVisualContext::bytesLoaded() const
487 {
488     if (!m_movie)
489         return 0;
490     float dur = duration();
491     float maxTime = maxTimeLoaded();
492     if (!dur)
493         return 0;
494     return totalBytes() * maxTime / dur;
495 }
496
497 unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const
498 {
499     if (!m_movie)
500         return 0;
501     return m_movie->dataSize();
502 }
503
504 void MediaPlayerPrivateQuickTimeVisualContext::cancelLoad()
505 {
506     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
507         return;
508     
509     tearDownVideoRendering();
510
511     // Cancel the load by destroying the movie.
512     m_movie.clear();
513
514     updateStates();
515 }
516
517 void MediaPlayerPrivateQuickTimeVisualContext::updateStates()
518 {
519     MediaPlayer::NetworkState oldNetworkState = m_networkState;
520     MediaPlayer::ReadyState oldReadyState = m_readyState;
521   
522     long loadState = m_movie ? m_movie->loadState() : QTMovieLoadStateError;
523
524     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
525         m_movie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
526         if (m_player->inMediaDocument()) {
527             if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
528                 // This is a type of media that we do not handle directly with a <video> 
529                 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the 
530                 // MediaPlayerClient that we won't support it.
531                 sawUnsupportedTracks();
532                 return;
533             }
534         } else if (!m_enabledTrackCount)
535             loadState = QTMovieLoadStateError;
536     }
537
538     // "Loaded" is reserved for fully buffered movies, never the case when streaming
539     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
540         m_networkState = MediaPlayer::Loaded;
541         m_readyState = MediaPlayer::HaveEnoughData;
542     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
543         m_readyState = MediaPlayer::HaveEnoughData;
544     } else if (loadState >= QTMovieLoadStatePlayable) {
545         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
546         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
547     } else if (loadState >= QTMovieLoadStateLoaded) {
548         m_readyState = MediaPlayer::HaveMetadata;
549     } else if (loadState > QTMovieLoadStateError) {
550         m_networkState = MediaPlayer::Loading;
551         m_readyState = MediaPlayer::HaveNothing;        
552     } else {
553         if (m_player->inMediaDocument()) {
554             // Something went wrong in the loading of media within a standalone file. 
555             // This can occur with chained ref movies that eventually resolve to a
556             // file we don't support.
557             sawUnsupportedTracks();
558             return;
559         }
560
561         float loaded = maxTimeLoaded();
562         if (!loaded)
563             m_readyState = MediaPlayer::HaveNothing;
564
565         if (!m_enabledTrackCount)
566             m_networkState = MediaPlayer::FormatError;
567         else {
568             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
569             if (loaded > 0)
570                 m_networkState = MediaPlayer::DecodeError;
571             else
572                 m_readyState = MediaPlayer::HaveNothing;
573         }
574     }
575
576     if (isReadyForRendering() && !hasSetUpVideoRendering())
577         setUpVideoRendering();
578
579     if (seeking())
580         m_readyState = MediaPlayer::HaveNothing;
581     
582     if (m_networkState != oldNetworkState)
583         m_player->networkStateChanged();
584     if (m_readyState != oldReadyState)
585         m_player->readyStateChanged();
586 }
587
588 bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const
589 {
590     return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
591 }
592
593 void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks()
594 {
595     m_movie->setDisabled(true);
596     m_hasUnsupportedTracks = true;
597     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
598 }
599
600 void MediaPlayerPrivateQuickTimeVisualContext::didEnd()
601 {
602     if (m_hasUnsupportedTracks)
603         return;
604
605     m_startedPlaying = false;
606
607     updateStates();
608     m_player->timeChanged();
609 }
610
611 void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size) 
612
613     if (m_hasUnsupportedTracks || !m_movie || m_size == size)
614         return;
615     m_size = size;
616 }
617
618 void MediaPlayerPrivateQuickTimeVisualContext::setVisible(bool visible)
619 {
620     if (m_hasUnsupportedTracks || !m_movie || m_visible == visible)
621         return;
622
623     m_visible = visible;
624     if (m_visible) {
625         if (isReadyForRendering())
626             setUpVideoRendering();
627     } else
628         tearDownVideoRendering();
629 }
630
631 void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const IntRect& r)
632 {
633 #if USE(ACCELERATED_COMPOSITING)
634     if (m_qtVideoLayer)
635         return;
636 #endif
637     QTPixelBuffer buffer = m_visualContext->imageForTime(0);
638     if (buffer.pixelBufferRef()) {
639         CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
640         
641         CGContextRef context = p->platformContext();
642         CGContextSaveGState(context);
643         CGContextTranslateCTM(context, r.x(), r.y());
644         CGContextTranslateCTM(context, 0, r.height());
645         CGContextScaleCTM(context, 1, -1);
646         CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), image);
647         CGContextRestoreGState(context);
648
649         CGImageRelease(image);
650     }
651     paintCompleted(*p, r);
652 }
653
654 void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& context, const IntRect& rect)
655 {
656     m_newFrameAvailable = false;
657 }
658
659 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurrentImageProc(void* refcon)
660 {
661     static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurrentImage();
662 }
663
664 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailableForTime(const QTCVTimeStamp* timeStamp)
665 {
666     // This call may come in on another thread, so marshall to the main thread first:
667     callOnMainThread(&retrieveCurrentImageProc, m_parent);
668
669     // callOnMainThread must be paired with cancelCallOnMainThread in the destructor,
670     // in case this object is deleted before the main thread request is handled.
671 }
672
673 void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
674 {
675     if (m_visualContext && m_visualContext->isImageAvailableForTime(0))
676         retrieveCurrentImage();
677 }
678
679 static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef allocator, const UInt8* bytes, CFIndex length)
680 {
681     RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, bytes, length, kCFAllocatorNull));
682     if (!data)
683         return 0;
684
685     return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data.get(), kCFPropertyListImmutable, 0));
686 }
687
688 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer)
689 {
690 #if USE(ACCELERATED_COMPOSITING)
691     CGDataProviderRef provider = 0;
692     CGColorSpaceRef colorSpace = 0;
693     CGImageRef image = 0;
694
695     size_t bitsPerComponent = 0;
696     size_t bitsPerPixel = 0;
697     CGImageAlphaInfo alphaInfo = kCGImageAlphaNone;
698         
699     if (buffer.pixelFormatIs32BGRA()) {
700         bitsPerComponent = 8;
701         bitsPerPixel = 32;
702         alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
703     } else if (buffer.pixelFormatIs32ARGB()) {
704         bitsPerComponent = 8;
705         bitsPerPixel = 32;
706         alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
707     } else {
708         // All other pixel formats are currently unsupported:
709         ASSERT_NOT_REACHED();
710     }
711
712     CGDataProviderDirectAccessCallbacks callbacks = {
713         &QTPixelBuffer::dataProviderGetBytePointerCallback,
714         &QTPixelBuffer::dataProviderReleaseBytePointerCallback,
715         &QTPixelBuffer::dataProviderGetBytesAtPositionCallback,
716         &QTPixelBuffer::dataProviderReleaseInfoCallback,
717     };
718     
719     // Colorspace should be device, so that Quartz does not have to do an extra render.
720     colorSpace = CGColorSpaceCreateDeviceRGB();
721     require(colorSpace, Bail);
722             
723     provider = CGDataProviderCreateDirectAccess(buffer.pixelBufferRef(), buffer.dataSize(), &callbacks);
724     require(provider, Bail);
725
726     // CGDataProvider does not retain the buffer, but it will release it later, so do an extra retain here:
727     QTPixelBuffer::retainCallback(buffer.pixelBufferRef());
728         
729     image = CGImageCreate(buffer.width(), buffer.height(), bitsPerComponent, bitsPerPixel, buffer.bytesPerRow(), colorSpace, alphaInfo, provider, 0, false, kCGRenderingIntentDefault);
730  
731 Bail:
732     // Once the image is created we can release our reference to the provider and the colorspace, they are retained by the image
733     if (provider)
734         CGDataProviderRelease(provider);
735     if (colorSpace)
736         CGColorSpaceRelease(colorSpace);
737  
738     return image;
739 #else
740     return 0;
741 #endif
742 }
743
744
745 void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage()
746 {
747     if (!m_visualContext)
748         return;
749
750 #if USE(ACCELERATED_COMPOSITING)
751     if (m_qtVideoLayer) {
752
753         QTPixelBuffer buffer = m_visualContext->imageForTime(0);
754         if (!buffer.pixelBufferRef())
755             return;
756
757         WKCACFLayer* layer = static_cast<WKCACFLayer*>(m_qtVideoLayer->platformLayer());
758
759             if (!buffer.lockBaseAddress()) {
760                CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime();
761
762                 CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
763                 layer->setContents(image);
764                 CGImageRelease(image);
765
766                 buffer.unlockBaseAddress();
767                 layer->rootLayer()->setNeedsRender();
768             }
769     } else
770 #endif
771         m_player->repaint();
772
773     m_visualContext->task();
774 }
775
776 static HashSet<String> mimeTypeCache()
777 {
778     DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
779     static bool typeListInitialized = false;
780
781     if (!typeListInitialized) {
782         unsigned count = QTMovie::countSupportedTypes();
783         for (unsigned n = 0; n < count; n++) {
784             const UChar* character;
785             unsigned len;
786             QTMovie::getSupportedType(n, character, len);
787             if (len)
788                 typeCache.add(String(character, len));
789         }
790
791         typeListInitialized = true;
792     }
793     
794     return typeCache;
795 }
796
797 void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>& types)
798 {
799     types = mimeTypeCache();
800
801
802 bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable()
803 {
804     return QTMovie::initializeQuickTime();
805 }
806
807 MediaPlayer::SupportsType MediaPlayerPrivateQuickTimeVisualContext::supportsType(const String& type, const String& codecs)
808 {
809     // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
810     //  extended MIME type
811     return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
812 }
813
814 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieEnded(QTMovie* movie)
815 {
816     if (m_parent->m_hasUnsupportedTracks)
817         return;
818
819     m_parent->m_visualContextTimer.stop();
820
821     ASSERT(m_parent->m_movie.get() == movie);
822     m_parent->didEnd();
823 }
824
825 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieLoadStateChanged(QTMovie* movie)
826 {
827     if (m_parent->m_hasUnsupportedTracks)
828         return;
829
830     ASSERT(m_parent->m_movie.get() == movie);
831     m_parent->updateStates();
832 }
833
834 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieTimeChanged(QTMovie* movie)
835 {
836     if (m_parent->m_hasUnsupportedTracks)
837         return;
838
839     ASSERT(m_parent->m_movie.get() == movie);
840     m_parent->updateStates();
841     m_parent->m_player->timeChanged();
842 }
843
844 bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const
845 {
846     // We tell quicktime to disallow resources that come from different origins
847     // so we all media is single origin.
848     return true;
849 }
850
851 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::currentRenderingMode() const
852 {
853     if (!m_movie)
854         return MediaRenderingNone;
855
856 #if USE(ACCELERATED_COMPOSITING)
857     if (m_qtVideoLayer)
858         return MediaRenderingMovieLayer;
859 #endif
860
861     return MediaRenderingSoftwareRenderer;
862 }
863
864 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::preferredRenderingMode() const
865 {
866     if (!m_player->frameView() || !m_movie)
867         return MediaRenderingNone;
868
869 #if USE(ACCELERATED_COMPOSITING)
870     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
871         return MediaRenderingMovieLayer;
872 #endif
873
874     return MediaRenderingSoftwareRenderer;
875 }
876
877 void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering()
878 {
879     MediaRenderingMode currentMode = currentRenderingMode();
880     MediaRenderingMode preferredMode = preferredRenderingMode();
881
882 #if !USE(ACCELERATED_COMPOSITING)
883     ASSERT(preferredMode != MediaRenderingMovieLayer);
884 #endif
885
886     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
887         return;
888
889     if (currentMode != MediaRenderingNone)  
890         tearDownVideoRendering();
891
892     if (preferredMode == MediaRenderingMovieLayer)
893         createLayerForMovie();
894
895 #if USE(ACCELERATED_COMPOSITING)
896     if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
897         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
898 #endif
899 }
900
901 void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering()
902 {
903 #if USE(ACCELERATED_COMPOSITING)
904     if (m_qtVideoLayer)
905         destroyLayerForMovie();
906 #endif
907 }
908
909 bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const
910 {
911 #if USE(ACCELERATED_COMPOSITING)
912     return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer;
913 #else
914     return true;
915 #endif
916 }
917
918 void MediaPlayerPrivateQuickTimeVisualContext::createLayerForMovie()
919 {
920 #if USE(ACCELERATED_COMPOSITING)
921     ASSERT(supportsAcceleratedRendering());
922
923     if (!m_movie || m_qtVideoLayer)
924         return;
925
926     // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used 
927     // as a wrapper for a WKCACFLayer which gets inserted as the content layer of the video 
928     // renderer's GraphicsLayer.
929     m_qtVideoLayer.set(new GraphicsLayerCACF(m_layerClient.get()));
930     if (!m_qtVideoLayer)
931         return;
932
933     // Mark the layer as drawing itself, anchored in the top left, and bottom-up.
934     m_qtVideoLayer->setDrawsContent(true);
935     m_qtVideoLayer->setAnchorPoint(FloatPoint3D());
936     m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp);
937 #ifndef NDEBUG
938     m_qtVideoLayer->setName("Video layer");
939 #endif
940     // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
941 #endif
942 }
943
944 void MediaPlayerPrivateQuickTimeVisualContext::destroyLayerForMovie()
945 {
946 #if USE(ACCELERATED_COMPOSITING)
947     if (!m_qtVideoLayer)
948         return;
949     m_qtVideoLayer = 0;
950 #endif
951 }
952
953 #if USE(ACCELERATED_COMPOSITING)
954 void MediaPlayerPrivateQuickTimeVisualContext::LayerClient::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip)
955 {
956 }
957
958 bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() const
959 {
960     return isReadyForRendering();
961 }
962
963 void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged()
964 {
965     // Set up or change the rendering path if necessary.
966     setUpVideoRendering();
967 }
968
969 #endif
970
971
972 }
973
974 #endif