/* * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #if ENABLE(VIDEO) #import "MediaPlayerPrivateQTKit.h" #ifdef BUILDING_ON_TIGER #import "AutodrainedPool.h" #endif #import "BlockExceptions.h" #import "FrameView.h" #import "GraphicsContext.h" #import "KURL.h" #import "MIMETypeRegistry.h" #import "SoftLinking.h" #import "WebCoreSystemInterface.h" #import #import #import #if DRAW_FRAME_RATE #import "Font.h" #import "Frame.h" #import "Document.h" #import "RenderObject.h" #import "RenderStyle.h" #endif #ifdef BUILDING_ON_TIGER static IMP method_setImplementation(Method m, IMP imp) { IMP result = m->method_imp; m->method_imp = imp; return result; } #endif SOFT_LINK_FRAMEWORK(QTKit) SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale)) SOFT_LINK_CLASS(QTKit, QTMovie) SOFT_LINK_CLASS(QTKit, QTMovieView) SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *) SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *) #ifndef BUILDING_ON_TIGER SOFT_LINK_POINTER(QTKit, QTMovieApertureModeClean, NSString *) SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *) #endif #define QTMovie getQTMovieClass() #define QTMovieView getQTMovieViewClass() #define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute() #define QTMediaTypeAttribute getQTMediaTypeAttribute() #define QTMediaTypeBase getQTMediaTypeBase() #define QTMediaTypeMPEG getQTMediaTypeMPEG() #define QTMediaTypeSound getQTMediaTypeSound() #define QTMediaTypeText getQTMediaTypeText() #define QTMediaTypeVideo getQTMediaTypeVideo() #define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute() #define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute() #define QTMovieDidEndNotification getQTMovieDidEndNotification() #define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute() #define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute() #define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute() #define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification() #define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute() #define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute() #define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute() #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() #define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification() #define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification() #define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute() #define QTMovieURLAttribute getQTMovieURLAttribute() #define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification() #define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute() #define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification() #ifndef BUILDING_ON_TIGER #define QTMovieApertureModeClean getQTMovieApertureModeClean() #define QTMovieApertureModeAttribute getQTMovieApertureModeAttribute() #endif // Older versions of the QTKit header don't have these constants. #if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0 enum { QTMovieLoadStateError = -1L, QTMovieLoadStateLoaded = 2000L, QTMovieLoadStatePlayable = 10000L, QTMovieLoadStatePlaythroughOK = 20000L, QTMovieLoadStateComplete = 100000L }; #endif using namespace WebCore; using namespace std; @interface WebCoreMovieObserver : NSObject { MediaPlayerPrivate* m_callback; NSView* m_view; BOOL m_delayCallbacks; } -(id)initWithCallback:(MediaPlayerPrivate*)callback; -(void)disconnect; -(void)setView:(NSView*)view; -(void)repaint; -(void)setDelayCallbacks:(BOOL)shouldDelay; -(void)loadStateChanged:(NSNotification *)notification; -(void)rateChanged:(NSNotification *)notification; -(void)sizeChanged:(NSNotification *)notification; -(void)timeChanged:(NSNotification *)notification; -(void)didEnd:(NSNotification *)notification; @end @protocol WebKitVideoRenderingDetails -(void)setMovie:(id)movie; -(void)drawInRect:(NSRect)rect; @end namespace WebCore { #ifdef BUILDING_ON_TIGER static const long minimumQuickTimeVersion = 0x07300000; // 7.3 #endif MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) { return new MediaPlayerPrivate(player); } void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) { if (isAvailable()) registrar(create, getSupportedTypes, supportsType); } MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) , m_seekTo(-1) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) , m_visible(false) , m_rect() , m_scaleFactor(1, 1) , m_enabledTrackCount(0) , m_totalTrackCount(0) , m_hasUnsupportedTracks(false) , m_duration(-1.0f) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) , m_timeStoppedPlaying(0) #endif { } MediaPlayerPrivate::~MediaPlayerPrivate() { tearDownVideoRendering(); [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; [m_objcObserver.get() disconnect]; } void MediaPlayerPrivate::createQTMovie(const String& url) { [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()]; if (m_qtMovie) { destroyQTVideoRenderer(); m_qtMovie = 0; } // Disable streaming support for now, if (protocolIs(url, "rtsp")) return; NSURL *cocoaURL = KURL(url); NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: cocoaURL, QTMovieURLAttribute, [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute", #endif #ifndef BUILDING_ON_TIGER QTMovieApertureModeClean, QTMovieApertureModeAttribute, #endif nil]; NSError *error = nil; m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); // FIXME: Find a proper way to detect streaming content. m_isStreaming = protocolIs(url, "rtsp"); if (!m_qtMovie) return; [m_qtMovie.get() setVolume:m_player->volume()]; [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(loadStateChanged:) name:QTMovieLoadStateDidChangeNotification object:m_qtMovie.get()]; // In updateState(), we track when maxTimeLoaded() == duration(). // In newer version of QuickTime, a notification is emitted when maxTimeLoaded changes. // In older version of QuickTime, QTMovieLoadStateDidChangeNotification be fired. if (NSString *maxTimeLoadedChangeNotification = wkQTMovieMaxTimeLoadedChangeNotification()) { [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(loadStateChanged:) name:maxTimeLoadedChangeNotification object:m_qtMovie.get()]; } [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(rateChanged:) name:QTMovieRateDidChangeNotification object:m_qtMovie.get()]; [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(sizeChanged:) name:QTMovieSizeDidChangeNotification object:m_qtMovie.get()]; [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(timeChanged:) name:QTMovieTimeDidChangeNotification object:m_qtMovie.get()]; [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:QTMovieDidEndNotification object:m_qtMovie.get()]; } static void mainThreadSetNeedsDisplay(id self, SEL) { id movieView = [self superview]; ASSERT(!movieView || [movieView isKindOfClass:[QTMovieView class]]); if (!movieView || ![movieView isKindOfClass:[QTMovieView class]]) return; WebCoreMovieObserver* delegate = [movieView delegate]; ASSERT(!delegate || [delegate isKindOfClass:[WebCoreMovieObserver class]]); if (!delegate || ![delegate isKindOfClass:[WebCoreMovieObserver class]]) return; [delegate repaint]; } static Class QTVideoRendererClass() { static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly"); return QTVideoRendererWebKitOnlyClass; } void MediaPlayerPrivate::createQTMovieView() { detachQTMovieView(); static bool addedCustomMethods = false; if (!m_player->inMediaDocument() && !addedCustomMethods) { Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); ASSERT(QTMovieContentViewClass); Method mainThreadSetNeedsDisplayMethod = class_getInstanceMethod(QTMovieContentViewClass, @selector(_mainThreadSetNeedsDisplay)); ASSERT(mainThreadSetNeedsDisplayMethod); method_setImplementation(mainThreadSetNeedsDisplayMethod, reinterpret_cast(mainThreadSetNeedsDisplay)); addedCustomMethods = true; } // delay callbacks as we *will* get notifications during setup [m_objcObserver.get() setDelayCallbacks:YES]; m_qtMovieView.adoptNS([[QTMovieView alloc] init]); setSize(m_player->size()); NSView* parentView = m_player->frameView()->documentView(); [parentView addSubview:m_qtMovieView.get()]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:m_objcObserver.get()]; #else [m_qtMovieView.get() setDelegate:m_objcObserver.get()]; #endif [m_objcObserver.get() setView:m_qtMovieView.get()]; [m_qtMovieView.get() setMovie:m_qtMovie.get()]; [m_qtMovieView.get() setControllerVisible:NO]; [m_qtMovieView.get() setPreservesAspectRatio:NO]; // the area not covered by video should be transparent [m_qtMovieView.get() setFillColor:[NSColor clearColor]]; // If we're in a media document, allow QTMovieView to render in its default mode; // otherwise tell it to draw synchronously. // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. if (!m_player->inMediaDocument()) wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::detachQTMovieView() { if (m_qtMovieView) { [m_objcObserver.get() setView:nil]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:nil]; #else [m_qtMovieView.get() setDelegate:nil]; #endif [m_qtMovieView.get() removeFromSuperview]; m_qtMovieView = nil; } } void MediaPlayerPrivate::createQTVideoRenderer() { destroyQTVideoRenderer(); m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]); if (!m_qtVideoRenderer) return; // associate our movie with our instance of QTVideoRendererWebKitOnly [(id)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()]; // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(newImageAvailable:) name:QTVideoRendererWebKitOnlyNewImageAvailableNotification object:m_qtVideoRenderer.get()]; } void MediaPlayerPrivate::destroyQTVideoRenderer() { if (!m_qtVideoRenderer) return; // stop observing the renderer's notifications before we toss it [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get() name:QTVideoRendererWebKitOnlyNewImageAvailableNotification object:m_qtVideoRenderer.get()]; // disassociate our movie from our instance of QTVideoRendererWebKitOnly [(id)m_qtVideoRenderer.get() setMovie:nil]; m_qtVideoRenderer = nil; } void MediaPlayerPrivate::setUpVideoRendering() { if (!m_player->frameView() || !m_qtMovie) return; if (m_player->inMediaDocument() || !QTVideoRendererClass() ) createQTMovieView(); else createQTVideoRenderer(); } void MediaPlayerPrivate::tearDownVideoRendering() { if (m_qtMovieView) detachQTMovieView(); else destroyQTVideoRenderer(); } QTTime MediaPlayerPrivate::createQTTime(float time) const { if (!metaDataAvailable()) return QTMakeTime(0, 600); long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; return QTMakeTime(time * timeScale, timeScale); } void MediaPlayerPrivate::load(const String& url) { if (m_networkState != MediaPlayer::Loading) { m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } if (m_readyState != MediaPlayer::HaveNothing) { m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); [m_objcObserver.get() setDelayCallbacks:YES]; createQTMovie(url); [m_objcObserver.get() loadStateChanged:nil]; [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::play() { if (!metaDataAvailable()) return; m_startedPlaying = true; #if DRAW_FRAME_RATE m_frameCountWhilePlaying = 0; #endif [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() setRate:m_player->rate()]; [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::pause() { if (!metaDataAvailable()) return; m_startedPlaying = false; #if DRAW_FRAME_RATE m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; #endif [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() stop]; [m_objcObserver.get() setDelayCallbacks:NO]; } float MediaPlayerPrivate::duration() const { if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() duration]; if (time.flags == kQTTimeIsIndefinite) return numeric_limits::infinity(); return static_cast(time.timeValue) / time.timeScale; } float MediaPlayerPrivate::currentTime() const { if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() currentTime]; return static_cast(time.timeValue) / time.timeScale; } void MediaPlayerPrivate::seek(float time) { cancelSeek(); if (!metaDataAvailable()) return; if (time > duration()) time = duration(); m_seekTo = time; if (maxTimeLoaded() >= m_seekTo) doSeek(); else m_seekTimer.start(0, 0.5f); } void MediaPlayerPrivate::doSeek() { QTTime qttime = createQTTime(m_seekTo); // setCurrentTime generates several event callbacks, update afterwards [m_objcObserver.get() setDelayCallbacks:YES]; float oldRate = [m_qtMovie.get() rate]; [m_qtMovie.get() setRate:0]; [m_qtMovie.get() setCurrentTime:qttime]; float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; cancelSeek(); [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::cancelSeek() { m_seekTo = -1; m_seekTimer.stop(); } void MediaPlayerPrivate::seekTimerFired(Timer*) { if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { cancelSeek(); updateStates(); m_player->timeChanged(); return; } if (maxTimeLoaded() >= m_seekTo) doSeek(); else { MediaPlayer::NetworkState state = networkState(); if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { cancelSeek(); updateStates(); m_player->timeChanged(); } } } void MediaPlayerPrivate::setEndTime(float) { } bool MediaPlayerPrivate::paused() const { if (!metaDataAvailable()) return true; return [m_qtMovie.get() rate] == 0; } bool MediaPlayerPrivate::seeking() const { if (!metaDataAvailable()) return false; return m_seekTo >= 0; } IntSize MediaPlayerPrivate::naturalSize() const { if (!metaDataAvailable()) return IntSize(); // In spite of the name of this method, return QTMovieNaturalSizeAttribute transformed by the // initial movie scale because the spec says intrinsic size is: // // ... the dimensions of the resource in CSS pixels after taking into account the resource's // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the // format used by the resource NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; return IntSize(naturalSize.width * m_scaleFactor.width(), naturalSize.height * m_scaleFactor.height()); } bool MediaPlayerPrivate::hasVideo() const { if (!metaDataAvailable()) return false; return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; } void MediaPlayerPrivate::setVolume(float volume) { if (!metaDataAvailable()) return; [m_qtMovie.get() setVolume:volume]; } void MediaPlayerPrivate::setRate(float rate) { if (!metaDataAvailable()) return; if (!paused()) [m_qtMovie.get() setRate:rate]; } int MediaPlayerPrivate::dataRate() const { if (!metaDataAvailable()) return 0; return wkQTMovieDataRate(m_qtMovie.get()); } float MediaPlayerPrivate::maxTimeBuffered() const { // rtsp streams are not buffered return m_isStreaming ? 0 : maxTimeLoaded(); } float MediaPlayerPrivate::maxTimeSeekable() const { if (!metaDataAvailable()) return 0; // infinite duration means live stream if (isinf(duration())) return 0; return wkQTMovieMaxTimeSeekable(m_qtMovie.get()); } float MediaPlayerPrivate::maxTimeLoaded() const { if (!metaDataAvailable()) return 0; return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); } unsigned MediaPlayerPrivate::bytesLoaded() const { float dur = duration(); if (!dur) return 0; return totalBytes() * maxTimeLoaded() / dur; } bool MediaPlayerPrivate::totalBytesKnown() const { return totalBytes() > 0; } unsigned MediaPlayerPrivate::totalBytes() const { if (!metaDataAvailable()) return 0; return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; } void MediaPlayerPrivate::cancelLoad() { // FIXME: Is there a better way to check for this? if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) return; tearDownVideoRendering(); m_qtMovie = nil; updateStates(); } void MediaPlayerPrivate::cacheMovieScale() { NSSize initialSize = NSZeroSize; NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) // QTMovieCurrentSizeAttribute is not allowed with instances of QTMovie that have been // opened with QTMovieOpenForPlaybackAttribute, so ask for the display transform attribute instead. NSAffineTransform *displayTransform = [m_qtMovie.get() attributeForKey:@"QTMoviePreferredTransformAttribute"]; if (displayTransform) initialSize = [displayTransform transformSize:naturalSize]; else { initialSize.width = naturalSize.width; initialSize.height = naturalSize.height; } #else initialSize = [[m_qtMovie.get() attributeForKey:QTMovieCurrentSizeAttribute] sizeValue]; #endif if (naturalSize.width) m_scaleFactor.setWidth(initialSize.width / naturalSize.width); if (naturalSize.height) m_scaleFactor.setHeight(initialSize.height / naturalSize.height); } void MediaPlayerPrivate::updateStates() { MediaPlayer::NetworkState oldNetworkState = m_networkState; MediaPlayer::ReadyState oldReadyState = m_readyState; long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast(QTMovieLoadStateError); if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { disableUnsupportedTracks(); if (m_player->inMediaDocument()) { if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) { // This is a type of media that we do not handle directly with a