OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 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 "DocumentLoader.h" | |
34 #include "Frame.h" | |
35 #include "FrameView.h" | |
36 #include "GraphicsContext.h" | |
37 #include "KURL.h" | |
38 #include "MediaPlayerPrivateTaskTimer.h" | |
39 #include "Page.h" | |
40 #include "QTCFDictionary.h" | |
41 #include "QTDecompressionSession.h" | |
42 #include "QTMovie.h" | |
43 #include "QTMovieTask.h" | |
44 #include "QTMovieVisualContext.h" | |
45 #include "ScrollView.h" | |
46 #include "Settings.h" | |
47 #include "SoftLinking.h" | |
48 #include "TimeRanges.h" | |
49 #include "Timer.h" | |
50 #include <AssertMacros.h> | |
51 #include <CoreGraphics/CGAffineTransform.h> | |
52 #include <CoreGraphics/CGContext.h> | |
53 #include <QuartzCore/CATransform3D.h> | |
54 #include <Wininet.h> | |
55 #include <wtf/CurrentTime.h> | |
56 #include <wtf/HashSet.h> | |
57 #include <wtf/MainThread.h> | |
58 #include <wtf/MathExtras.h> | |
59 #include <wtf/StdLibExtras.h> | |
60 #include <wtf/text/StringBuilder.h> | |
61 #include <wtf/text/StringHash.h> | |
62 | |
63 #if USE(ACCELERATED_COMPOSITING) | |
64 #include "PlatformCALayer.h" | |
65 #include "WKCAImageQueue.h" | |
66 #endif | |
67 | |
68 using namespace std; | |
69 | |
70 namespace WebCore { | |
71 | |
72 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer); | |
73 static bool requiredDllsAvailable(); | |
74 | |
75 SOFT_LINK_LIBRARY(Wininet) | |
76 SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWST
R lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved),
(lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved)) | |
77 | |
78 // Interface declaration for MediaPlayerPrivateQuickTimeVisualContext's QTMovieC
lient aggregate | |
79 class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClie
nt { | |
80 public: | |
81 MovieClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(par
ent) {} | |
82 virtual ~MovieClient() { m_parent = 0; } | |
83 virtual void movieEnded(QTMovie*); | |
84 virtual void movieLoadStateChanged(QTMovie*); | |
85 virtual void movieTimeChanged(QTMovie*); | |
86 private: | |
87 MediaPlayerPrivateQuickTimeVisualContext* m_parent; | |
88 }; | |
89 | |
90 #if USE(ACCELERATED_COMPOSITING) | |
91 class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public PlatformCAL
ayerClient { | |
92 public: | |
93 LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(par
ent) {} | |
94 virtual ~LayerClient() { m_parent = 0; } | |
95 | |
96 private: | |
97 virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*); | |
98 virtual bool platformCALayerRespondsToLayoutChanges() const { return true; } | |
99 | |
100 virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { } | |
101 virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerCont
entsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp;
} | |
102 virtual void platformCALayerPaintContents(GraphicsContext&, const IntRect& i
nClip) { } | |
103 virtual bool platformCALayerShowDebugBorders() const { return false; } | |
104 virtual bool platformCALayerShowRepaintCounter(PlatformCALayer*) const { ret
urn false; } | |
105 virtual int platformCALayerIncrementRepaintCount() { return 0; } | |
106 | |
107 virtual bool platformCALayerContentsOpaque() const { return false; } | |
108 virtual bool platformCALayerDrawsContent() const { return false; } | |
109 virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { } | |
110 virtual void platformCALayerDidCreateTiles(const Vector<FloatRect>&) { } | |
111 virtual float platformCALayerDeviceScaleFactor() { return 1; } | |
112 | |
113 MediaPlayerPrivateQuickTimeVisualContext* m_parent; | |
114 }; | |
115 | |
116 void MediaPlayerPrivateQuickTimeVisualContext::LayerClient::platformCALayerLayou
tSublayersOfLayer(PlatformCALayer* layer) | |
117 { | |
118 ASSERT(m_parent); | |
119 ASSERT(m_parent->m_transformLayer == layer); | |
120 | |
121 FloatSize parentSize = layer->bounds().size(); | |
122 FloatSize naturalSize = m_parent->naturalSize(); | |
123 | |
124 // Calculate the ratio of these two sizes and use that ratio to scale the qt
VideoLayer: | |
125 FloatSize ratio(parentSize.width() / naturalSize.width(), parentSize.height(
) / naturalSize.height()); | |
126 | |
127 int videoWidth = 0; | |
128 int videoHeight = 0; | |
129 m_parent->m_movie->getNaturalSize(videoWidth, videoHeight); | |
130 FloatRect videoBounds(0, 0, videoWidth * ratio.width(), videoHeight * ratio.
height()); | |
131 FloatPoint3D videoAnchor = m_parent->m_qtVideoLayer->anchorPoint(); | |
132 | |
133 // Calculate the new position based on the parent's size: | |
134 FloatPoint position(parentSize.width() * 0.5 - videoBounds.width() * (0.5 -
videoAnchor.x()), | |
135 parentSize.height() * 0.5 - videoBounds.height() * (0.5 - videoAnchor.y(
))); | |
136 | |
137 m_parent->m_qtVideoLayer->setBounds(videoBounds); | |
138 m_parent->m_qtVideoLayer->setPosition(position); | |
139 } | |
140 #endif | |
141 | |
142 class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTM
ovieVisualContextClient { | |
143 public: | |
144 VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_pa
rent(parent) {} | |
145 virtual ~VisualContextClient() { m_parent = 0; } | |
146 void imageAvailableForTime(const QTCVTimeStamp*); | |
147 static void retrieveCurrentImageProc(void*); | |
148 private: | |
149 MediaPlayerPrivateQuickTimeVisualContext* m_parent; | |
150 }; | |
151 | |
152 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateQuickTimeVisualContext
::create(MediaPlayer* player) | |
153 { | |
154 return adoptPtr(new MediaPlayerPrivateQuickTimeVisualContext(player)); | |
155 } | |
156 | |
157 void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRe
gistrar registrar) | |
158 { | |
159 if (isAvailable()) | |
160 registrar(create, getSupportedTypes, supportsType, 0, 0, 0); | |
161 } | |
162 | |
163 MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualConte
xt(MediaPlayer* player) | |
164 : m_player(player) | |
165 , m_seekTo(-1) | |
166 , m_seekTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::seekTimerFire
d) | |
167 , m_visualContextTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::visu
alContextTimerFired) | |
168 , m_networkState(MediaPlayer::Empty) | |
169 , m_readyState(MediaPlayer::HaveNothing) | |
170 , m_enabledTrackCount(0) | |
171 , m_totalTrackCount(0) | |
172 , m_hasUnsupportedTracks(false) | |
173 , m_startedPlaying(false) | |
174 , m_isStreaming(false) | |
175 , m_visible(false) | |
176 , m_newFrameAvailable(false) | |
177 , m_movieClient(adoptPtr(new MediaPlayerPrivateQuickTimeVisualContext::Movie
Client(this))) | |
178 #if USE(ACCELERATED_COMPOSITING) | |
179 , m_layerClient(adoptPtr(new MediaPlayerPrivateQuickTimeVisualContext::Layer
Client(this))) | |
180 , m_movieTransform(CGAffineTransformIdentity) | |
181 #endif | |
182 , m_visualContextClient(adoptPtr(new MediaPlayerPrivateQuickTimeVisualContex
t::VisualContextClient(this))) | |
183 , m_delayingLoad(false) | |
184 , m_privateBrowsing(false) | |
185 , m_preload(MediaPlayer::Auto) | |
186 , m_maxTimeLoadedAtLastDidLoadingProgress(0) | |
187 { | |
188 } | |
189 | |
190 MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualCont
ext() | |
191 { | |
192 tearDownVideoRendering(); | |
193 cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this)
; | |
194 } | |
195 | |
196 bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const | |
197 { | |
198 #if USE(ACCELERATED_COMPOSITING) | |
199 Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocumen
t(); | |
200 if (document && document->settings()) | |
201 return document->settings()->acceleratedCompositingEnabled(); | |
202 #endif | |
203 return false; | |
204 } | |
205 | |
206 PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const | |
207 { | |
208 PlatformMedia p; | |
209 p.type = PlatformMedia::QTMovieVisualContextType; | |
210 p.media.qtMovieVisualContext = m_visualContext.get(); | |
211 return p; | |
212 } | |
213 #if USE(ACCELERATED_COMPOSITING) | |
214 | |
215 PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const | |
216 { | |
217 return m_transformLayer ? m_transformLayer->platformLayer() : 0; | |
218 } | |
219 #endif | |
220 | |
221 String MediaPlayerPrivateQuickTimeVisualContext::rfc2616DateStringFromTime(CFAbs
oluteTime time) | |
222 { | |
223 static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat", "Sun" }; | |
224 static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May
", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; | |
225 static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02
d:%02d GMT"); | |
226 static CFTimeZoneRef gmtTimeZone; | |
227 if (!gmtTimeZone) | |
228 gmtTimeZone = CFTimeZoneCopyDefault(); | |
229 | |
230 CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone
); | |
231 if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits)) | |
232 return String(); | |
233 | |
234 time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone); | |
235 SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0); | |
236 | |
237 RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0,
dateFormatString, dayStrings[day - 1], dateValue.day, | |
238 monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateV
alue.minute, (int)dateValue.second)); | |
239 return dateCFString.get(); | |
240 } | |
241 | |
242 static void addCookieParam(StringBuilder& cookieBuilder, const String& name, con
st String& value) | |
243 { | |
244 if (name.isEmpty()) | |
245 return; | |
246 | |
247 // If this isn't the first parameter added, terminate the previous one. | |
248 if (cookieBuilder.length()) | |
249 cookieBuilder.append("; "); | |
250 | |
251 // Add parameter name, and value if there is one. | |
252 cookieBuilder.append(name); | |
253 if (!value.isEmpty()) { | |
254 cookieBuilder.append('='); | |
255 cookieBuilder.append(value); | |
256 } | |
257 } | |
258 | |
259 void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const St
ring& url) | |
260 { | |
261 // WebCore loaded the page with the movie URL with CFNetwork but QuickTime w
ill | |
262 // use WinINet to download the movie, so we need to copy any cookies needed
to | |
263 // download the movie into WinInet before asking QuickTime to open it. | |
264 Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocumen
t(); | |
265 Frame* frame = document ? document->frame() : 0; | |
266 if (!frame || !frame->page() || !frame->page()->settings()->cookieEnabled()) | |
267 return; | |
268 | |
269 KURL movieURL = KURL(KURL(), url); | |
270 Vector<Cookie> documentCookies; | |
271 if (!getRawCookies(frame->document(), movieURL, documentCookies)) | |
272 return; | |
273 | |
274 for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) { | |
275 const Cookie& cookie = documentCookies[ndx]; | |
276 | |
277 if (cookie.name.isEmpty()) | |
278 continue; | |
279 | |
280 // Build up the cookie string with as much information as we can get so
WinINet | |
281 // knows what to do with it. | |
282 StringBuilder cookieBuilder; | |
283 addCookieParam(cookieBuilder, cookie.name, cookie.value); | |
284 addCookieParam(cookieBuilder, "path", cookie.path); | |
285 if (cookie.expires) | |
286 addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(c
ookie.expires)); | |
287 if (cookie.httpOnly) | |
288 addCookieParam(cookieBuilder, "httpOnly", String()); | |
289 cookieBuilder.append(';'); | |
290 | |
291 String cookieURL; | |
292 if (!cookie.domain.isEmpty()) { | |
293 StringBuilder urlBuilder; | |
294 | |
295 urlBuilder.append(movieURL.protocol()); | |
296 urlBuilder.append("://"); | |
297 if (cookie.domain[0] == '.') | |
298 urlBuilder.append(cookie.domain.substring(1)); | |
299 else | |
300 urlBuilder.append(cookie.domain); | |
301 if (cookie.path.length() > 1) | |
302 urlBuilder.append(cookie.path); | |
303 | |
304 cookieURL = urlBuilder.toString(); | |
305 } else | |
306 cookieURL = movieURL; | |
307 | |
308 String string = cookieBuilder.toString(); | |
309 InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, strin
g.charactersWithNullTermination(), 0, 0); | |
310 } | |
311 } | |
312 | |
313 static void disableComponentsOnce() | |
314 { | |
315 static bool sComponentsDisabled = false; | |
316 if (sComponentsDisabled) | |
317 return; | |
318 sComponentsDisabled = true; | |
319 | |
320 uint32_t componentsToDisable[][5] = { | |
321 {'eat ', 'TEXT', 'text', 0, 0}, | |
322 {'eat ', 'TXT ', 'text', 0, 0}, | |
323 {'eat ', 'utxt', 'text', 0, 0}, | |
324 {'eat ', 'TEXT', 'tx3g', 0, 0}, | |
325 }; | |
326 | |
327 for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i) | |
328 QTMovie::disableComponent(componentsToDisable[i]); | |
329 } | |
330 | |
331 void MediaPlayerPrivateQuickTimeVisualContext::resumeLoad() | |
332 { | |
333 m_delayingLoad = false; | |
334 | |
335 if (!m_movieURL.isEmpty()) | |
336 loadInternal(m_movieURL); | |
337 } | |
338 | |
339 void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url) | |
340 { | |
341 m_movieURL = url; | |
342 | |
343 if (m_preload == MediaPlayer::None) { | |
344 m_delayingLoad = true; | |
345 return; | |
346 } | |
347 | |
348 loadInternal(url); | |
349 } | |
350 | |
351 void MediaPlayerPrivateQuickTimeVisualContext::loadInternal(const String& url) | |
352 { | |
353 if (!QTMovie::initializeQuickTime()) { | |
354 // FIXME: is this the right error to return? | |
355 m_networkState = MediaPlayer::DecodeError; | |
356 m_player->networkStateChanged(); | |
357 return; | |
358 } | |
359 | |
360 disableComponentsOnce(); | |
361 | |
362 // Initialize the task timer. | |
363 MediaPlayerPrivateTaskTimer::initialize(); | |
364 | |
365 if (m_networkState != MediaPlayer::Loading) { | |
366 m_networkState = MediaPlayer::Loading; | |
367 m_player->networkStateChanged(); | |
368 } | |
369 if (m_readyState != MediaPlayer::HaveNothing) { | |
370 m_readyState = MediaPlayer::HaveNothing; | |
371 m_player->readyStateChanged(); | |
372 } | |
373 cancelSeek(); | |
374 | |
375 setUpCookiesForQuickTime(url); | |
376 | |
377 m_movie = adoptRef(new QTMovie(m_movieClient.get())); | |
378 | |
379 m_movie->load(url.characters(), url.length(), m_player->preservesPitch()); | |
380 m_movie->setVolume(m_player->volume()); | |
381 } | |
382 | |
383 void MediaPlayerPrivateQuickTimeVisualContext::prepareToPlay() | |
384 { | |
385 if (!m_movie || m_delayingLoad) | |
386 resumeLoad(); | |
387 } | |
388 | |
389 void MediaPlayerPrivateQuickTimeVisualContext::play() | |
390 { | |
391 if (!m_movie) | |
392 return; | |
393 m_startedPlaying = true; | |
394 | |
395 m_movie->play(); | |
396 m_visualContextTimer.startRepeating(1.0 / 30); | |
397 } | |
398 | |
399 void MediaPlayerPrivateQuickTimeVisualContext::pause() | |
400 { | |
401 if (!m_movie) | |
402 return; | |
403 m_startedPlaying = false; | |
404 | |
405 m_movie->pause(); | |
406 m_visualContextTimer.stop(); | |
407 } | |
408 | |
409 float MediaPlayerPrivateQuickTimeVisualContext::duration() const | |
410 { | |
411 if (!m_movie) | |
412 return 0; | |
413 return m_movie->duration(); | |
414 } | |
415 | |
416 float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const | |
417 { | |
418 if (!m_movie) | |
419 return 0; | |
420 return m_movie->currentTime(); | |
421 } | |
422 | |
423 void MediaPlayerPrivateQuickTimeVisualContext::seek(float time) | |
424 { | |
425 cancelSeek(); | |
426 | |
427 if (!m_movie) | |
428 return; | |
429 | |
430 if (time > duration()) | |
431 time = duration(); | |
432 | |
433 m_seekTo = time; | |
434 if (maxTimeLoaded() >= m_seekTo) | |
435 doSeek(); | |
436 else | |
437 m_seekTimer.start(0, 0.5f); | |
438 } | |
439 | |
440 void MediaPlayerPrivateQuickTimeVisualContext::doSeek() | |
441 { | |
442 float oldRate = m_movie->rate(); | |
443 if (oldRate) | |
444 m_movie->setRate(0); | |
445 m_movie->setCurrentTime(m_seekTo); | |
446 float timeAfterSeek = currentTime(); | |
447 // restore playback only if not at end, othewise QTMovie will loop | |
448 if (oldRate && timeAfterSeek < duration()) | |
449 m_movie->setRate(oldRate); | |
450 cancelSeek(); | |
451 } | |
452 | |
453 void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek() | |
454 { | |
455 m_seekTo = -1; | |
456 m_seekTimer.stop(); | |
457 } | |
458 | |
459 void MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired(Timer<MediaPlayerP
rivateQuickTimeVisualContext>*) | |
460 { | |
461 if (!m_movie || !seeking() || currentTime() == m_seekTo) { | |
462 cancelSeek(); | |
463 updateStates(); | |
464 m_player->timeChanged(); | |
465 return; | |
466 } | |
467 | |
468 if (maxTimeLoaded() >= m_seekTo) | |
469 doSeek(); | |
470 else { | |
471 MediaPlayer::NetworkState state = networkState(); | |
472 if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { | |
473 cancelSeek(); | |
474 updateStates(); | |
475 m_player->timeChanged(); | |
476 } | |
477 } | |
478 } | |
479 | |
480 bool MediaPlayerPrivateQuickTimeVisualContext::paused() const | |
481 { | |
482 if (!m_movie) | |
483 return true; | |
484 return (!m_movie->rate()); | |
485 } | |
486 | |
487 bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const | |
488 { | |
489 if (!m_movie) | |
490 return false; | |
491 return m_seekTo >= 0; | |
492 } | |
493 | |
494 IntSize MediaPlayerPrivateQuickTimeVisualContext::naturalSize() const | |
495 { | |
496 if (!m_movie) | |
497 return IntSize(); | |
498 int width; | |
499 int height; | |
500 m_movie->getNaturalSize(width, height); | |
501 #if USE(ACCELERATED_COMPOSITING) | |
502 CGSize originalSize = {width, height}; | |
503 CGSize transformedSize = CGSizeApplyAffineTransform(originalSize, m_movieTra
nsform); | |
504 return IntSize(abs(transformedSize.width), abs(transformedSize.height)); | |
505 #else | |
506 return IntSize(width, height); | |
507 #endif | |
508 } | |
509 | |
510 bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const | |
511 { | |
512 if (!m_movie) | |
513 return false; | |
514 return m_movie->hasVideo(); | |
515 } | |
516 | |
517 bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const | |
518 { | |
519 if (!m_movie) | |
520 return false; | |
521 return m_movie->hasAudio(); | |
522 } | |
523 | |
524 void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume) | |
525 { | |
526 if (!m_movie) | |
527 return; | |
528 m_movie->setVolume(volume); | |
529 } | |
530 | |
531 void MediaPlayerPrivateQuickTimeVisualContext::setRate(float rate) | |
532 { | |
533 if (!m_movie) | |
534 return; | |
535 | |
536 // Do not call setRate(...) unless we have started playing; otherwise | |
537 // QuickTime's VisualContext can get wedged waiting for a rate change | |
538 // call which will never come. | |
539 if (m_startedPlaying) | |
540 m_movie->setRate(rate); | |
541 } | |
542 | |
543 void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesP
itch) | |
544 { | |
545 if (!m_movie) | |
546 return; | |
547 m_movie->setPreservesPitch(preservesPitch); | |
548 } | |
549 | |
550 bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const | |
551 { | |
552 if (!m_movie) | |
553 return false; | |
554 return m_movie->hasClosedCaptions(); | |
555 } | |
556 | |
557 void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool vis
ible) | |
558 { | |
559 if (!m_movie) | |
560 return; | |
561 m_movie->setClosedCaptionsVisible(visible); | |
562 } | |
563 | |
564 PassRefPtr<TimeRanges> MediaPlayerPrivateQuickTimeVisualContext::buffered() cons
t | |
565 { | |
566 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); | |
567 float loaded = maxTimeLoaded(); | |
568 // rtsp streams are not buffered | |
569 if (!m_isStreaming && loaded > 0) | |
570 timeRanges->add(0, loaded); | |
571 return timeRanges.release(); | |
572 } | |
573 | |
574 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const | |
575 { | |
576 // infinite duration means live stream | |
577 return !std::isfinite(duration()) ? 0 : maxTimeLoaded(); | |
578 } | |
579 | |
580 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const | |
581 { | |
582 if (!m_movie) | |
583 return 0; | |
584 return m_movie->maxTimeLoaded(); | |
585 } | |
586 | |
587 bool MediaPlayerPrivateQuickTimeVisualContext::didLoadingProgress() const | |
588 { | |
589 if (!m_movie || !duration()) | |
590 return false; | |
591 float currentMaxTimeLoaded = maxTimeLoaded(); | |
592 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLo
adingProgress; | |
593 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded; | |
594 return didLoadingProgress; | |
595 } | |
596 | |
597 unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const | |
598 { | |
599 if (!m_movie) | |
600 return 0; | |
601 return m_movie->dataSize(); | |
602 } | |
603 | |
604 void MediaPlayerPrivateQuickTimeVisualContext::cancelLoad() | |
605 { | |
606 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::
Loaded) | |
607 return; | |
608 | |
609 tearDownVideoRendering(); | |
610 | |
611 // Cancel the load by destroying the movie. | |
612 m_movie.clear(); | |
613 | |
614 updateStates(); | |
615 } | |
616 | |
617 void MediaPlayerPrivateQuickTimeVisualContext::updateStates() | |
618 { | |
619 MediaPlayer::NetworkState oldNetworkState = m_networkState; | |
620 MediaPlayer::ReadyState oldReadyState = m_readyState; | |
621 | |
622 long loadState = m_movie ? m_movie->loadState() : QTMovieLoadStateError; | |
623 | |
624 if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveM
etadata) { | |
625 m_movie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount
); | |
626 if (m_player->inMediaDocument()) { | |
627 if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount
) { | |
628 // This is a type of media that we do not handle directly with a
<video> | |
629 // element, eg. QuickTime VR, a movie with a sprite track, etc.
Tell the | |
630 // MediaPlayerClient that we won't support it. | |
631 sawUnsupportedTracks(); | |
632 return; | |
633 } | |
634 } else if (!m_enabledTrackCount) | |
635 loadState = QTMovieLoadStateError; | |
636 } | |
637 | |
638 // "Loaded" is reserved for fully buffered movies, never the case when strea
ming | |
639 if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { | |
640 m_networkState = MediaPlayer::Loaded; | |
641 m_readyState = MediaPlayer::HaveEnoughData; | |
642 } else if (loadState >= QTMovieLoadStatePlaythroughOK) { | |
643 m_readyState = MediaPlayer::HaveEnoughData; | |
644 } else if (loadState >= QTMovieLoadStatePlayable) { | |
645 // FIXME: This might not work correctly in streaming case, <rdar://probl
em/5693967> | |
646 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFuture
Data : MediaPlayer::HaveCurrentData; | |
647 } else if (loadState >= QTMovieLoadStateLoaded) { | |
648 m_readyState = MediaPlayer::HaveMetadata; | |
649 } else if (loadState > QTMovieLoadStateError) { | |
650 m_networkState = MediaPlayer::Loading; | |
651 m_readyState = MediaPlayer::HaveNothing; | |
652 } else { | |
653 if (m_player->inMediaDocument()) { | |
654 // Something went wrong in the loading of media within a standalone
file. | |
655 // This can occur with chained ref movies that eventually resolve to
a | |
656 // file we don't support. | |
657 sawUnsupportedTracks(); | |
658 return; | |
659 } | |
660 | |
661 float loaded = maxTimeLoaded(); | |
662 if (!loaded) | |
663 m_readyState = MediaPlayer::HaveNothing; | |
664 | |
665 if (!m_enabledTrackCount) | |
666 m_networkState = MediaPlayer::FormatError; | |
667 else { | |
668 // FIXME: We should differentiate between load/network errors and de
code errors <rdar://problem/5605692> | |
669 if (loaded > 0) | |
670 m_networkState = MediaPlayer::DecodeError; | |
671 else | |
672 m_readyState = MediaPlayer::HaveNothing; | |
673 } | |
674 } | |
675 | |
676 if (isReadyForRendering() && !hasSetUpVideoRendering()) | |
677 setUpVideoRendering(); | |
678 | |
679 if (seeking()) | |
680 m_readyState = MediaPlayer::HaveNothing; | |
681 | |
682 if (m_networkState != oldNetworkState) | |
683 m_player->networkStateChanged(); | |
684 if (m_readyState != oldReadyState) | |
685 m_player->readyStateChanged(); | |
686 } | |
687 | |
688 bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const | |
689 { | |
690 return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); | |
691 } | |
692 | |
693 void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks() | |
694 { | |
695 m_movie->setDisabled(true); | |
696 m_hasUnsupportedTracks = true; | |
697 m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); | |
698 } | |
699 | |
700 void MediaPlayerPrivateQuickTimeVisualContext::didEnd() | |
701 { | |
702 if (m_hasUnsupportedTracks) | |
703 return; | |
704 | |
705 m_startedPlaying = false; | |
706 | |
707 updateStates(); | |
708 m_player->timeChanged(); | |
709 } | |
710 | |
711 void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size) | |
712 { | |
713 if (m_hasUnsupportedTracks || !m_movie || m_size == size) | |
714 return; | |
715 m_size = size; | |
716 } | |
717 | |
718 void MediaPlayerPrivateQuickTimeVisualContext::setVisible(bool visible) | |
719 { | |
720 if (m_hasUnsupportedTracks || !m_movie || m_visible == visible) | |
721 return; | |
722 | |
723 m_visible = visible; | |
724 if (m_visible) { | |
725 if (isReadyForRendering()) | |
726 setUpVideoRendering(); | |
727 } else | |
728 tearDownVideoRendering(); | |
729 } | |
730 | |
731 void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const I
ntRect& r) | |
732 { | |
733 MediaRenderingMode currentMode = currentRenderingMode(); | |
734 | |
735 if (currentMode == MediaRenderingNone) | |
736 return; | |
737 | |
738 if (currentMode == MediaRenderingSoftwareRenderer && !m_visualContext) | |
739 return; | |
740 | |
741 QTPixelBuffer buffer = m_visualContext->imageForTime(0); | |
742 if (buffer.pixelBufferRef()) { | |
743 #if USE(ACCELERATED_COMPOSITING) | |
744 if (m_qtVideoLayer) { | |
745 // We are probably being asked to render the video into a canvas, bu
t | |
746 // there's a good chance the QTPixelBuffer is not ARGB and thus can'
t be | |
747 // drawn using CG. If so, fire up an ICMDecompressionSession and co
nvert | |
748 // the current frame into something which can be rendered by CG. | |
749 if (!buffer.pixelFormatIs32ARGB() && !buffer.pixelFormatIs32BGRA())
{ | |
750 // The decompression session will only decompress a specific pix
elFormat | |
751 // at a specific width and height; if these differ, the session
must be | |
752 // recreated with the new parameters. | |
753 if (!m_decompressionSession || !m_decompressionSession->canDecom
press(buffer)) | |
754 m_decompressionSession = QTDecompressionSession::create(buff
er.pixelFormatType(), buffer.width(), buffer.height()); | |
755 buffer = m_decompressionSession->decompress(buffer); | |
756 } | |
757 } | |
758 #endif | |
759 CGImageRef image = CreateCGImageFromPixelBuffer(buffer); | |
760 | |
761 CGContextRef context = p->platformContext(); | |
762 CGContextSaveGState(context); | |
763 CGContextTranslateCTM(context, r.x(), r.y()); | |
764 CGContextTranslateCTM(context, 0, r.height()); | |
765 CGContextScaleCTM(context, 1, -1); | |
766 CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), ima
ge); | |
767 CGContextRestoreGState(context); | |
768 | |
769 CGImageRelease(image); | |
770 } | |
771 paintCompleted(*p, r); | |
772 } | |
773 | |
774 void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& c
ontext, const IntRect& rect) | |
775 { | |
776 m_newFrameAvailable = false; | |
777 } | |
778 | |
779 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurr
entImageProc(void* refcon) | |
780 { | |
781 static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurr
entImage(); | |
782 } | |
783 | |
784 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailab
leForTime(const QTCVTimeStamp* timeStamp) | |
785 { | |
786 // This call may come in on another thread, so marshall to the main thread f
irst: | |
787 callOnMainThread(&retrieveCurrentImageProc, m_parent); | |
788 | |
789 // callOnMainThread must be paired with cancelCallOnMainThread in the destru
ctor, | |
790 // in case this object is deleted before the main thread request is handled. | |
791 } | |
792 | |
793 void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<Med
iaPlayerPrivateQuickTimeVisualContext>*) | |
794 { | |
795 if (m_visualContext && m_visualContext->isImageAvailableForTime(0)) | |
796 retrieveCurrentImage(); | |
797 } | |
798 | |
799 static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef alloc
ator, const UInt8* bytes, CFIndex length) | |
800 { | |
801 RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, by
tes, length, kCFAllocatorNull)); | |
802 if (!data) | |
803 return 0; | |
804 | |
805 return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(all
ocator, data.get(), kCFPropertyListImmutable, 0)); | |
806 } | |
807 | |
808 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer) | |
809 { | |
810 #if USE(ACCELERATED_COMPOSITING) | |
811 CGDataProviderRef provider = 0; | |
812 CGColorSpaceRef colorSpace = 0; | |
813 CGImageRef image = 0; | |
814 | |
815 size_t bitsPerComponent = 0; | |
816 size_t bitsPerPixel = 0; | |
817 CGImageAlphaInfo alphaInfo = kCGImageAlphaNone; | |
818 | |
819 if (buffer.pixelFormatIs32BGRA()) { | |
820 bitsPerComponent = 8; | |
821 bitsPerPixel = 32; | |
822 alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByt
eOrder32Little); | |
823 } else if (buffer.pixelFormatIs32ARGB()) { | |
824 bitsPerComponent = 8; | |
825 bitsPerPixel = 32; | |
826 alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipLast | kCGBitmapByte
Order32Big); | |
827 } else { | |
828 // All other pixel formats are currently unsupported: | |
829 ASSERT_NOT_REACHED(); | |
830 } | |
831 | |
832 CGDataProviderDirectAccessCallbacks callbacks = { | |
833 &QTPixelBuffer::dataProviderGetBytePointerCallback, | |
834 &QTPixelBuffer::dataProviderReleaseBytePointerCallback, | |
835 &QTPixelBuffer::dataProviderGetBytesAtPositionCallback, | |
836 &QTPixelBuffer::dataProviderReleaseInfoCallback, | |
837 }; | |
838 | |
839 // Colorspace should be device, so that Quartz does not have to do an extra
render. | |
840 colorSpace = CGColorSpaceCreateDeviceRGB(); | |
841 require(colorSpace, Bail); | |
842 | |
843 provider = CGDataProviderCreateDirectAccess(buffer.pixelBufferRef(), buffer.
dataSize(), &callbacks); | |
844 require(provider, Bail); | |
845 | |
846 // CGDataProvider does not retain the buffer, but it will release it later,
so do an extra retain here: | |
847 QTPixelBuffer::retainCallback(buffer.pixelBufferRef()); | |
848 | |
849 image = CGImageCreate(buffer.width(), buffer.height(), bitsPerComponent, bit
sPerPixel, buffer.bytesPerRow(), colorSpace, alphaInfo, provider, 0, false, kCGR
enderingIntentDefault); | |
850 | |
851 Bail: | |
852 // Once the image is created we can release our reference to the provider an
d the colorspace, they are retained by the image | |
853 if (provider) | |
854 CGDataProviderRelease(provider); | |
855 if (colorSpace) | |
856 CGColorSpaceRelease(colorSpace); | |
857 | |
858 return image; | |
859 #else | |
860 return 0; | |
861 #endif | |
862 } | |
863 | |
864 | |
865 void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage() | |
866 { | |
867 if (!m_visualContext) | |
868 return; | |
869 | |
870 #if USE(ACCELERATED_COMPOSITING) | |
871 if (m_qtVideoLayer) { | |
872 | |
873 QTPixelBuffer buffer = m_visualContext->imageForTime(0); | |
874 if (!buffer.pixelBufferRef()) | |
875 return; | |
876 | |
877 PlatformCALayer* layer = m_qtVideoLayer.get(); | |
878 | |
879 if (!buffer.lockBaseAddress()) { | |
880 if (requiredDllsAvailable()) { | |
881 if (!m_imageQueue) { | |
882 m_imageQueue = adoptPtr(new WKCAImageQueue(buffer.width(), b
uffer.height(), 30)); | |
883 m_imageQueue->setFlags(WKCAImageQueue::Fill, WKCAImageQueue:
:Fill); | |
884 layer->setContents(m_imageQueue->get()); | |
885 } | |
886 | |
887 // Debug QuickTime links against a non-Debug version of CoreFoun
dation, so the | |
888 // CFDictionary attached to the CVPixelBuffer cannot be directly
passed on into the | |
889 // CAImageQueue without being converted to a non-Debug CFDiction
ary. Additionally, | |
890 // old versions of QuickTime used a non-AAS CoreFoundation, so t
he types are not | |
891 // interchangable even in the release case. | |
892 RetainPtr<CFDictionaryRef> attachments(AdoptCF, QTCFDictionaryCr
eateCopyWithDataCallback(kCFAllocatorDefault, buffer.attachments(), &QTCFDiction
aryCreateWithDataCallback)); | |
893 CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime
(); | |
894 | |
895 m_imageQueue->collect(); | |
896 | |
897 uint64_t imageId = m_imageQueue->registerPixelBuffer(buffer.base
Address(), buffer.dataSize(), buffer.bytesPerRow(), buffer.width(), buffer.heigh
t(), buffer.pixelFormatType(), attachments.get(), 0); | |
898 | |
899 if (m_imageQueue->insertImage(imageTime, WKCAImageQueue::Buffer,
imageId, WKCAImageQueue::Opaque | WKCAImageQueue::Flush, &QTPixelBuffer::imageQ
ueueReleaseCallback, buffer.pixelBufferRef())) { | |
900 // Retain the buffer one extra time so it doesn't dissappear
before CAImageQueue decides to release it: | |
901 QTPixelBuffer::retainCallback(buffer.pixelBufferRef()); | |
902 } | |
903 | |
904 } else { | |
905 CGImageRef image = CreateCGImageFromPixelBuffer(buffer); | |
906 layer->setContents(image); | |
907 CGImageRelease(image); | |
908 } | |
909 | |
910 buffer.unlockBaseAddress(); | |
911 layer->setNeedsCommit(); | |
912 } | |
913 } else | |
914 #endif | |
915 m_player->repaint(); | |
916 | |
917 m_visualContext->task(); | |
918 } | |
919 | |
920 static HashSet<String> mimeTypeCache() | |
921 { | |
922 DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); | |
923 static bool typeListInitialized = false; | |
924 | |
925 if (!typeListInitialized) { | |
926 unsigned count = QTMovie::countSupportedTypes(); | |
927 for (unsigned n = 0; n < count; n++) { | |
928 const UChar* character; | |
929 unsigned len; | |
930 QTMovie::getSupportedType(n, character, len); | |
931 if (len) | |
932 typeCache.add(String(character, len)); | |
933 } | |
934 | |
935 typeListInitialized = true; | |
936 } | |
937 | |
938 return typeCache; | |
939 } | |
940 | |
941 static CFStringRef createVersionStringFromModuleName(LPCWSTR moduleName) | |
942 { | |
943 HMODULE module = GetModuleHandleW(moduleName); | |
944 if (!module) | |
945 return 0; | |
946 | |
947 wchar_t filePath[MAX_PATH] = {0}; | |
948 if (!GetModuleFileNameW(module, filePath, MAX_PATH)) | |
949 return 0; | |
950 | |
951 DWORD versionInfoSize = GetFileVersionInfoSizeW(filePath, 0); | |
952 if (!versionInfoSize) | |
953 return 0; | |
954 | |
955 CFStringRef versionString = 0; | |
956 void* versionInfo = calloc(versionInfoSize, sizeof(char)); | |
957 if (GetFileVersionInfo(filePath, 0, versionInfoSize, versionInfo)) { | |
958 VS_FIXEDFILEINFO* fileInfo = 0; | |
959 UINT fileInfoLength = 0; | |
960 | |
961 if (VerQueryValueW(versionInfo, L"\\", reinterpret_cast<LPVOID*>(&fileIn
fo), &fileInfoLength)) { | |
962 versionString = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFS
TR("%d.%d.%d.%d"), | |
963 HIWORD(fileInfo->dwFileVersionMS), LOWORD(fileInfo->dwFileVersio
nMS), | |
964 HIWORD(fileInfo->dwFileVersionLS), LOWORD(fileInfo->dwFileVersio
nLS)); | |
965 } | |
966 } | |
967 free(versionInfo); | |
968 | |
969 return versionString; | |
970 } | |
971 | |
972 static bool requiredDllsAvailable() | |
973 { | |
974 static bool s_prerequisitesChecked = false; | |
975 static bool s_prerequisitesSatisfied; | |
976 static const CFStringRef kMinQuartzCoreVersion = CFSTR("1.0.42.0"); | |
977 static const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.1.0"); | |
978 | |
979 if (s_prerequisitesChecked) | |
980 return s_prerequisitesSatisfied; | |
981 s_prerequisitesChecked = true; | |
982 s_prerequisitesSatisfied = false; | |
983 | |
984 CFStringRef quartzCoreString = createVersionStringFromModuleName(L"QuartzCor
e"); | |
985 if (!quartzCoreString) | |
986 quartzCoreString = createVersionStringFromModuleName(L"QuartzCore_debug"
); | |
987 | |
988 CFStringRef coreVideoString = createVersionStringFromModuleName(L"CoreVideo"
); | |
989 if (!coreVideoString) | |
990 coreVideoString = createVersionStringFromModuleName(L"CoreVideo_debug"); | |
991 | |
992 s_prerequisitesSatisfied = (quartzCoreString && coreVideoString | |
993 && CFStringCompare(quartzCoreString, kMinQuartzCoreVersion, kCFCompareNu
merically) != kCFCompareLessThan | |
994 && CFStringCompare(coreVideoString, kMinCoreVideoVersion, kCFCompareNume
rically) != kCFCompareLessThan); | |
995 | |
996 if (quartzCoreString) | |
997 CFRelease(quartzCoreString); | |
998 if (coreVideoString) | |
999 CFRelease(coreVideoString); | |
1000 | |
1001 return s_prerequisitesSatisfied; | |
1002 } | |
1003 | |
1004 void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>
& types) | |
1005 { | |
1006 types = mimeTypeCache(); | |
1007 } | |
1008 | |
1009 bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable() | |
1010 { | |
1011 return QTMovie::initializeQuickTime(); | |
1012 } | |
1013 | |
1014 MediaPlayer::SupportsType MediaPlayerPrivateQuickTimeVisualContext::supportsType
(const String& type, const String& codecs, const KURL&) | |
1015 { | |
1016 // only return "IsSupported" if there is no codecs parameter for now as ther
e is no way to ask QT if it supports an | |
1017 // extended MIME type | |
1018 return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::May
BeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; | |
1019 } | |
1020 | |
1021 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieEnded(QTMovie*
movie) | |
1022 { | |
1023 if (m_parent->m_hasUnsupportedTracks) | |
1024 return; | |
1025 | |
1026 m_parent->m_visualContextTimer.stop(); | |
1027 | |
1028 ASSERT(m_parent->m_movie.get() == movie); | |
1029 m_parent->didEnd(); | |
1030 } | |
1031 | |
1032 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieLoadStateChange
d(QTMovie* movie) | |
1033 { | |
1034 if (m_parent->m_hasUnsupportedTracks) | |
1035 return; | |
1036 | |
1037 ASSERT(m_parent->m_movie.get() == movie); | |
1038 m_parent->updateStates(); | |
1039 } | |
1040 | |
1041 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieTimeChanged(QTM
ovie* movie) | |
1042 { | |
1043 if (m_parent->m_hasUnsupportedTracks) | |
1044 return; | |
1045 | |
1046 ASSERT(m_parent->m_movie.get() == movie); | |
1047 m_parent->updateStates(); | |
1048 m_parent->m_player->timeChanged(); | |
1049 } | |
1050 | |
1051 bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const | |
1052 { | |
1053 // We tell quicktime to disallow resources that come from different origins | |
1054 // so we all media is single origin. | |
1055 return true; | |
1056 } | |
1057 | |
1058 void MediaPlayerPrivateQuickTimeVisualContext::setPreload(MediaPlayer::Preload p
reload) | |
1059 { | |
1060 m_preload = preload; | |
1061 if (m_delayingLoad && m_preload != MediaPlayer::None) | |
1062 resumeLoad(); | |
1063 } | |
1064 | |
1065 float MediaPlayerPrivateQuickTimeVisualContext::mediaTimeForTimeValue(float time
Value) const | |
1066 { | |
1067 long timeScale; | |
1068 if (m_readyState < MediaPlayer::HaveMetadata || !(timeScale = m_movie->timeS
cale())) | |
1069 return timeValue; | |
1070 | |
1071 long mediaTimeValue = lroundf(timeValue * timeScale); | |
1072 return static_cast<float>(mediaTimeValue) / timeScale; | |
1073 } | |
1074 | |
1075 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQ
uickTimeVisualContext::currentRenderingMode() const | |
1076 { | |
1077 if (!m_movie) | |
1078 return MediaRenderingNone; | |
1079 | |
1080 #if USE(ACCELERATED_COMPOSITING) | |
1081 if (m_qtVideoLayer) | |
1082 return MediaRenderingMovieLayer; | |
1083 #endif | |
1084 | |
1085 return m_visualContext ? MediaRenderingSoftwareRenderer : MediaRenderingNone
; | |
1086 } | |
1087 | |
1088 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQ
uickTimeVisualContext::preferredRenderingMode() const | |
1089 { | |
1090 if (!m_player->frameView() || !m_movie) | |
1091 return MediaRenderingNone; | |
1092 | |
1093 #if USE(ACCELERATED_COMPOSITING) | |
1094 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPl
ayerRenderingCanBeAccelerated(m_player)) | |
1095 return MediaRenderingMovieLayer; | |
1096 #endif | |
1097 | |
1098 return MediaRenderingSoftwareRenderer; | |
1099 } | |
1100 | |
1101 void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering() | |
1102 { | |
1103 MediaRenderingMode currentMode = currentRenderingMode(); | |
1104 MediaRenderingMode preferredMode = preferredRenderingMode(); | |
1105 | |
1106 #if !USE(ACCELERATED_COMPOSITING) | |
1107 ASSERT(preferredMode != MediaRenderingMovieLayer); | |
1108 #endif | |
1109 | |
1110 if (currentMode == preferredMode && currentMode != MediaRenderingNone) | |
1111 return; | |
1112 | |
1113 if (currentMode != MediaRenderingNone) | |
1114 tearDownVideoRendering(); | |
1115 | |
1116 if (preferredMode == MediaRenderingMovieLayer) | |
1117 createLayerForMovie(); | |
1118 | |
1119 #if USE(ACCELERATED_COMPOSITING) | |
1120 if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderi
ngMovieLayer) | |
1121 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player)
; | |
1122 #endif | |
1123 | |
1124 QTPixelBuffer::Type contextType = requiredDllsAvailable() && preferredMode =
= MediaRenderingMovieLayer ? QTPixelBuffer::ConfigureForCAImageQueue : QTPixelBu
ffer::ConfigureForCGImage; | |
1125 m_visualContext = QTMovieVisualContext::create(m_visualContextClient.get(),
contextType); | |
1126 m_visualContext->setMovie(m_movie.get()); | |
1127 } | |
1128 | |
1129 void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering() | |
1130 { | |
1131 #if USE(ACCELERATED_COMPOSITING) | |
1132 if (m_qtVideoLayer) | |
1133 destroyLayerForMovie(); | |
1134 #endif | |
1135 | |
1136 m_visualContext = 0; | |
1137 } | |
1138 | |
1139 bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const | |
1140 { | |
1141 #if USE(ACCELERATED_COMPOSITING) | |
1142 return m_qtVideoLayer || (currentRenderingMode() != MediaRenderingMovieLayer
&& m_visualContext); | |
1143 #else | |
1144 return true; | |
1145 #endif | |
1146 } | |
1147 | |
1148 void MediaPlayerPrivateQuickTimeVisualContext::retrieveAndResetMovieTransform() | |
1149 { | |
1150 #if USE(ACCELERATED_COMPOSITING) | |
1151 // First things first, reset the total movie transform so that | |
1152 // we can bail out early: | |
1153 m_movieTransform = CGAffineTransformIdentity; | |
1154 | |
1155 if (!m_movie || !m_movie->hasVideo()) | |
1156 return; | |
1157 | |
1158 // This trick will only work on movies with a single video track, | |
1159 // so bail out early if the video contains more than one (or zero) | |
1160 // video tracks. | |
1161 QTTrackArray videoTracks = m_movie->videoTracks(); | |
1162 if (videoTracks.size() != 1) | |
1163 return; | |
1164 | |
1165 QTTrack* track = videoTracks[0].get(); | |
1166 ASSERT(track); | |
1167 | |
1168 CGAffineTransform movieTransform = m_movie->getTransform(); | |
1169 if (!CGAffineTransformEqualToTransform(movieTransform, CGAffineTransformIden
tity)) | |
1170 m_movie->resetTransform(); | |
1171 | |
1172 CGAffineTransform trackTransform = track->getTransform(); | |
1173 if (!CGAffineTransformEqualToTransform(trackTransform, CGAffineTransformIden
tity)) | |
1174 track->resetTransform(); | |
1175 | |
1176 // Multiply the two transforms together, taking care to | |
1177 // do so in the correct order, track * movie = final: | |
1178 m_movieTransform = CGAffineTransformConcat(trackTransform, movieTransform); | |
1179 #endif | |
1180 } | |
1181 | |
1182 void MediaPlayerPrivateQuickTimeVisualContext::createLayerForMovie() | |
1183 { | |
1184 #if USE(ACCELERATED_COMPOSITING) | |
1185 ASSERT(supportsAcceleratedRendering()); | |
1186 | |
1187 if (!m_movie || m_qtVideoLayer) | |
1188 return; | |
1189 | |
1190 // Create a PlatformCALayer which will transform the contents of the video l
ayer | |
1191 // which is in m_qtVideoLayer. | |
1192 m_transformLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer,
m_layerClient.get()); | |
1193 if (!m_transformLayer) | |
1194 return; | |
1195 | |
1196 // Mark the layer as anchored in the top left. | |
1197 m_transformLayer->setAnchorPoint(FloatPoint3D()); | |
1198 | |
1199 m_qtVideoLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, 0)
; | |
1200 if (!m_qtVideoLayer) | |
1201 return; | |
1202 | |
1203 if (CGAffineTransformEqualToTransform(m_movieTransform, CGAffineTransformIde
ntity)) | |
1204 retrieveAndResetMovieTransform(); | |
1205 CGAffineTransform t = m_movieTransform; | |
1206 | |
1207 // Remove the translation portion of the transform, since we will always rot
ate about | |
1208 // the layer's center point. In our limited use-case (a single video track)
, this is | |
1209 // safe: | |
1210 t.tx = t.ty = 0; | |
1211 m_qtVideoLayer->setTransform(CATransform3DMakeAffineTransform(t)); | |
1212 | |
1213 #ifndef NDEBUG | |
1214 m_qtVideoLayer->setName("Video layer"); | |
1215 #endif | |
1216 m_transformLayer->appendSublayer(m_qtVideoLayer.get()); | |
1217 m_transformLayer->setNeedsLayout(); | |
1218 // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerC
onfiguration(). | |
1219 #endif | |
1220 | |
1221 // Fill the newly created layer with image data, so we're not looking at | |
1222 // an empty layer until the next time a new image is available, which could | |
1223 // be a long time if we're paused. | |
1224 if (m_visualContext) | |
1225 retrieveCurrentImage(); | |
1226 } | |
1227 | |
1228 void MediaPlayerPrivateQuickTimeVisualContext::destroyLayerForMovie() | |
1229 { | |
1230 #if USE(ACCELERATED_COMPOSITING) | |
1231 if (m_qtVideoLayer) { | |
1232 m_qtVideoLayer->removeFromSuperlayer(); | |
1233 m_qtVideoLayer = 0; | |
1234 } | |
1235 | |
1236 if (m_transformLayer) | |
1237 m_transformLayer = 0; | |
1238 | |
1239 if (m_imageQueue) | |
1240 m_imageQueue = nullptr; | |
1241 #endif | |
1242 } | |
1243 | |
1244 #if USE(ACCELERATED_COMPOSITING) | |
1245 bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() co
nst | |
1246 { | |
1247 return isReadyForRendering(); | |
1248 } | |
1249 | |
1250 void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged(
) | |
1251 { | |
1252 // Set up or change the rendering path if necessary. | |
1253 setUpVideoRendering(); | |
1254 } | |
1255 | |
1256 void MediaPlayerPrivateQuickTimeVisualContext::setPrivateBrowsingMode(bool priva
teBrowsing) | |
1257 { | |
1258 m_privateBrowsing = privateBrowsing; | |
1259 if (m_movie) | |
1260 m_movie->setPrivateBrowsingMode(m_privateBrowsing); | |
1261 } | |
1262 | |
1263 #endif | |
1264 | |
1265 | |
1266 } | |
1267 | |
1268 #endif | |
OLD | NEW |