]> git.saurik.com Git - wxWidgets.git/blame - src/msw/mediactrl.cpp
refactored code to do brush adjustment for bg drawing in only one place
[wxWidgets.git] / src / msw / mediactrl.cpp
CommitLineData
1a680109
RN
1/////////////////////////////////////////////////////////////////////////////
2// Name: msw/mediactrl.cpp
ff4aedc5 3// Purpose: Built-in Media Backends for Windows
1a680109 4// Author: Ryan Norton <wxprojects@comcast.net>
72259e00 5// Modified by:
1a680109
RN
6// Created: 11/07/04
7// RCS-ID: $Id$
8// Copyright: (c) Ryan Norton
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
ff4aedc5
RN
12//===========================================================================
13// DECLARATIONS
14//===========================================================================
15
1a680109 16//---------------------------------------------------------------------------
ff4aedc5 17// Pre-compiled header stuff
1a680109
RN
18//---------------------------------------------------------------------------
19
ff4aedc5
RN
20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21#pragma implementation "mediactrl.h"
22#endif
1a680109
RN
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28#pragma hdrstop
29#endif
30
ff4aedc5
RN
31//---------------------------------------------------------------------------
32// Includes
33//---------------------------------------------------------------------------
1a680109
RN
34#include "wx/mediactrl.h"
35
ff4aedc5
RN
36//---------------------------------------------------------------------------
37// Compilation guard
38//---------------------------------------------------------------------------
1a680109
RN
39#if wxUSE_MEDIACTRL
40
1a680109 41//---------------------------------------------------------------------------
ff4aedc5 42// Externals (somewhere in src/msw/app.cpp)
1a680109 43//---------------------------------------------------------------------------
ff4aedc5
RN
44extern "C" WXDLLIMPEXP_BASE HINSTANCE wxGetInstance(void);
45#ifdef __WXWINCE__
46extern wxChar *wxCanvasClassName;
47#else
48extern const wxChar *wxCanvasClassName;
49#endif
1a680109 50
ff4aedc5
RN
51//===========================================================================
52// BACKEND DECLARATIONS
53//===========================================================================
1a680109
RN
54
55//---------------------------------------------------------------------------
ff4aedc5
RN
56//
57// wxAMMediaBackend
58//
1a680109
RN
59//---------------------------------------------------------------------------
60
ff4aedc5
RN
61//---------------------------------------------------------------------------
62// Compilation guard for DirectShow
63//---------------------------------------------------------------------------
1a680109
RN
64#if wxUSE_DIRECTSHOW
65
ff4aedc5
RN
66//---------------------------------------------------------------------------
67// DirectShow includes
68//---------------------------------------------------------------------------
1a680109
RN
69#include <dshow.h>
70
ff4aedc5 71class wxAMMediaBackend : public wxMediaBackend
1a680109
RN
72{
73public:
ff4aedc5 74 wxAMMediaBackend();
1a680109 75
ff4aedc5 76 virtual ~wxAMMediaBackend();
1a680109 77
226ec5a7 78 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 79 wxWindowID id,
226ec5a7 80 const wxPoint& pos,
ff4aedc5 81 const wxSize& size,
226ec5a7 82 long style,
ff4aedc5
RN
83 const wxValidator& validator,
84 const wxString& name);
1a680109
RN
85
86 virtual bool Play();
87 virtual bool Pause();
88 virtual bool Stop();
89
90 virtual bool Load(const wxString& fileName);
91 virtual bool Load(const wxURI& location);
92
93 virtual wxMediaState GetState();
94
ff4aedc5
RN
95 virtual bool SetPosition(wxLongLong where);
96 virtual wxLongLong GetPosition();
97 virtual wxLongLong GetDuration();
1a680109 98
ff4aedc5
RN
99 virtual void Move(int x, int y, int w, int h);
100 wxSize GetVideoSize() const;
1a680109
RN
101
102 virtual double GetPlaybackRate();
103 virtual bool SetPlaybackRate(double);
104
105 void Cleanup();
106
107 bool m_bVideo;
108
226ec5a7 109 static LRESULT CALLBACK NotifyWndProc(HWND hWnd, UINT nMsg,
ff4aedc5 110 WPARAM wParam, LPARAM lParam);
226ec5a7
WS
111
112 LRESULT CALLBACK OnNotifyWndProc(HWND hWnd, UINT nMsg,
ff4aedc5 113 WPARAM wParam, LPARAM lParam);
1a680109 114
ff4aedc5 115 wxControl* m_ctrl;
1a680109 116
226ec5a7 117 IGraphBuilder* m_pGB;
1a680109
RN
118 IMediaControl* m_pMC;
119 IMediaEventEx* m_pME;
120 IVideoWindow* m_pVW;
121 IBasicAudio* m_pBA;
122 IBasicVideo* m_pBV;
123 IMediaSeeking* m_pMS;
124
ff4aedc5 125 HWND m_hNotifyWnd;
1a680109 126 wxSize m_bestSize;
ff4aedc5
RN
127
128 DECLARE_DYNAMIC_CLASS(wxAMMediaBackend);
1a680109
RN
129};
130
131#endif //wxUSE_DIRECTSHOW
132
133//---------------------------------------------------------------------------
ff4aedc5
RN
134//
135// wxMCIMediaBackend
136//
1a680109
RN
137//---------------------------------------------------------------------------
138
ff4aedc5
RN
139//---------------------------------------------------------------------------
140// MCI Includes
141//---------------------------------------------------------------------------
1a680109 142#include <mmsystem.h>
1a680109 143
ff4aedc5 144class wxMCIMediaBackend : public wxMediaBackend
1a680109
RN
145{
146public:
3f9a3bf9 147
ff4aedc5
RN
148 wxMCIMediaBackend();
149 ~wxMCIMediaBackend();
1a680109 150
226ec5a7 151 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 152 wxWindowID id,
226ec5a7 153 const wxPoint& pos,
ff4aedc5 154 const wxSize& size,
226ec5a7 155 long style,
ff4aedc5
RN
156 const wxValidator& validator,
157 const wxString& name);
1a680109
RN
158
159 virtual bool Play();
160 virtual bool Pause();
161 virtual bool Stop();
162
163 virtual bool Load(const wxString& fileName);
164 virtual bool Load(const wxURI& location);
165
166 virtual wxMediaState GetState();
167
ff4aedc5
RN
168 virtual bool SetPosition(wxLongLong where);
169 virtual wxLongLong GetPosition();
170 virtual wxLongLong GetDuration();
1a680109 171
ff4aedc5
RN
172 virtual void Move(int x, int y, int w, int h);
173 wxSize GetVideoSize() const;
1a680109
RN
174
175 virtual double GetPlaybackRate();
ff4aedc5 176 virtual bool SetPlaybackRate(double dRate);
3f9a3bf9 177
226ec5a7 178 static LRESULT CALLBACK NotifyWndProc(HWND hWnd, UINT nMsg,
ff4aedc5 179 WPARAM wParam, LPARAM lParam);
5987f174 180
226ec5a7 181 LRESULT CALLBACK OnNotifyWndProc(HWND hWnd, UINT nMsg,
ff4aedc5 182 WPARAM wParam, LPARAM lParam);
1a680109 183
ff4aedc5
RN
184 MCIDEVICEID m_hDev; //Our MCI Device ID/Handler
185 wxControl* m_ctrl; //Parent control
186 HWND m_hNotifyWnd; //Window to use for MCI events
187 bool m_bVideo; //Whether or not we have video
188
189 DECLARE_DYNAMIC_CLASS(wxMCIMediaBackend);
190};
1a680109
RN
191
192//---------------------------------------------------------------------------
193//
ff4aedc5 194// wxQTMediaBackend
1a680109
RN
195//
196//---------------------------------------------------------------------------
197
ff4aedc5
RN
198//---------------------------------------------------------------------------
199// QT Compilation Guard
200//---------------------------------------------------------------------------
201#if wxUSE_QUICKTIME
1a680109 202
ff4aedc5
RN
203//---------------------------------------------------------------------------
204// QT Includes
205//---------------------------------------------------------------------------
206#include <qtml.h> //Windoze QT include
207#include <QuickTimeComponents.h> //Standard QT stuff
1a680109 208
ff4aedc5
RN
209class wxQTMediaBackend : public wxMediaBackend
210{
211public:
1a680109 212
ff4aedc5
RN
213 wxQTMediaBackend();
214 ~wxQTMediaBackend();
1a680109 215
226ec5a7 216 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 217 wxWindowID id,
226ec5a7 218 const wxPoint& pos,
ff4aedc5 219 const wxSize& size,
226ec5a7 220 long style,
ff4aedc5
RN
221 const wxValidator& validator,
222 const wxString& name);
1a680109 223
ff4aedc5
RN
224 virtual bool Play();
225 virtual bool Pause();
226 virtual bool Stop();
1a680109 227
ff4aedc5
RN
228 virtual bool Load(const wxString& fileName);
229 virtual bool Load(const wxURI& location);
1a680109 230
ff4aedc5 231 virtual wxMediaState GetState();
1a680109 232
ff4aedc5
RN
233 virtual bool SetPosition(wxLongLong where);
234 virtual wxLongLong GetPosition();
235 virtual wxLongLong GetDuration();
1a680109 236
ff4aedc5
RN
237 virtual void Move(int x, int y, int w, int h);
238 wxSize GetVideoSize() const;
1a680109 239
ff4aedc5
RN
240 virtual double GetPlaybackRate();
241 virtual bool SetPlaybackRate(double dRate);
1a680109 242
ff4aedc5
RN
243 void Cleanup();
244 void FinishLoad();
1a680109 245
ff4aedc5
RN
246 wxSize m_bestSize; //Original movie size
247 struct MovieRecord* m_movie; //QT Movie handle/instance
248 wxControl* m_ctrl; //Parent control
249 bool m_bVideo; //Whether or not we have video
250 class _wxQTTimer* m_timer; //Timer for streaming the movie
1a680109 251
ff4aedc5
RN
252 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend);
253};
1a680109 254
ff4aedc5
RN
255//---------------------------------------------------------------------------
256// End QT Compilation Guard
257//---------------------------------------------------------------------------
258#endif //wxUSE_QUICKTIME
1a680109 259
ff4aedc5
RN
260//===========================================================================
261// IMPLEMENTATION
262//===========================================================================
1a680109 263
ff4aedc5
RN
264//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
265//
266// wxAMMediaBackend
267//
268//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1a680109 269
ff4aedc5
RN
270//---------------------------------------------------------------------------
271// Only use if user wants it -
272//---------------------------------------------------------------------------
273#if wxUSE_DIRECTSHOW
1a680109 274
ff4aedc5 275IMPLEMENT_DYNAMIC_CLASS(wxAMMediaBackend, wxMediaBackend);
1a680109 276
ff4aedc5
RN
277// Numerical value for when the graph reaches the stop position
278#define WM_GRAPHNOTIFY WM_USER+13
1a680109 279
ff4aedc5
RN
280//---------------------------------------------------------------------------
281// Usual debugging macros
282//---------------------------------------------------------------------------
283#ifdef __WXDEBUG__
284#define wxAMVERIFY(x) \
285{ \
286 HRESULT hrdsv = (x); \
287 if ( FAILED(hrdsv) ) \
288 { \
289 /*TCHAR szError[MAX_ERROR_TEXT_LEN];*/ \
290 /*if( AMGetErrorText(hrdsv, szError, MAX_ERROR_TEXT_LEN) == 0)*/ \
291 /*{*/ \
292 /*wxFAIL_MSG( wxString::Format(wxT("DirectShow error \"%s\" ")*/\
293 /*wxT("occured at line %i in ")*/ \
294 /*wxT("mediactrl.cpp"),*/ \
295 /*szError, __LINE__) );*/ \
296 /*}*/ \
297 /*else*/ \
298 wxFAIL_MSG( wxString::Format(wxT("Unknown error (%i) ") \
299 wxT("occured at") \
300 wxT(" line %i in mediactrl.cpp."), \
301 (int)hrdsv, __LINE__) ); \
302 } \
1a680109 303}
ff4aedc5
RN
304#define wxVERIFY(x) wxASSERT((x))
305#else
306#define wxAMVERIFY(x) (x)
307#define wxVERIFY(x) (x)
308#endif
1a680109 309
ff4aedc5
RN
310//---------------------------------------------------------------------------
311// Standard macros for ease of use
312//---------------------------------------------------------------------------
313#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }
1a680109 314
ff4aedc5
RN
315//---------------------------------------------------------------------------
316// wxAMMediaBackend Constructor
317//
318// Sets m_hNotifyWnd to NULL to signify that we haven't loaded anything yet
226ec5a7 319//---------------------------------------------------------------------------
ff4aedc5 320wxAMMediaBackend::wxAMMediaBackend() : m_hNotifyWnd(NULL)
1a680109 321{
1a680109
RN
322}
323
ff4aedc5
RN
324//---------------------------------------------------------------------------
325// wxAMMediaBackend Destructor
326//
226ec5a7
WS
327// Cleans up everything
328//---------------------------------------------------------------------------
ff4aedc5 329wxAMMediaBackend::~wxAMMediaBackend()
1a680109 330{
ff4aedc5
RN
331 if (m_hNotifyWnd)
332 Cleanup();
1a680109
RN
333}
334
1a680109 335//---------------------------------------------------------------------------
ff4aedc5 336// wxAMMediaBackend::CreateControl
1a680109 337//
ff4aedc5
RN
338// ActiveMovie does not really have any native control to speak of,
339// so we just create a normal control with a black background.
1a680109 340//
ff4aedc5 341// We also check to see if ActiveMovie is installed
226ec5a7
WS
342//---------------------------------------------------------------------------
343bool wxAMMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 344 wxWindowID id,
226ec5a7 345 const wxPoint& pos,
ff4aedc5 346 const wxSize& size,
226ec5a7 347 long style,
ff4aedc5
RN
348 const wxValidator& validator,
349 const wxString& name)
226ec5a7 350{
1a680109 351 //create our filter graph
ff4aedc5 352 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
1a680109
RN
353 IID_IGraphBuilder, (void**)&m_pGB);
354
355 //directshow not installed?
356 if ( FAILED(hr) )
357 return false;
358
ff4aedc5
RN
359 //release the filter graph - we don't need it yet
360 m_pGB->Release();
361 m_pGB = NULL;
362
363 //
364 // Create window
365 // By default wxWindow(s) is created with a border -
366 // so we need to get rid of those, and create with
367 // wxCLIP_CHILDREN, so that if the driver/backend
368 // is a child window, it refereshes properly
369 //
370 if ( !ctrl->wxControl::Create(parent, id, pos, size,
371 (style | wxNO_BORDER) | wxCLIP_CHILDREN,
372 validator, name) )
373 return false;
1a680109 374
ff4aedc5
RN
375 //
376 //Set our background color to black by default
377 //
378 ctrl->SetBackgroundColour(*wxBLACK);
226ec5a7 379
ff4aedc5 380 m_ctrl = ctrl;
1a680109
RN
381 return true;
382}
383
1a680109 384
ff4aedc5
RN
385//---------------------------------------------------------------------------
386// wxAMMediaBackend::Load (file version)
387//
388// Creates an Active Movie filter graph from a file or url
226ec5a7 389//---------------------------------------------------------------------------
ff4aedc5 390bool wxAMMediaBackend::Load(const wxString& fileName)
1a680109 391{
ff4aedc5 392 if(m_hNotifyWnd)
72259e00
RL
393 Cleanup();
394
1a680109
RN
395 CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
396 IID_IGraphBuilder, (void**)&m_pGB);
397
72259e00 398 //load the graph & render
1a680109
RN
399 if( FAILED(m_pGB->RenderFile(fileName.wc_str(wxConvLocal), NULL)) )
400 return false;
401
402 //get the interfaces, all of them
ff4aedc5
RN
403 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaControl, (void**)&m_pMC) );
404 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaEventEx, (void**)&m_pME) );
405 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaSeeking, (void**)&m_pMS) );
406 wxAMVERIFY( m_pGB->QueryInterface(IID_IVideoWindow, (void**)&m_pVW) );
407 wxAMVERIFY( m_pGB->QueryInterface(IID_IBasicAudio, (void**)&m_pBA) );
408 wxAMVERIFY( m_pGB->QueryInterface(IID_IBasicVideo, (void**)&m_pBV) );
409
226ec5a7 410 //We could tell if the media has audio or not by
ff4aedc5
RN
411 //something like
412 //-----
1a680109 413 //long lVolume;
ff4aedc5
RN
414 //pBA->get_Volume(&lVolume) == E_NOTIMPL
415 //-----
416 //here...
417
418 //
419 //Obtain the _actual_ size of the movie & remember it
420 //
226ec5a7 421 long nX,
ff4aedc5
RN
422 nY;
423
424 m_bestSize.x = m_bestSize.y = 0;
425
226ec5a7 426 m_bVideo = SUCCEEDED( m_pVW->GetWindowPosition( &nX,
ff4aedc5
RN
427 &nY,
428 (long*)&m_bestSize.x,
429 (long*)&m_bestSize.y) );
430
431 //
432 //If we have video in the media - set it up so that
433 //its a child window of the control, its visible,
434 //and that the control is the owner of the video window
435 //
436 if (m_bVideo)
1a680109 437 {
ff4aedc5
RN
438 wxAMVERIFY( m_pVW->put_Owner((OAHWND)m_ctrl->GetHandle()) );
439 wxAMVERIFY( m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS) );
440 wxAMVERIFY( m_pVW->put_Visible(OATRUE) ); //OATRUE == -1
1a680109
RN
441 }
442
ff4aedc5
RN
443 //
444 // Create a hidden window and register to handle
445 // directshow events for this graph
226ec5a7 446 // Note that wxCanvasClassName is already registered
ff4aedc5 447 // and used by all wxWindows and normal wxControls
226ec5a7 448 //
ff4aedc5
RN
449 m_hNotifyWnd = ::CreateWindow
450 (
451 wxCanvasClassName,
452 NULL,
453 0, 0, 0, 0,
454 0,
455 (HWND) NULL,
456 (HMENU)NULL,
457 wxGetInstance(),
458 (LPVOID) NULL
459 );
460
461 if(!m_hNotifyWnd)
1a680109 462 {
ff4aedc5
RN
463 wxLogSysError( wxT("Could not create hidden needed for ")
464 wxT("registering for DirectShow events!") );
1a680109 465
ff4aedc5
RN
466 return false;
467 }
226ec5a7
WS
468
469 ::SetWindowLongPtr(m_hNotifyWnd, GWLP_WNDPROC,
ff4aedc5 470 (LONG_PTR)wxAMMediaBackend::NotifyWndProc);
1a680109 471
ff4aedc5
RN
472 ::SetWindowLong(m_hNotifyWnd, GWL_USERDATA,
473 (LONG) this);
1a680109 474
226ec5a7 475 wxAMVERIFY( m_pME->SetNotifyWindow((OAHWND)m_hNotifyWnd,
ff4aedc5 476 WM_GRAPHNOTIFY, 0) );
3f9a3bf9 477
ff4aedc5
RN
478 //
479 //set the time format
480 //
481 wxAMVERIFY( m_pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME) );
482
483 //
484 // Force the parent window of this control to recalculate
485 // the size of this if sizers are being used
486 // and render the results immediately
487 //
3f9a3bf9
RN
488 m_ctrl->InvalidateBestSize();
489 m_ctrl->GetParent()->Layout();
1a680109
RN
490 m_ctrl->GetParent()->Refresh();
491 m_ctrl->GetParent()->Update();
492
1a680109
RN
493 return true;
494}
495
ff4aedc5
RN
496//---------------------------------------------------------------------------
497// wxAMMediaBackend::Load (URL Version)
498//
499// Loads media from a URL. Interestingly enough DirectShow
500// appears (?) to escape the URL for us, at least on normal
501// files
226ec5a7 502//---------------------------------------------------------------------------
ff4aedc5 503bool wxAMMediaBackend::Load(const wxURI& location)
1a680109
RN
504{
505 return Load(location.BuildUnescapedURI());
506}
507
ff4aedc5
RN
508//---------------------------------------------------------------------------
509// wxAMMediaBackend::Play
510//
511// Plays the stream. If it is non-seekable, it will restart it.
226ec5a7 512//---------------------------------------------------------------------------
ff4aedc5 513bool wxAMMediaBackend::Play()
1a680109
RN
514{
515 return SUCCEEDED( m_pMC->Run() );
516}
517
ff4aedc5
RN
518//---------------------------------------------------------------------------
519// wxAMMediaBackend::Pause
520//
521// Pauses the stream.
226ec5a7 522//---------------------------------------------------------------------------
ff4aedc5 523bool wxAMMediaBackend::Pause()
1a680109
RN
524{
525 return SUCCEEDED( m_pMC->Pause() );
526}
527
ff4aedc5
RN
528//---------------------------------------------------------------------------
529// wxAMMediaBackend::Stop
530//
531// Stops the stream.
226ec5a7 532//---------------------------------------------------------------------------
ff4aedc5 533bool wxAMMediaBackend::Stop()
1a680109 534{
e4b12a53
RN
535 bool bOK = SUCCEEDED( m_pMC->Stop() );
536
537 //We don't care if it can't get to the beginning in directshow -
538 //it could be a non-seeking filter (wince midi) in which case playing
539 //starts all over again
540 SetPosition(0);
541 return bOK;
1a680109
RN
542}
543
ff4aedc5
RN
544//---------------------------------------------------------------------------
545// wxAMMediaBackend::SetPosition
546//
226ec5a7 547// 1) Translates the current position's time to directshow time,
ff4aedc5
RN
548// which is in a scale of 100 nanoseconds
549// 2) Sets the play position of the IMediaSeeking interface -
550// passing NULL as the stop position means to keep the old
551// stop position
226ec5a7 552//---------------------------------------------------------------------------
ff4aedc5 553bool wxAMMediaBackend::SetPosition(wxLongLong where)
1a680109 554{
ff4aedc5 555 LONGLONG pos = ((LONGLONG)where.GetValue()) * 10000;
1a680109
RN
556
557 return SUCCEEDED( m_pMS->SetPositions(
72259e00 558 &pos,
1a680109 559 AM_SEEKING_AbsolutePositioning,
72259e00 560 NULL,
1a680109
RN
561 AM_SEEKING_NoPositioning
562 ) );
563}
564
ff4aedc5
RN
565//---------------------------------------------------------------------------
566// wxAMMediaBackend::GetPosition
567//
568// 1) Obtains the current play and stop positions from IMediaSeeking
569// 2) Returns the play position translated to our time base
226ec5a7 570//---------------------------------------------------------------------------
ff4aedc5 571wxLongLong wxAMMediaBackend::GetPosition()
1a680109
RN
572{
573 LONGLONG outCur, outStop;
ff4aedc5 574 wxAMVERIFY( m_pMS->GetPositions(&outCur, &outStop) );
1a680109
RN
575
576 //h,m,s,milli - outdur is in 100 nanos
577 return outCur/10000;
578}
579
ff4aedc5
RN
580//---------------------------------------------------------------------------
581// wxAMMediaBackend::GetDuration
582//
583// 1) Obtains the duration of the media from the IMediaSeeking interface
584// 2) Converts that value to our time base, and returns it
226ec5a7 585//---------------------------------------------------------------------------
ff4aedc5 586wxLongLong wxAMMediaBackend::GetDuration()
1a680109
RN
587{
588 LONGLONG outDuration;
ff4aedc5 589 wxAMVERIFY( m_pMS->GetDuration(&outDuration) );
1a680109
RN
590
591 //h,m,s,milli - outdur is in 100 nanos
592 return outDuration/10000;
593}
594
ff4aedc5
RN
595//---------------------------------------------------------------------------
596// wxAMMediaBackend::GetState
597//
598// Obtains the state from the IMediaControl interface.
599// Note that it's enumeration values for stopping/playing
600// etc. are the same as ours, so we just do a straight cast.
226ec5a7 601// TODO: MS recommends against INFINITE here for
ff4aedc5 602// IMediaControl::GetState- do it in stages
226ec5a7 603//---------------------------------------------------------------------------
ff4aedc5 604wxMediaState wxAMMediaBackend::GetState()
1a680109 605{
1a680109
RN
606 HRESULT hr;
607 OAFilterState theState;
608 hr = m_pMC->GetState(INFINITE, &theState);
72259e00 609
1a680109
RN
610 wxASSERT( SUCCEEDED(hr) );
611
612 //MSW state is the same as ours
613 //State_Stopped = 0,
614 //State_Paused = State_Stopped + 1,
615 //State_Running = State_Paused + 1
616
617 return (wxMediaState) theState;
618}
619
ff4aedc5
RN
620//---------------------------------------------------------------------------
621// wxAMMediaBackend::GetPlaybackRate
622//
623// Pretty simple way of obtaining the playback rate from
624// the IMediaSeeking interface
226ec5a7 625//---------------------------------------------------------------------------
ff4aedc5 626double wxAMMediaBackend::GetPlaybackRate()
1a680109
RN
627{
628 double dRate;
ff4aedc5 629 wxAMVERIFY( m_pMS->GetRate(&dRate) );
1a680109
RN
630 return dRate;
631}
632
ff4aedc5
RN
633//---------------------------------------------------------------------------
634// wxAMMediaBackend::SetPlaybackRate
635//
636// Sets the playback rate of the media - DirectShow is pretty good
637// about this, actually
226ec5a7 638//---------------------------------------------------------------------------
ff4aedc5 639bool wxAMMediaBackend::SetPlaybackRate(double dRate)
1a680109
RN
640{
641 return SUCCEEDED( m_pMS->SetRate(dRate) );
642}
643
ff4aedc5
RN
644//---------------------------------------------------------------------------
645// wxAMMediaBackend::NotifyWndProc
646//
226ec5a7 647// Here we check to see if DirectShow tells us we've reached the stop
ff4aedc5
RN
648// position in our stream - if it has, it may not actually stop
649// the stream - which we need to do...
226ec5a7
WS
650//---------------------------------------------------------------------------
651LRESULT CALLBACK wxAMMediaBackend::NotifyWndProc(HWND hWnd, UINT nMsg,
652 WPARAM wParam,
ff4aedc5
RN
653 LPARAM lParam)
654{
655 wxAMMediaBackend* backend = (wxAMMediaBackend*)
656 ::GetWindowLong(hWnd, GWL_USERDATA);
657
658 return backend->OnNotifyWndProc(hWnd, nMsg, wParam, lParam);
659}
660
226ec5a7
WS
661LRESULT CALLBACK wxAMMediaBackend::OnNotifyWndProc(HWND hWnd, UINT nMsg,
662 WPARAM wParam,
ff4aedc5 663 LPARAM lParam)
1a680109
RN
664{
665 if (nMsg == WM_GRAPHNOTIFY)
666 {
226ec5a7
WS
667 LONG evCode,
668 evParam1,
ff4aedc5
RN
669 evParam2;
670
671 //
672 // DirectShow keeps a list of queued events, and we need
673 // to go through them one by one, stopping at (Hopefully only one)
674 // EC_COMPLETE message
675 //
1a680109 676 while(SUCCEEDED(m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
72259e00
RL
677 (LONG_PTR *) &evParam2, 0)
678 )
1a680109
RN
679 )
680 {
ff4aedc5
RN
681 // Cleanup memory that GetEvent allocated
682 wxAMVERIFY( m_pME->FreeEventParams(evCode, evParam1, evParam2) );
1a680109
RN
683
684 // If this is the end of the clip, notify handler
685 if(EC_COMPLETE == evCode)
686 {
ff4aedc5
RN
687 //send the event to our child
688 wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId());
689 m_ctrl->ProcessEvent(theEvent);
690
691 //if the user didn't veto it, stop the stream
692 if (theEvent.IsAllowed())
693 {
694 //Interestingly enough, DirectShow does not actually stop
695 //the filters - even when it reaches the end!
696 wxVERIFY( Stop() );
226ec5a7 697
ff4aedc5 698 //send the event to our child
226ec5a7 699 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
ff4aedc5
RN
700 m_ctrl->GetId());
701 m_ctrl->ProcessEvent(theEvent);
702 }
1a680109 703 }
72259e00 704 }
1a680109 705 }
ff4aedc5 706 return DefWindowProc(hWnd, nMsg, wParam, lParam);
1a680109
RN
707}
708
ff4aedc5
RN
709//---------------------------------------------------------------------------
710// wxAMMediaBackend::Cleanup
711//
226ec5a7 712// 1) Hide/disowns the video window (MS says bad things will happen if
ff4aedc5
RN
713// you don't)
714// 2) Releases all the directshow interfaces we use
715// TODO: Maybe there's a way to redirect the IGraphBuilder each time
716// we load, rather then creating and destroying the interfaces
717// each time?
226ec5a7 718//---------------------------------------------------------------------------
ff4aedc5 719void wxAMMediaBackend::Cleanup()
1a680109
RN
720{
721 // Hide then disown the window
722 if(m_pVW)
723 {
724 m_pVW->put_Visible(OAFALSE); //OSFALSE == 0
725 m_pVW->put_Owner(NULL);
726 }
727
728 // Release and zero DirectShow interfaces
729 SAFE_RELEASE(m_pME);
730 SAFE_RELEASE(m_pMS);
731 SAFE_RELEASE(m_pMC);
732 SAFE_RELEASE(m_pBA);
733 SAFE_RELEASE(m_pBV);
734 SAFE_RELEASE(m_pVW);
1a680109 735 SAFE_RELEASE(m_pGB);
226ec5a7 736
ff4aedc5
RN
737 // Get rid of our hidden Window
738 DestroyWindow(m_hNotifyWnd);
739 m_hNotifyWnd = NULL;
1a680109
RN
740}
741
ff4aedc5
RN
742
743//---------------------------------------------------------------------------
744// wxAMMediaBackend::GetVideoSize
745//
746// Obtains the cached original video size
226ec5a7 747//---------------------------------------------------------------------------
ff4aedc5 748wxSize wxAMMediaBackend::GetVideoSize() const
1a680109
RN
749{
750 return m_bestSize;
751}
752
ff4aedc5
RN
753//---------------------------------------------------------------------------
754// wxAMMediaBackend::Move
755//
756// Resizes the IVideoWindow to the size of the control window
226ec5a7 757//---------------------------------------------------------------------------
ff4aedc5 758void wxAMMediaBackend::Move(int x, int y, int w, int h)
1a680109 759{
ff4aedc5 760 if(m_hNotifyWnd && m_bVideo)
1a680109 761 {
ff4aedc5 762 wxAMVERIFY( m_pVW->SetWindowPosition(0, 0, w, h) );
1a680109
RN
763 }
764}
765
ff4aedc5
RN
766//---------------------------------------------------------------------------
767// End of wxAMMediaBackend
768//---------------------------------------------------------------------------
1a680109
RN
769#endif //wxUSE_DIRECTSHOW
770
ff4aedc5 771//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1a680109 772//
ff4aedc5 773// wxMCIMediaBackend
226ec5a7 774//
ff4aedc5
RN
775//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
776
777
778IMPLEMENT_DYNAMIC_CLASS(wxMCIMediaBackend, wxMediaBackend);
779
780//---------------------------------------------------------------------------
781// Usual debugging macros for MCI returns
1a680109
RN
782//---------------------------------------------------------------------------
783
ff4aedc5
RN
784#ifdef __WXDEBUG__
785#define wxMCIVERIFY(arg) \
786{ \
787 DWORD nRet; \
788 if ( (nRet = (arg)) != 0) \
789 { \
790 TCHAR sz[5000]; \
791 mciGetErrorString(nRet, sz, 5000); \
792 wxFAIL_MSG(wxString::Format(_T("MCI Error:%s"), sz)); \
793 } \
794}
795#else
796#define wxMCIVERIFY(arg) (arg);
797#endif
798
799//---------------------------------------------------------------------------
800// Simulation for <digitalv.h>
33d8e2fc 801//
ff4aedc5 802// Mingw and possibly other compilers don't have the digitalv.h header
226ec5a7 803// that is needed to have some essential features of mci work with
ff4aedc5
RN
804// windows - so we provide the declarations for the types we use here
805//---------------------------------------------------------------------------
33d8e2fc
RN
806
807typedef struct {
808 DWORD_PTR dwCallback;
809#ifdef MCI_USE_OFFEXT
810 POINT ptOffset;
811 POINT ptExtent;
226ec5a7 812#else
33d8e2fc
RN
813 RECT rc;
814#endif
815} MCI_DGV_RECT_PARMS;
816
817typedef struct {
818 DWORD_PTR dwCallback;
819 HWND hWnd;
820#ifndef _WIN32
821 WORD wReserved1;
822#endif
823 UINT nCmdShow;
824#ifndef _WIN32
825 WORD wReserved2;
826#endif
ff4aedc5
RN
827 wxChar* lpstrText;
828} MCI_DGV_WINDOW_PARMS;
33d8e2fc
RN
829
830typedef struct {
226ec5a7
WS
831 DWORD_PTR dwCallback;
832 DWORD dwTimeFormat;
833 DWORD dwAudio;
834 DWORD dwFileFormat;
835 DWORD dwSpeed;
836} MCI_DGV_SET_PARMS;
33d8e2fc 837
ff4aedc5
RN
838//---------------------------------------------------------------------------
839// wxMCIMediaBackend Constructor
840//
841// Here we don't need to do much except say we don't have any video :)
842//---------------------------------------------------------------------------
843wxMCIMediaBackend::wxMCIMediaBackend() : m_hNotifyWnd(NULL), m_bVideo(false)
844{
3f9a3bf9
RN
845}
846
ff4aedc5
RN
847//---------------------------------------------------------------------------
848// wxMCIMediaBackend Destructor
849//
850// We close the mci device - note that there may not be an mci device here,
851// or it may fail - but we don't really care, since we're destructing
852//---------------------------------------------------------------------------
853wxMCIMediaBackend::~wxMCIMediaBackend()
3f9a3bf9 854{
ff4aedc5
RN
855 if(m_hNotifyWnd)
856 {
857 mciSendCommand(m_hDev, MCI_CLOSE, 0, 0);
858 DestroyWindow(m_hNotifyWnd);
859 m_hNotifyWnd = NULL;
860 }
3f9a3bf9
RN
861}
862
ff4aedc5
RN
863//---------------------------------------------------------------------------
864// wxMCIMediaBackend::Create
865//
866// Here we just tell wxMediaCtrl that mci does exist (which it does, on all
867// msw systems, at least in some form dating back to win16 days)
868//---------------------------------------------------------------------------
226ec5a7 869bool wxMCIMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 870 wxWindowID id,
226ec5a7 871 const wxPoint& pos,
ff4aedc5 872 const wxSize& size,
226ec5a7 873 long style,
ff4aedc5
RN
874 const wxValidator& validator,
875 const wxString& name)
72259e00 876{
ff4aedc5
RN
877 //
878 // Create window
879 // By default wxWindow(s) is created with a border -
880 // so we need to get rid of those, and create with
881 // wxCLIP_CHILDREN, so that if the driver/backend
882 // is a child window, it refereshes properly
883 //
884 if ( !ctrl->wxControl::Create(parent, id, pos, size,
885 (style & ~wxBORDER_MASK) | wxCLIP_CHILDREN,
886 validator, name) )
887 return false;
888
889 //
890 //Set our background color to black by default
891 //
892 ctrl->SetBackgroundColour(*wxBLACK);
893
3f9a3bf9
RN
894 m_ctrl = ctrl;
895 return true;
896}
897
ff4aedc5
RN
898//---------------------------------------------------------------------------
899// wxMCIMediaBackend::Load (file version)
900//
901// Here we have MCI load a file and device, set the time format to our
902// default (milliseconds), and set the video (if any) to play in the control
903//---------------------------------------------------------------------------
904bool wxMCIMediaBackend::Load(const wxString& fileName)
3f9a3bf9 905{
ff4aedc5
RN
906 //
907 //if the user already called load close the previous MCI device
908 //
909 if(m_hNotifyWnd)
910 {
3f9a3bf9 911 mciSendCommand(m_hDev, MCI_CLOSE, 0, 0);
ff4aedc5
RN
912 DestroyWindow(m_hNotifyWnd);
913 m_hNotifyWnd = NULL;
914 }
3f9a3bf9 915
ff4aedc5
RN
916 //
917 //Opens a file and has MCI select a device. Normally you'd put
918 //MCI_OPEN_TYPE in addition to MCI_OPEN_ELEMENT - however if you
226ec5a7 919 //omit this it tells MCI to select the device instead. This is
ff4aedc5
RN
920 //good because we have no reliable way of "enumerating" the devices
921 //in MCI
922 //
3f9a3bf9 923 MCI_OPEN_PARMS openParms;
3f9a3bf9
RN
924 openParms.lpstrElementName = (wxChar*) fileName.c_str();
925
226ec5a7 926 if ( mciSendCommand(0, MCI_OPEN, MCI_OPEN_ELEMENT,
ff4aedc5
RN
927 (DWORD)(LPVOID)&openParms) != 0)
928 return false;
3f9a3bf9
RN
929
930 m_hDev = openParms.wDeviceID;
931
ff4aedc5
RN
932 //
933 //Now set the time format for the device to milliseconds
934 //
935 MCI_SET_PARMS setParms;
3f9a3bf9
RN
936 setParms.dwCallback = 0;
937 setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
938
939 if (mciSendCommand(m_hDev, MCI_SET, MCI_SET_TIME_FORMAT,
940 (DWORD)(LPVOID)&setParms) != 0)
941 return false;
942
ff4aedc5
RN
943 //
944 //Now tell the MCI device to display the video in our wxMediaCtrl
945 //
3f9a3bf9 946 MCI_DGV_WINDOW_PARMS windowParms;
5987f174 947 windowParms.hWnd = (HWND)m_ctrl->GetHandle();
3f9a3bf9 948
226ec5a7 949 m_bVideo = (mciSendCommand(m_hDev, MCI_WINDOW,
ff4aedc5
RN
950 0x00010000L, //MCI_DGV_WINDOW_HWND
951 (DWORD)(LPVOID)&windowParms) == 0);
952
953 //
954 // Create a hidden window and register to handle
955 // MCI events
226ec5a7 956 // Note that wxCanvasClassName is already registered
ff4aedc5 957 // and used by all wxWindows and normal wxControls
226ec5a7 958 //
ff4aedc5
RN
959 m_hNotifyWnd = ::CreateWindow
960 (
961 wxCanvasClassName,
962 NULL,
963 0, 0, 0, 0,
964 0,
965 (HWND) NULL,
966 (HMENU)NULL,
967 wxGetInstance(),
968 (LPVOID) NULL
969 );
970
971 if(!m_hNotifyWnd)
972 {
973 wxLogSysError( wxT("Could not create hidden needed for ")
974 wxT("registering for DirectShow events!") );
975
976 return false;
977 }
226ec5a7
WS
978
979 ::SetWindowLong(m_hNotifyWnd, GWL_WNDPROC,
ff4aedc5
RN
980 (LONG)wxMCIMediaBackend::NotifyWndProc);
981
982 ::SetWindowLong(m_hNotifyWnd, GWL_USERDATA,
983 (LONG) this);
984
985 //
986 //Here, if the parent of the control has a sizer - we
987 //tell it to recalculate the size of this control since
988 //the user opened a seperate media file
989 //
5987f174
RN
990 m_ctrl->InvalidateBestSize();
991 m_ctrl->GetParent()->Layout();
992 m_ctrl->GetParent()->Refresh();
993 m_ctrl->GetParent()->Update();
72259e00 994
3f9a3bf9
RN
995 return true;
996}
997
ff4aedc5
RN
998//---------------------------------------------------------------------------
999// wxMCIMediaBackend::Load (URL version)
1000//
1001// MCI doesn't support URLs directly (?)
1002//
1003// TODO: Use wxURL/wxFileSystem and mmioInstallProc
1004//---------------------------------------------------------------------------
1005bool wxMCIMediaBackend::Load(const wxURI& WXUNUSED(location))
3f9a3bf9
RN
1006{
1007 return false;
1008}
1009
ff4aedc5
RN
1010//---------------------------------------------------------------------------
1011// wxMCIMediaBackend::Play
1012//
1013// Plays/Resumes the MCI device... a couple notes:
1014// 1) Certain drivers will crash and burn if we don't pass them an
1015// MCI_PLAY_PARMS, despite the documentation that says otherwise...
1016// 2) There is a MCI_RESUME command, but MCI_PLAY does the same thing
226ec5a7 1017// and will resume from a stopped state also, so there's no need to
ff4aedc5
RN
1018// call both, for example
1019//---------------------------------------------------------------------------
1020bool wxMCIMediaBackend::Play()
5987f174 1021{
5987f174 1022 MCI_PLAY_PARMS playParms;
ff4aedc5
RN
1023 playParms.dwCallback = (DWORD)m_hNotifyWnd;
1024
226ec5a7 1025 return ( mciSendCommand(m_hDev, MCI_PLAY, MCI_NOTIFY,
ff4aedc5 1026 (DWORD)(LPVOID)&playParms) == 0 );
5987f174
RN
1027}
1028
ff4aedc5
RN
1029//---------------------------------------------------------------------------
1030// wxMCIMediaBackend::Pause
1031//
1032// Pauses the MCI device - nothing special
226ec5a7 1033//---------------------------------------------------------------------------
ff4aedc5 1034bool wxMCIMediaBackend::Pause()
5987f174
RN
1035{
1036 return (mciSendCommand(m_hDev, MCI_PAUSE, MCI_WAIT, 0) == 0);
1037}
1038
ff4aedc5
RN
1039//---------------------------------------------------------------------------
1040// wxMCIMediaBackend::Stop
1041//
1042// Stops the MCI device & seeks to the beginning as wxMediaCtrl docs outline
226ec5a7 1043//---------------------------------------------------------------------------
ff4aedc5 1044bool wxMCIMediaBackend::Stop()
5987f174
RN
1045{
1046 return (mciSendCommand(m_hDev, MCI_STOP, MCI_WAIT, 0) == 0) &&
1047 (mciSendCommand(m_hDev, MCI_SEEK, MCI_SEEK_TO_START, 0) == 0);
1048}
1049
ff4aedc5
RN
1050//---------------------------------------------------------------------------
1051// wxMCIMediaBackend::GetState
1052//
1053// Here we get the state and convert it to a wxMediaState -
1054// since we use direct comparisons with MCI_MODE_PLAY and
1055// MCI_MODE_PAUSE, we don't care if the MCI_STATUS call
1056// fails or not
226ec5a7 1057//---------------------------------------------------------------------------
ff4aedc5 1058wxMediaState wxMCIMediaBackend::GetState()
3f9a3bf9
RN
1059{
1060 MCI_STATUS_PARMS statusParms;
1061 statusParms.dwItem = MCI_STATUS_MODE;
ff4aedc5 1062
3f9a3bf9
RN
1063 mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
1064 (DWORD)(LPVOID)&statusParms);
1065
1066 if(statusParms.dwReturn == MCI_MODE_PAUSE)
1067 return wxMEDIASTATE_PAUSED;
1068 else if(statusParms.dwReturn == MCI_MODE_PLAY)
1069 return wxMEDIASTATE_PLAYING;
1070 else
1071 return wxMEDIASTATE_STOPPED;
1072}
1073
ff4aedc5
RN
1074//---------------------------------------------------------------------------
1075// wxMCIMediaBackend::SetPosition
1076//
1077// Here we set the position of the device in the stream.
226ec5a7 1078// Note that MCI actually stops the device after you seek it if the
ff4aedc5 1079// device is playing/paused, so we need to play the file after
226ec5a7
WS
1080// MCI seeks like normal APIs would
1081//---------------------------------------------------------------------------
ff4aedc5 1082bool wxMCIMediaBackend::SetPosition(wxLongLong where)
3f9a3bf9
RN
1083{
1084 MCI_SEEK_PARMS seekParms;
1085 seekParms.dwCallback = 0;
226ec5a7 1086 seekParms.dwTo = (DWORD)where.GetValue();
3f9a3bf9 1087
ff4aedc5 1088 //device was playing?
3f9a3bf9
RN
1089 bool bReplay = GetState() == wxMEDIASTATE_PLAYING;
1090
226ec5a7 1091 if( mciSendCommand(m_hDev, MCI_SEEK, MCI_TO,
ff4aedc5 1092 (DWORD)(LPVOID)&seekParms) != 0)
3f9a3bf9 1093 return false;
3f9a3bf9 1094
ff4aedc5 1095 //If the device was playing, resume it
3f9a3bf9
RN
1096 if (bReplay)
1097 return Play();
1098 else
1099 return true;
1100}
1101
ff4aedc5
RN
1102//---------------------------------------------------------------------------
1103// wxMCIMediaBackend::GetPosition
1104//
1105// Gets the position of the device in the stream using the current
1106// time format... nothing special here...
226ec5a7 1107//---------------------------------------------------------------------------
ff4aedc5 1108wxLongLong wxMCIMediaBackend::GetPosition()
3f9a3bf9
RN
1109{
1110 MCI_STATUS_PARMS statusParms;
72259e00 1111 statusParms.dwItem = MCI_STATUS_POSITION;
ff4aedc5 1112
72259e00 1113 if (mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
ff4aedc5 1114 (DWORD)(LPSTR)&statusParms) != 0)
3f9a3bf9
RN
1115 return 0;
1116
1117 return statusParms.dwReturn;
1118}
1119
ff4aedc5
RN
1120//---------------------------------------------------------------------------
1121// wxMCIMediaBackend::GetDuration
1122//
1123// Gets the duration of the stream... nothing special
226ec5a7 1124//---------------------------------------------------------------------------
ff4aedc5 1125wxLongLong wxMCIMediaBackend::GetDuration()
3f9a3bf9
RN
1126{
1127 MCI_STATUS_PARMS statusParms;
72259e00 1128 statusParms.dwItem = MCI_STATUS_LENGTH;
ff4aedc5 1129
72259e00 1130 if (mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
ff4aedc5 1131 (DWORD)(LPSTR)&statusParms) != 0)
3f9a3bf9
RN
1132 return 0;
1133
1134 return statusParms.dwReturn;
1135}
1136
ff4aedc5
RN
1137//---------------------------------------------------------------------------
1138// wxMCIMediaBackend::Move
1139//
1140// Moves the window to a location
226ec5a7
WS
1141//---------------------------------------------------------------------------
1142void wxMCIMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y),
ff4aedc5 1143 int w, int h)
3f9a3bf9 1144{
ff4aedc5
RN
1145 if (m_hNotifyWnd && m_bVideo)
1146 {
1147 MCI_DGV_RECT_PARMS putParms; //ifdefed MCI_DGV_PUT_PARMS
1148 putParms.rc.top = 0;
1149 putParms.rc.bottom = 0;
1150 putParms.rc.right = w;
1151 putParms.rc.bottom = h;
1152
226ec5a7 1153 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_PUT,
ff4aedc5
RN
1154 0x00040000L, //MCI_DGV_PUT_DESTINATION
1155 (DWORD)(LPSTR)&putParms) );
1156 }
3f9a3bf9
RN
1157}
1158
ff4aedc5
RN
1159//---------------------------------------------------------------------------
1160// wxMCIMediaBackend::GetVideoSize
1161//
1162// Gets the original size of the movie for sizers
226ec5a7 1163//---------------------------------------------------------------------------
ff4aedc5 1164wxSize wxMCIMediaBackend::GetVideoSize() const
3f9a3bf9
RN
1165{
1166 if(m_bVideo)
1167 {
ff4aedc5 1168 MCI_DGV_RECT_PARMS whereParms; //ifdefed MCI_DGV_WHERE_PARMS
3f9a3bf9 1169
226ec5a7 1170 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_WHERE,
ff4aedc5
RN
1171 0x00020000L, //MCI_DGV_WHERE_SOURCE
1172 (DWORD)(LPSTR)&whereParms) );
226ec5a7 1173
ff4aedc5 1174 return wxSize(whereParms.rc.right, whereParms.rc.bottom);
3f9a3bf9
RN
1175 }
1176 return wxSize(0,0);
1177}
1178
ff4aedc5
RN
1179//---------------------------------------------------------------------------
1180// wxMCIMediaBackend::GetPlaybackRate
1181//
1182// TODO
226ec5a7 1183//---------------------------------------------------------------------------
ff4aedc5 1184double wxMCIMediaBackend::GetPlaybackRate()
3f9a3bf9
RN
1185{
1186 return 1.0;
1187}
1188
ff4aedc5
RN
1189//---------------------------------------------------------------------------
1190// wxMCIMediaBackend::SetPlaybackRate
1191//
1192// TODO
226ec5a7 1193//---------------------------------------------------------------------------
ff4aedc5 1194bool wxMCIMediaBackend::SetPlaybackRate(double WXUNUSED(dRate))
3f9a3bf9 1195{
ff4aedc5
RN
1196/*
1197 MCI_WAVE_SET_SAMPLESPERSEC
1198 MCI_DGV_SET_PARMS setParms;
1199 setParms.dwSpeed = (DWORD) (dRate * 1000.0);
1200
226ec5a7 1201 return (mciSendCommand(m_hDev, MCI_SET,
ff4aedc5
RN
1202 0x00020000L, //MCI_DGV_SET_SPEED
1203 (DWORD)(LPSTR)&setParms) == 0);
1204*/
3f9a3bf9
RN
1205 return false;
1206}
1207
ff4aedc5
RN
1208//---------------------------------------------------------------------------
1209// [static] wxMCIMediaBackend::MSWWindowProc
1210//
226ec5a7 1211// Here we process a message when MCI reaches the stopping point
ff4aedc5 1212// in the stream
226ec5a7
WS
1213//---------------------------------------------------------------------------
1214LRESULT CALLBACK wxMCIMediaBackend::NotifyWndProc(HWND hWnd, UINT nMsg,
1215 WPARAM wParam,
ff4aedc5
RN
1216 LPARAM lParam)
1217{
1218 wxMCIMediaBackend* backend = (wxMCIMediaBackend*)
1219 ::GetWindowLong(hWnd, GWL_USERDATA);
1220 wxASSERT(backend);
1221
1222 return backend->OnNotifyWndProc(hWnd, nMsg, wParam, lParam);
1223}
1224
226ec5a7
WS
1225LRESULT CALLBACK wxMCIMediaBackend::OnNotifyWndProc(HWND hWnd, UINT nMsg,
1226 WPARAM wParam,
ff4aedc5 1227 LPARAM lParam)
3f9a3bf9 1228{
5987f174
RN
1229 if(nMsg == MM_MCINOTIFY)
1230 {
ff4aedc5
RN
1231 wxASSERT(lParam == (LPARAM) m_hDev);
1232 if(wParam == MCI_NOTIFY_SUCCESSFUL && lParam == (LPARAM)m_hDev)
5987f174 1233 {
ff4aedc5
RN
1234 wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId());
1235 m_ctrl->ProcessEvent(theEvent);
1236
1237 if(theEvent.IsAllowed())
1238 {
226ec5a7 1239 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_SEEK,
ff4aedc5 1240 MCI_SEEK_TO_START, 0) );
3f9a3bf9 1241
ff4aedc5 1242 //send the event to our child
226ec5a7 1243 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
ff4aedc5
RN
1244 m_ctrl->GetId());
1245 m_ctrl->ProcessEvent(theEvent);
1246 }
5987f174 1247 }
5987f174 1248 }
ff4aedc5 1249 return DefWindowProc(hWnd, nMsg, wParam, lParam);
3f9a3bf9 1250}
ff4aedc5
RN
1251//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1252//
1253// wxQTMediaBackend
226ec5a7 1254//
ff4aedc5
RN
1255// TODO: Use a less cludgy way to pause/get state/set state
1256// TODO: Dynamically load from qtml.dll
1257//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1258
1259#if wxUSE_QUICKTIME
1260
1261IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
1262
1263//Time between timer calls
1264#define MOVIE_DELAY 100
1265
1266#include "wx/timer.h"
1267
1268// --------------------------------------------------------------------------
1269// wxQTTimer - Handle Asyncronous Playing
1270// --------------------------------------------------------------------------
1271class _wxQTTimer : public wxTimer
1272{
1273public:
1274 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
1275 m_movie(movie), m_bPaused(false), m_parent(parent)
1276 {
1277 }
1278
1279 ~_wxQTTimer()
1280 {
1281 }
1282
1283 bool GetPaused() {return m_bPaused;}
1284 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
1285
1286 //-----------------------------------------------------------------------
1287 // _wxQTTimer::Notify
1288 //
1289 // 1) Checks to see if the movie is done, and if not continues
1290 // streaming the movie
1291 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
1292 // the movie.
1293 //-----------------------------------------------------------------------
1294 void Notify()
1295 {
1296 if (!m_bPaused)
1297 {
1298 if(!IsMovieDone(m_movie))
226ec5a7 1299 MoviesTask(m_movie, MOVIE_DELAY);
ff4aedc5
RN
1300 else
1301 {
226ec5a7 1302 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
ff4aedc5
RN
1303 m_parent->m_ctrl->GetId());
1304 m_parent->m_ctrl->ProcessEvent(theEvent);
1305
1306 if(theEvent.IsAllowed())
1307 {
1308 Stop();
1309 m_parent->Stop();
1310 wxASSERT(::GetMoviesError() == noErr);
1311
1312 //send the event to our child
226ec5a7 1313 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
ff4aedc5
RN
1314 m_parent->m_ctrl->GetId());
1315 m_parent->m_ctrl->ProcessEvent(theEvent);
1316 }
1317 }
1318 }
1319 }
1320
1321protected:
1322 Movie m_movie; //Our movie instance
1323 bool m_bPaused; //Whether we are paused or not
1324 wxQTMediaBackend* m_parent; //Backend pointer
1325};
1326
1327//---------------------------------------------------------------------------
1328// wxQTMediaBackend Destructor
1329//
1330// Sets m_timer to NULL signifying we havn't loaded anything yet
1331//---------------------------------------------------------------------------
1332wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
1333{
1334}
1335
1336//---------------------------------------------------------------------------
1337// wxQTMediaBackend Destructor
1338//
1339// 1) Cleans up the QuickTime movie instance
1340// 2) Decrements the QuickTime reference counter - if this reaches
1341// 0, QuickTime shuts down
1342// 3) Decrements the QuickTime Windows Media Layer reference counter -
1343// if this reaches 0, QuickTime shuts down the Windows Media Layer
1344//---------------------------------------------------------------------------
1345wxQTMediaBackend::~wxQTMediaBackend()
1346{
1347 if(m_timer)
1348 Cleanup();
1349
1350 //Note that ExitMovies() is not neccessary, but
1351 //the docs are fuzzy on whether or not TerminateQTML is
1352 ExitMovies();
1353 TerminateQTML();
1354}
1355
1356//---------------------------------------------------------------------------
1357// wxQTMediaBackend::CreateControl
1358//
1359// 1) Intializes QuickTime
1360// 2) Creates the control window
1361//---------------------------------------------------------------------------
226ec5a7 1362bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 1363 wxWindowID id,
226ec5a7 1364 const wxPoint& pos,
ff4aedc5 1365 const wxSize& size,
226ec5a7 1366 long style,
ff4aedc5
RN
1367 const wxValidator& validator,
1368 const wxString& name)
1369{
1370 int nError;
1371 if ((nError = InitializeQTML(0)) != noErr) //-2093 no dll
1372 {
1373 wxFAIL_MSG(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
1374 return false;
1375 }
1376 EnterMovies();
1377
1378 //
1379 // Create window
1380 // By default wxWindow(s) is created with a border -
1381 // so we need to get rid of those
1382 //
1383 // Since we don't have a child window like most other
1384 // backends, we don't need wxCLIP_CHILDREN
1385 //
1386 if ( !ctrl->wxControl::Create(parent, id, pos, size,
1387 (style & ~wxBORDER_MASK),
1388 validator, name) )
1389 return false;
1390
1391 //
1392 //Set our background color to black by default
1393 //
1394 ctrl->SetBackgroundColour(*wxBLACK);
1395
1396 m_ctrl = ctrl;
1397 return true;
1398}
1399
1400//---------------------------------------------------------------------------
1401// wxQTMediaBackend::Load (file version)
1402//
1403// 1) Get an FSSpec from the Windows path name
1404// 2) Open the movie
1405// 3) Obtain the movie instance from the movie resource
226ec5a7 1406// 4)
ff4aedc5
RN
1407//---------------------------------------------------------------------------
1408bool wxQTMediaBackend::Load(const wxString& fileName)
1409{
1410 if(m_timer)
1411 Cleanup();
3f9a3bf9 1412
ff4aedc5
RN
1413 OSErr err = noErr;
1414 short movieResFile;
1415 FSSpec sfFile;
1416
226ec5a7 1417 if (NativePathNameToFSSpec ((char*) (const char*) fileName.mb_str(),
78450975 1418 &sfFile, 0) != noErr)
ff4aedc5 1419 return false;
226ec5a7 1420
ff4aedc5
RN
1421 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
1422 return false;
1423
1424 short movieResID = 0;
1425 Str255 movieName;
1426
1427 err = NewMovieFromFile (
1428 &m_movie,
1429 movieResFile,
1430 &movieResID,
1431 movieName,
1432 newMovieActive,
1433 NULL); //wasChanged
1434
1435 CloseMovieFile (movieResFile);
1436
1437 if (err != noErr)
1438 return false;
1439
1440 FinishLoad();
1441
1442 return ::GetMoviesError() == noErr;
1443}
1444
1445//---------------------------------------------------------------------------
1446// wxQTMediaBackend::Move
1447//
1448// TODO
1449//---------------------------------------------------------------------------
1450bool wxQTMediaBackend::Load(const wxURI& location)
1451{
1452 if(m_timer)
1453 Cleanup();
1454
1455 wxString theURI = location.BuildURI();
1456
1457 OSErr err = noErr;
1458
1459 Handle theHandle = NewHandleClear(theURI.length() + 1);
1460 wxASSERT(theHandle);
1461
1462 BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
1463
1464 //create the movie from the handle that refers to the URI
226ec5a7
WS
1465 err = NewMovieFromDataRef(&m_movie, newMovieActive,
1466 NULL, theHandle,
ff4aedc5
RN
1467 URLDataHandlerSubType);
1468
1469 DisposeHandle(theHandle);
1470
1471 if (err != noErr)
1472 return false;
1473
1474 //preroll movie for streaming
1475 //TODO:Async this?
1476 TimeValue timeNow;
1477 Fixed playRate;
1478 timeNow = GetMovieTime(m_movie, NULL);
1479 playRate = GetMoviePreferredRate(m_movie);
1480 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
1481 PrerollMovie(m_movie, timeNow, playRate);
1482 SetMovieRate(m_movie, playRate);
1483
1484 FinishLoad();
1485
1486 return ::GetMoviesError() == noErr;
1487}
1488
1489//---------------------------------------------------------------------------
1490// wxQTMediaBackend::Move
1491//
1492// TODO
1493//---------------------------------------------------------------------------
1494void wxQTMediaBackend::FinishLoad()
1495{
1496 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
1497 wxASSERT(m_timer);
1498
1499 //get the real size of the movie
1500 Rect outRect;
1501 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
1502 wxASSERT(::GetMoviesError() == noErr);
1503
1504 m_bestSize.x = outRect.right - outRect.left;
1505 m_bestSize.y = outRect.bottom - outRect.top;
226ec5a7 1506
ff4aedc5 1507 //reparent movie/*AudioMediaCharacteristic*/
226ec5a7
WS
1508 if(GetMovieIndTrackType(m_movie, 1,
1509 VisualMediaCharacteristic,
1510 movieTrackCharacteristic |
ff4aedc5
RN
1511 movieTrackEnabledOnly) != NULL)
1512 {
1513 CreatePortAssociation(m_ctrl->GetHWND(), NULL, 0L);
226ec5a7
WS
1514
1515 SetMovieGWorld(m_movie,
1516 (CGrafPtr) GetNativeWindowPort(m_ctrl->GetHWND()),
ff4aedc5
RN
1517 nil);
1518 }
1519
1520 //we want millisecond precision
1521 ::SetMovieTimeScale(m_movie, 1000);
1522 wxASSERT(::GetMoviesError() == noErr);
1523
1524 //
1525 //Here, if the parent of the control has a sizer - we
1526 //tell it to recalculate the size of this control since
1527 //the user opened a seperate media file
1528 //
1529 m_ctrl->InvalidateBestSize();
1530 m_ctrl->GetParent()->Layout();
1531 m_ctrl->GetParent()->Refresh();
1532 m_ctrl->GetParent()->Update();
1533}
1534
1535//---------------------------------------------------------------------------
1536// wxQTMediaBackend::Move
1537//
1538// TODO
1539//---------------------------------------------------------------------------
1540bool wxQTMediaBackend::Play()
1541{
1542 ::StartMovie(m_movie);
1543 m_timer->SetPaused(false);
1544 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
1545 return ::GetMoviesError() == noErr;
1546}
1547
1548//---------------------------------------------------------------------------
1549// wxQTMediaBackend::Move
1550//
1551// TODO
1552//---------------------------------------------------------------------------
1553bool wxQTMediaBackend::Pause()
1554{
1555 ::StopMovie(m_movie);
1556 m_timer->SetPaused(true);
1557 m_timer->Stop();
1558 return ::GetMoviesError() == noErr;
1559}
1560
1561//---------------------------------------------------------------------------
1562// wxQTMediaBackend::Move
1563//
1564// TODO
1565//---------------------------------------------------------------------------
1566bool wxQTMediaBackend::Stop()
1567{
1568 m_timer->SetPaused(false);
1569 m_timer->Stop();
1570
1571 ::StopMovie(m_movie);
1572 if(::GetMoviesError() != noErr)
1573 return false;
226ec5a7 1574
ff4aedc5
RN
1575 ::GoToBeginningOfMovie(m_movie);
1576 return ::GetMoviesError() == noErr;
1577}
1578
1579//---------------------------------------------------------------------------
1580// wxQTMediaBackend::Move
1581//
1582// TODO
1583//---------------------------------------------------------------------------
1584double wxQTMediaBackend::GetPlaybackRate()
1585{
1586 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
1587}
1588
1589//---------------------------------------------------------------------------
1590// wxQTMediaBackend::Move
1591//
1592// TODO
1593//---------------------------------------------------------------------------
1594bool wxQTMediaBackend::SetPlaybackRate(double dRate)
1595{
1596 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
1597 return ::GetMoviesError() == noErr;
1598}
1599
1600//---------------------------------------------------------------------------
1601// wxQTMediaBackend::Move
1602//
1603// TODO
1604//---------------------------------------------------------------------------
1605bool wxQTMediaBackend::SetPosition(wxLongLong where)
1606{
1607 TimeRecord theTimeRecord;
1608 memset(&theTimeRecord, 0, sizeof(TimeRecord));
1609 theTimeRecord.value.lo = where.GetValue();
1610 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
1611 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
1612 ::SetMovieTime(m_movie, &theTimeRecord);
1613
1614 if (::GetMoviesError() != noErr)
1615 return false;
1616
1617 return true;
1618}
1619
1620//---------------------------------------------------------------------------
1621// wxQTMediaBackend::Move
1622//
1623// TODO
1624//---------------------------------------------------------------------------
1625wxLongLong wxQTMediaBackend::GetPosition()
1626{
1627 return ::GetMovieTime(m_movie, NULL);
1628}
1629
1630//---------------------------------------------------------------------------
1631// wxQTMediaBackend::Move
1632//
1633// TODO
1634//---------------------------------------------------------------------------
1635wxLongLong wxQTMediaBackend::GetDuration()
1636{
1637 return ::GetMovieDuration(m_movie);
1638}
1639
1640//---------------------------------------------------------------------------
1641// wxQTMediaBackend::Move
1642//
1643// TODO
1644//---------------------------------------------------------------------------
1645wxMediaState wxQTMediaBackend::GetState()
1646{
226ec5a7 1647 if ( !m_timer || (m_timer->IsRunning() == false &&
ff4aedc5
RN
1648 m_timer->GetPaused() == false) )
1649 return wxMEDIASTATE_STOPPED;
1650
1651 if( m_timer->IsRunning() == true )
1652 return wxMEDIASTATE_PLAYING;
1653 else
1654 return wxMEDIASTATE_PAUSED;
1655}
1656
1657//---------------------------------------------------------------------------
1658// wxQTMediaBackend::Move
1659//
1660// TODO
1661//---------------------------------------------------------------------------
1662void wxQTMediaBackend::Cleanup()
1663{
1664 delete m_timer;
1665 m_timer = NULL;
1666
1667 StopMovie(m_movie);
1668 DisposeMovie(m_movie);
1669}
1670
1671//---------------------------------------------------------------------------
1672// wxQTMediaBackend::Move
1673//
1674// TODO
1675//---------------------------------------------------------------------------
1676wxSize wxQTMediaBackend::GetVideoSize() const
1677{
1678 return m_bestSize;
1679}
1680
1681//---------------------------------------------------------------------------
1682// wxQTMediaBackend::Move
1683//
1684// TODO
1685//---------------------------------------------------------------------------
1686void wxQTMediaBackend::Move(int x, int y, int w, int h)
1687{
1688 if(m_timer)
1689 {
1690 Rect theRect = {0, 0, h, w};
1691
1692 ::SetMovieBox(m_movie, &theRect);
1693 wxASSERT(::GetMoviesError() == noErr);
1694 }
1695}
1696
1697//---------------------------------------------------------------------------
1698// End QT Compilation Guard
1699//---------------------------------------------------------------------------
1700#endif //wxUSE_QUICKTIME
1701
1702//in source file that contains stuff you don't directly use
1703#include <wx/html/forcelnk.h>
1704FORCE_LINK_ME(basewxmediabackends);
1705
1706//---------------------------------------------------------------------------
1707// End wxMediaCtrl Compilation Guard and this file
1708//---------------------------------------------------------------------------
72259e00
RL
1709#endif //wxUSE_MEDIACTRL
1710
ff4aedc5 1711