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#import "config.h" 27 28#if ENABLE(VIDEO) 29 30#import "MediaPlayerPrivateQTKit.h" 31 32#if ENABLE(OFFLINE_WEB_APPLICATIONS) 33#include "ApplicationCacheHost.h" 34#include "ApplicationCacheResource.h" 35#include "DocumentLoader.h" 36#endif 37 38 39#import "BlockExceptions.h" 40#import "DocumentLoader.h" 41#import "FrameView.h" 42#import "HostWindow.h" 43#import "GraphicsContext.h" 44#import "KURL.h" 45#import "MIMETypeRegistry.h" 46#import "SecurityOrigin.h" 47#import "SoftLinking.h" 48#import "TimeRanges.h" 49#import "WebCoreSystemInterface.h" 50 51#if PLATFORM(QT) 52// Avoid clash with slots member in CALayer.h 53#include <qobjectdefs.h> 54#if defined(slots) 55#undef slots 56#endif 57#endif 58 59#import <QTKit/QTKit.h> 60#import <objc/objc-runtime.h> 61#import <wtf/UnusedParam.h> 62 63#if USE(ACCELERATED_COMPOSITING) 64#include "GraphicsLayer.h" 65#endif 66 67#if DRAW_FRAME_RATE 68#import "Font.h" 69#import "Frame.h" 70#import "Document.h" 71#import "RenderObject.h" 72#import "RenderStyle.h" 73#endif 74 75SOFT_LINK_FRAMEWORK(QTKit) 76 77SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale)) 78 79SOFT_LINK_CLASS(QTKit, QTMovie) 80SOFT_LINK_CLASS(QTKit, QTMovieView) 81SOFT_LINK_CLASS(QTKit, QTMovieLayer) 82 83SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *) 84SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) 85SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) 86SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *) 87SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) 88SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) 89SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) 90SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *) 91SOFT_LINK_POINTER(QTKit, QTMovieLoopsAttribute, NSString *) 92SOFT_LINK_POINTER(QTKit, QTMovieDataAttribute, NSString *) 93SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *) 94SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *) 95SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *) 96SOFT_LINK_POINTER(QTKit, QTMovieHasAudioAttribute, NSString *) 97SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *) 98SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *) 99SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *) 100SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *) 101SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *) 102SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *) 103SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *) 104SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) 105SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *) 106SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *) 107SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *) 108SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *) 109SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *) 110SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) 111SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoLocalToRemoteSiteAttribute, NSString *) 112SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoRemoteToLocalSiteAttribute, NSString *) 113SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *) 114SOFT_LINK_POINTER(QTKit, QTMovieApertureModeClean, NSString *) 115SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) 116 117#define QTMovie getQTMovieClass() 118#define QTMovieView getQTMovieViewClass() 119#define QTMovieLayer getQTMovieLayerClass() 120 121#define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute() 122#define QTMediaTypeAttribute getQTMediaTypeAttribute() 123#define QTMediaTypeBase getQTMediaTypeBase() 124#define QTMediaTypeMPEG getQTMediaTypeMPEG() 125#define QTMediaTypeSound getQTMediaTypeSound() 126#define QTMediaTypeText getQTMediaTypeText() 127#define QTMediaTypeVideo getQTMediaTypeVideo() 128#define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute() 129#define QTMovieLoopsAttribute getQTMovieLoopsAttribute() 130#define QTMovieDataAttribute getQTMovieDataAttribute() 131#define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute() 132#define QTMovieDidEndNotification getQTMovieDidEndNotification() 133#define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute() 134#define QTMovieHasAudioAttribute getQTMovieHasAudioAttribute() 135#define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute() 136#define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute() 137#define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification() 138#define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute() 139#define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute() 140#define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute() 141#define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute() 142#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() 143#define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification() 144#define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification() 145#define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute() 146#define QTMovieURLAttribute getQTMovieURLAttribute() 147#define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification() 148#define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute() 149#define QTSecurityPolicyNoLocalToRemoteSiteAttribute getQTSecurityPolicyNoLocalToRemoteSiteAttribute() 150#define QTSecurityPolicyNoRemoteToLocalSiteAttribute getQTSecurityPolicyNoRemoteToLocalSiteAttribute() 151#define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification() 152#define QTMovieApertureModeClean getQTMovieApertureModeClean() 153#define QTMovieApertureModeAttribute getQTMovieApertureModeAttribute() 154 155// Older versions of the QTKit header don't have these constants. 156#if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0 157enum { 158 QTMovieLoadStateError = -1L, 159 QTMovieLoadStateLoaded = 2000L, 160 QTMovieLoadStatePlayable = 10000L, 161 QTMovieLoadStatePlaythroughOK = 20000L, 162 QTMovieLoadStateComplete = 100000L 163}; 164#endif 165 166@interface FakeQTMovieView : NSObject 167- (WebCoreMovieObserver *)delegate; 168@end 169 170using namespace WebCore; 171using namespace std; 172 173@interface WebCoreMovieObserver : NSObject 174{ 175 MediaPlayerPrivateQTKit* m_callback; 176 NSView* m_view; 177 BOOL m_delayCallbacks; 178} 179-(id)initWithCallback:(MediaPlayerPrivateQTKit*)callback; 180-(void)disconnect; 181-(void)setView:(NSView*)view; 182-(void)repaint; 183-(void)setDelayCallbacks:(BOOL)shouldDelay; 184-(void)loadStateChanged:(NSNotification *)notification; 185-(void)rateChanged:(NSNotification *)notification; 186-(void)sizeChanged:(NSNotification *)notification; 187-(void)timeChanged:(NSNotification *)notification; 188-(void)didEnd:(NSNotification *)notification; 189-(void)layerHostChanged:(NSNotification *)notification; 190@end 191 192@protocol WebKitVideoRenderingDetails 193-(void)setMovie:(id)movie; 194-(void)drawInRect:(NSRect)rect; 195@end 196 197namespace WebCore { 198 199PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateQTKit::create(MediaPlayer* player) 200{ 201 return adoptPtr(new MediaPlayerPrivateQTKit(player)); 202} 203 204void MediaPlayerPrivateQTKit::registerMediaEngine(MediaEngineRegistrar registrar) 205{ 206 if (isAvailable()) 207 registrar(create, getSupportedTypes, supportsType, getSitesInMediaCache, clearMediaCache, clearMediaCacheForSite); 208} 209 210MediaPlayerPrivateQTKit::MediaPlayerPrivateQTKit(MediaPlayer* player) 211 : m_player(player) 212 , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) 213 , m_seekTo(-1) 214 , m_seekTimer(this, &MediaPlayerPrivateQTKit::seekTimerFired) 215 , m_networkState(MediaPlayer::Empty) 216 , m_readyState(MediaPlayer::HaveNothing) 217 , m_rect() 218 , m_scaleFactor(1, 1) 219 , m_enabledTrackCount(0) 220 , m_totalTrackCount(0) 221 , m_reportedDuration(-1) 222 , m_cachedDuration(-1) 223 , m_timeToRestore(-1) 224 , m_preload(MediaPlayer::Auto) 225 , m_startedPlaying(false) 226 , m_isStreaming(false) 227 , m_visible(false) 228 , m_hasUnsupportedTracks(false) 229 , m_videoFrameHasDrawn(false) 230 , m_isAllowedToRender(false) 231 , m_privateBrowsing(false) 232#if DRAW_FRAME_RATE 233 , m_frameCountWhilePlaying(0) 234 , m_timeStartedPlaying(0) 235 , m_timeStoppedPlaying(0) 236#endif 237{ 238} 239 240MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit() 241{ 242 tearDownVideoRendering(); 243 244 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; 245 [m_objcObserver.get() disconnect]; 246} 247 248NSMutableDictionary *MediaPlayerPrivateQTKit::commonMovieAttributes() 249{ 250 NSMutableDictionary *movieAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys: 251 [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute, 252 [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, 253 [NSNumber numberWithBool:NO], QTSecurityPolicyNoCrossSiteAttribute, 254 [NSNumber numberWithBool:YES], QTSecurityPolicyNoRemoteToLocalSiteAttribute, 255 [NSNumber numberWithBool:YES], QTSecurityPolicyNoLocalToRemoteSiteAttribute, 256 [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, 257 [NSNumber numberWithBool:NO], QTMovieLoopsAttribute, 258 [NSNumber numberWithBool:!m_privateBrowsing], @"QTMovieAllowPersistentCacheAttribute", 259 QTMovieApertureModeClean, QTMovieApertureModeAttribute, 260 nil]; 261 262 if (m_preload < MediaPlayer::Auto) 263 [movieAttributes setValue:[NSNumber numberWithBool:YES] forKey:@"QTMovieLimitReadAheadAttribute"]; 264 265 return movieAttributes; 266} 267 268void MediaPlayerPrivateQTKit::createQTMovie(const String& url) 269{ 270 NSURL *cocoaURL = KURL(ParsedURLString, url); 271 NSMutableDictionary *movieAttributes = commonMovieAttributes(); 272 [movieAttributes setValue:cocoaURL forKey:QTMovieURLAttribute]; 273 274#if !defined(BUILDING_ON_LEOPARD) 275 CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings(); 276 CFArrayRef proxiesForURL = CFNetworkCopyProxiesForURL((CFURLRef)cocoaURL, proxySettings); 277 BOOL willUseProxy = YES; 278 279 if (!proxiesForURL || !CFArrayGetCount(proxiesForURL)) 280 willUseProxy = NO; 281 282 if (CFArrayGetCount(proxiesForURL) == 1) { 283 CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxiesForURL, 0); 284 ASSERT(CFGetTypeID(proxy) == CFDictionaryGetTypeID()); 285 286 CFStringRef proxyType = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey); 287 ASSERT(CFGetTypeID(proxyType) == CFStringGetTypeID()); 288 289 if (CFStringCompare(proxyType, kCFProxyTypeNone, 0) == kCFCompareEqualTo) 290 willUseProxy = NO; 291 } 292 293 if (!willUseProxy) { 294 // Only pass the QTMovieOpenForPlaybackAttribute flag if there are no proxy servers, due 295 // to rdar://problem/7531776. 296 [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; 297 } 298 299 if (proxiesForURL) 300 CFRelease(proxiesForURL); 301 if (proxySettings) 302 CFRelease(proxySettings); 303#endif 304 305 createQTMovie(cocoaURL, movieAttributes); 306} 307 308void MediaPlayerPrivateQTKit::createQTMovie(ApplicationCacheResource* resource) 309{ 310#if ENABLE(OFFLINE_WEB_APPLICATIONS) 311 ASSERT(resource); 312 313 NSMutableDictionary *movieAttributes = commonMovieAttributes(); 314 [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; 315 316 // ApplicationCacheResources can supply either a data pointer, or a path to a locally cached 317 // flat file. We would prefer the path over the data, but QTKit can handle either: 318 String localPath = resource->path(); 319 NSURL* cocoaURL = !localPath.isEmpty() ? [NSURL fileURLWithPath:localPath isDirectory:NO] : nil; 320 if (cocoaURL) 321 [movieAttributes setValue:cocoaURL forKey:QTMovieURLAttribute]; 322 else { 323 NSData* movieData = resource->data()->createNSData(); 324 [movieAttributes setValue:movieData forKey:QTMovieDataAttribute]; 325 [movieData release]; 326 } 327 328 createQTMovie(cocoaURL, movieAttributes); 329 330#else 331 ASSERT_NOT_REACHED(); 332#endif 333} 334 335static void disableComponentsOnce() 336{ 337 static bool sComponentsDisabled = false; 338 if (sComponentsDisabled) 339 return; 340 sComponentsDisabled = true; 341 342 // eat/PDF and grip/PDF components must be disabled twice since they are registered twice 343 // with different flags. However, there is currently a bug in 64-bit QTKit (<rdar://problem/8378237>) 344 // which causes subsequent disable component requests of exactly the same type to be ignored if 345 // QTKitServer has not yet started. As a result, we must pass in exactly the flags we want to 346 // disable per component. As a failsafe, if in the future these flags change, we will disable the 347 // PDF components for a third time with a wildcard flags field: 348 uint32_t componentsToDisable[11][5] = { 349 {'eat ', 'TEXT', 'text', 0, 0}, 350 {'eat ', 'TXT ', 'text', 0, 0}, 351 {'eat ', 'utxt', 'text', 0, 0}, 352 {'eat ', 'TEXT', 'tx3g', 0, 0}, 353 {'eat ', 'PDF ', 'vide', 0x44802, 0}, 354 {'eat ', 'PDF ', 'vide', 0x45802, 0}, 355 {'eat ', 'PDF ', 'vide', 0, 0}, 356 {'grip', 'PDF ', 'appl', 0x844a00, 0}, 357 {'grip', 'PDF ', 'appl', 0x845a00, 0}, 358 {'grip', 'PDF ', 'appl', 0, 0}, 359 {'imdc', 'pdf ', 'appl', 0, 0}, 360 }; 361 362 for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i) 363 wkQTMovieDisableComponent(componentsToDisable[i]); 364} 365 366void MediaPlayerPrivateQTKit::createQTMovie(NSURL *url, NSDictionary *movieAttributes) 367{ 368 disableComponentsOnce(); 369 370 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; 371 372 bool recreating = false; 373 if (m_qtMovie) { 374 recreating = true; 375 destroyQTVideoRenderer(); 376 m_qtMovie = 0; 377 } 378 379 // Disable rtsp streams for now, <rdar://problem/5693967> 380 if (protocolIs([url scheme], "rtsp")) 381 return; 382 383 NSError *error = nil; 384 m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); 385 386 if (!m_qtMovie) 387 return; 388 389 [m_qtMovie.get() setVolume:m_player->volume()]; 390 391 if (recreating && hasVideo()) 392 createQTVideoRenderer(QTVideoRendererModeListensForNewImages); 393 394 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 395 selector:@selector(loadStateChanged:) 396 name:QTMovieLoadStateDidChangeNotification 397 object:m_qtMovie.get()]; 398 399 // In updateState(), we track when maxTimeLoaded() == duration(). 400 // In newer version of QuickTime, a notification is emitted when maxTimeLoaded changes. 401 // In older version of QuickTime, QTMovieLoadStateDidChangeNotification be fired. 402 if (NSString *maxTimeLoadedChangeNotification = wkQTMovieMaxTimeLoadedChangeNotification()) { 403 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 404 selector:@selector(loadStateChanged:) 405 name:maxTimeLoadedChangeNotification 406 object:m_qtMovie.get()]; 407 } 408 409 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 410 selector:@selector(rateChanged:) 411 name:QTMovieRateDidChangeNotification 412 object:m_qtMovie.get()]; 413 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 414 selector:@selector(sizeChanged:) 415 name:QTMovieSizeDidChangeNotification 416 object:m_qtMovie.get()]; 417 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 418 selector:@selector(timeChanged:) 419 name:QTMovieTimeDidChangeNotification 420 object:m_qtMovie.get()]; 421 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 422 selector:@selector(didEnd:) 423 name:QTMovieDidEndNotification 424 object:m_qtMovie.get()]; 425#if defined(BUILDING_ON_SNOW_LEOPARD) 426 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 427 selector:@selector(layerHostChanged:) 428 name:@"WebKitLayerHostChanged" 429 object:nil]; 430#endif 431} 432 433static void mainThreadSetNeedsDisplay(id self, SEL) 434{ 435 id view = [self superview]; 436 ASSERT(!view || [view isKindOfClass:[QTMovieView class]]); 437 if (!view || ![view isKindOfClass:[QTMovieView class]]) 438 return; 439 440 FakeQTMovieView *movieView = static_cast<FakeQTMovieView *>(view); 441 WebCoreMovieObserver* delegate = [movieView delegate]; 442 ASSERT(!delegate || [delegate isKindOfClass:[WebCoreMovieObserver class]]); 443 if (!delegate || ![delegate isKindOfClass:[WebCoreMovieObserver class]]) 444 return; 445 446 [delegate repaint]; 447} 448 449static Class QTVideoRendererClass() 450{ 451 static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly"); 452 return QTVideoRendererWebKitOnlyClass; 453} 454 455void MediaPlayerPrivateQTKit::createQTMovieView() 456{ 457 detachQTMovieView(); 458 459 static bool addedCustomMethods = false; 460 if (!m_player->inMediaDocument() && !addedCustomMethods) { 461 Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); 462 ASSERT(QTMovieContentViewClass); 463 464 Method mainThreadSetNeedsDisplayMethod = class_getInstanceMethod(QTMovieContentViewClass, @selector(_mainThreadSetNeedsDisplay)); 465 ASSERT(mainThreadSetNeedsDisplayMethod); 466 467 method_setImplementation(mainThreadSetNeedsDisplayMethod, reinterpret_cast<IMP>(mainThreadSetNeedsDisplay)); 468 addedCustomMethods = true; 469 } 470 471 // delay callbacks as we *will* get notifications during setup 472 [m_objcObserver.get() setDelayCallbacks:YES]; 473 474 m_qtMovieView.adoptNS([[QTMovieView alloc] init]); 475 setSize(m_player->size()); 476 NSView* parentView = 0; 477#if PLATFORM(MAC) 478 parentView = m_player->frameView()->documentView(); 479 [parentView addSubview:m_qtMovieView.get()]; 480#endif 481 [m_qtMovieView.get() setDelegate:m_objcObserver.get()]; 482 [m_objcObserver.get() setView:m_qtMovieView.get()]; 483 [m_qtMovieView.get() setMovie:m_qtMovie.get()]; 484 [m_qtMovieView.get() setControllerVisible:NO]; 485 [m_qtMovieView.get() setPreservesAspectRatio:NO]; 486 // the area not covered by video should be transparent 487 [m_qtMovieView.get() setFillColor:[NSColor clearColor]]; 488 489 // If we're in a media document, allow QTMovieView to render in its default mode; 490 // otherwise tell it to draw synchronously. 491 // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. 492 if (!m_player->inMediaDocument()) 493 wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); 494 495 [m_objcObserver.get() setDelayCallbacks:NO]; 496} 497 498void MediaPlayerPrivateQTKit::detachQTMovieView() 499{ 500 if (m_qtMovieView) { 501 [m_objcObserver.get() setView:nil]; 502 [m_qtMovieView.get() setDelegate:nil]; 503 [m_qtMovieView.get() removeFromSuperview]; 504 m_qtMovieView = nil; 505 } 506} 507 508void MediaPlayerPrivateQTKit::createQTVideoRenderer(QTVideoRendererMode rendererMode) 509{ 510 destroyQTVideoRenderer(); 511 512 m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]); 513 if (!m_qtVideoRenderer) 514 return; 515 516 // associate our movie with our instance of QTVideoRendererWebKitOnly 517 [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; 518 519 if (rendererMode == QTVideoRendererModeListensForNewImages) { 520 // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification 521 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() 522 selector:@selector(newImageAvailable:) 523 name:QTVideoRendererWebKitOnlyNewImageAvailableNotification 524 object:m_qtVideoRenderer.get()]; 525 } 526} 527 528void MediaPlayerPrivateQTKit::destroyQTVideoRenderer() 529{ 530 if (!m_qtVideoRenderer) 531 return; 532 533 // stop observing the renderer's notifications before we toss it 534 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get() 535 name:QTVideoRendererWebKitOnlyNewImageAvailableNotification 536 object:m_qtVideoRenderer.get()]; 537 538 // disassociate our movie from our instance of QTVideoRendererWebKitOnly 539 [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil]; 540 541 m_qtVideoRenderer = nil; 542} 543 544void MediaPlayerPrivateQTKit::createQTMovieLayer() 545{ 546#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 547 if (!m_qtMovie) 548 return; 549 550 ASSERT(supportsAcceleratedRendering()); 551 552 if (!m_qtVideoLayer) { 553 m_qtVideoLayer.adoptNS([[QTMovieLayer alloc] init]); 554 if (!m_qtVideoLayer) 555 return; 556 557 [m_qtVideoLayer.get() setMovie:m_qtMovie.get()]; 558#ifndef NDEBUG 559 [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"]; 560#endif 561 // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration(). 562 } 563#endif 564} 565 566void MediaPlayerPrivateQTKit::destroyQTMovieLayer() 567{ 568#if USE(ACCELERATED_COMPOSITING) 569 if (!m_qtVideoLayer) 570 return; 571 572 // disassociate our movie from our instance of QTMovieLayer 573 [m_qtVideoLayer.get() setMovie:nil]; 574 m_qtVideoLayer = nil; 575#endif 576} 577 578MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::currentRenderingMode() const 579{ 580 if (m_qtMovieView) 581 return MediaRenderingMovieView; 582 583 if (m_qtVideoLayer) 584 return MediaRenderingMovieLayer; 585 586 if (m_qtVideoRenderer) 587 return MediaRenderingSoftwareRenderer; 588 589 return MediaRenderingNone; 590} 591 592MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::preferredRenderingMode() const 593{ 594 if (!m_player->frameView() || !m_qtMovie) 595 return MediaRenderingNone; 596 597#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 598 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) 599 return MediaRenderingMovieLayer; 600#endif 601 602 if (!QTVideoRendererClass()) 603 return MediaRenderingMovieView; 604 605 return MediaRenderingSoftwareRenderer; 606} 607 608void MediaPlayerPrivateQTKit::setUpVideoRendering() 609{ 610 if (!isReadyForVideoSetup()) 611 return; 612 613 MediaRenderingMode currentMode = currentRenderingMode(); 614 MediaRenderingMode preferredMode = preferredRenderingMode(); 615 if (currentMode == preferredMode && currentMode != MediaRenderingNone) 616 return; 617 618 if (currentMode != MediaRenderingNone) 619 tearDownVideoRendering(); 620 621 switch (preferredMode) { 622 case MediaRenderingMovieView: 623 createQTMovieView(); 624 break; 625 case MediaRenderingNone: 626 case MediaRenderingSoftwareRenderer: 627 createQTVideoRenderer(QTVideoRendererModeListensForNewImages); 628 break; 629 case MediaRenderingMovieLayer: 630 createQTMovieLayer(); 631 break; 632 } 633 634 // If using a movie layer, inform the client so the compositing tree is updated. 635 if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer) 636 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 637} 638 639void MediaPlayerPrivateQTKit::tearDownVideoRendering() 640{ 641 if (m_qtMovieView) 642 detachQTMovieView(); 643 if (m_qtVideoRenderer) 644 destroyQTVideoRenderer(); 645 if (m_qtVideoLayer) 646 destroyQTMovieLayer(); 647} 648 649bool MediaPlayerPrivateQTKit::hasSetUpVideoRendering() const 650{ 651 return m_qtMovieView 652 || m_qtVideoLayer 653 || m_qtVideoRenderer; 654} 655 656QTTime MediaPlayerPrivateQTKit::createQTTime(float time) const 657{ 658 if (!metaDataAvailable()) 659 return QTMakeTime(0, 600); 660 long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; 661 return QTMakeTime(lroundf(time * timeScale), timeScale); 662} 663 664void MediaPlayerPrivateQTKit::resumeLoad() 665{ 666 if (!m_movieURL.isNull()) 667 loadInternal(m_movieURL); 668} 669 670void MediaPlayerPrivateQTKit::load(const String& url) 671{ 672 m_movieURL = url; 673 674 // If the element is not supposed to load any data return immediately. 675 if (m_preload == MediaPlayer::None) 676 return; 677 678 loadInternal(url); 679} 680 681void MediaPlayerPrivateQTKit::loadInternal(const String& url) 682{ 683 if (m_networkState != MediaPlayer::Loading) { 684 m_networkState = MediaPlayer::Loading; 685 m_player->networkStateChanged(); 686 } 687 if (m_readyState != MediaPlayer::HaveNothing) { 688 m_readyState = MediaPlayer::HaveNothing; 689 m_player->readyStateChanged(); 690 } 691 cancelSeek(); 692 m_videoFrameHasDrawn = false; 693 694 [m_objcObserver.get() setDelayCallbacks:YES]; 695 696#if ENABLE(OFFLINE_WEB_APPLICATIONS) 697 Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; 698 ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : NULL; 699 ApplicationCacheResource* resource = NULL; 700 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource) && resource) 701 createQTMovie(resource); 702 else 703#endif 704 createQTMovie(url); 705 706 [m_objcObserver.get() loadStateChanged:nil]; 707 [m_objcObserver.get() setDelayCallbacks:NO]; 708} 709 710void MediaPlayerPrivateQTKit::prepareToPlay() 711{ 712 setPreload(MediaPlayer::Auto); 713} 714 715PlatformMedia MediaPlayerPrivateQTKit::platformMedia() const 716{ 717 PlatformMedia pm; 718 pm.type = PlatformMedia::QTMovieType; 719 pm.media.qtMovie = m_qtMovie.get(); 720 return pm; 721} 722 723#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 724PlatformLayer* MediaPlayerPrivateQTKit::platformLayer() const 725{ 726 return m_qtVideoLayer.get(); 727} 728#endif 729 730void MediaPlayerPrivateQTKit::play() 731{ 732 if (!metaDataAvailable()) 733 return; 734 m_startedPlaying = true; 735#if DRAW_FRAME_RATE 736 m_frameCountWhilePlaying = 0; 737#endif 738 [m_objcObserver.get() setDelayCallbacks:YES]; 739 [m_qtMovie.get() setRate:m_player->rate()]; 740 [m_objcObserver.get() setDelayCallbacks:NO]; 741} 742 743void MediaPlayerPrivateQTKit::pause() 744{ 745 if (!metaDataAvailable()) 746 return; 747 m_startedPlaying = false; 748#if DRAW_FRAME_RATE 749 m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; 750#endif 751 [m_objcObserver.get() setDelayCallbacks:YES]; 752 [m_qtMovie.get() stop]; 753 [m_objcObserver.get() setDelayCallbacks:NO]; 754} 755 756float MediaPlayerPrivateQTKit::duration() const 757{ 758 if (!metaDataAvailable()) 759 return 0; 760 761 if (m_cachedDuration != -1.0f) 762 return m_cachedDuration; 763 764 QTTime time = [m_qtMovie.get() duration]; 765 if (time.flags == kQTTimeIsIndefinite) 766 return numeric_limits<float>::infinity(); 767 return static_cast<float>(time.timeValue) / time.timeScale; 768} 769 770float MediaPlayerPrivateQTKit::currentTime() const 771{ 772 if (!metaDataAvailable()) 773 return 0; 774 QTTime time = [m_qtMovie.get() currentTime]; 775 return static_cast<float>(time.timeValue) / time.timeScale; 776} 777 778void MediaPlayerPrivateQTKit::seek(float time) 779{ 780 // Nothing to do if we are already in the middle of a seek to the same time. 781 if (time == m_seekTo) 782 return; 783 784 cancelSeek(); 785 786 if (!metaDataAvailable()) 787 return; 788 789 if (time > duration()) 790 time = duration(); 791 792 m_seekTo = time; 793 if (maxTimeSeekable() >= m_seekTo) 794 doSeek(); 795 else 796 m_seekTimer.start(0, 0.5f); 797} 798 799void MediaPlayerPrivateQTKit::doSeek() 800{ 801 QTTime qttime = createQTTime(m_seekTo); 802 // setCurrentTime generates several event callbacks, update afterwards 803 [m_objcObserver.get() setDelayCallbacks:YES]; 804 float oldRate = [m_qtMovie.get() rate]; 805 806 if (oldRate) 807 [m_qtMovie.get() setRate:0]; 808 [m_qtMovie.get() setCurrentTime:qttime]; 809 810 // restore playback only if not at end, otherwise QTMovie will loop 811 float timeAfterSeek = currentTime(); 812 if (oldRate && timeAfterSeek < duration()) 813 [m_qtMovie.get() setRate:oldRate]; 814 815 cancelSeek(); 816 [m_objcObserver.get() setDelayCallbacks:NO]; 817} 818 819void MediaPlayerPrivateQTKit::cancelSeek() 820{ 821 m_seekTo = -1; 822 m_seekTimer.stop(); 823} 824 825void MediaPlayerPrivateQTKit::seekTimerFired(Timer<MediaPlayerPrivateQTKit>*) 826{ 827 if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { 828 cancelSeek(); 829 updateStates(); 830 m_player->timeChanged(); 831 return; 832 } 833 834 if (maxTimeSeekable() >= m_seekTo) 835 doSeek(); 836 else { 837 MediaPlayer::NetworkState state = networkState(); 838 if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { 839 cancelSeek(); 840 updateStates(); 841 m_player->timeChanged(); 842 } 843 } 844} 845 846bool MediaPlayerPrivateQTKit::paused() const 847{ 848 if (!metaDataAvailable()) 849 return true; 850 return [m_qtMovie.get() rate] == 0; 851} 852 853bool MediaPlayerPrivateQTKit::seeking() const 854{ 855 if (!metaDataAvailable()) 856 return false; 857 return m_seekTo >= 0; 858} 859 860IntSize MediaPlayerPrivateQTKit::naturalSize() const 861{ 862 if (!metaDataAvailable()) 863 return IntSize(); 864 865 // In spite of the name of this method, return QTMovieNaturalSizeAttribute transformed by the 866 // initial movie scale because the spec says intrinsic size is: 867 // 868 // ... the dimensions of the resource in CSS pixels after taking into account the resource's 869 // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 870 // format used by the resource 871 872 FloatSize naturalSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]); 873 if (naturalSize.isEmpty() && m_isStreaming) { 874 // HTTP Live Streams will occasionally return {0,0} natural sizes while scrubbing. 875 // Work around this problem (<rdar://problem/9078563>) by returning the last valid 876 // cached natural size: 877 naturalSize = m_cachedNaturalSize; 878 } else { 879 // Unfortunately, due to another QTKit bug (<rdar://problem/9082071>) we won't get a sizeChanged 880 // event when this happens, so we must cache the last valid naturalSize here: 881 m_cachedNaturalSize = naturalSize; 882 } 883 884 return IntSize(naturalSize.width() * m_scaleFactor.width(), naturalSize.height() * m_scaleFactor.height()); 885} 886 887bool MediaPlayerPrivateQTKit::hasVideo() const 888{ 889 if (!metaDataAvailable()) 890 return false; 891 return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; 892} 893 894bool MediaPlayerPrivateQTKit::hasAudio() const 895{ 896 if (!m_qtMovie) 897 return false; 898 return [[m_qtMovie.get() attributeForKey:QTMovieHasAudioAttribute] boolValue]; 899} 900 901bool MediaPlayerPrivateQTKit::supportsFullscreen() const 902{ 903#ifndef BUILDING_ON_LEOPARD 904 return true; 905#else 906 // See <rdar://problem/7389945> 907 return false; 908#endif 909} 910 911void MediaPlayerPrivateQTKit::setVolume(float volume) 912{ 913 if (m_qtMovie) 914 [m_qtMovie.get() setVolume:volume]; 915} 916 917bool MediaPlayerPrivateQTKit::hasClosedCaptions() const 918{ 919 if (!metaDataAvailable()) 920 return false; 921 return wkQTMovieHasClosedCaptions(m_qtMovie.get()); 922} 923 924void MediaPlayerPrivateQTKit::setClosedCaptionsVisible(bool closedCaptionsVisible) 925{ 926 if (metaDataAvailable()) { 927 wkQTMovieSetShowClosedCaptions(m_qtMovie.get(), closedCaptionsVisible); 928 929#if USE(ACCELERATED_COMPOSITING) && !defined(BUILDING_ON_LEOPARD) 930 if (closedCaptionsVisible && m_qtVideoLayer) { 931 // Captions will be rendered upside down unless we flag the movie as flipped (again). See <rdar://7408440>. 932 [m_qtVideoLayer.get() setGeometryFlipped:YES]; 933 } 934#endif 935 } 936} 937 938void MediaPlayerPrivateQTKit::setRate(float rate) 939{ 940 if (m_qtMovie) 941 [m_qtMovie.get() setRate:rate]; 942} 943 944void MediaPlayerPrivateQTKit::setPreservesPitch(bool preservesPitch) 945{ 946 if (!m_qtMovie) 947 return; 948 949 // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation. 950 // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect. 951 if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch) 952 return; 953 954 RetainPtr<NSDictionary> movieAttributes(AdoptNS, [[m_qtMovie.get() movieAttributes] mutableCopy]); 955 ASSERT(movieAttributes); 956 [movieAttributes.get() setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute]; 957 m_timeToRestore = currentTime(); 958 959 createQTMovie([movieAttributes.get() valueForKey:QTMovieURLAttribute], movieAttributes.get()); 960} 961 962PassRefPtr<TimeRanges> MediaPlayerPrivateQTKit::buffered() const 963{ 964 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 965 float loaded = maxTimeLoaded(); 966 if (loaded > 0) 967 timeRanges->add(0, loaded); 968 return timeRanges.release(); 969} 970 971float MediaPlayerPrivateQTKit::maxTimeSeekable() const 972{ 973 if (!metaDataAvailable()) 974 return 0; 975 976 // infinite duration means live stream 977 if (isinf(duration())) 978 return 0; 979 980 return wkQTMovieMaxTimeSeekable(m_qtMovie.get()); 981} 982 983float MediaPlayerPrivateQTKit::maxTimeLoaded() const 984{ 985 if (!metaDataAvailable()) 986 return 0; 987 return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); 988} 989 990unsigned MediaPlayerPrivateQTKit::bytesLoaded() const 991{ 992 float dur = duration(); 993 if (!dur) 994 return 0; 995 return totalBytes() * maxTimeLoaded() / dur; 996} 997 998unsigned MediaPlayerPrivateQTKit::totalBytes() const 999{ 1000 if (!metaDataAvailable()) 1001 return 0; 1002 return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; 1003} 1004 1005void MediaPlayerPrivateQTKit::cancelLoad() 1006{ 1007 // FIXME: Is there a better way to check for this? 1008 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) 1009 return; 1010 1011 tearDownVideoRendering(); 1012 m_qtMovie = nil; 1013 1014 updateStates(); 1015} 1016 1017void MediaPlayerPrivateQTKit::cacheMovieScale() 1018{ 1019 NSSize initialSize = NSZeroSize; 1020 NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; 1021 1022#ifndef BUILDING_ON_LEOPARD 1023 // QTMovieCurrentSizeAttribute is not allowed with instances of QTMovie that have been 1024 // opened with QTMovieOpenForPlaybackAttribute, so ask for the display transform attribute instead. 1025 NSAffineTransform *displayTransform = [m_qtMovie.get() attributeForKey:@"QTMoviePreferredTransformAttribute"]; 1026 if (displayTransform) 1027 initialSize = [displayTransform transformSize:naturalSize]; 1028 else { 1029 initialSize.width = naturalSize.width; 1030 initialSize.height = naturalSize.height; 1031 } 1032#else 1033 initialSize = [[m_qtMovie.get() attributeForKey:QTMovieCurrentSizeAttribute] sizeValue]; 1034#endif 1035 1036 if (naturalSize.width) 1037 m_scaleFactor.setWidth(initialSize.width / naturalSize.width); 1038 if (naturalSize.height) 1039 m_scaleFactor.setHeight(initialSize.height / naturalSize.height); 1040} 1041 1042bool MediaPlayerPrivateQTKit::isReadyForVideoSetup() const 1043{ 1044 return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); 1045} 1046 1047void MediaPlayerPrivateQTKit::prepareForRendering() 1048{ 1049 if (m_isAllowedToRender) 1050 return; 1051 m_isAllowedToRender = true; 1052 1053 if (!hasSetUpVideoRendering()) 1054 setUpVideoRendering(); 1055 1056 // If using a movie layer, inform the client so the compositing tree is updated. This is crucial if the movie 1057 // has a poster, as it will most likely not have a layer and we will now be rendering frames to the movie layer. 1058 if (currentRenderingMode() == MediaRenderingMovieLayer || preferredRenderingMode() == MediaRenderingMovieLayer) 1059 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 1060} 1061 1062void MediaPlayerPrivateQTKit::updateStates() 1063{ 1064 MediaPlayer::NetworkState oldNetworkState = m_networkState; 1065 MediaPlayer::ReadyState oldReadyState = m_readyState; 1066 1067 long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError); 1068 1069 if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { 1070 disableUnsupportedTracks(); 1071 if (m_player->inMediaDocument()) { 1072 if (!m_enabledTrackCount || m_hasUnsupportedTracks) { 1073 // This has a type of media that we do not handle directly with a <video> 1074 // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient 1075 // that we noticed. 1076 sawUnsupportedTracks(); 1077 return; 1078 } 1079 } else if (!m_enabledTrackCount) 1080 loadState = QTMovieLoadStateError; 1081 1082 if (loadState != QTMovieLoadStateError) { 1083 wkQTMovieSelectPreferredAlternates(m_qtMovie.get()); 1084 cacheMovieScale(); 1085 MediaPlayer::MovieLoadType movieType = movieLoadType(); 1086 m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream; 1087 } 1088 } 1089 1090 // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it. 1091 if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore != -1.0f) { 1092 QTTime qttime = createQTTime(m_timeToRestore); 1093 m_timeToRestore = -1.0f; 1094 1095 // Disable event callbacks from setCurrentTime for restoring time in a recreated video 1096 [m_objcObserver.get() setDelayCallbacks:YES]; 1097 [m_qtMovie.get() setCurrentTime:qttime]; 1098 [m_qtMovie.get() setRate:m_player->rate()]; 1099 [m_objcObserver.get() setDelayCallbacks:NO]; 1100 } 1101 1102 BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete); 1103 1104 // Note: QT indicates that we are fully loaded with QTMovieLoadStateComplete. 1105 // However newer versions of QT do not, so we check maxTimeLoaded against duration. 1106 if (!completelyLoaded && !m_isStreaming && metaDataAvailable()) 1107 completelyLoaded = maxTimeLoaded() == duration(); 1108 1109 if (completelyLoaded) { 1110 // "Loaded" is reserved for fully buffered movies, never the case when streaming 1111 m_networkState = MediaPlayer::Loaded; 1112 m_readyState = MediaPlayer::HaveEnoughData; 1113 } else if (loadState >= QTMovieLoadStatePlaythroughOK) { 1114 m_readyState = MediaPlayer::HaveEnoughData; 1115 m_networkState = MediaPlayer::Loading; 1116 } else if (loadState >= QTMovieLoadStatePlayable) { 1117 // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> 1118 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; 1119 m_networkState = MediaPlayer::Loading; 1120 } else if (loadState >= QTMovieLoadStateLoaded) { 1121 m_readyState = MediaPlayer::HaveMetadata; 1122 m_networkState = MediaPlayer::Loading; 1123 } else if (loadState > QTMovieLoadStateError) { 1124 m_readyState = MediaPlayer::HaveNothing; 1125 m_networkState = MediaPlayer::Loading; 1126 } else { 1127 // Loading or decoding failed. 1128 1129 if (m_player->inMediaDocument()) { 1130 // Something went wrong in the loading of media within a standalone file. 1131 // This can occur with chained refmovies pointing to streamed media. 1132 sawUnsupportedTracks(); 1133 return; 1134 } 1135 1136 float loaded = maxTimeLoaded(); 1137 if (!loaded) 1138 m_readyState = MediaPlayer::HaveNothing; 1139 1140 if (!m_enabledTrackCount) 1141 m_networkState = MediaPlayer::FormatError; 1142 else { 1143 // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> 1144 if (loaded > 0) 1145 m_networkState = MediaPlayer::DecodeError; 1146 else 1147 m_readyState = MediaPlayer::HaveNothing; 1148 } 1149 } 1150 1151 if (isReadyForVideoSetup() && !hasSetUpVideoRendering()) 1152 setUpVideoRendering(); 1153 1154 if (seeking()) 1155 m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing; 1156 1157 // Streaming movies don't use the network when paused. 1158 if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0) 1159 m_networkState = MediaPlayer::Idle; 1160 1161 if (m_networkState != oldNetworkState) 1162 m_player->networkStateChanged(); 1163 1164 if (m_readyState != oldReadyState) 1165 m_player->readyStateChanged(); 1166 1167 if (loadState >= QTMovieLoadStateLoaded) { 1168 float dur = duration(); 1169 if (dur != m_reportedDuration) { 1170 if (m_reportedDuration != -1.0f) 1171 m_player->durationChanged(); 1172 m_reportedDuration = dur; 1173 } 1174 } 1175} 1176 1177void MediaPlayerPrivateQTKit::loadStateChanged() 1178{ 1179 if (!m_hasUnsupportedTracks) 1180 updateStates(); 1181} 1182 1183void MediaPlayerPrivateQTKit::rateChanged() 1184{ 1185 if (m_hasUnsupportedTracks) 1186 return; 1187 1188 updateStates(); 1189 m_player->rateChanged(); 1190} 1191 1192void MediaPlayerPrivateQTKit::sizeChanged() 1193{ 1194 if (!m_hasUnsupportedTracks) 1195 m_player->sizeChanged(); 1196} 1197 1198void MediaPlayerPrivateQTKit::timeChanged() 1199{ 1200 if (m_hasUnsupportedTracks) 1201 return; 1202 1203 // It may not be possible to seek to a specific time in a streamed movie. When seeking in a 1204 // stream QuickTime sets the movie time to closest time possible and posts a timechanged 1205 // notification. Update m_seekTo so we can detect when the seek completes. 1206 if (m_seekTo != -1) 1207 m_seekTo = currentTime(); 1208 1209 m_timeToRestore = -1.0f; 1210 updateStates(); 1211 m_player->timeChanged(); 1212} 1213 1214void MediaPlayerPrivateQTKit::didEnd() 1215{ 1216 if (m_hasUnsupportedTracks) 1217 return; 1218 1219 m_startedPlaying = false; 1220#if DRAW_FRAME_RATE 1221 m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; 1222#endif 1223 1224 // Hang onto the current time and use it as duration from now on since QuickTime is telling us we 1225 // are at the end. Do this because QuickTime sometimes reports one time for duration and stops 1226 // playback at another time, which causes problems in HTMLMediaElement. QTKit's 'ended' event 1227 // fires when playing in reverse so don't update duration when at time zero! 1228 float now = currentTime(); 1229 if (now > 0) 1230 m_cachedDuration = now; 1231 1232 updateStates(); 1233 m_player->timeChanged(); 1234} 1235 1236#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 1237#if defined(BUILDING_ON_SNOW_LEOPARD) 1238static bool layerIsDescendentOf(PlatformLayer* child, PlatformLayer* descendent) 1239{ 1240 if (!child || !descendent) 1241 return false; 1242 1243 do { 1244 if (child == descendent) 1245 return true; 1246 } while((child = [child superlayer])); 1247 1248 return false; 1249} 1250#endif 1251 1252void MediaPlayerPrivateQTKit::layerHostChanged(PlatformLayer* rootLayer) 1253{ 1254#if defined(BUILDING_ON_SNOW_LEOPARD) 1255 if (!rootLayer) 1256 return; 1257 1258 if (layerIsDescendentOf(m_qtVideoLayer.get(), rootLayer)) { 1259 // We own a child layer of a layer which has switched contexts. 1260 // Tear down our layer, and set m_visible to false, so that the 1261 // next time setVisible(true) is called, the layer will be re- 1262 // created in the correct context. 1263 tearDownVideoRendering(); 1264 m_visible = false; 1265 } 1266#else 1267 UNUSED_PARAM(rootLayer); 1268#endif 1269} 1270#endif 1271 1272void MediaPlayerPrivateQTKit::setSize(const IntSize&) 1273{ 1274 // Don't resize the view now because [view setFrame] also resizes the movie itself, and because 1275 // the renderer calls this function immediately when we report a size change (QTMovieSizeDidChangeNotification) 1276 // we can get into a feedback loop observing the size change and resetting the size, and this can cause 1277 // QuickTime to miss resetting a movie's size when the media size changes (as happens with an rtsp movie 1278 // once the rtsp server sends the track sizes). Instead we remember the size passed to paint() and resize 1279 // the view when it changes. 1280 // <rdar://problem/6336092> REGRESSION: rtsp movie does not resize correctly 1281} 1282 1283void MediaPlayerPrivateQTKit::setVisible(bool b) 1284{ 1285 if (m_visible != b) { 1286 m_visible = b; 1287 if (b) 1288 setUpVideoRendering(); 1289 else 1290 tearDownVideoRendering(); 1291 } 1292} 1293 1294bool MediaPlayerPrivateQTKit::hasAvailableVideoFrame() const 1295{ 1296 // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable 1297 // because although we don't *know* when the first frame has decoded, by the time we get and 1298 // process the notification a frame should have propagated the VisualContext and been set on 1299 // the layer. 1300 if (currentRenderingMode() == MediaRenderingMovieLayer) 1301 return m_readyState >= MediaPlayer::HaveCurrentData; 1302 1303 // When using the software renderer QuickTime signals that a frame is available so we might as well 1304 // wait until we know that a frame has been drawn. 1305 return m_videoFrameHasDrawn; 1306} 1307 1308void MediaPlayerPrivateQTKit::repaint() 1309{ 1310 if (m_hasUnsupportedTracks) 1311 return; 1312 1313#if DRAW_FRAME_RATE 1314 if (m_startedPlaying) { 1315 m_frameCountWhilePlaying++; 1316 // to eliminate preroll costs from our calculation, 1317 // our frame rate calculation excludes the first frame drawn after playback starts 1318 if (1==m_frameCountWhilePlaying) 1319 m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate]; 1320 } 1321#endif 1322 m_videoFrameHasDrawn = true; 1323 m_player->repaint(); 1324} 1325 1326void MediaPlayerPrivateQTKit::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r) 1327{ 1328 id qtVideoRenderer = m_qtVideoRenderer.get(); 1329 if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) { 1330 // We're being told to render into a context, but we already have the 1331 // MovieLayer going. This probably means we've been called from <canvas>. 1332 // Set up a QTVideoRenderer to use, but one that doesn't register for 1333 // update callbacks. That way, it won't bother us asking to repaint. 1334 createQTVideoRenderer(QTVideoRendererModeDefault); 1335 qtVideoRenderer = m_qtVideoRenderer.get(); 1336 } 1337 paint(context, r); 1338} 1339 1340void MediaPlayerPrivateQTKit::paint(GraphicsContext* context, const IntRect& r) 1341{ 1342 if (context->paintingDisabled() || m_hasUnsupportedTracks) 1343 return; 1344 NSView *view = m_qtMovieView.get(); 1345 id qtVideoRenderer = m_qtVideoRenderer.get(); 1346 if (!view && !qtVideoRenderer) 1347 return; 1348 1349 [m_objcObserver.get() setDelayCallbacks:YES]; 1350 BEGIN_BLOCK_OBJC_EXCEPTIONS; 1351 NSGraphicsContext* newContext; 1352 FloatSize scaleFactor(1.0f, -1.0f); 1353 IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height())); 1354 1355#if PLATFORM(QT) && USE(QTKIT) 1356 // In Qt, GraphicsContext is a QPainter so every transformations applied on it won't matter because here 1357 // the video is rendered by QuickTime not by Qt. 1358 CGContextRef cgContext = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 1359 CGContextSaveGState(cgContext); 1360 CGContextSetInterpolationQuality(cgContext, kCGInterpolationLow); 1361 CGContextTranslateCTM(cgContext, r.x(), r.y() + r.height()); 1362 CGContextScaleCTM(cgContext, scaleFactor.width(), scaleFactor.height()); 1363 1364 newContext = [NSGraphicsContext currentContext]; 1365#else 1366 GraphicsContextStateSaver stateSaver(*context); 1367 context->translate(r.x(), r.y() + r.height()); 1368 context->scale(scaleFactor); 1369 context->setImageInterpolationQuality(InterpolationLow); 1370 1371 newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO]; 1372#endif 1373 // draw the current video frame 1374 if (qtVideoRenderer) { 1375 [NSGraphicsContext saveGraphicsState]; 1376 [NSGraphicsContext setCurrentContext:newContext]; 1377 [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect]; 1378 [NSGraphicsContext restoreGraphicsState]; 1379 } else { 1380 if (m_rect != r) { 1381 m_rect = r; 1382 if (m_player->inMediaDocument()) { 1383 // the QTMovieView needs to be placed in the proper location for document mode 1384 [view setFrame:m_rect]; 1385 } 1386 else { 1387 // We don't really need the QTMovieView in any specific location so let's just get it out of the way 1388 // where it won't intercept events or try to bring up the context menu. 1389 IntRect farAwayButCorrectSize(m_rect); 1390 farAwayButCorrectSize.move(-1000000, -1000000); 1391 [view setFrame:farAwayButCorrectSize]; 1392 } 1393 } 1394 1395 if (m_player->inMediaDocument()) { 1396 // If we're using a QTMovieView in a media document, the view may get layer-backed. AppKit won't update 1397 // the layer hosting correctly if we call displayRectIgnoringOpacity:inContext:, so use displayRectIgnoringOpacity: 1398 // in this case. See <rdar://problem/6702882>. 1399 [view displayRectIgnoringOpacity:paintRect]; 1400 } else 1401 [view displayRectIgnoringOpacity:paintRect inContext:newContext]; 1402 } 1403 1404#if DRAW_FRAME_RATE 1405 // Draw the frame rate only after having played more than 10 frames. 1406 if (m_frameCountWhilePlaying > 10) { 1407 Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; 1408 Document* document = frame ? frame->document() : NULL; 1409 RenderObject* renderer = document ? document->renderer() : NULL; 1410 RenderStyle* styleToUse = renderer ? renderer->style() : NULL; 1411 if (styleToUse) { 1412 double frameRate = (m_frameCountWhilePlaying - 1) / ( m_startedPlaying ? ([NSDate timeIntervalSinceReferenceDate] - m_timeStartedPlaying) : 1413 (m_timeStoppedPlaying - m_timeStartedPlaying) ); 1414 String text = String::format("%1.2f", frameRate); 1415 TextRun textRun(text.characters(), text.length()); 1416 const Color color(255, 0, 0); 1417 context->scale(FloatSize(1.0f, -1.0f)); 1418 context->setStrokeColor(color, styleToUse->colorSpace()); 1419 context->setStrokeStyle(SolidStroke); 1420 context->setStrokeThickness(1.0f); 1421 context->setFillColor(color, styleToUse->colorSpace()); 1422 context->drawText(styleToUse->font(), textRun, IntPoint(2, -3)); 1423 } 1424 } 1425#endif 1426#if PLATFORM(QT) && USE(QTKIT) 1427 CGContextRestoreGState(cgContext); 1428#endif 1429 END_BLOCK_OBJC_EXCEPTIONS; 1430 [m_objcObserver.get() setDelayCallbacks:NO]; 1431} 1432 1433static void addFileTypesToCache(NSArray * fileTypes, HashSet<String> &cache) 1434{ 1435 int count = [fileTypes count]; 1436 for (int n = 0; n < count; n++) { 1437 CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); 1438 RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); 1439 if (!uti) 1440 continue; 1441 RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); 1442 if (mime) 1443 cache.add(mime.get()); 1444 1445 // -movieFileTypes: returns both file extensions and OSTypes. The later are surrounded by single 1446 // quotes, eg. 'MooV', so don't bother looking at those. 1447 if (CFStringGetCharacterAtIndex(ext, 0) != '\'') { 1448 // UTI is missing many media related MIME types supported by QTKit (see rdar://6434168), and not all 1449 // web servers use the MIME type UTI returns for an extension (see rdar://7875393), so even if UTI 1450 // has a type for this extension add any types in hard coded table in the MIME type regsitry. 1451 Vector<String> typesForExtension = MIMETypeRegistry::getMediaMIMETypesForExtension(ext); 1452 unsigned count = typesForExtension.size(); 1453 for (unsigned ndx = 0; ndx < count; ++ndx) { 1454 if (!cache.contains(typesForExtension[ndx])) 1455 cache.add(typesForExtension[ndx]); 1456 } 1457 } 1458 } 1459} 1460 1461static HashSet<String> mimeCommonTypesCache() 1462{ 1463 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); 1464 static bool typeListInitialized = false; 1465 1466 if (!typeListInitialized) { 1467 typeListInitialized = true; 1468 NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; 1469 addFileTypesToCache(fileTypes, cache); 1470 } 1471 1472 return cache; 1473} 1474 1475static HashSet<String> mimeModernTypesCache() 1476{ 1477 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); 1478 static bool typeListInitialized = false; 1479 1480 if (!typeListInitialized) { 1481 typeListInitialized = true; 1482 NSArray* fileTypes = [QTMovie movieFileTypes:(QTMovieFileTypeOptions)wkQTIncludeOnlyModernMediaFileTypes()]; 1483 addFileTypesToCache(fileTypes, cache); 1484 } 1485 1486 return cache; 1487} 1488 1489void MediaPlayerPrivateQTKit::getSupportedTypes(HashSet<String>& supportedTypes) 1490{ 1491 supportedTypes = mimeModernTypesCache(); 1492 1493 // Note: this method starts QTKitServer if it isn't already running when in 64-bit because it has to return the list 1494 // of every MIME type supported by QTKit. 1495 HashSet<String> commonTypes = mimeCommonTypesCache(); 1496 HashSet<String>::const_iterator it = commonTypes.begin(); 1497 HashSet<String>::const_iterator end = commonTypes.end(); 1498 for (; it != end; ++it) 1499 supportedTypes.add(*it); 1500} 1501 1502MediaPlayer::SupportsType MediaPlayerPrivateQTKit::supportsType(const String& type, const String& codecs) 1503{ 1504 // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an 1505 // extended MIME type yet. 1506 1507 // We check the "modern" type cache first, as it doesn't require QTKitServer to start. 1508 if (mimeModernTypesCache().contains(type) || mimeCommonTypesCache().contains(type)) 1509 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; 1510 1511 return MediaPlayer::IsNotSupported; 1512} 1513 1514bool MediaPlayerPrivateQTKit::isAvailable() 1515{ 1516 // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded. 1517 return QTKitLibrary(); 1518} 1519 1520void MediaPlayerPrivateQTKit::getSitesInMediaCache(Vector<String>& sites) 1521{ 1522 NSArray *mediaSites = wkQTGetSitesInMediaDownloadCache(); 1523 for (NSString *site in mediaSites) 1524 sites.append(site); 1525} 1526 1527void MediaPlayerPrivateQTKit::clearMediaCache() 1528{ 1529 wkQTClearMediaDownloadCache(); 1530} 1531 1532void MediaPlayerPrivateQTKit::clearMediaCacheForSite(const String& site) 1533{ 1534 wkQTClearMediaDownloadCacheForSite(site); 1535} 1536 1537void MediaPlayerPrivateQTKit::disableUnsupportedTracks() 1538{ 1539 if (!m_qtMovie) { 1540 m_enabledTrackCount = 0; 1541 m_totalTrackCount = 0; 1542 return; 1543 } 1544 1545 static HashSet<String>* allowedTrackTypes = 0; 1546 if (!allowedTrackTypes) { 1547 allowedTrackTypes = new HashSet<String>; 1548 allowedTrackTypes->add(QTMediaTypeVideo); 1549 allowedTrackTypes->add(QTMediaTypeSound); 1550 allowedTrackTypes->add(QTMediaTypeText); 1551 allowedTrackTypes->add(QTMediaTypeBase); 1552 allowedTrackTypes->add(QTMediaTypeMPEG); 1553 allowedTrackTypes->add("clcp"); // Closed caption 1554 allowedTrackTypes->add("sbtl"); // Subtitle 1555 allowedTrackTypes->add("odsm"); // MPEG-4 object descriptor stream 1556 allowedTrackTypes->add("sdsm"); // MPEG-4 scene description stream 1557 allowedTrackTypes->add("tmcd"); // timecode 1558 allowedTrackTypes->add("tc64"); // timcode-64 1559 allowedTrackTypes->add("tmet"); // timed metadata 1560 } 1561 1562 NSArray *tracks = [m_qtMovie.get() tracks]; 1563 1564 m_totalTrackCount = [tracks count]; 1565 m_enabledTrackCount = m_totalTrackCount; 1566 for (unsigned trackIndex = 0; trackIndex < m_totalTrackCount; trackIndex++) { 1567 // Grab the track at the current index. If there isn't one there, then 1568 // we can move onto the next one. 1569 QTTrack *track = [tracks objectAtIndex:trackIndex]; 1570 if (!track) 1571 continue; 1572 1573 // Check to see if the track is disabled already, we should move along. 1574 // We don't need to re-disable it. 1575 if (![track isEnabled]) { 1576 --m_enabledTrackCount; 1577 continue; 1578 } 1579 1580 // Get the track's media type. 1581 NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute]; 1582 if (!mediaType) 1583 continue; 1584 1585 // Test whether the media type is in our white list. 1586 if (!allowedTrackTypes->contains(mediaType)) { 1587 // If this track type is not allowed, then we need to disable it. 1588 [track setEnabled:NO]; 1589 --m_enabledTrackCount; 1590 m_hasUnsupportedTracks = true; 1591 } 1592 1593 // Disable chapter tracks. These are most likely to lead to trouble, as 1594 // they will be composited under the video tracks, forcing QT to do extra 1595 // work. 1596 QTTrack *chapterTrack = [track performSelector:@selector(chapterlist)]; 1597 if (!chapterTrack) 1598 continue; 1599 1600 // Try to grab the media for the track. 1601 QTMedia *chapterMedia = [chapterTrack media]; 1602 if (!chapterMedia) 1603 continue; 1604 1605 // Grab the media type for this track. 1606 id chapterMediaType = [chapterMedia attributeForKey:QTMediaTypeAttribute]; 1607 if (!chapterMediaType) 1608 continue; 1609 1610 // Check to see if the track is a video track. We don't care about 1611 // other non-video tracks. 1612 if (![chapterMediaType isEqual:QTMediaTypeVideo]) 1613 continue; 1614 1615 // Check to see if the track is already disabled. If it is, we 1616 // should move along. 1617 if (![chapterTrack isEnabled]) 1618 continue; 1619 1620 // Disable the evil, evil track. 1621 [chapterTrack setEnabled:NO]; 1622 --m_enabledTrackCount; 1623 m_hasUnsupportedTracks = true; 1624 } 1625} 1626 1627void MediaPlayerPrivateQTKit::sawUnsupportedTracks() 1628{ 1629 m_hasUnsupportedTracks = true; 1630 m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); 1631} 1632 1633#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 1634bool MediaPlayerPrivateQTKit::supportsAcceleratedRendering() const 1635{ 1636 return isReadyForVideoSetup() && getQTMovieLayerClass() != Nil; 1637} 1638 1639void MediaPlayerPrivateQTKit::acceleratedRenderingStateChanged() 1640{ 1641 // Set up or change the rendering path if necessary. 1642 setUpVideoRendering(); 1643} 1644#endif 1645 1646bool MediaPlayerPrivateQTKit::hasSingleSecurityOrigin() const 1647{ 1648 if (!m_qtMovie) 1649 return false; 1650 1651 RefPtr<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(KURL(wkQTMovieResolvedURL(m_qtMovie.get()))); 1652 RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(m_movieURL); 1653 return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get()); 1654} 1655 1656MediaPlayer::MovieLoadType MediaPlayerPrivateQTKit::movieLoadType() const 1657{ 1658 if (!m_qtMovie) 1659 return MediaPlayer::Unknown; 1660 1661 MediaPlayer::MovieLoadType movieType = (MediaPlayer::MovieLoadType)wkQTMovieGetType(m_qtMovie.get()); 1662 1663 // Can't include WebKitSystemInterface from WebCore so we can't get the enum returned 1664 // by wkQTMovieGetType, but at least verify that the value is in the valid range. 1665 ASSERT(movieType >= MediaPlayer::Unknown && movieType <= MediaPlayer::LiveStream); 1666 1667 return movieType; 1668} 1669 1670void MediaPlayerPrivateQTKit::setPreload(MediaPlayer::Preload preload) 1671{ 1672 m_preload = preload; 1673 if (m_preload == MediaPlayer::None) 1674 return; 1675 1676 if (!m_qtMovie) 1677 resumeLoad(); 1678 else if (m_preload == MediaPlayer::Auto) 1679 [m_qtMovie.get() setAttribute:[NSNumber numberWithBool:NO] forKey:@"QTMovieLimitReadAheadAttribute"]; 1680} 1681 1682float MediaPlayerPrivateQTKit::mediaTimeForTimeValue(float timeValue) const 1683{ 1684 if (!metaDataAvailable()) 1685 return timeValue; 1686 1687 QTTime qttime = createQTTime(timeValue); 1688 return static_cast<float>(qttime.timeValue) / qttime.timeScale; 1689} 1690 1691void MediaPlayerPrivateQTKit::setPrivateBrowsingMode(bool privateBrowsing) 1692{ 1693 m_privateBrowsing = privateBrowsing; 1694 if (!m_qtMovie) 1695 return; 1696 [m_qtMovie.get() setAttribute:[NSNumber numberWithBool:!privateBrowsing] forKey:@"QTMovieAllowPersistentCacheAttribute"]; 1697} 1698 1699} // namespace WebCore 1700 1701@implementation WebCoreMovieObserver 1702 1703- (id)initWithCallback:(MediaPlayerPrivateQTKit*)callback 1704{ 1705 m_callback = callback; 1706 return [super init]; 1707} 1708 1709- (void)disconnect 1710{ 1711 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 1712 m_callback = 0; 1713} 1714 1715-(NSMenu*)menuForEventDelegate:(NSEvent*)theEvent 1716{ 1717 // Get the contextual menu from the QTMovieView's superview, the frame view 1718 return [[m_view superview] menuForEvent:theEvent]; 1719} 1720 1721-(void)setView:(NSView*)view 1722{ 1723 m_view = view; 1724} 1725 1726-(void)repaint 1727{ 1728 if (m_delayCallbacks) 1729 [self performSelector:_cmd withObject:nil afterDelay:0.]; 1730 else if (m_callback) 1731 m_callback->repaint(); 1732} 1733 1734- (void)loadStateChanged:(NSNotification *)unusedNotification 1735{ 1736 UNUSED_PARAM(unusedNotification); 1737 if (m_delayCallbacks) 1738 [self performSelector:_cmd withObject:nil afterDelay:0]; 1739 else 1740 m_callback->loadStateChanged(); 1741} 1742 1743- (void)rateChanged:(NSNotification *)unusedNotification 1744{ 1745 UNUSED_PARAM(unusedNotification); 1746 if (m_delayCallbacks) 1747 [self performSelector:_cmd withObject:nil afterDelay:0]; 1748 else 1749 m_callback->rateChanged(); 1750} 1751 1752- (void)sizeChanged:(NSNotification *)unusedNotification 1753{ 1754 UNUSED_PARAM(unusedNotification); 1755 if (m_delayCallbacks) 1756 [self performSelector:_cmd withObject:nil afterDelay:0]; 1757 else 1758 m_callback->sizeChanged(); 1759} 1760 1761- (void)timeChanged:(NSNotification *)unusedNotification 1762{ 1763 UNUSED_PARAM(unusedNotification); 1764 if (m_delayCallbacks) 1765 [self performSelector:_cmd withObject:nil afterDelay:0]; 1766 else 1767 m_callback->timeChanged(); 1768} 1769 1770- (void)didEnd:(NSNotification *)unusedNotification 1771{ 1772 UNUSED_PARAM(unusedNotification); 1773 if (m_delayCallbacks) 1774 [self performSelector:_cmd withObject:nil afterDelay:0]; 1775 else 1776 m_callback->didEnd(); 1777} 1778 1779- (void)newImageAvailable:(NSNotification *)unusedNotification 1780{ 1781 UNUSED_PARAM(unusedNotification); 1782 [self repaint]; 1783} 1784 1785- (void)layerHostChanged:(NSNotification *)notification 1786{ 1787#if USE(ACCELERATED_COMPOSITING) && !(PLATFORM(QT) && USE(QTKIT)) 1788 CALayer* rootLayer = static_cast<CALayer*>([notification object]); 1789 m_callback->layerHostChanged(rootLayer); 1790#else 1791 UNUSED_PARAM(notification); 1792#endif 1793} 1794 1795- (void)setDelayCallbacks:(BOOL)shouldDelay 1796{ 1797 m_delayCallbacks = shouldDelay; 1798} 1799 1800@end 1801 1802#endif 1803