📄 mediactrl.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
// Name: src/mac/carbon/mediactrl.cpp
// Purpose: Built-in Media Backends for Mac
// Author: Ryan Norton <wxprojects@comcast.net>
// Modified by:
// Created: 11/07/04
// RCS-ID: $Id: mediactrl.cpp,v 1.28 2006/05/23 11:04:17 ABX Exp $
// Copyright: (c) 2004-2006 Ryan Norton
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// OK, a casual overseer of this file may wonder why we don't use
// either CreateMovieControl or HIMovieView...
//
// CreateMovieControl
// 1) Need to dispose and create each time a new movie is loaded
// 2) Not that many real advantages
// 3) Progressively buggier in higher OSX versions
// (see main.c of QTCarbonShell sample for details)
// HIMovieView
// 1) Crashes on destruction in ALL cases on quite a few systems!
// (With the only real "alternative" is to simply not
// dispose of it and let it leak...)
// 2) Massive refreshing bugs with its movie controller between
// movies
//
// At one point we had a complete implementation for CreateMovieControl
// and on my (RN) local copy I had one for HIMovieView - but they
// were simply deemed to be too buggy/unuseful. HIMovieView could
// have been useful as well because it uses OpenGL contexts instead
// of GWorlds. Perhaps someday when someone comes out with some
// ingenious workarounds :).
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_MEDIACTRL
#include "wx/mediactrl.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/timer.h"
#endif
// uma is for wxMacFSSpec
#include "wx/mac/uma.h"
// standard QT stuff
#ifndef __DARWIN__
#include <Movies.h>
#include <Gestalt.h>
#include <QuickTimeComponents.h>
#else
#include <QuickTime/QuickTimeComponents.h>
#endif
//---------------------------------------------------------------------------
// Height and Width of movie controller in the movie control (apple samples)
//---------------------------------------------------------------------------
#define wxMCWIDTH 320
#define wxMCHEIGHT 16
//===========================================================================
// BACKEND DECLARATIONS
//===========================================================================
//---------------------------------------------------------------------------
// wxQTMediaBackend
//---------------------------------------------------------------------------
class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
{
public:
wxQTMediaBackend();
~wxQTMediaBackend();
virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name);
virtual bool Load(const wxString& fileName);
virtual bool Load(const wxURI& location);
virtual bool Play();
virtual bool Pause();
virtual bool Stop();
virtual wxMediaState GetState();
virtual bool SetPosition(wxLongLong where);
virtual wxLongLong GetPosition();
virtual wxLongLong GetDuration();
virtual void Move(int x, int y, int w, int h);
wxSize GetVideoSize() const;
virtual double GetPlaybackRate();
virtual bool SetPlaybackRate(double dRate);
virtual double GetVolume();
virtual bool SetVolume(double);
void Cleanup();
void FinishLoad();
virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags);
virtual wxLongLong GetDownloadProgress();
virtual wxLongLong GetDownloadTotal();
virtual void MacVisibilityChanged();
//
// ------ Implementation from now on --------
//
bool DoPause();
bool DoStop();
void DoLoadBestSize();
void DoSetControllerVisible(wxMediaCtrlPlayerControls flags);
wxLongLong GetDataSizeFromStart(TimeValue end);
Boolean IsQuickTime4Installed();
void DoNewMovieController();
static pascal void PPRMProc(
Movie theMovie, OSErr theErr, void* theRefCon);
//TODO: Last param actually long - does this work on 64bit machines?
static pascal Boolean MCFilterProc(MovieController theController,
short action, void *params, long refCon);
static pascal OSStatus WindowEventHandler(
EventHandlerCallRef inHandlerCallRef,
EventRef inEvent, void *inUserData );
wxSize m_bestSize; // Original movie size
Movie m_movie; // Movie instance
bool m_bPlaying; // Whether media is playing or not
class wxTimer* m_timer; // Timer for streaming the movie
MovieController m_mc; // MovieController instance
wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags
// Event handlers and UPPs/Callbacks
EventHandlerRef m_windowEventHandler;
EventHandlerUPP m_windowUPP;
MoviePrePrerollCompleteUPP m_preprerollupp;
MCActionFilterWithRefConUPP m_mcactionupp;
GWorldPtr m_movieWorld; //Offscreen movie GWorld
friend class wxQTMediaEvtHandler;
DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
};
// helper to hijack background erasing for the QT window
class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
{
public:
wxQTMediaEvtHandler(wxQTMediaBackend *qtb)
{
m_qtb = qtb;
qtb->m_ctrl->Connect(
qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground),
NULL, this);
}
void OnEraseBackground(wxEraseEvent& event);
private:
wxQTMediaBackend *m_qtb;
DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
};
//===========================================================================
// IMPLEMENTATION
//===========================================================================
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// wxQTMediaBackend
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend)
//Time between timer calls - this is the Apple recommondation to the TCL
//team I believe
#define MOVIE_DELAY 20
//---------------------------------------------------------------------------
// wxQTMediaLoadTimer
//
// QT, esp. QT for Windows is very picky about how you go about
// async loading. If you were to go through a Windows message loop
// or a MoviesTask or both and then check the movie load state
// it would still return 1000 (loading)... even (pre)prerolling doesn't
// help. However, making a load timer like this works
//---------------------------------------------------------------------------
class wxQTMediaLoadTimer : public wxTimer
{
public:
wxQTMediaLoadTimer(wxQTMediaBackend* parent) :
m_parent(parent) {}
void Notify()
{
::MCIdle(m_parent->m_mc);
// kMovieLoadStatePlayable is not enough on MAC:
// it plays, but IsMovieDone might return true (!)
// sure we need to wait until kMovieLoadStatePlaythroughOK
if (::GetMovieLoadState(m_parent->m_movie) >= 20000)
{
m_parent->FinishLoad();
delete this;
}
}
protected:
wxQTMediaBackend *m_parent; // Backend pointer
};
// --------------------------------------------------------------------------
// wxQTMediaPlayTimer - Handle Asyncronous Playing
//
// 1) Checks to see if the movie is done, and if not continues
// streaming the movie
// 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
// the movie.
// --------------------------------------------------------------------------
class wxQTMediaPlayTimer : public wxTimer
{
public:
wxQTMediaPlayTimer(wxQTMediaBackend* parent) :
m_parent(parent) {}
void Notify()
{
//
// OK, a little explaining - basically originally
// we only called MoviesTask if the movie was actually
// playing (not paused or stopped)... this was before
// we realized MoviesTask actually handles repainting
// of the current frame - so if you were to resize
// or something it would previously not redraw that
// portion of the movie.
//
// So now we call MoviesTask always so that it repaints
// correctly.
//
::MCIdle(m_parent->m_mc);
//
// Handle the stop event - if the movie has reached
// the end, notify our handler
//
if (::IsMovieDone(m_parent->m_movie))
{
if ( m_parent->SendStopEvent() )
{
m_parent->Stop();
wxASSERT(::GetMoviesError() == noErr);
m_parent->QueueFinishEvent();
}
}
}
protected:
wxQTMediaBackend* m_parent; // Backend pointer
};
//---------------------------------------------------------------------------
// wxQTMediaBackend Constructor
//
// Sets m_timer to NULL signifying we havn't loaded anything yet
//---------------------------------------------------------------------------
wxQTMediaBackend::wxQTMediaBackend()
: m_movie(NULL), m_bPlaying(false), m_timer(NULL)
, m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
, m_preprerollupp(NULL), m_movieWorld(NULL)
{
}
//---------------------------------------------------------------------------
// wxQTMediaBackend Destructor
//
// 1) Cleans up the QuickTime movie instance
// 2) Decrements the QuickTime reference counter - if this reaches
// 0, QuickTime shuts down
// 3) Decrements the QuickTime Windows Media Layer reference counter -
// if this reaches 0, QuickTime shuts down the Windows Media Layer
//---------------------------------------------------------------------------
wxQTMediaBackend::~wxQTMediaBackend()
{
if (m_movie)
Cleanup();
// Cleanup for moviecontroller
if (m_mc)
{
// destroy wxQTMediaEvtHandler we pushed on it
m_ctrl->PopEventHandler(true);
RemoveEventHandler(m_windowEventHandler);
DisposeEventHandlerUPP(m_windowUPP);
// Dispose of the movie controller
::DisposeMovieController(m_mc);
m_mc = NULL;
DisposeMCActionFilterWithRefConUPP(m_mcactionupp);
// Dispose of offscreen GWorld
::DisposeGWorld(m_movieWorld);
}
// Note that ExitMovies() is not necessary...
ExitMovies();
}
//---------------------------------------------------------------------------
// wxQTMediaBackend::CreateControl
//
// 1) Intializes QuickTime
// 2) Creates the control window
//---------------------------------------------------------------------------
bool wxQTMediaBackend::CreateControl(
wxControl* ctrl,
wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
if (!IsQuickTime4Installed())
return false;
EnterMovies();
wxMediaCtrl* mediactrl = (wxMediaCtrl*)ctrl;
//
// Create window
// By default wxWindow(s) is created with a border -
// so we need to get rid of those
//
// Since we don't have a child window like most other
// backends, we don't need wxCLIP_CHILDREN
//
if ( !mediactrl->wxControl::Create(
parent, id, pos, size,
wxWindow::MacRemoveBordersFromStyle(style),
validator, name))
{
return false;
}
#if wxUSE_VALIDATORS
mediactrl->SetValidator(validator);
#endif
m_ctrl = mediactrl;
return true;
}
//---------------------------------------------------------------------------
// wxQTMediaBackend::IsQuickTime4Installed
//
// Determines whether version 4 of QT is installed
// (Pretty much for Classic only)
//---------------------------------------------------------------------------
Boolean wxQTMediaBackend::IsQuickTime4Installed()
{
OSErr error;
long result;
error = Gestalt(gestaltQuickTime, &result);
return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
}
//---------------------------------------------------------------------------
// wxQTMediaBackend::Load (file version)
//
// 1) Get an FSSpec from the Windows path name
// 2) Open the movie
// 3) Obtain the movie instance from the movie resource
// 4) Close the movie resource
// 5) Finish loading
//---------------------------------------------------------------------------
bool wxQTMediaBackend::Load(const wxString& fileName)
{
if (m_movie)
Cleanup();
::ClearMoviesStickyError(); // clear previous errors so
// GetMoviesStickyError is useful
OSErr err = noErr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -