📄 qtmoviewin.cpp
字号:
/* * 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. */#include "config.h"#include <windows.h>#include "QTMovieWin.h"// Put Movies.h first so build failures here point clearly to QuickTime#include <Movies.h>#include <QuickTimeComponents.h>#include <GXMath.h>#include <QTML.h>#include "QTMovieWinTimer.h"#include <wtf/Assertions.h>#include <wtf/HashSet.h>#include <wtf/Noncopyable.h>#include <wtf/Vector.h>using namespace std;static const long minimumQuickTimeVersion = 0x07300000; // 7.3// Resizing GWorlds is slow, give them a minimum size so size of small // videos can be animated smoothlystatic const int cGWorldMinWidth = 640;static const int cGWorldMinHeight = 360;static const float cNonContinuousTimeChange = 0.2f;union UppParam { long longValue; void* ptr;};static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0;static HashSet<QTMovieWinPrivate*>* gTaskList;static Vector<CFStringRef>* gSupportedTypes = 0;static SInt32 quickTimeVersion = 0;static void updateTaskTimer(int maxInterval = 1000){ if (!gTaskList->size()) { stopSharedTimer(); return; } long intervalInMS; QTGetTimeUntilNextTask(&intervalInMS, 1000); if (intervalInMS > maxInterval) intervalInMS = maxInterval; setSharedTimerFireDelay(static_cast<float>(intervalInMS) / 1000);}class QTMovieWinPrivate : Noncopyable {public: QTMovieWinPrivate(); ~QTMovieWinPrivate(); void task(); void startTask(); void endTask(); void createMovieController(); void registerDrawingCallback(); void drawingComplete(); void updateGWorld(); void createGWorld(); void deleteGWorld(); void clearGWorld(); void setSize(int, int); QTMovieWin* m_movieWin; Movie m_movie; MovieController m_movieController; bool m_tasking; QTMovieWinClient* m_client; long m_loadState; bool m_ended; bool m_seeking; float m_lastMediaTime; double m_lastLoadStateCheckTime; int m_width; int m_height; bool m_visible; GWorldPtr m_gWorld; int m_gWorldWidth; int m_gWorldHeight; GWorldPtr m_savedGWorld; long m_loadError;};QTMovieWinPrivate::QTMovieWinPrivate() : m_movieWin(0) , m_movie(0) , m_movieController(0) , m_tasking(false) , m_client(0) , 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_gWorld(0) , m_gWorldWidth(0) , m_gWorldHeight(0) , m_savedGWorld(0) , m_loadError(0){}QTMovieWinPrivate::~QTMovieWinPrivate(){ endTask(); if (m_gWorld) deleteGWorld(); if (m_movieController) DisposeMovieController(m_movieController); if (m_movie) DisposeMovie(m_movie);}static void taskTimerFired(){ // The hash content might change during task() Vector<QTMovieWinPrivate*> tasks; copyToVector(*gTaskList, tasks); size_t count = tasks.size(); for (unsigned n = 0; n < count; ++n) tasks[n]->task(); updateTaskTimer();}void QTMovieWinPrivate::startTask() { if (m_tasking) return; if (!gTaskList) gTaskList = new HashSet<QTMovieWinPrivate*>; gTaskList->add(this); m_tasking = true; updateTaskTimer();}void QTMovieWinPrivate::endTask() { if (!m_tasking) return; gTaskList->remove(this); m_tasking = false; updateTaskTimer();}void QTMovieWinPrivate::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 kMovieLoadStateComplete. // This is different from QTKit API and seems strange. long loadState = m_loadError ? kMovieLoadStateError : 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 if (loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded && m_visible) clearGWorld(); m_loadState = loadState; if (!m_movieController && m_loadState >= kMovieLoadStateLoaded) createMovieController(); m_client->movieLoadStateChanged(m_movieWin); } m_lastLoadStateCheckTime = systemTime(); } bool ended = !!IsMovieDone(m_movie); if (ended != m_ended) { m_ended = ended; if (m_client && ended) m_client->movieEnded(m_movieWin); } float time = m_movieWin->currentTime(); if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) { m_seeking = false; if (m_client) m_client->movieTimeChanged(m_movieWin); } m_lastMediaTime = time; if (m_loadError) endTask();}void QTMovieWinPrivate::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; MCSetControllerPort(m_movieController, m_gWorld); MCSetControllerAttached(m_movieController, false);}void QTMovieWinPrivate::registerDrawingCallback(){ UppParam param; param.ptr = this; SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDrawingCompleteUPP, param.longValue);}void QTMovieWinPrivate::drawingComplete(){ if (!m_gWorld || m_loadState < kMovieLoadStateLoaded) return; m_client->movieNewImageAvailable(m_movieWin);}void QTMovieWinPrivate::updateGWorld(){ bool shouldBeVisible = m_visible; if (!m_height || !m_width) shouldBeVisible = false; if (shouldBeVisible && !m_gWorld) createGWorld(); else if (!shouldBeVisible && m_gWorld) deleteGWorld(); else if (m_gWorld && (m_width > m_gWorldWidth || m_height > m_gWorldHeight)) { // need a bigger, better gWorld deleteGWorld(); createGWorld(); }}void QTMovieWinPrivate::createGWorld(){ ASSERT(!m_gWorld); if (!m_movie) return; m_gWorldWidth = max(cGWorldMinWidth, m_width); m_gWorldHeight = max(cGWorldMinHeight, m_height); Rect bounds; bounds.top = 0; bounds.left = 0; bounds.right = m_gWorldWidth; bounds.bottom = m_gWorldHeight; OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, NULL, NULL, NULL); if (err) return; GetMovieGWorld(m_movie, &m_savedGWorld, 0); if (m_movieController) MCSetControllerPort(m_movieController, m_gWorld); SetMovieGWorld(m_movie, m_gWorld, 0); bounds.right = m_width; bounds.bottom = m_height; if (m_movieController) MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds);}void QTMovieWinPrivate::clearGWorld(){ if (!m_movie||!m_gWorld) return; GrafPtr savePort; GetPort(&savePort); MacSetPort((GrafPtr)m_gWorld); Rect bounds; bounds.top = 0; bounds.left = 0; bounds.right = m_gWorldWidth; bounds.bottom = m_gWorldHeight; EraseRect(&bounds); MacSetPort(savePort);}void QTMovieWinPrivate::setSize(int width, int height){ if (m_width == width && m_height == height) return; m_width = width; m_height = height; if (!m_movie) return; Rect bounds; bounds.top = 0; bounds.left = 0; bounds.right = width; bounds.bottom = height; if (m_movieController) MCSetControllerBoundsRect(m_movieController, &bounds); SetMovieBox(m_movie, &bounds); updateGWorld();}void QTMovieWinPrivate::deleteGWorld(){ ASSERT(m_gWorld); if (m_movieController) MCSetControllerPort(m_movieController, m_savedGWorld); if (m_movie) SetMovieGWorld(m_movie, m_savedGWorld, 0); m_savedGWorld = 0; DisposeGWorld(m_gWorld); m_gWorld = 0; m_gWorldWidth = 0; m_gWorldHeight = 0;}QTMovieWin::QTMovieWin(QTMovieWinClient* client) : m_private(new QTMovieWinPrivate()){ m_private->m_movieWin = this; m_private->m_client = client; initializeQuickTime();}QTMovieWin::~QTMovieWin(){ delete m_private;}void QTMovieWin::play(){ 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 QTMovieWin::pause(){ if (m_private->m_movieController) MCDoAction(m_private->m_movieController, mcActionPlay, 0); else StopMovie(m_private->m_movie); updateTaskTimer();}float QTMovieWin::rate() const{ if (!m_private->m_movie) return 0; return FixedToFloat(GetMovieRate(m_private->m_movie));}void QTMovieWin::setRate(float rate){ if (!m_private->m_movie) return; if (m_private->m_movieController) MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate)); else SetMovieRate(m_private->m_movie, FloatToFixed(rate)); updateTaskTimer();}float QTMovieWin::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 QTMovieWin::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 QTMovieWin::setCurrentTime(float time) const{ if (!m_private->m_movie) return; m_private->m_seeking = true; TimeScale scale = GetMovieTimeScale(m_private->m_movie); if (m_private->m_movieController){ QTRestartAtTimeRecord restart = { time * scale , 0 }; MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart); } else SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); updateTaskTimer();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -