| /* |
| * Copyright (C) 2007, 2008, 2009, 2010, 2011 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. |
| */ |
| #include "config.h" |
| |
| #include "QTMovie.h" |
| |
| #include "QTMovieTask.h" |
| #include "QTMovieWinTimer.h" |
| #include <FixMath.h> |
| #include <GXMath.h> |
| #include <Movies.h> |
| #include <QTML.h> |
| #include <QuickTimeComponents.h> |
| #include <WebKitSystemInterface/WebKitSystemInterface.h> |
| #include <wtf/Assertions.h> |
| #include <wtf/MathExtras.h> |
| #include <wtf/Noncopyable.h> |
| #include <wtf/Vector.h> |
| |
| using namespace std; |
| |
| static const long minimumQuickTimeVersion = 0x07300000; // 7.3 |
| |
| static const long closedCaptionTrackType = 'clcp'; |
| static const long subTitleTrackType = 'sbtl'; |
| static const long mpeg4ObjectDescriptionTrackType = 'odsm'; |
| static const long mpeg4SceneDescriptionTrackType = 'sdsm'; |
| static const long closedCaptionDisplayPropertyID = 'disp'; |
| |
| // Resizing GWorlds is slow, give them a minimum size so size of small |
| // videos can be animated smoothly |
| static const int cGWorldMinWidth = 640; |
| static const int cGWorldMinHeight = 360; |
| |
| static const float cNonContinuousTimeChange = 0.2f; |
| |
| union UppParam { |
| long longValue; |
| void* ptr; |
| }; |
| |
| static CFMutableArrayRef gSupportedTypes = 0; |
| static SInt32 quickTimeVersion = 0; |
| |
| class QTMoviePrivate : public QTMovieTaskClient { |
| WTF_MAKE_NONCOPYABLE(QTMoviePrivate); |
| public: |
| QTMoviePrivate(); |
| ~QTMoviePrivate(); |
| void task(); |
| void startTask(); |
| void endTask(); |
| |
| void createMovieController(); |
| void cacheMovieScale(); |
| |
| QTMovie* m_movieWin; |
| Movie m_movie; |
| MovieController m_movieController; |
| bool m_tasking; |
| bool m_disabled; |
| Vector<QTMovieClient*> m_clients; |
| long m_loadState; |
| bool m_ended; |
| bool m_seeking; |
| float m_lastMediaTime; |
| double m_lastLoadStateCheckTime; |
| int m_width; |
| int m_height; |
| bool m_visible; |
| long m_loadError; |
| float m_widthScaleFactor; |
| float m_heightScaleFactor; |
| CFURLRef m_currentURL; |
| float m_timeToRestore; |
| float m_rateToRestore; |
| bool m_privateBrowsing; |
| #if !ASSERT_DISABLED |
| bool m_scaleCached; |
| #endif |
| }; |
| |
| QTMoviePrivate::QTMoviePrivate() |
| : m_movieWin(0) |
| , m_movie(0) |
| , m_movieController(0) |
| , m_tasking(false) |
| , m_loadState(0) |
| , m_ended(false) |
| , m_seeking(false) |
| , m_lastMediaTime(0) |
| , m_lastLoadStateCheckTime(0) |
| , m_width(0) |
| , m_height(0) |
| , m_visible(false) |
| , m_loadError(0) |
| , m_widthScaleFactor(1) |
| , m_heightScaleFactor(1) |
| , m_currentURL(0) |
| , m_timeToRestore(-1.0f) |
| , m_rateToRestore(-1.0f) |
| , m_disabled(false) |
| , m_privateBrowsing(false) |
| #if !ASSERT_DISABLED |
| , m_scaleCached(false) |
| #endif |
| { |
| } |
| |
| QTMoviePrivate::~QTMoviePrivate() |
| { |
| endTask(); |
| if (m_movieController) |
| DisposeMovieController(m_movieController); |
| if (m_movie) |
| DisposeMovie(m_movie); |
| if (m_currentURL) |
| CFRelease(m_currentURL); |
| } |
| |
| void QTMoviePrivate::startTask() |
| { |
| if (!m_tasking) { |
| QTMovieTask::sharedTask()->addTaskClient(this); |
| m_tasking = true; |
| } |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| void QTMoviePrivate::endTask() |
| { |
| if (m_tasking) { |
| QTMovieTask::sharedTask()->removeTaskClient(this); |
| m_tasking = false; |
| } |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| void QTMoviePrivate::task() |
| { |
| ASSERT(m_tasking); |
| |
| if (!m_loadError) { |
| if (m_movieController) |
| MCIdle(m_movieController); |
| else |
| MoviesTask(m_movie, 0); |
| } |
| |
| // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second. |
| if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) { |
| // If load fails QT's load state is QTMovieLoadStateComplete. |
| // This is different from QTKit API and seems strange. |
| long loadState = m_loadError ? QTMovieLoadStateError : GetMovieLoadState(m_movie); |
| if (loadState != m_loadState) { |
| // we only need to erase the movie gworld when the load state changes to loaded while it |
| // is visible as the gworld is destroyed/created when visibility changes |
| bool shouldRestorePlaybackState = false; |
| bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded; |
| m_loadState = loadState; |
| if (movieNewlyPlayable) { |
| cacheMovieScale(); |
| shouldRestorePlaybackState = true; |
| } |
| |
| if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded) |
| createMovieController(); |
| |
| for (size_t i = 0; i < m_clients.size(); ++i) |
| m_clients[i]->movieLoadStateChanged(m_movieWin); |
| |
| if (shouldRestorePlaybackState && m_timeToRestore != -1.0f) { |
| m_movieWin->setCurrentTime(m_timeToRestore); |
| m_timeToRestore = -1.0f; |
| m_movieWin->setRate(m_rateToRestore); |
| m_rateToRestore = -1.0f; |
| } |
| |
| if (m_disabled) { |
| endTask(); |
| return; |
| } |
| } |
| m_lastLoadStateCheckTime = systemTime(); |
| } |
| |
| bool ended = !!IsMovieDone(m_movie); |
| if (ended != m_ended) { |
| m_ended = ended; |
| if (ended) { |
| for (size_t i = 0; i < m_clients.size(); ++i) |
| m_clients[i]->movieEnded(m_movieWin); |
| } |
| } |
| |
| float time = m_movieWin->currentTime(); |
| if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) { |
| m_seeking = false; |
| for (size_t i = 0; i < m_clients.size(); ++i) |
| m_clients[i]->movieTimeChanged(m_movieWin); |
| } |
| m_lastMediaTime = time; |
| |
| if (m_loadError) |
| endTask(); |
| else |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| void QTMoviePrivate::createMovieController() |
| { |
| Rect bounds; |
| long flags; |
| |
| if (!m_movie) |
| return; |
| |
| if (m_movieController) |
| DisposeMovieController(m_movieController); |
| |
| GetMovieBox(m_movie, &bounds); |
| flags = mcTopLeftMovie | mcNotVisible; |
| m_movieController = NewMovieController(m_movie, &bounds, flags); |
| if (!m_movieController) |
| return; |
| |
| // Disable automatic looping. |
| MCDoAction(m_movieController, mcActionSetLooping, 0); |
| } |
| |
| void QTMoviePrivate::cacheMovieScale() |
| { |
| Rect naturalRect; |
| Rect initialRect; |
| |
| GetMovieNaturalBoundsRect(m_movie, &naturalRect); |
| GetMovieBox(m_movie, &initialRect); |
| |
| float naturalWidth = naturalRect.right - naturalRect.left; |
| float naturalHeight = naturalRect.bottom - naturalRect.top; |
| |
| if (naturalWidth) |
| m_widthScaleFactor = (initialRect.right - initialRect.left) / naturalWidth; |
| if (naturalHeight) |
| m_heightScaleFactor = (initialRect.bottom - initialRect.top) / naturalHeight; |
| #if !ASSERT_DISABLED |
| m_scaleCached = true; |
| #endif |
| } |
| |
| QTMovie::QTMovie(QTMovieClient* client) |
| : m_private(new QTMoviePrivate()) |
| { |
| m_private->m_movieWin = this; |
| if (client) |
| m_private->m_clients.append(client); |
| initializeQuickTime(); |
| } |
| |
| QTMovie::~QTMovie() |
| { |
| delete m_private; |
| } |
| |
| void QTMovie::disableComponent(uint32_t cd[5]) |
| { |
| ComponentDescription nullDesc = {'null', 'base', kAppleManufacturer, 0, 0}; |
| Component nullComp = FindNextComponent(0, &nullDesc); |
| Component disabledComp = 0; |
| |
| while (disabledComp = FindNextComponent(disabledComp, (ComponentDescription*)&cd[0])) |
| CaptureComponent(disabledComp, nullComp); |
| } |
| |
| void QTMovie::addClient(QTMovieClient* client) |
| { |
| if (client) |
| m_private->m_clients.append(client); |
| } |
| |
| void QTMovie::removeClient(QTMovieClient* client) |
| { |
| size_t indexOfClient = m_private->m_clients.find(client); |
| if (indexOfClient != notFound) |
| m_private->m_clients.remove(indexOfClient); |
| } |
| |
| void QTMovie::play() |
| { |
| m_private->m_timeToRestore = -1.0f; |
| |
| if (m_private->m_movieController) |
| MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie)); |
| else |
| StartMovie(m_private->m_movie); |
| m_private->startTask(); |
| } |
| |
| void QTMovie::pause() |
| { |
| m_private->m_timeToRestore = -1.0f; |
| |
| if (m_private->m_movieController) |
| MCDoAction(m_private->m_movieController, mcActionPlay, 0); |
| else |
| StopMovie(m_private->m_movie); |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| float QTMovie::rate() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| return FixedToFloat(GetMovieRate(m_private->m_movie)); |
| } |
| |
| void QTMovie::setRate(float rate) |
| { |
| if (!m_private->m_movie) |
| return; |
| m_private->m_timeToRestore = -1.0f; |
| |
| if (m_private->m_movieController) |
| MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); |
| else |
| SetMovieRate(m_private->m_movie, FloatToFixed(rate)); |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| float QTMovie::duration() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| TimeValue val = GetMovieDuration(m_private->m_movie); |
| TimeScale scale = GetMovieTimeScale(m_private->m_movie); |
| return static_cast<float>(val) / scale; |
| } |
| |
| float QTMovie::currentTime() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| TimeValue val = GetMovieTime(m_private->m_movie, 0); |
| TimeScale scale = GetMovieTimeScale(m_private->m_movie); |
| return static_cast<float>(val) / scale; |
| } |
| |
| void QTMovie::setCurrentTime(float time) const |
| { |
| if (!m_private->m_movie) |
| return; |
| |
| m_private->m_timeToRestore = -1.0f; |
| |
| m_private->m_seeking = true; |
| TimeScale scale = GetMovieTimeScale(m_private->m_movie); |
| if (m_private->m_movieController) { |
| QTRestartAtTimeRecord restart = { lroundf(time * scale) , 0 }; |
| MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart); |
| } else |
| SetMovieTimeValue(m_private->m_movie, TimeValue(lroundf(time * scale))); |
| QTMovieTask::sharedTask()->updateTaskTimer(); |
| } |
| |
| void QTMovie::setVolume(float volume) |
| { |
| if (!m_private->m_movie) |
| return; |
| SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256)); |
| } |
| |
| void QTMovie::setPreservesPitch(bool preservesPitch) |
| { |
| if (!m_private->m_movie || !m_private->m_currentURL) |
| return; |
| |
| OSErr error; |
| bool prop = false; |
| |
| error = QTGetMovieProperty(m_private->m_movie, kQTPropertyClass_Audio, kQTAudioPropertyID_RateChangesPreservePitch, |
| sizeof(kQTAudioPropertyID_RateChangesPreservePitch), static_cast<QTPropertyValuePtr>(&prop), 0); |
| |
| if (error || prop == preservesPitch) |
| return; |
| |
| m_private->m_timeToRestore = currentTime(); |
| m_private->m_rateToRestore = rate(); |
| load(m_private->m_currentURL, preservesPitch); |
| } |
| |
| unsigned QTMovie::dataSize() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie)); |
| } |
| |
| float QTMovie::maxTimeLoaded() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| TimeValue val; |
| GetMaxLoadedTimeInMovie(m_private->m_movie, &val); |
| TimeScale scale = GetMovieTimeScale(m_private->m_movie); |
| return static_cast<float>(val) / scale; |
| } |
| |
| long QTMovie::loadState() const |
| { |
| return m_private->m_loadState; |
| } |
| |
| void QTMovie::getNaturalSize(int& width, int& height) |
| { |
| Rect rect = { 0, }; |
| |
| if (m_private->m_movie) |
| GetMovieNaturalBoundsRect(m_private->m_movie, &rect); |
| width = (rect.right - rect.left) * m_private->m_widthScaleFactor; |
| height = (rect.bottom - rect.top) * m_private->m_heightScaleFactor; |
| } |
| |
| void QTMovie::loadPath(const UChar* url, int len, bool preservesPitch) |
| { |
| CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len); |
| CFURLRef cfURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlStringRef, kCFURLWindowsPathStyle, false); |
| |
| load(cfURL, preservesPitch); |
| |
| if (cfURL) |
| CFRelease(cfURL); |
| if (urlStringRef) |
| CFRelease(urlStringRef); |
| } |
| |
| void QTMovie::load(const UChar* url, int len, bool preservesPitch) |
| { |
| CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len); |
| CFURLRef cfURL = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0); |
| |
| load(cfURL, preservesPitch); |
| |
| if (cfURL) |
| CFRelease(cfURL); |
| if (urlStringRef) |
| CFRelease(urlStringRef); |
| } |
| |
| void QTMovie::load(CFURLRef url, bool preservesPitch) |
| { |
| if (!url) |
| return; |
| |
| if (m_private->m_movie) { |
| m_private->endTask(); |
| if (m_private->m_movieController) |
| DisposeMovieController(m_private->m_movieController); |
| m_private->m_movieController = 0; |
| DisposeMovie(m_private->m_movie); |
| m_private->m_movie = 0; |
| m_private->m_loadState = 0; |
| } |
| |
| // Define a property array for NewMovieFromProperties. |
| QTNewMoviePropertyElement movieProps[9]; |
| ItemCount moviePropCount = 0; |
| |
| bool boolTrue = true; |
| |
| // Disable streaming support for now. |
| CFStringRef scheme = CFURLCopyScheme(url); |
| bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:")); |
| CFRelease(scheme); |
| |
| if (isRTSP) { |
| m_private->m_loadError = noMovieFound; |
| goto end; |
| } |
| |
| if (m_private->m_currentURL) { |
| if (m_private->m_currentURL != url) { |
| CFRelease(m_private->m_currentURL); |
| m_private->m_currentURL = url; |
| CFRetain(url); |
| } |
| } else { |
| m_private->m_currentURL = url; |
| CFRetain(url); |
| } |
| |
| // Add the movie data location to the property array |
| movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation; |
| movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL; |
| movieProps[moviePropCount].propValueSize = sizeof(m_private->m_currentURL); |
| movieProps[moviePropCount].propValueAddress = &(m_private->m_currentURL); |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; |
| movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; |
| movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_AsyncOK; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; |
| movieProps[moviePropCount].propID = kQTNewMoviePropertyID_Active; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; |
| movieProps[moviePropCount].propID = kQTNewMoviePropertyID_DontInteractWithUser; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; |
| movieProps[moviePropCount].propID = '!url'; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; |
| movieProps[moviePropCount].propID = 'site'; |
| movieProps[moviePropCount].propValueSize = sizeof(boolTrue); |
| movieProps[moviePropCount].propValueAddress = &boolTrue; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| movieProps[moviePropCount].propClass = kQTPropertyClass_Audio; |
| movieProps[moviePropCount].propID = kQTAudioPropertyID_RateChangesPreservePitch; |
| movieProps[moviePropCount].propValueSize = sizeof(preservesPitch); |
| movieProps[moviePropCount].propValueAddress = &preservesPitch; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| bool allowCaching = !m_private->m_privateBrowsing; |
| movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; |
| movieProps[moviePropCount].propID = 'pers'; |
| movieProps[moviePropCount].propValueSize = sizeof(allowCaching); |
| movieProps[moviePropCount].propValueAddress = &allowCaching; |
| movieProps[moviePropCount].propStatus = 0; |
| moviePropCount++; |
| |
| ASSERT(moviePropCount <= WTF_ARRAY_LENGTH(movieProps)); |
| m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie); |
| |
| end: |
| m_private->startTask(); |
| // get the load fail callback quickly |
| if (m_private->m_loadError) |
| QTMovieTask::sharedTask()->updateTaskTimer(0); |
| else { |
| OSType mode = kQTApertureMode_CleanAperture; |
| |
| // Set the aperture mode property on a movie to signal that we want aspect ratio |
| // and clean aperture dimensions. Don't worry about errors, we can't do anything if |
| // the installed version of QT doesn't support it and it isn't serious enough to |
| // warrant failing. |
| QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode); |
| } |
| } |
| |
| void QTMovie::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount) |
| { |
| if (!m_private->m_movie) { |
| totalTrackCount = 0; |
| enabledTrackCount = 0; |
| return; |
| } |
| |
| static HashSet<OSType>* allowedTrackTypes = 0; |
| if (!allowedTrackTypes) { |
| allowedTrackTypes = new HashSet<OSType>; |
| allowedTrackTypes->add(VideoMediaType); |
| allowedTrackTypes->add(SoundMediaType); |
| allowedTrackTypes->add(TextMediaType); |
| allowedTrackTypes->add(BaseMediaType); |
| allowedTrackTypes->add(closedCaptionTrackType); |
| allowedTrackTypes->add(subTitleTrackType); |
| allowedTrackTypes->add(mpeg4ObjectDescriptionTrackType); |
| allowedTrackTypes->add(mpeg4SceneDescriptionTrackType); |
| allowedTrackTypes->add(TimeCodeMediaType); |
| allowedTrackTypes->add(TimeCode64MediaType); |
| } |
| |
| long trackCount = GetMovieTrackCount(m_private->m_movie); |
| enabledTrackCount = trackCount; |
| totalTrackCount = trackCount; |
| |
| // Track indexes are 1-based. yuck. These things must descend from old- |
| // school mac resources or something. |
| for (long trackIndex = 1; trackIndex <= trackCount; trackIndex++) { |
| // Grab the track at the current index. If there isn't one there, then |
| // we can move onto the next one. |
| Track currentTrack = GetMovieIndTrack(m_private->m_movie, trackIndex); |
| if (!currentTrack) |
| continue; |
| |
| // Check to see if the track is disabled already, we should move along. |
| // We don't need to re-disable it. |
| if (!GetTrackEnabled(currentTrack)) |
| continue; |
| |
| // Grab the track's media. We're going to check to see if we need to |
| // disable the tracks. They could be unsupported. |
| Media trackMedia = GetTrackMedia(currentTrack); |
| if (!trackMedia) |
| continue; |
| |
| // Grab the media type for this track. Make sure that we don't |
| // get an error in doing so. If we do, then something really funky is |
| // wrong. |
| OSType mediaType; |
| GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil); |
| OSErr mediaErr = GetMoviesError(); |
| if (mediaErr != noErr) |
| continue; |
| |
| if (!allowedTrackTypes->contains(mediaType)) { |
| |
| // Different mpeg variants import as different track types so check for the "mpeg |
| // characteristic" instead of hard coding the (current) list of mpeg media types. |
| if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly)) |
| continue; |
| |
| SetTrackEnabled(currentTrack, false); |
| --enabledTrackCount; |
| } |
| |
| // Grab the track reference count for chapters. This will tell us if it |
| // has chapter tracks in it. If there aren't any references, then we |
| // can move on the next track. |
| long referenceCount = GetTrackReferenceCount(currentTrack, kTrackReferenceChapterList); |
| if (referenceCount <= 0) |
| continue; |
| |
| long referenceIndex = 0; |
| while (1) { |
| // If we get nothing here, we've overstepped our bounds and can stop |
| // looking. Chapter indices here are 1-based as well - hence, the |
| // pre-increment. |
| referenceIndex++; |
| Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex); |
| if (!chapterTrack) |
| break; |
| |
| // Try to grab the media for the track. |
| Media chapterMedia = GetTrackMedia(chapterTrack); |
| if (!chapterMedia) |
| continue; |
| |
| // Grab the media type for this track. Make sure that we don't |
| // get an error in doing so. If we do, then something really |
| // funky is wrong. |
| OSType mediaType; |
| GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil); |
| OSErr mediaErr = GetMoviesError(); |
| if (mediaErr != noErr) |
| continue; |
| |
| // Check to see if the track is a video track. We don't care about |
| // other non-video tracks. |
| if (mediaType != VideoMediaType) |
| continue; |
| |
| // Check to see if the track is already disabled. If it is, we |
| // should move along. |
| if (!GetTrackEnabled(chapterTrack)) |
| continue; |
| |
| // Disabled the evil, evil track. |
| SetTrackEnabled(chapterTrack, false); |
| --enabledTrackCount; |
| } |
| } |
| } |
| |
| bool QTMovie::isDisabled() const |
| { |
| return m_private->m_disabled; |
| } |
| |
| void QTMovie::setDisabled(bool b) |
| { |
| m_private->m_disabled = b; |
| } |
| |
| |
| bool QTMovie::hasVideo() const |
| { |
| if (!m_private->m_movie) |
| return false; |
| return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)); |
| } |
| |
| bool QTMovie::hasAudio() const |
| { |
| if (!m_private->m_movie) |
| return false; |
| return (GetMovieIndTrackType(m_private->m_movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)); |
| } |
| |
| QTTrackArray QTMovie::videoTracks() const |
| { |
| QTTrackArray tracks; |
| long trackIndex = 1; |
| |
| while (Track theTrack = GetMovieIndTrackType(m_private->m_movie, trackIndex++, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly)) |
| tracks.append(QTTrack::create(theTrack)); |
| |
| return tracks; |
| } |
| |
| bool QTMovie::hasClosedCaptions() const |
| { |
| if (!m_private->m_movie) |
| return false; |
| return GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType); |
| } |
| |
| void QTMovie::setClosedCaptionsVisible(bool visible) |
| { |
| if (!m_private->m_movie) |
| return; |
| |
| Track ccTrack = GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType); |
| if (!ccTrack) |
| return; |
| |
| Boolean doDisplay = visible; |
| QTSetTrackProperty(ccTrack, closedCaptionTrackType, closedCaptionDisplayPropertyID, sizeof(doDisplay), &doDisplay); |
| } |
| |
| long QTMovie::timeScale() const |
| { |
| if (!m_private->m_movie) |
| return 0; |
| |
| return GetMovieTimeScale(m_private->m_movie); |
| } |
| |
| static void getMIMETypeCallBack(const char* type); |
| |
| static void initializeSupportedTypes() |
| { |
| if (gSupportedTypes) |
| return; |
| |
| gSupportedTypes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); |
| if (quickTimeVersion < minimumQuickTimeVersion) { |
| LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion); |
| return; |
| } |
| |
| // QuickTime doesn't have an importer for video/quicktime. Add it manually. |
| CFArrayAppendValue(gSupportedTypes, CFSTR("video/quicktime")); |
| |
| wkGetQuickTimeMIMETypeList(getMIMETypeCallBack); |
| } |
| |
| static void getMIMETypeCallBack(const char* type) |
| { |
| ASSERT(type); |
| CFStringRef cfType = CFStringCreateWithCString(kCFAllocatorDefault, type, kCFStringEncodingMacRoman); |
| if (!cfType) |
| return; |
| |
| // Filter out all non-audio or -video MIME Types, and only add each type once: |
| if (CFStringHasPrefix(cfType, CFSTR("audio/")) || CFStringHasPrefix(cfType, CFSTR("video/"))) { |
| CFRange range = CFRangeMake(0, CFArrayGetCount(gSupportedTypes)); |
| if (!CFArrayContainsValue(gSupportedTypes, range, cfType)) |
| CFArrayAppendValue(gSupportedTypes, cfType); |
| } |
| |
| CFRelease(cfType); |
| } |
| |
| unsigned QTMovie::countSupportedTypes() |
| { |
| initializeSupportedTypes(); |
| return static_cast<unsigned>(CFArrayGetCount(gSupportedTypes)); |
| } |
| |
| void QTMovie::getSupportedType(unsigned index, const UChar*& str, unsigned& len) |
| { |
| initializeSupportedTypes(); |
| ASSERT(index < CFArrayGetCount(gSupportedTypes)); |
| |
| // Allocate sufficient buffer to hold any MIME type |
| static UniChar* staticBuffer = 0; |
| if (!staticBuffer) |
| staticBuffer = new UniChar[32]; |
| |
| CFStringRef cfstr = (CFStringRef)CFArrayGetValueAtIndex(gSupportedTypes, index); |
| len = CFStringGetLength(cfstr); |
| CFRange range = { 0, len }; |
| CFStringGetCharacters(cfstr, range, staticBuffer); |
| str = reinterpret_cast<const UChar*>(staticBuffer); |
| |
| } |
| |
| CGAffineTransform QTMovie::getTransform() const |
| { |
| ASSERT(m_private->m_movie); |
| MatrixRecord m = {0}; |
| GetMovieMatrix(m_private->m_movie, &m); |
| |
| ASSERT(!m.matrix[0][2]); |
| ASSERT(!m.matrix[1][2]); |
| CGAffineTransform transform = CGAffineTransformMake( |
| Fix2X(m.matrix[0][0]), |
| Fix2X(m.matrix[0][1]), |
| Fix2X(m.matrix[1][0]), |
| Fix2X(m.matrix[1][1]), |
| Fix2X(m.matrix[2][0]), |
| Fix2X(m.matrix[2][1])); |
| return transform; |
| } |
| |
| void QTMovie::setTransform(CGAffineTransform t) |
| { |
| ASSERT(m_private->m_movie); |
| MatrixRecord m = {{ |
| {X2Fix(t.a), X2Fix(t.b), 0}, |
| {X2Fix(t.c), X2Fix(t.d), 0}, |
| {X2Fix(t.tx), X2Fix(t.ty), fract1}, |
| }}; |
| |
| SetMovieMatrix(m_private->m_movie, &m); |
| m_private->cacheMovieScale(); |
| } |
| |
| void QTMovie::resetTransform() |
| { |
| ASSERT(m_private->m_movie); |
| SetMovieMatrix(m_private->m_movie, 0); |
| m_private->cacheMovieScale(); |
| } |
| |
| void QTMovie::setPrivateBrowsingMode(bool privateBrowsing) |
| { |
| m_private->m_privateBrowsing = privateBrowsing; |
| if (m_private->m_movie) { |
| bool allowCaching = !m_private->m_privateBrowsing; |
| QTSetMovieProperty(m_private->m_movie, 'cach', 'pers', sizeof(allowCaching), &allowCaching); |
| } |
| } |
| |
| bool QTMovie::initializeQuickTime() |
| { |
| static bool initialized = false; |
| static bool initializationSucceeded = false; |
| if (!initialized) { |
| initialized = true; |
| // Initialize and check QuickTime version |
| OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface); |
| if (result == noErr) |
| result = Gestalt(gestaltQuickTime, &quickTimeVersion); |
| if (result != noErr) { |
| LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support."); |
| return false; |
| } |
| if (quickTimeVersion < minimumQuickTimeVersion) { |
| LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion); |
| return false; |
| } |
| EnterMovies(); |
| initializationSucceeded = true; |
| } |
| return initializationSucceeded; |
| } |
| |
| Movie QTMovie::getMovieHandle() const |
| { |
| return m_private->m_movie; |
| } |
| |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
| { |
| switch (fdwReason) { |
| case DLL_PROCESS_ATTACH: |
| return TRUE; |
| case DLL_PROCESS_DETACH: |
| case DLL_THREAD_ATTACH: |
| case DLL_THREAD_DETACH: |
| return FALSE; |
| } |
| ASSERT_NOT_REACHED(); |
| return FALSE; |
| } |