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