From: Ryan Norton Date: Sun, 7 Nov 2004 08:27:51 +0000 (+0000) Subject: wxMovieCtrl - for playing movies - mac carbon QT 3 MSW-compatable implementation... X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/4ad7af2b83d24c75a0cdf32c7e5ee1c2cc4dd543?ds=inline wxMovieCtrl - for playing movies - mac carbon QT 3 MSW-compatable implementation and MSW DirectX7 directshow implementation git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@30332 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/mac/carbon/moviectrl.h b/include/wx/mac/carbon/moviectrl.h new file mode 100644 index 0000000000..eac2847c6f --- /dev/null +++ b/include/wx/mac/carbon/moviectrl.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/moviectrl.h +// Purpose: DirectX7+ wxMovieCtrl MSW +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/defs.h" + +#if wxUSE_MOVIECTRL + +#include "wx/datetime.h" + +enum wxMovieCtrlState +{ + wxMOVIECTRL_STOPPED, + wxMOVIECTRL_PAUSED, + wxMOVIECTRL_PLAYING +}; + +class wxMovieCtrl : public wxControl +{ +public: + wxMovieCtrl() + { } + + wxMovieCtrl(wxWindow* parent, wxWindowID id, const wxString& fileName, const wxString& label = wxT(""), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = 0, const wxString& name = wxPanelNameStr) + { Create(parent, id, fileName, label, pos, size, style, name); } + + ~wxMovieCtrl(); + + bool Create(wxWindow* parent, wxWindowID id, const wxString& fileName, const wxString& label = wxT(""), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = 0, const wxString& name = wxPanelNameStr); + + bool Play(); + bool Pause(); + bool Stop(); + + wxMovieCtrlState GetState(); + +#if wxUSE_DATETIME + bool Seek(const wxTimeSpan& where); +#endif + + virtual void SetLabel(const wxString& label); + +protected: + void OnSize(wxSizeEvent& evt); + wxSize DoGetBestSize() const; + bool InitQT(); + + struct MovieRecord* m_movie; + wxSize m_bestSize; + class _wxQTTimer* m_timer; + + DECLARE_DYNAMIC_CLASS(wxMovieCtrl); + DECLARE_EVENT_TABLE() +}; + +#endif // wxUSE_MOVIECTRL \ No newline at end of file diff --git a/include/wx/moviectrl.h b/include/wx/moviectrl.h new file mode 100644 index 0000000000..7b8ec9c5fb --- /dev/null +++ b/include/wx/moviectrl.h @@ -0,0 +1,16 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/moviectrl.h +// Purpose: wxMovieCtrl class +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#if defined(__WXMSW__) +#include "wx/msw/moviectrl.h" +#elif defined(__WXMAC__) +#include "wx/mac/carbon/moviectrl.h" +#endif \ No newline at end of file diff --git a/include/wx/msw/moviectrl.h b/include/wx/msw/moviectrl.h new file mode 100644 index 0000000000..ceee950629 --- /dev/null +++ b/include/wx/msw/moviectrl.h @@ -0,0 +1,77 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/moviectrl.h +// Purpose: DirectX7+ wxMovieCtrl MSW +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/defs.h" + +#if wxUSE_MOVIECTRL + +#include "wx/datetime.h" + +enum wxMovieCtrlState +{ + wxMOVIECTRL_STOPPED, + wxMOVIECTRL_PAUSED, + wxMOVIECTRL_PLAYING +}; + +class wxMovieCtrl : public wxControl +{ +public: + wxMovieCtrl() + { } + + wxMovieCtrl(wxWindow* parent, wxWindowID id, const wxString& fileName, const wxString& label = wxT(""), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = 0, const wxString& name = wxPanelNameStr) + { Create(parent, id, fileName, label, pos, size, style, name); } + + ~wxMovieCtrl(); + + bool Create(wxWindow* parent, wxWindowID id, const wxString& fileName, const wxString& label = wxT(""), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = 0, const wxString& name = wxPanelNameStr); + + bool Play(); + bool Pause(); + bool Stop(); + + wxMovieCtrlState GetState(); + +#if wxUSE_DATETIME + bool Seek(const wxTimeSpan& where); +#endif + + virtual void SetLabel(const wxString& label); + +protected: + void OnSize(wxSizeEvent& evt); + wxSize DoGetBestSize() const; + +// void OnActivate(wxActivateEvent& evt); + + //msw-specific - we need to overload the window proc +// WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); + + void* m_pGB; + void* m_pMC; + void* m_pME; + void* m_pVW; + void* m_pBA; + void* m_pBV; + void* m_pMS; + + wxSize m_bestSize; + + DECLARE_DYNAMIC_CLASS(wxMovieCtrl); + DECLARE_EVENT_TABLE() +}; + +#endif // wxUSE_MOVIECTRL \ No newline at end of file diff --git a/src/mac/carbon/moviectrl.cpp b/src/mac/carbon/moviectrl.cpp new file mode 100644 index 0000000000..db687e759c --- /dev/null +++ b/src/mac/carbon/moviectrl.cpp @@ -0,0 +1,304 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: mac/carbon/moviectrl.cpp +// Purpose: wxMovieCtrl MAC CARBON QT +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +//#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) +//#pragma implementation "moviectrl.h" +//#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#define wxUSE_MOVIECTRL 1 + +#if wxUSE_MOVIECTRL + +#include "wx/moviectrl.h" +#include "wx/sound.h" +#include "wx/timer.h" + +IMPLEMENT_CLASS(wxMovieCtrl, wxControl); + +BEGIN_EVENT_TABLE(wxMovieCtrl, wxControl) + EVT_SIZE(wxMovieCtrl::OnSize) +END_EVENT_TABLE() + +//MESSY headers +#ifdef __WXMAC__ +#include "wx/mac/uma.h" +#include +#include +#endif + +//quicktime media layer only required for mac emulation on pc +#ifndef __WXMAC__ +#include +#endif + +#include + +//Time between timer calls +#define MOVIE_DELAY 100 + +// ------------------------------------------------------------------ +// wxQTTimer - Handle Asyncronous Playing +// ------------------------------------------------------------------ +class _wxQTTimer : public wxTimer +{ +public: + _wxQTTimer(Movie movie) : + m_movie(movie), m_bPaused(false) + { + } + + ~_wxQTTimer() + { + } + + bool GetPaused() {return m_bPaused;} + void SetPaused(bool bPaused) {m_bPaused = bPaused;} + + void Notify() + { + if (!m_bPaused) + { + if(!IsMovieDone(m_movie)) + MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie + else + Stop(); + } + } + +protected: + Movie m_movie; + bool m_bPaused; +}; + +//Determines whether version 3 of QT is installed +Boolean wxIsQuickTime3Installed (void) +{ +#ifdef __WXMAC__ + short error; + long result; + + error = Gestalt (gestaltQuickTime, &result); + return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0300); +#else + return true; +#endif +} + +bool wxMovieCtrl::InitQT () +{ + if (wxIsQuickTime3Installed()) + { + #ifndef __WXMAC__ + int nError; + //-2093 no dll + if ((nError = InitializeQTML(0)) != noErr) + { + wxFAIL_MSG(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError)); + } + #endif + EnterMovies(); + return true; + } + else + { + wxFAIL_MSG(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4.")); + return false; + } +} + +bool wxMovieCtrl::Create(wxWindow* parent, wxWindowID id, const wxString& fileName, + const wxString& label, const wxPoint& pos, const wxSize& size, + long WXUNUSED(style), const wxString& name) +{ + if ( !InitQT() ) + return false; + + OSErr err = noErr; + short movieResFile; + FSSpec sfFile; +#ifdef __WXMAC__ + wxMacFilename2FSSpec( m_sndname , &sfFile ) ; +#else + int nError; + if ((nError = NativePathNameToFSSpec ((char*) fileName.c_str(), &sfFile, 0)) != noErr) + { + wxFAIL_MSG(wxString::Format(wxT("File:%s does not exist\nError:%i"), + fileName.c_str(), nError)); + return false; + } +#endif + if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr) + { + wxFAIL_MSG(wxT("Quicktime couldn't open the file")); + return false; + } + short movieResID = 0; + Str255 movieName; + + err = NewMovieFromFile ( + &m_movie, + movieResFile, + &movieResID, + movieName, + newMovieActive, + NULL); //wasChanged + + CloseMovieFile (movieResFile); + + if (err != noErr) + { + wxFAIL_MSG(wxT("Could not create movie")); + return false; + } + + m_timer = new _wxQTTimer(m_movie); + wxASSERT(m_timer); + + //get the real size of the movie + Rect outRect; + ::GetMovieNaturalBoundsRect (m_movie, &outRect); + + m_bestSize.x = outRect.right - outRect.left; + m_bestSize.y = outRect.bottom - outRect.top; + + //do some window stuff + if ( !wxControl::Create(parent, id, pos, size, wxNO_BORDER, wxDefaultValidator, name) ) + return false; + + //Set our background color to black by default + SetBackgroundColour(*wxBLACK); + + //reparent movie +#ifdef __WXMSW__ + CreatePortAssociation(this->GetHWND(), NULL, 0L); +#endif + SetMovieGWorld(m_movie, (CGrafPtr) + +#ifdef __WXMSW__ + GetNativeWindowPort(this->GetHWND()) +#else + this->GetHandle() +#endif + , nil); + + //go! + SetLabel(label); + Play(); + + return true; +} + +void wxMovieCtrl::SetLabel(const wxString& label) +{ + wxControl::SetLabel(label); +} + +bool wxMovieCtrl::Play() +{ + ::StartMovie(m_movie); + m_timer->SetPaused(false); + m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + return ::GetMoviesError() == noErr; +} + +bool wxMovieCtrl::Pause() +{ + ::StopMovie(m_movie); + m_timer->SetPaused(true); + return ::GetMoviesError() == noErr; +} + +bool wxMovieCtrl::Stop() +{ + m_timer->SetPaused(false); + + ::StopMovie(m_movie); + if(::GetMoviesError() != noErr) + return false; + + ::GoToEndOfMovie(m_movie); + return ::GetMoviesError() == noErr; +} + +#if wxUSE_DATETIME + +bool wxMovieCtrl::Seek(const wxTimeSpan& where) +{ + TimeRecord theTimeRecord; + theTimeRecord.value.lo = ((size_t)where.GetMilliseconds().ToLong()) * 10; + theTimeRecord.scale = ::GetMovieTimeScale(m_movie); + theTimeRecord.base = ::GetMovieTimeBase(m_movie); + ::SetMovieTime(m_movie, &theTimeRecord); + + if (::GetMoviesError() != noErr) + return false; + + return true; +} + +#endif // wxUSE_DATETIME + +wxMovieCtrlState wxMovieCtrl::GetState() +{ + if( m_timer->IsRunning() == true ) + return wxMOVIECTRL_STOPPED; + + if ( m_timer->GetPaused() == false ) + return wxMOVIECTRL_PLAYING; + else + return wxMOVIECTRL_PAUSED; +} + +wxMovieCtrl::~wxMovieCtrl() +{ + if (m_timer) + { + delete m_timer; + + StopMovie(m_movie); + DisposeMovie(m_movie); + + //Note that ExitMovies() is not neccessary, but + //the docs are fuzzy on whether or not TerminateQTML is + ExitMovies(); + + #ifndef __WXMAC__ + TerminateQTML(); + #endif + } +} + +wxSize wxMovieCtrl::DoGetBestSize() const +{ + return m_bestSize; +} + +void wxMovieCtrl::OnSize(wxSizeEvent& evt) +{ + Rect theRect; + theRect.left = 0; + theRect.top = 0; + theRect.bottom = evt.GetSize().y; + theRect.right = evt.GetSize().x; + + ::SetMovieBox(m_movie, &theRect); + + wxASSERT(::GetMoviesError() == noErr); + evt.Skip(); +} +#endif //wxUSE_MOVIECTRL diff --git a/src/msw/moviectrl.cpp b/src/msw/moviectrl.cpp new file mode 100644 index 0000000000..50ef6b352e --- /dev/null +++ b/src/msw/moviectrl.cpp @@ -0,0 +1,295 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: msw/moviectrl.cpp +// Purpose: wxMovieCtrl MSW +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +//#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) +//#pragma implementation "moviectrl.h" +//#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#if wxUSE_MOVIECTRL + +#include "wx/moviectrl.h" +#include "wx/msw/ole/oleutils.h" //for wxBasicString + +#include + +IMPLEMENT_CLASS(wxMovieCtrl, wxControl); + +#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; } + +#define WM_GRAPHNOTIFY WM_USER+13 + +#ifdef __WXDEBUG__ +#define wxDSVERIFY(x) wxASSERT( SUCCEEDED ((x)) ) +#else +#define wxDSVERIFY(x) (x) +#endif + +BEGIN_EVENT_TABLE(wxMovieCtrl, wxControl) + EVT_SIZE(wxMovieCtrl::OnSize) +// EVT_ACTIVATE(wxMovieCtrl::OnActivate) +END_EVENT_TABLE() + +//it's there someplace :) +extern "C" WXDLLIMPEXP_BASE HWND +wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc); + +bool wxMovieCtrl::Create(wxWindow* parent, wxWindowID id, const wxString& fileName, + const wxString& label, const wxPoint& pos, const wxSize& size, + long style, const wxString& name) +{ + //cast helpers + IGraphBuilder*& pGB = (IGraphBuilder*&) m_pGB; + IMediaControl*& pMC = (IMediaControl*&) m_pMC; + IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; + IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; + IBasicAudio*& pBA = (IBasicAudio*&) m_pBA; + IBasicVideo*& pBV = (IBasicVideo*&) m_pBV; + IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; + + //create our filter graph + CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + IID_IGraphBuilder, (void**)&pGB); + + //load the graph & render + if (FAILED(pGB->RenderFile(fileName.wc_str(wxConvLocal), NULL))) + { + wxFAIL_MSG("Could not load movie!"); + return false; + } + + //get the interfaces, all of them + wxDSVERIFY( pGB->QueryInterface(IID_IMediaControl, (void**)&pMC) ); + wxDSVERIFY( pGB->QueryInterface(IID_IMediaEventEx, (void**)&pME) ); + wxDSVERIFY( pGB->QueryInterface(IID_IMediaSeeking, (void**)&pMS) ); + wxDSVERIFY( pGB->QueryInterface(IID_IVideoWindow, (void**)&pVW) ); + wxDSVERIFY( pGB->QueryInterface(IID_IBasicAudio, (void**)&pBA) ); + wxDSVERIFY( pGB->QueryInterface(IID_IBasicVideo, (void**)&pBV) ); + + //get the _actual_ size of the movie & remember it + long nX, nY, nSX, nSY; + pVW->GetWindowPosition(&nX,&nY,&nSX,&nSY); + + m_bestSize.x = nSX; + m_bestSize.y = nSY; + + //do some window stuff - ORDER IS IMPORTANT + //base create + if ( !wxControl::Create(parent, id, pos, size, wxNO_BORDER | wxCLIP_CHILDREN, wxDefaultValidator, name) ) + return false; + + //Set our background color to black by default + SetBackgroundColour(*wxBLACK); + + wxDSVERIFY( pVW->put_Owner((OAHWND)this->GetHandle()) ); + wxDSVERIFY( pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS) ); +// wxDSVERIFY( pME->SetNotifyWindow((OAHWND)this->GetHandle(), WM_GRAPHNOTIFY, 0) ); + wxDSVERIFY( pVW->put_Visible(OATRUE) ); //OATRUE actually == -1 :) + + //set the time format + wxDSVERIFY( pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME) ); + + SetLabel(label); + Play(); + + return true; +} + +void wxMovieCtrl::SetLabel(const wxString& label) +{ + wxControl::SetLabel(label); + + IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; + + //wxBasicString will have a null string on an + //empty wxString - gotta love those workarounds!! + if(!label.empty()) + { + wxBasicString theBasicString(label.mb_str()); + wxDSVERIFY( pVW->put_Caption(theBasicString.Get()) ); + } +} + +bool wxMovieCtrl::Play() +{ + return SUCCEEDED( ((IMediaControl*&)m_pMC)->Run() ); +} + +bool wxMovieCtrl::Pause() +{ + return SUCCEEDED( ((IMediaControl*&)m_pMC)->Pause() ); +} + +bool wxMovieCtrl::Stop() +{ + return SUCCEEDED( ((IMediaControl*&)m_pMC)->Stop() ); +} + +#if wxUSE_DATETIME + +bool wxMovieCtrl::Seek(const wxTimeSpan& where) +{ + //DS uses 100 nanos - so we need a 10 mult + LONGLONG pos = ((size_t)where.GetMilliseconds().ToLong()) * 10; + + return SUCCEEDED( ((IMediaSeeking*&)m_pMS)->SetPositions( + &pos, + AM_SEEKING_AbsolutePositioning, + NULL, + AM_SEEKING_NoPositioning + ) ); +} + +#endif // wxUSE_DATETIME + +wxMovieCtrlState wxMovieCtrl::GetState() +{ + HRESULT hr; + OAFilterState theState; + hr = ((IMediaControl*&)m_pMC)->GetState(INFINITE, &theState); + + wxASSERT( SUCCEEDED(hr) ); + + //MSW state is the same as ours + //State_Stopped = 0, + //State_Paused = State_Stopped + 1, + //State_Running = State_Paused + 1 + + return (wxMovieCtrlState) theState; +} + +//WXLRESULT wxMovieCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +//{ +/* + //cast helpers + IMediaControl*& pMC = (IMediaControl*&) m_pMC; + IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; + IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; + + if (nMsg == WM_GRAPHNOTIFY) + { + LONG evCode, evParam1, evParam2; + HRESULT hr=S_OK; + + //make sure we exist + if (!pME) + return S_OK; + + // Process all queued events + while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, + (LONG_PTR *) &evParam2, 0) + ) + ) + { + // Free memory associated with callback, since we're not using it + hr = pME->FreeEventParams(evCode, evParam1, evParam2); + + // If this is the end of the clip, reset to beginning + if(EC_COMPLETE == evCode) + { + LONGLONG pos=0; + + // Reset to first frame of movie + hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning , + NULL, AM_SEEKING_NoPositioning); + if (FAILED(hr)) + { + hr = pMC->Stop(); + // for filters that don't support seeking do a rewind + if (FAILED(hr)) + { + wxFAIL_MSG( + wxString::Format(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr) + ); + break; + } + + hr = hr = pMC->Run(); + + if (FAILED(hr)) + { + wxFAIL_MSG( + wxString::Format(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr) + ); + break; + } + } + } + } + return wxControl::MSWDefWindowProc(nMsg, wParam, lParam); + } +*/ + //pass the event to our parent +// return wxControl::MSWWindowProc(nMsg, wParam, lParam); +//} + +wxMovieCtrl::~wxMovieCtrl() +{ + //cast helpers + IGraphBuilder*& pGB = (IGraphBuilder*&) m_pGB; + IMediaControl*& pMC = (IMediaControl*&) m_pMC; + IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; + IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; + IBasicAudio*& pBA = (IBasicAudio*&) m_pBA; + IBasicVideo*& pBV = (IBasicVideo*&) m_pBV; + IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; + + // Hide then disown the window + if(pVW) + { + pVW->put_Visible(OAFALSE); //OSFALSE == 0 + pVW->put_Owner(NULL); + } + + // Release and zero DirectShow interfaces + SAFE_RELEASE(pME); + SAFE_RELEASE(pMS); + SAFE_RELEASE(pMC); + SAFE_RELEASE(pBA); + SAFE_RELEASE(pBV); + SAFE_RELEASE(pVW); + SAFE_RELEASE(pGB); +} + +wxSize wxMovieCtrl::DoGetBestSize() const +{ + return m_bestSize; +} + +// +//EVENT OVERRIDES +// + +void wxMovieCtrl::OnSize(wxSizeEvent& evt) +{ + IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; + wxDSVERIFY( pVW->SetWindowPosition(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight()) ); + + evt.Skip(); +} +/* +void wxMovieCtrl::OnActivate(wxActivateEvent& evt) +{ + if (evt.GetActive()) + { + //HACK: Make the window show :) + SetSize(GetSize()); + } +} +*/ + +#endif //wxUSE_MOVIECTRL \ No newline at end of file