| 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 |