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