]> git.saurik.com Git - wxWidgets.git/blob - src/msw/mediactrl.cpp
Applied patch [ 1213066 ] correct device names for Joystick in Linux
[wxWidgets.git] / src / msw / mediactrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Windows
4 // Author: Ryan Norton <wxprojects@comcast.net>
5 // Modified by:
6 // Created: 11/07/04
7 // RCS-ID: $Id$
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 //===========================================================================
13 // DECLARATIONS
14 //===========================================================================
15
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mediactrl.h"
22 #endif
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
31 //---------------------------------------------------------------------------
32 // Includes
33 //---------------------------------------------------------------------------
34 #include "wx/mediactrl.h"
35
36 //---------------------------------------------------------------------------
37 // Compilation guard
38 //---------------------------------------------------------------------------
39 #if wxUSE_MEDIACTRL
40
41 //---------------------------------------------------------------------------
42 // Externals (somewhere in src/msw/app.cpp)
43 //---------------------------------------------------------------------------
44 extern "C" WXDLLIMPEXP_BASE HINSTANCE wxGetInstance(void);
45 #ifdef __WXWINCE__
46 extern WXDLLIMPEXP_CORE wxChar *wxCanvasClassName;
47 #else
48 extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName;
49 #endif
50
51 //===========================================================================
52 // BACKEND DECLARATIONS
53 //===========================================================================
54
55 //---------------------------------------------------------------------------
56 //
57 // wxAMMediaBackend
58 //
59 //---------------------------------------------------------------------------
60 //
61 //####################THE BIG DIRECTSHOW OVERVIEW############################
62 //
63 //
64 // OK... this deserves its own little tutorial. Knowledge of COM and class
65 // factories is assumed throughout this code.
66 //
67 // Basically, the way directshow works is that you tell it to render
68 // a file, and it builds and connects a bunch of filters together.
69 //
70 // There are many, many ways to do this.
71 //
72 // WAYS TO RENDER A FILE (URLS WORK IN DS ALSO)
73 //
74 // 1) Create an instance of IGraphBuilder and call RenderFile on it
75 // 2) Create an instance of IMediaControl and call RenderFile on it
76 // 3) Create an instance of IAMMultiMediaStream, call
77 // IAMMultiMediaStream::AddStream and pass an IDirectDraw instance for
78 // the video, and pass an IDirectSound(Buffer?) instance or use the
79 // default sound renderer, then call RenderFile or RenderMoniker
80 // 4) Create a Moniker instance for the file and create and build
81 // all of the filtergraph manually
82 //
83 // Our issue here is that we can't use the default representation of 1 and 2
84 // because the IVideoWindow instance hogs a lot of the useful window
85 // messages such as WM_SETCURSOR.
86 //
87 // Solution #1 was to use #3 by creating a seperate IDirectDraw instance
88 // for our window and blitting to that through a thread... unfortunately
89 // the blitting resizing is very low quality and its quite slow.
90 //
91 // The current way is to use windowless rendering and have directshow
92 // do all the DirectDraw-style clipping to our window
93 //
94 // ~~~~~~~~~~~~~~AFTER RENDERING THE FILE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95 //
96 // When done rendering the file, we need to get several interfaces from
97 // either a IMediaControl or IGraphBuilder instance -
98 //
99 // IMediaPosition - we can set the rate with this... we can also get
100 // positions and set positions through this with REFTIME (double) instead
101 // of the normal LONGLONG that IAMMultiMediaStream and IMediaControl use
102 //
103 // IBasicAudio - we need this for setting/getting the volume
104 //
105 // Interfaces that we don't use but might be useful one day -
106 //
107 // IDirectDrawVideo - you can get this through the IFilter returned
108 // from L"Video Renderer" filter from FindFilter on the IGraphBuilder.
109 // Through this we can set the IDirectDraw instance DrawShow uses.
110 //
111 // ~~~~~~~~~~~~~~STOPPING~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112 //
113 // There are two ways we can do this -
114 // 1) Have a thread compare the current position to the end position
115 // about every 10 milliseconds
116 // 2) Have IMediaSeekingEx send a message to a windowproc or signal a
117 // windows event
118 //
119 // Note that we can do these both, I.E. if an IMediaSeekingEx interface
120 // is unavailable we can check the position instead of an event
121 //
122 //---------------------------------------------------------------------------
123
124 //---------------------------------------------------------------------------
125 // COM includes
126 //---------------------------------------------------------------------------
127 #include "wx/msw/ole/oleutils.h" //wxBasicString, IID etc.
128 #include "wx/msw/ole/uuid.h" //IID etc..
129
130 //---------------------------------------------------------------------------
131 // COM compatability definitions
132 //---------------------------------------------------------------------------
133 #ifndef STDMETHODCALLTYPE
134 #define STDMETHODCALLTYPE __stdcall
135 #endif
136 #ifndef STDMETHOD
137 #define STDMETHOD(funcname) virtual HRESULT STDMETHODCALLTYPE funcname
138 #endif
139 #ifndef PURE
140 #define PURE = 0
141 #endif
142 //---------------------------------------------------------------------------
143 // IIDS - used by CoCreateInstance and IUnknown::QueryInterface
144 // Dumped from amstream.idl, quartz.idl, direct draw and with some
145 // confirmation from WINE
146 //
147 // Some of these are not used but are kept here for future reference anyway
148 //---------------------------------------------------------------------------
149
150 //QUARTZ
151 const IID LIBID_QuartzTypeLib = {0x56A868B0,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
152 const IID IID_IAMCollection = {0x56A868B9,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
153 const IID IID_IMediaControl = {0x56A868B1,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
154 const IID IID_IMediaEvent = {0x56A868B6,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
155 const IID IID_IMediaEventEx = {0x56A868C0,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
156 const IID IID_IMediaPosition = {0x56A868B2,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
157 const IID IID_IBasicAudio = {0x56A868B3,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
158 const IID IID_IVideoWindow = {0x56A868B4,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
159 const IID IID_IBasicVideo = {0x56A868B5,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
160 const IID IID_IBasicVideo2 = {0x329BB360,0xF6EA,0x11D1,{0x90,0x38,0x00,0xA0,0xC9,0x69,0x72,0x98}};
161 const IID IID_IDeferredCommand = {0x56A868B8,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
162 const IID IID_IQueueCommand = {0x56A868B7,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
163 const IID IID_IFilterInfo = {0x56A868BA,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
164 const IID IID_IRegFilterInfo = {0x56A868BB,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
165 const IID IID_IMediaTypeInfo = {0x56A868BC,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
166 const IID IID_IPinInfo = {0x56A868BD,0x0AD4,0x11CE,{0xB0,0x3A,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
167 const IID IID_IAMStats = {0xBC9BCF80,0xDCD2,0x11D2,{0xAB,0xF6,0x00,0xA0,0xC9,0x05,0xF3,0x75}};
168 const CLSID CLSID_FilgraphManager = {0xE436EBB3,0x524F,0x11CE,{0x9F,0x53,0x00,0x20,0xAF,0x0B,0xA7,0x70}};
169
170 //AMSTREAM
171 const CLSID CLSID_AMMultiMediaStream = {0x49C47CE5, 0x9BA4, 0x11D0,{0x82, 0x12, 0x00, 0xC0, 0x4F, 0xC3, 0x2C, 0x45}};
172 const IID IID_IAMMultiMediaStream = {0xBEBE595C, 0x9A6F, 0x11D0,{0x8F, 0xDE, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0x9D}};
173 const IID IID_IDirectDrawMediaStream = {0xF4104FCE, 0x9A70, 0x11D0,{0x8F, 0xDE, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0x9D}};
174 const GUID MSPID_PrimaryVideo = {0xa35FF56A, 0x9FDA, 0x11D0,{0x8F, 0xDF, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0x9D}};
175 const GUID MSPID_PrimaryAudio = {0xa35FF56B, 0x9FDA, 0x11D0,{0x8F, 0xDF, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0x9D}};
176
177 //DDRAW
178 const IID IID_IDirectDraw = {0x6C14DB80,0xA733,0x11CE,{0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60}};
179 const CLSID CLSID_DirectDraw = {0xD7B70EE0,0x4340,0x11CF,{0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35}};
180
181 //?? QUARTZ Also?
182 const CLSID CLSID_VideoMixingRenderer = {0xB87BEB7B, 0x8D29, 0x423F,{0xAE, 0x4D, 0x65, 0x82, 0xC1, 0x01, 0x75, 0xAC}};
183 const IID IID_IVMRWindowlessControl = {0x0EB1088C, 0x4DCD, 0x46F0,{0x87, 0x8F, 0x39, 0xDA, 0xE8, 0x6A, 0x51, 0xB7}};
184 const IID IID_IFilterGraph = {0x56A8689F, 0x0AD4, 0x11CE,{0xB0, 0x3A, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70}};
185 const IID IID_IGraphBuilder = {0x56A868A9, 0x0AD4, 0x11CE,{0xB0, 0x3A, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70}};
186 const IID IID_IVMRFilterConfig = {0x9E5530C5, 0x7034, 0x48B4,{0xBB, 0x46, 0x0B, 0x8A, 0x6E, 0xFC, 0x8E, 0x36}};
187 const IID IID_IBaseFilter = {0x56A86895, 0x0AD4, 0x11CE,{0xB0, 0x3A, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70}};
188
189 //---------------------------------------------------------------------------
190 // DIRECTDRAW COM INTERFACES
191 //---------------------------------------------------------------------------
192 //DDSURFACESDESC - we don't need most of the stuff here, esp. DDPIXELFORMAT,
193 //so just put stubs in
194 struct DDPIXELFORMAT {DWORD dw1,dw2,dw3,dw4,dw5,dw6,dw7,dw8;};
195 struct DDCOLORKEY {DWORD dwLow, dwHigh;};
196
197 typedef struct IDirectDrawClipper* LPDIRECTDRAWCLIPPER;
198 typedef struct IDirectDraw* LPDIRECTDRAW;
199 typedef struct IDirectDrawSurface* LPDIRECTDRAWSURFACE;
200 typedef struct DDSURFACEDESC* LPDDSURFACEDESC;
201 typedef struct IDirectDrawPalette* LPDIRECTDRAWPALETTE;
202 typedef struct DDSCAPS* LPDDSCAPS;
203 typedef DDCOLORKEY* LPDDCOLORKEY;
204 typedef DDPIXELFORMAT* LPDDPIXELFORMAT;
205 typedef struct DDCAPS* LPDDCAPS;
206
207 struct DDSURFACEDESC
208 {
209 DWORD dwSize;
210 DWORD dwFlags;
211 DWORD dwHeight;
212 DWORD dwWidth;
213 union
214 {
215 LONG lPitch;
216 DWORD dwLinearSize;
217 };
218 DWORD dwBackBufferCount;
219 union
220 {
221 DWORD dwMipMapCount;
222 DWORD dwZBufferBitDepth;
223 DWORD dwRefreshRate;
224 };
225 DWORD dwAlphaBitDepth;
226 DWORD dwReserved;
227 LPVOID lpSurface;
228 DDCOLORKEY ddckCKDestOverlay;
229 DDCOLORKEY ddckCKDestBlt;
230 DDCOLORKEY ddckCKSrcOverlay;
231 DDCOLORKEY ddckCKSrcBlt;
232 DDPIXELFORMAT ddpfPixelFormat;
233 struct DDSCAPS {DWORD dwCaps;} ddsCaps;
234 };
235
236 struct IDirectDrawClipper : public IUnknown
237 {
238 STDMETHOD(GetClipList)(LPRECT, LPRGNDATA, LPDWORD) PURE;
239 STDMETHOD(GetHWnd)(HWND*) PURE;
240 STDMETHOD(Initialize)(LPDIRECTDRAW, DWORD) PURE;
241 STDMETHOD(IsClipListChanged)(BOOL*) PURE;
242 STDMETHOD(SetClipList)(LPRGNDATA,DWORD) PURE;
243 STDMETHOD(SetHWnd)(DWORD, HWND) PURE;
244 };
245
246 struct IDirectDrawSurface : public IUnknown
247 {
248 STDMETHOD(AddAttachedSurface)(LPDIRECTDRAWSURFACE) PURE;
249 STDMETHOD(AddOverlayDirtyRect)(LPRECT) PURE;
250 STDMETHOD(Blt)(LPRECT,LPDIRECTDRAWSURFACE, LPRECT,DWORD, struct DDBLTFX*) PURE;
251 STDMETHOD(BltBatch)(struct DDBLTBATCH*, DWORD, DWORD ) PURE;
252 STDMETHOD(BltFast)(DWORD,DWORD,LPDIRECTDRAWSURFACE, LPRECT,DWORD) PURE;
253 STDMETHOD(DeleteAttachedSurface)(DWORD,LPDIRECTDRAWSURFACE) PURE;
254 STDMETHOD(EnumAttachedSurfaces)(LPVOID, LPVOID/*LPDDENUMSURFACESCALLBACK*/) PURE;
255 STDMETHOD(EnumOverlayZOrders)(DWORD,LPVOID,LPVOID/*LPDDENUMSURFACESCALLBACK*/) PURE;
256 STDMETHOD(Flip)(LPDIRECTDRAWSURFACE, DWORD) PURE;
257 STDMETHOD(GetAttachedSurface)(LPDDSCAPS, LPDIRECTDRAWSURFACE*) PURE;
258 STDMETHOD(GetBltStatus)(DWORD) PURE;
259 STDMETHOD(GetCaps)(LPDDSCAPS) PURE;
260 STDMETHOD(GetClipper)(LPDIRECTDRAWCLIPPER*) PURE;
261 STDMETHOD(GetColorKey)(DWORD, LPDDCOLORKEY) PURE;
262 STDMETHOD(GetDC)(HDC *) PURE;
263 STDMETHOD(GetFlipStatus)(DWORD) PURE;
264 STDMETHOD(GetOverlayPosition)(LPLONG, LPLONG ) PURE;
265 STDMETHOD(GetPalette)(LPDIRECTDRAWPALETTE FAR*) PURE;
266 STDMETHOD(GetPixelFormat)(LPDDPIXELFORMAT) PURE;
267 STDMETHOD(GetSurfaceDesc)(LPDDSURFACEDESC) PURE;
268 STDMETHOD(Initialize)(LPDIRECTDRAW, LPDDSURFACEDESC) PURE;
269 STDMETHOD(IsLost)(THIS) PURE;
270 STDMETHOD(Lock)(LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE;
271 STDMETHOD(ReleaseDC)(HDC) PURE;
272 STDMETHOD(Restore)(THIS) PURE;
273 STDMETHOD(SetClipper)(LPDIRECTDRAWCLIPPER) PURE;
274 STDMETHOD(SetColorKey)(DWORD, LPDDCOLORKEY) PURE;
275 STDMETHOD(SetOverlayPosition)(LONG, LONG ) PURE;
276 STDMETHOD(SetPalette)(IUnknown*) PURE;
277 STDMETHOD(Unlock)(LPVOID) PURE;
278 STDMETHOD(UpdateOverlay)(LPRECT, LPDIRECTDRAWSURFACE,LPRECT,
279 DWORD, struct DDOVERLAYFX*) PURE;
280 STDMETHOD(UpdateOverlayDisplay)(DWORD) PURE;
281 STDMETHOD(UpdateOverlayZOrder)(DWORD, LPDIRECTDRAWSURFACE) PURE;
282 };
283
284 struct IDirectDraw : public IUnknown
285 {
286 STDMETHOD(Compact)() PURE;
287 STDMETHOD(CreateClipper)(DWORD, LPDIRECTDRAWCLIPPER*, IUnknown * ) PURE;
288 STDMETHOD(CreatePalette)(DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE *, IUnknown * ) PURE;
289 STDMETHOD(CreateSurface)(LPDDSURFACEDESC, LPDIRECTDRAWSURFACE *, IUnknown *) PURE;
290 STDMETHOD(DuplicateSurface)(LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE * ) PURE;
291 STDMETHOD(EnumDisplayModes)(DWORD, LPDDSURFACEDESC, LPVOID, LPVOID ) PURE;
292 STDMETHOD(EnumSurfaces)(DWORD, LPDDSURFACEDESC, LPVOID,LPVOID ) PURE;
293 STDMETHOD(FlipToGDISurface)() PURE;
294 STDMETHOD(GetCaps)(LPDDCAPS, LPDDCAPS) PURE;
295 STDMETHOD(GetDisplayMode)(LPDDSURFACEDESC) PURE;
296 STDMETHOD(GetFourCCCodes)(LPDWORD, LPDWORD ) PURE;
297 STDMETHOD(GetGDISurface)(LPDIRECTDRAWSURFACE *) PURE;
298 STDMETHOD(GetMonitorFrequency)(LPDWORD) PURE;
299 STDMETHOD(GetScanLine)(LPDWORD) PURE;
300 STDMETHOD(GetVerticalBlankStatus)(LPBOOL ) PURE;
301 STDMETHOD(Initialize)(GUID *) PURE;
302 STDMETHOD(RestoreDisplayMode)() PURE;
303 STDMETHOD(SetCooperativeLevel)(HWND, DWORD) PURE;
304 STDMETHOD(SetDisplayMode)(DWORD, DWORD,DWORD, DWORD, DWORD) PURE;
305 STDMETHOD(WaitForVerticalBlank)(DWORD, HANDLE ) PURE;
306 };
307
308 //---------------------------------------------------------------------------
309 // AMMEDIA COM INTERFACES
310 //---------------------------------------------------------------------------
311 struct IMediaStream;
312 struct IMultiMediaStream;
313 struct IStreamSample : public IUnknown
314 {
315 public:
316 STDMETHOD(GetMediaStream)(IMediaStream **) PURE;
317 STDMETHOD(GetSampleTimes)(LONGLONG *, LONGLONG *, LONGLONG *) PURE;
318 STDMETHOD(SetSampleTimes)(const LONGLONG *, const LONGLONG *) PURE;
319 STDMETHOD(Update)(DWORD, HANDLE, LPVOID, DWORD_PTR) PURE;
320 STDMETHOD(CompletionStatus)(DWORD, DWORD) PURE;
321 };
322
323 struct IDirectDrawStreamSample : public IStreamSample
324 {
325 public:
326 STDMETHOD(GetSurface)(IDirectDrawSurface **, RECT *) PURE;
327 STDMETHOD(SetRect)(const RECT *) PURE;
328 };
329
330 struct IMediaStream : public IUnknown
331 {
332 STDMETHOD(GetMultiMediaStream)(IMultiMediaStream **) PURE;
333 STDMETHOD(GetInformation)(GUID *, int *) PURE;
334 STDMETHOD(SetSameFormat)(IMediaStream *, DWORD) PURE;
335 STDMETHOD(AllocateSample)(DWORD, IStreamSample **) PURE;
336 STDMETHOD(CreateSharedSample)(IStreamSample *, DWORD,
337 IStreamSample **) PURE;
338 STDMETHOD(SendEndOfStream)(DWORD dwFlags) PURE;
339 };
340
341 struct IDirectDrawMediaStream : public IMediaStream
342 {
343 STDMETHOD(GetFormat)(DDSURFACEDESC *, IDirectDrawPalette **,
344 DDSURFACEDESC *, DWORD *) PURE;
345 STDMETHOD(SetFormat)(const DDSURFACEDESC *, IDirectDrawPalette *) PURE;
346 STDMETHOD(GetDirectDraw)(IDirectDraw **) PURE;
347 STDMETHOD(SetDirectDraw)(IDirectDraw *) PURE;
348 STDMETHOD(CreateSample)(IDirectDrawSurface *, const RECT *,
349 DWORD, IDirectDrawStreamSample **) PURE;
350 STDMETHOD(GetTimePerFrame)(LONGLONG *) PURE;
351 };
352
353 struct IMultiMediaStream : public IUnknown
354 {
355 STDMETHOD(GetInformation)(DWORD *, int *) PURE;
356 STDMETHOD(GetMediaStream)(REFGUID, IMediaStream **) PURE;
357 STDMETHOD(EnumMediaStreams)(long, IMediaStream **) PURE;
358 STDMETHOD(GetState)(int *pCurrentState) PURE;
359 STDMETHOD(SetState)(int NewState) PURE;
360 STDMETHOD(GetTime)(LONGLONG *pCurrentTime) PURE;
361 STDMETHOD(GetDuration)(LONGLONG *pDuration) PURE;
362 STDMETHOD(Seek)(LONGLONG SeekTime) PURE;
363 STDMETHOD(GetEndOfStreamEventHandle)(HANDLE *phEOS) PURE;
364 };
365
366 struct IAMMultiMediaStream : public IMultiMediaStream
367 {
368 STDMETHOD(Initialize)(int, DWORD, IUnknown *) PURE;
369 STDMETHOD(GetFilterGraph)(IUnknown **) PURE;
370 STDMETHOD(GetFilter)(IUnknown **) PURE;
371 STDMETHOD(AddMediaStream)(IUnknown *, const GUID*, DWORD,
372 IMediaStream **) PURE;
373 STDMETHOD(OpenFile)(LPCWSTR, DWORD) PURE;
374 STDMETHOD(OpenMoniker)(IBindCtx *, IMoniker *, DWORD) PURE;
375 STDMETHOD(Render)(DWORD) PURE;
376 };
377
378 //---------------------------------------------------------------------------
379 // QUARTZ COM INTERFACES (dumped from quartz.idl from MSVC COM Browser)
380 //---------------------------------------------------------------------------
381 struct IAMCollection : public IDispatch
382 {
383 STDMETHOD(get_Count)(long *) PURE;
384 STDMETHOD(Item)(long, IUnknown **) PURE;
385 STDMETHOD(get__NewEnum)(IUnknown **) PURE;
386 };
387
388 struct IMediaControl : public IDispatch
389 {
390 STDMETHOD(Run)() PURE;
391 STDMETHOD(Pause)() PURE;
392 STDMETHOD(Stop)() PURE;
393 STDMETHOD(GetState)(long, long*) PURE;
394 STDMETHOD(RenderFile)(BSTR) PURE;
395 STDMETHOD(AddSourceFilter)(BSTR, IDispatch **) PURE;
396 STDMETHOD(get_FilterCollection)(IDispatch **) PURE;
397 STDMETHOD(get_RegFilterCollection)(IDispatch **) PURE;
398 STDMETHOD(StopWhenReady)() PURE;
399 };
400
401 struct IMediaEvent : public IDispatch
402 {
403 STDMETHOD(GetEventHandle)(LONG_PTR *) PURE;
404 STDMETHOD(GetEvent)(long *, LONG_PTR *, LONG_PTR *, long) PURE;
405 STDMETHOD(WaitForCompletion)(long, long *) PURE;
406 STDMETHOD(CancelDefaultHandling)(long) PURE;
407 STDMETHOD(RestoreDefaultHandling)(long) PURE;
408 STDMETHOD(FreeEventParams)(long, LONG_PTR, LONG_PTR) PURE;
409 };
410
411 struct IMediaEventEx : public IMediaEvent
412 {
413 STDMETHOD(SetNotifyWindow)(LONG_PTR, long, LONG_PTR) PURE;
414 STDMETHOD(SetNotifyFlags)(long) PURE;
415 STDMETHOD(GetNotifyFlags)(long *) PURE;
416 };
417
418 struct IMediaPosition : public IDispatch
419 {
420 STDMETHOD(get_Duration)(double *) PURE;
421 STDMETHOD(put_CurrentPosition)(double) PURE;
422 STDMETHOD(get_CurrentPosition)(double *) PURE;
423 STDMETHOD(get_StopTime)(double *) PURE;
424 STDMETHOD(put_StopTime)(double) PURE;
425 STDMETHOD(get_PrerollTime)(double *) PURE;
426 STDMETHOD(put_PrerollTime)(double) PURE;
427 STDMETHOD(put_Rate)(double) PURE;
428 STDMETHOD(get_Rate)(double *) PURE;
429 STDMETHOD(CanSeekForward)(long *) PURE;
430 STDMETHOD(CanSeekBackward)(long *) PURE;
431 };
432
433 struct IBasicAudio : public IDispatch
434 {
435 STDMETHOD(put_Volume)(long) PURE;
436 STDMETHOD(get_Volume)(long *) PURE;
437 STDMETHOD(put_Balance)(long) PURE;
438 STDMETHOD(get_Balance)(long *) PURE;
439 };
440
441 //---------------------------------------------------------------------------
442 // MISC COM INTERFACES
443 //---------------------------------------------------------------------------
444 struct IVMRWindowlessControl : public IUnknown
445 {
446 STDMETHOD(GetNativeVideoSize)(LONG *, LONG *, LONG *, LONG *) PURE;
447 STDMETHOD(GetMinIdealVideoSize)(LONG *, LONG *) PURE;
448 STDMETHOD(GetMaxIdealVideoSize)(LONG *, LONG *) PURE;
449 STDMETHOD(SetVideoPosition)(const LPRECT,const LPRECT) PURE;
450 STDMETHOD(GetVideoPosition)(LPRECT, LPRECT) PURE;
451 STDMETHOD(GetAspectRatioMode)(DWORD *) PURE;
452 STDMETHOD(SetAspectRatioMode)(DWORD) PURE;
453 STDMETHOD(SetVideoClippingWindow)(HWND) PURE;
454 STDMETHOD(RepaintVideo)(HWND, HDC) PURE;
455 STDMETHOD(DisplayModeChanged)() PURE;
456 STDMETHOD(GetCurrentImage)(BYTE **) PURE;
457 STDMETHOD(SetBorderColor)(COLORREF) PURE;
458 STDMETHOD(GetBorderColor)(COLORREF *) PURE;
459 STDMETHOD(SetColorKey)(COLORREF) PURE;
460 STDMETHOD(GetColorKey)(COLORREF *) PURE;
461 };
462
463 typedef IUnknown IVMRImageCompositor;
464
465 struct IVMRFilterConfig : public IUnknown
466 {
467 STDMETHOD(SetImageCompositor)(IVMRImageCompositor *) PURE;
468 STDMETHOD(SetNumberOfStreams)(DWORD) PURE;
469 STDMETHOD(GetNumberOfStreams)(DWORD *) PURE;
470 STDMETHOD(SetRenderingPrefs)(DWORD) PURE;
471 STDMETHOD(GetRenderingPrefs)(DWORD *) PURE;
472 STDMETHOD(SetRenderingMode)(DWORD) PURE;
473 STDMETHOD(GetRenderingMode)(DWORD *) PURE;
474 };
475
476 typedef IUnknown IBaseFilter;
477 typedef IUnknown IPin;
478 typedef IUnknown IEnumFilters;
479 typedef int AM_MEDIA_TYPE;
480
481 struct IFilterGraph : public IUnknown
482 {
483 STDMETHOD(AddFilter)(IBaseFilter *, LPCWSTR) PURE;
484 STDMETHOD(RemoveFilter)(IBaseFilter *) PURE;
485 STDMETHOD(EnumFilters)(IEnumFilters **) PURE;
486 STDMETHOD(FindFilterByName)(LPCWSTR, IBaseFilter **) PURE;
487 STDMETHOD(ConnectDirect)(IPin *, IPin *, const AM_MEDIA_TYPE *) PURE;
488 STDMETHOD(Reconnect)(IPin *) PURE;
489 STDMETHOD(Disconnect)(IPin *) PURE;
490 STDMETHOD(SetDefaultSyncSource)() PURE;
491 };
492
493 struct IGraphBuilder : public IFilterGraph
494 {
495 public:
496 STDMETHOD(Connect)(IPin *, IPin *) PURE;
497 STDMETHOD(Render)(IPin *) PURE;
498 STDMETHOD(RenderFile)(LPCWSTR, LPCWSTR) PURE;
499 STDMETHOD(AddSourceFilter)(LPCWSTR, LPCWSTR, IBaseFilter **) PURE;
500 STDMETHOD(SetLogFile)(DWORD_PTR) PURE;
501 STDMETHOD(Abort)() PURE;
502 STDMETHOD(ShouldOperationContinue)() PURE;
503 };
504
505 //------------------------------------------------------------------
506 // wxAMMediaBackend (Active Movie)
507 //------------------------------------------------------------------
508 class WXDLLIMPEXP_MEDIA wxAMMediaThread : public wxThread
509 {
510 public:
511 virtual ExitCode Entry();
512
513 class wxAMMediaBackend* pThis;
514 };
515
516 //cludgy workaround for wx events. slots would be nice :)
517 class WXDLLIMPEXP_MEDIA wxAMMediaEvtHandler : public wxEvtHandler
518 {
519 public:
520 void OnPaint(wxPaintEvent&);
521 void OnMove(wxMoveEvent&);
522 void OnSize(wxSizeEvent&);
523 void OnEraseBackground(wxEraseEvent&);
524 };
525
526 typedef BOOL (WINAPI* LPAMGETERRORTEXT)(HRESULT, wxChar *, DWORD);
527
528 class WXDLLIMPEXP_MEDIA wxAMMediaBackend : public wxMediaBackend
529 {
530 public:
531 wxAMMediaBackend();
532
533 virtual ~wxAMMediaBackend();
534
535 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
536 wxWindowID id,
537 const wxPoint& pos,
538 const wxSize& size,
539 long style,
540 const wxValidator& validator,
541 const wxString& name);
542
543 virtual bool Play();
544 virtual bool Pause();
545 virtual bool Stop();
546
547 virtual bool Load(const wxString& fileName);
548 virtual bool Load(const wxURI& location);
549
550 virtual wxMediaState GetState();
551
552 virtual bool SetPosition(wxLongLong where);
553 virtual wxLongLong GetPosition();
554 virtual wxLongLong GetDuration();
555
556 virtual void Move(int x, int y, int w, int h);
557 wxSize GetVideoSize() const;
558
559 virtual double GetPlaybackRate();
560 virtual bool SetPlaybackRate(double);
561
562 virtual double GetVolume();
563 virtual bool SetVolume(double);
564
565 void Cleanup();
566 void OnStop();
567 bool SetWindowlessMode(IGraphBuilder* pGB,
568 IVMRWindowlessControl** ppVMC = NULL);
569
570 wxControl* m_ctrl;
571
572 wxMediaState m_state;
573 wxCriticalSection m_rendercs;
574
575 IVMRWindowlessControl* m_pVMC;
576 IGraphBuilder* m_pGB;
577 IBasicAudio* m_pBA;
578 IMediaControl* m_pMC;
579 IMediaEvent* m_pME;
580 IMediaPosition* m_pMS;
581 bool m_bVideo;
582
583 wxAMMediaThread* m_pThread;
584
585 wxSize m_bestSize;
586
587 #ifdef __WXDEBUG__
588 HMODULE m_hQuartzDll;
589 LPAMGETERRORTEXT m_lpAMGetErrorText;
590 #endif
591
592 friend class wxAMMediaThread;
593 friend class wxAMMediaEvtHandler;
594
595 DECLARE_DYNAMIC_CLASS(wxAMMediaBackend)
596 };
597
598 //---------------------------------------------------------------------------
599 //
600 // wxMCIMediaBackend
601 //
602 //---------------------------------------------------------------------------
603
604 //---------------------------------------------------------------------------
605 // MCI Includes
606 //---------------------------------------------------------------------------
607 #include <mmsystem.h>
608
609 class WXDLLIMPEXP_MEDIA wxMCIMediaBackend : public wxMediaBackend
610 {
611 public:
612
613 wxMCIMediaBackend();
614 ~wxMCIMediaBackend();
615
616 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
617 wxWindowID id,
618 const wxPoint& pos,
619 const wxSize& size,
620 long style,
621 const wxValidator& validator,
622 const wxString& name);
623
624 virtual bool Play();
625 virtual bool Pause();
626 virtual bool Stop();
627
628 virtual bool Load(const wxString& fileName);
629 virtual bool Load(const wxURI& location);
630
631 virtual wxMediaState GetState();
632
633 virtual bool SetPosition(wxLongLong where);
634 virtual wxLongLong GetPosition();
635 virtual wxLongLong GetDuration();
636
637 virtual void Move(int x, int y, int w, int h);
638 wxSize GetVideoSize() const;
639
640 virtual double GetPlaybackRate();
641 virtual bool SetPlaybackRate(double dRate);
642
643 virtual double GetVolume();
644 virtual bool SetVolume(double);
645
646 static LRESULT CALLBACK NotifyWndProc(HWND hWnd, UINT nMsg,
647 WPARAM wParam, LPARAM lParam);
648
649 LRESULT CALLBACK OnNotifyWndProc(HWND hWnd, UINT nMsg,
650 WPARAM wParam, LPARAM lParam);
651
652 MCIDEVICEID m_hDev; //Our MCI Device ID/Handler
653 wxControl* m_ctrl; //Parent control
654 HWND m_hNotifyWnd; //Window to use for MCI events
655 bool m_bVideo; //Whether or not we have video
656
657 DECLARE_DYNAMIC_CLASS(wxMCIMediaBackend)
658 };
659
660 //---------------------------------------------------------------------------
661 //
662 // wxQTMediaBackend
663 //
664 //---------------------------------------------------------------------------
665
666 //---------------------------------------------------------------------------
667 // QT Includes
668 //---------------------------------------------------------------------------
669 //#include <qtml.h> //Windoze QT include
670 //#include <QuickTimeComponents.h> //Standard QT stuff
671 #include "wx/dynlib.h"
672
673 //---------------------------------------------------------------------------
674 // QT Types
675 //---------------------------------------------------------------------------
676 typedef struct MovieRecord* Movie;
677 typedef wxInt16 OSErr;
678 typedef wxInt32 OSStatus;
679 #define noErr 0
680 #define fsRdPerm 1
681 typedef unsigned char Str255[256];
682 #define StringPtr unsigned char*
683 #define newMovieActive 1
684 #define Ptr char*
685 #define Handle Ptr*
686 #define Fixed long
687 #define OSType unsigned long
688 #define CGrafPtr struct GrafPort *
689 #define TimeScale long
690 #define TimeBase struct TimeBaseRecord *
691
692 #ifndef URLDataHandlerSubType
693 #if defined(__WATCOMC__) || defined(__MINGW32__)
694 // use magic numbers for compilers which complain about multicharacter integers
695 const OSType URLDataHandlerSubType = 1970433056;
696 const OSType VisualMediaCharacteristic = 1702454643;
697 #else
698 const OSType URLDataHandlerSubType = 'url ';
699 const OSType VisualMediaCharacteristic = 'eyes';
700 #endif
701 #endif
702
703 struct FSSpec {
704 short vRefNum;
705 long parID;
706 Str255 name; /*Str63 on mac, Str255 on msw */
707 };
708
709 struct Rect {
710 short top;
711 short left;
712 short bottom;
713 short right;
714 };
715
716 struct wide {
717 wxInt32 hi;
718 wxUint32 lo;
719 };
720
721 struct TimeRecord {
722 wide value; /* units */
723 TimeScale scale; /* units per second */
724 TimeBase base;
725 };
726
727 //---------------------------------------------------------------------------
728 // QT Library
729 //---------------------------------------------------------------------------
730 #define wxDL_METHOD_DEFINE( rettype, name, args, shortargs, defret ) \
731 typedef rettype (* name ## Type) args ; \
732 name ## Type pfn_ ## name; \
733 rettype name args \
734 { if (m_ok) return pfn_ ## name shortargs ; return defret; }
735
736 #define wxDL_VOIDMETHOD_DEFINE( name, args, shortargs ) \
737 typedef void (* name ## Type) args ; \
738 name ## Type pfn_ ## name; \
739 void name args \
740 { if (m_ok) pfn_ ## name shortargs ; }
741
742 #define wxDL_METHOD_LOAD( lib, name, success ) \
743 pfn_ ## name = (name ## Type) lib.GetSymbol( wxT(#name), &success ); \
744 if (!success) return false;
745
746 //Class that utilizes Robert Roeblings Dynamic Library Macros
747 class WXDLLIMPEXP_MEDIA wxQuickTimeLibrary
748 {
749 public:
750 ~wxQuickTimeLibrary()
751 {
752 if(m_dll.IsLoaded())
753 m_dll.Unload();
754 }
755
756 bool Initialize();
757 bool IsOk() const {return m_ok;}
758
759 protected:
760 wxDynamicLibrary m_dll;
761 bool m_ok;
762
763 public:
764 wxDL_VOIDMETHOD_DEFINE( StartMovie, (Movie m), (m) );
765 wxDL_VOIDMETHOD_DEFINE( StopMovie, (Movie m), (m) );
766 wxDL_METHOD_DEFINE( bool, IsMovieDone, (Movie m), (m), false);
767 wxDL_VOIDMETHOD_DEFINE( GoToBeginningOfMovie, (Movie m), (m) );
768 wxDL_METHOD_DEFINE( OSErr, GetMoviesError, (), (), -1);
769 wxDL_METHOD_DEFINE( OSErr, EnterMovies, (), (), -1);
770 wxDL_VOIDMETHOD_DEFINE( ExitMovies, (), () );
771 wxDL_METHOD_DEFINE( OSErr, InitializeQTML, (long flags), (flags), -1);
772 wxDL_VOIDMETHOD_DEFINE( TerminateQTML, (), () );
773
774 wxDL_METHOD_DEFINE( OSErr, NativePathNameToFSSpec,
775 (char* inName, FSSpec* outFile, long flags),
776 (inName, outFile, flags), -1);
777
778 wxDL_METHOD_DEFINE( OSErr, OpenMovieFile,
779 (const FSSpec * fileSpec, short * resRefNum, wxInt8 permission),
780 (fileSpec, resRefNum, permission), -1 );
781
782 wxDL_METHOD_DEFINE( OSErr, CloseMovieFile,
783 (short resRefNum), (resRefNum), -1);
784
785 wxDL_METHOD_DEFINE( OSErr, NewMovieFromFile,
786 (Movie * theMovie, short resRefNum, short * resId,
787 StringPtr resName, short newMovieFlags,
788 bool * dataRefWasChanged),
789 (theMovie, resRefNum, resId, resName, newMovieFlags,
790 dataRefWasChanged), -1);
791
792 wxDL_VOIDMETHOD_DEFINE( SetMovieRate, (Movie m, Fixed rate), (m, rate) );
793 wxDL_METHOD_DEFINE( Fixed, GetMovieRate, (Movie m), (m), 0);
794 wxDL_VOIDMETHOD_DEFINE( MoviesTask, (Movie m, long maxms), (m, maxms) );
795 wxDL_VOIDMETHOD_DEFINE( BlockMove,
796 (const char* p1, const char* p2, long s), (p1,p2,s) );
797 wxDL_METHOD_DEFINE( Handle, NewHandleClear, (long s), (s), NULL );
798
799 wxDL_METHOD_DEFINE( OSErr, NewMovieFromDataRef,
800 (Movie * m, short flags, short * id,
801 Handle dataRef, OSType dataRefType),
802 (m,flags,id,dataRef,dataRefType), -1 );
803
804 wxDL_VOIDMETHOD_DEFINE( DisposeHandle, (Handle h), (h) );
805 wxDL_VOIDMETHOD_DEFINE( GetMovieNaturalBoundsRect, (Movie m, Rect* r), (m,r) );
806 wxDL_METHOD_DEFINE( void*, GetMovieIndTrackType,
807 (Movie m, long index, OSType type, long flags),
808 (m,index,type,flags), NULL );
809 wxDL_VOIDMETHOD_DEFINE( CreatePortAssociation,
810 (void* hWnd, void* junk, long morejunk), (hWnd, junk, morejunk) );
811 wxDL_METHOD_DEFINE(void*, GetNativeWindowPort, (void* hWnd), (hWnd), NULL);
812 wxDL_VOIDMETHOD_DEFINE(SetMovieGWorld, (Movie m, CGrafPtr port, void* whatever),
813 (m, port, whatever) );
814 wxDL_VOIDMETHOD_DEFINE(DisposeMovie, (Movie m), (m) );
815 wxDL_VOIDMETHOD_DEFINE(SetMovieBox, (Movie m, Rect* r), (m,r));
816 wxDL_VOIDMETHOD_DEFINE(SetMovieTimeScale, (Movie m, long s), (m,s));
817 wxDL_METHOD_DEFINE(long, GetMovieDuration, (Movie m), (m), 0);
818 wxDL_METHOD_DEFINE(TimeBase, GetMovieTimeBase, (Movie m), (m), 0);
819 wxDL_METHOD_DEFINE(TimeScale, GetMovieTimeScale, (Movie m), (m), 0);
820 wxDL_METHOD_DEFINE(long, GetMovieTime, (Movie m, void* cruft), (m,cruft), 0);
821 wxDL_VOIDMETHOD_DEFINE(SetMovieTime, (Movie m, TimeRecord* tr), (m,tr) );
822 wxDL_METHOD_DEFINE(short, GetMovieVolume, (Movie m), (m), 0);
823 wxDL_VOIDMETHOD_DEFINE(SetMovieVolume, (Movie m, short sVolume), (m,sVolume) );
824 };
825
826 bool wxQuickTimeLibrary::Initialize()
827 {
828 m_ok = false;
829
830 if(!m_dll.Load(wxT("qtmlClient.dll")))
831 return false;
832
833 bool bOk;
834
835 wxDL_METHOD_LOAD( m_dll, StartMovie, bOk );
836 wxDL_METHOD_LOAD( m_dll, StopMovie, bOk );
837 wxDL_METHOD_LOAD( m_dll, IsMovieDone, bOk );
838 wxDL_METHOD_LOAD( m_dll, GoToBeginningOfMovie, bOk );
839 wxDL_METHOD_LOAD( m_dll, GetMoviesError, bOk );
840 wxDL_METHOD_LOAD( m_dll, EnterMovies, bOk );
841 wxDL_METHOD_LOAD( m_dll, ExitMovies, bOk );
842 wxDL_METHOD_LOAD( m_dll, InitializeQTML, bOk );
843 wxDL_METHOD_LOAD( m_dll, TerminateQTML, bOk );
844 wxDL_METHOD_LOAD( m_dll, NativePathNameToFSSpec, bOk );
845 wxDL_METHOD_LOAD( m_dll, OpenMovieFile, bOk );
846 wxDL_METHOD_LOAD( m_dll, CloseMovieFile, bOk );
847 wxDL_METHOD_LOAD( m_dll, NewMovieFromFile, bOk );
848 wxDL_METHOD_LOAD( m_dll, GetMovieRate, bOk );
849 wxDL_METHOD_LOAD( m_dll, SetMovieRate, bOk );
850 wxDL_METHOD_LOAD( m_dll, MoviesTask, bOk );
851 wxDL_METHOD_LOAD( m_dll, BlockMove, bOk );
852 wxDL_METHOD_LOAD( m_dll, NewHandleClear, bOk );
853 wxDL_METHOD_LOAD( m_dll, NewMovieFromDataRef, bOk );
854 wxDL_METHOD_LOAD( m_dll, DisposeHandle, bOk );
855 wxDL_METHOD_LOAD( m_dll, GetMovieNaturalBoundsRect, bOk );
856 wxDL_METHOD_LOAD( m_dll, GetMovieIndTrackType, bOk );
857 wxDL_METHOD_LOAD( m_dll, CreatePortAssociation, bOk );
858 wxDL_METHOD_LOAD( m_dll, GetNativeWindowPort, bOk );
859 wxDL_METHOD_LOAD( m_dll, SetMovieGWorld, bOk );
860 wxDL_METHOD_LOAD( m_dll, DisposeMovie, bOk );
861 wxDL_METHOD_LOAD( m_dll, SetMovieBox, bOk );
862 wxDL_METHOD_LOAD( m_dll, SetMovieTimeScale, bOk );
863 wxDL_METHOD_LOAD( m_dll, GetMovieDuration, bOk );
864 wxDL_METHOD_LOAD( m_dll, GetMovieTimeBase, bOk );
865 wxDL_METHOD_LOAD( m_dll, GetMovieTimeScale, bOk );
866 wxDL_METHOD_LOAD( m_dll, GetMovieTime, bOk );
867 wxDL_METHOD_LOAD( m_dll, SetMovieTime, bOk );
868 wxDL_METHOD_LOAD( m_dll, GetMovieVolume, bOk );
869 wxDL_METHOD_LOAD( m_dll, SetMovieVolume, bOk );
870
871 m_ok = true;
872
873 return true;
874 }
875
876 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
877 {
878 public:
879 wxQTMediaBackend();
880 ~wxQTMediaBackend();
881
882 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
883 wxWindowID id,
884 const wxPoint& pos,
885 const wxSize& size,
886 long style,
887 const wxValidator& validator,
888 const wxString& name);
889
890 virtual bool Play();
891 virtual bool Pause();
892 virtual bool Stop();
893
894 virtual bool Load(const wxString& fileName);
895 virtual bool Load(const wxURI& location);
896
897 virtual wxMediaState GetState();
898
899 virtual bool SetPosition(wxLongLong where);
900 virtual wxLongLong GetPosition();
901 virtual wxLongLong GetDuration();
902
903 virtual void Move(int x, int y, int w, int h);
904 wxSize GetVideoSize() const;
905
906 virtual double GetPlaybackRate();
907 virtual bool SetPlaybackRate(double dRate);
908
909 virtual double GetVolume();
910 virtual bool SetVolume(double);
911
912 void Cleanup();
913 void FinishLoad();
914
915 wxSize m_bestSize; //Original movie size
916 Movie m_movie; //QT Movie handle/instance
917 wxControl* m_ctrl; //Parent control
918 bool m_bVideo; //Whether or not we have video
919 class _wxQTTimer* m_timer; //Timer for streaming the movie
920 wxQuickTimeLibrary m_lib;
921
922 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
923 };
924
925
926 //===========================================================================
927 // IMPLEMENTATION
928 //===========================================================================
929
930 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
931 //
932 // wxAMMediaBackend
933 //
934 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
935
936 IMPLEMENT_DYNAMIC_CLASS(wxAMMediaBackend, wxMediaBackend);
937
938 //---------------------------------------------------------------------------
939 // Usual debugging macros
940 //---------------------------------------------------------------------------
941 #ifdef __WXDEBUG__
942 #include "wx/msgdlg.h"
943 #define MAX_ERROR_TEXT_LEN 160
944 //
945 // FIXME : Just use wxASSERT_MSG here instead of msgdlg - but
946 // stackwalker still crashes win2k, so do msgdlg for now...
947 //
948 #define wxAMVERIFY(x) \
949 { \
950 HRESULT hrdsv = (x); \
951 if ( FAILED(hrdsv) ) \
952 { \
953 wxChar szError[MAX_ERROR_TEXT_LEN]; \
954 if( m_lpAMGetErrorText != NULL && \
955 (*m_lpAMGetErrorText)(hrdsv, szError, MAX_ERROR_TEXT_LEN) == 0) \
956 { \
957 wxMessageBox( wxString::Format(wxT("DirectShow error \"%s\" \n")\
958 wxT("(numeric %i)\n")\
959 wxT("occured at line %i in ") \
960 wxT("mediactrl.cpp"), \
961 (int)hrdsv, szError, __LINE__) ); \
962 wxASSERT(false);\
963 } \
964 else \
965 { \
966 wxMessageBox( wxString::Format(wxT("Unknown error (%i) ") \
967 wxT("occurred at") \
968 wxT(" line %i in mediactrl.cpp."), \
969 (int)hrdsv, __LINE__) ); \
970 wxASSERT(false);\
971 } \
972 } \
973 }
974 #define wxVERIFY(x) wxASSERT((x))
975 #else
976 #define wxAMVERIFY(x) (x)
977 #define wxVERIFY(x) (x)
978 #endif
979
980 //---------------------------------------------------------------------------
981 // Standard macros for ease of use
982 //---------------------------------------------------------------------------
983 #define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }
984
985 //---------------------------------------------------------------------------
986 // wxAMMediaBackend Constructor
987 //
988 // Sets m_hNotifyWnd to NULL to signify that we haven't loaded anything yet
989 //---------------------------------------------------------------------------
990 wxAMMediaBackend::wxAMMediaBackend() : m_state(wxMEDIASTATE_STOPPED),
991 m_pVMC(NULL)
992 #ifdef __WXDEBUG__
993 , m_hQuartzDll(NULL)
994 #endif
995 {
996 }
997
998 //---------------------------------------------------------------------------
999 // wxAMMediaBackend Destructor
1000 //
1001 // Cleans up everything
1002 //---------------------------------------------------------------------------
1003 wxAMMediaBackend::~wxAMMediaBackend()
1004 {
1005 if (m_pVMC)
1006 Cleanup();
1007 #ifdef __WXDEBUG__
1008 if(m_hQuartzDll)
1009 ::FreeLibrary(m_hQuartzDll);
1010 #endif
1011 }
1012
1013 //---------------------------------------------------------------------------
1014 // wxAMMediaBackend::CreateControl
1015 //
1016 // 1) Check to see if Active Movie supports windowless controls
1017 // 2) Connect events to the media control and its TLW
1018 //---------------------------------------------------------------------------
1019 bool wxAMMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
1020 wxWindowID id,
1021 const wxPoint& pos,
1022 const wxSize& size,
1023 long style,
1024 const wxValidator& validator,
1025 const wxString& name)
1026 {
1027 #ifdef __WXDEBUG__
1028 m_hQuartzDll = ::LoadLibrary(wxT("quartz.dll"));
1029 if(m_hQuartzDll)
1030 {
1031 m_lpAMGetErrorText = (LPAMGETERRORTEXT) ::GetProcAddress(
1032 m_hQuartzDll,
1033 wxString::Format(wxT("AMGetErrorText%s"),
1034
1035 #ifdef __WXUNICODE__
1036 wxT("W")
1037 #else
1038 wxT("A")
1039 #endif
1040 #ifdef __WXWINCE__
1041 )
1042 #else
1043 ).mb_str(wxConvLocal)
1044 #endif
1045 );
1046 }
1047 #endif
1048
1049 //Make sure a valid windowless video mixing interface exists
1050 IGraphBuilder* pGB;
1051 if( CoCreateInstance(CLSID_FilgraphManager, NULL,
1052 CLSCTX_INPROC_SERVER,
1053 IID_IGraphBuilder, (void**)&pGB) != 0 )
1054 return false;
1055
1056 if( !SetWindowlessMode(pGB) )
1057 return false;
1058
1059 //clean up
1060 pGB->Release();
1061
1062 //
1063 // Create window
1064 // By default wxWindow(s) is created with a border -
1065 // so we need to get rid of those, and create with
1066 // wxCLIP_CHILDREN, so that if the driver/backend
1067 // is a child window, it refreshes properly
1068 //
1069 if ( !ctrl->wxControl::Create(parent, id, pos, size,
1070 (style & ~wxBORDER_MASK) | wxBORDER_NONE | wxCLIP_CHILDREN,
1071 validator, name) )
1072 return false;
1073
1074 m_ctrl = ctrl;
1075
1076 //
1077 // Connect Events
1078 //
1079 //TODO: Greg Hazel reports problems with this... but win2k seems fine on mine...
1080 //-------------------------------------------------------------------------------
1081 // My problem with this was only with a previous patch, probably the third rewrite
1082 // fixed it as a side-effect. In fact, the erase background style of drawing not
1083 // only works now, but is much better than paint-based updates (the paint event
1084 // handler flickers if the wxMediaCtrl shares a sizer with another child window,
1085 // or is on a notebook)
1086 // - Greg Hazel
1087 m_ctrl->Connect(m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
1088 wxEraseEventHandler(wxAMMediaEvtHandler::OnEraseBackground),
1089 NULL, (wxEvtHandler*) this);
1090 //m_ctrl->Connect(m_ctrl->GetId(), wxEVT_PAINT,
1091 // wxPaintEventHandler(wxAMMediaEvtHandler::OnPaint),
1092 // NULL, (wxEvtHandler*) this);
1093
1094 //
1095 // As noted below, we need to catch the Top Level Window's
1096 // move events because they are not sent to us if the media control
1097 // size remains the same but it actually moves in window coordinates
1098 //
1099 wxWindow* pTheTLW = m_ctrl->GetParent();
1100 while( pTheTLW->GetParent() )
1101 pTheTLW = pTheTLW->GetParent();
1102
1103 //
1104 // FIXMEHACKFIXMEHACKFIXME
1105 // This is really nasty... basically the deal is not only above
1106 // but the repainting is messed up when the parent is maximized
1107 // too, so we've got to catch all 4 events!
1108 //
1109 m_ctrl->Connect(m_ctrl->GetId(), wxEVT_MOVE,
1110 wxMoveEventHandler(wxAMMediaEvtHandler::OnMove),
1111 NULL, (wxEvtHandler*) this);
1112 m_ctrl->Connect(m_ctrl->GetId(), wxEVT_SIZE,
1113 wxSizeEventHandler(wxAMMediaEvtHandler::OnSize),
1114 NULL, (wxEvtHandler*) this);
1115
1116 pTheTLW->Connect(pTheTLW->GetId(), wxEVT_MOVE,
1117 wxMoveEventHandler(wxAMMediaEvtHandler::OnMove),
1118 NULL, (wxEvtHandler*) this);
1119 pTheTLW->Connect(pTheTLW->GetId(), wxEVT_SIZE,
1120 wxSizeEventHandler(wxAMMediaEvtHandler::OnSize),
1121 NULL, (wxEvtHandler*) this);
1122
1123 //
1124 // done...
1125 //
1126 return true;
1127 }
1128
1129 //---------------------------------------------------------------------------
1130 // wxAMMediaBackend::SetWindowlessMode
1131 //
1132 // Adds a Video Mixing Renderer to a Filter Graph and obtains the
1133 // windowless control from it
1134 //---------------------------------------------------------------------------
1135 bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB,
1136 IVMRWindowlessControl** ppVMC)
1137 {
1138 //
1139 // Create and add a custom Video Mixing Render to the graph
1140 //
1141 IBaseFilter* pVMR;
1142 if( CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC_SERVER,
1143 IID_IBaseFilter, (void**)&pVMR) != 0 )
1144 return false;
1145
1146 if ( pGB->AddFilter(pVMR, L"Video Mixing Renderer") != 0)
1147 {
1148 pVMR->Release();
1149 return false;
1150 }
1151
1152 //
1153 // Set the graph to windowless mode
1154 //
1155 IVMRFilterConfig* pConfig;
1156 if( pVMR->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig) != 0 )
1157 {
1158 pVMR->Release();
1159 return false;
1160 }
1161
1162 if( pConfig->SetRenderingMode(2) != 0) //2 == VMRMode_Windowless
1163 {
1164 pConfig->Release();
1165 pVMR->Release();
1166 return false;
1167 }
1168
1169 pConfig->Release();
1170
1171 //
1172 // Obtain the windowless control
1173 //
1174 IVMRWindowlessControl* pVMC;
1175 if( pVMR->QueryInterface(IID_IVMRWindowlessControl, (void**)&pVMC) != 0 )
1176 {
1177 pVMR->Release();
1178 return false;
1179 }
1180
1181 //
1182 // Success
1183 //
1184 if(ppVMC)
1185 *ppVMC = pVMC;
1186 else
1187 pVMC->Release();
1188
1189 pVMR->Release();
1190 return true;
1191 }
1192
1193 //---------------------------------------------------------------------------
1194 // wxAMMediaBackend::Load (file version)
1195 //
1196 // 1) Cleans up previously loaded data
1197 // 2) Creates a filter graph
1198 // 3) Add a video mixer, set the graph to windowless mode and clip
1199 // output to our media control
1200 // 4) Query interfaces to use later
1201 // 5) Get native video size (which becomes our best size)
1202 // 6) Refresh parent's sizers
1203 // 7) Start event/rendering thread
1204 //---------------------------------------------------------------------------
1205 bool wxAMMediaBackend::Load(const wxString& fileName)
1206 {
1207 //if previously loaded cleanup
1208 if(m_pVMC)
1209 Cleanup();
1210
1211 //Create interfaces - we already checked for success in CreateControl
1212 CoCreateInstance(CLSID_FilgraphManager, NULL, CLSCTX_INPROC_SERVER,
1213 IID_IGraphBuilder, (void**)&m_pGB);
1214
1215
1216 // Set and clip output
1217 SetWindowlessMode(m_pGB, &m_pVMC);
1218 if( m_pVMC->SetVideoClippingWindow((HWND)m_ctrl->GetHandle()) != 0 )
1219 {
1220 m_bestSize.x = m_bestSize.y = 0;
1221 wxASSERT(false);
1222 }
1223
1224 //load the graph & render
1225 if( m_pGB->RenderFile(fileName.wc_str(wxConvLocal), NULL) != 0 )
1226 return false;
1227
1228 //Get the interfaces, all of them
1229 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaEvent, (void**)&m_pME) );
1230 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaControl, (void**)&m_pMC) );
1231 wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaPosition, (void**)&m_pMS) );
1232 wxAMVERIFY( m_pGB->QueryInterface(IID_IBasicAudio, (void**)&m_pBA) );
1233
1234 // Get original video size
1235 if( m_pVMC->GetNativeVideoSize((LONG*)&m_bestSize.x, (LONG*)&m_bestSize.y,
1236 NULL, NULL) != 0 )
1237 {
1238 m_bestSize.x = m_bestSize.y = 0;
1239 wxASSERT(false);
1240 }
1241
1242 if(m_bestSize.x == 0 && m_bestSize.y == 0)
1243 m_bVideo = false;
1244 else
1245 m_bVideo = true;
1246
1247 //
1248 // Force the parent window of this control to recalculate
1249 // the size of this if sizers are being used
1250 // and render the results immediately
1251 //
1252 m_ctrl->InvalidateBestSize();
1253 m_ctrl->GetParent()->Layout();
1254 m_ctrl->GetParent()->Refresh();
1255 m_ctrl->GetParent()->Update();
1256 m_ctrl->SetSize(m_ctrl->GetSize());
1257
1258 m_pThread = new wxAMMediaThread;
1259 m_pThread->pThis = this;
1260 m_pThread->Create();
1261 m_pThread->Run();
1262
1263 return true;
1264 }
1265
1266 //---------------------------------------------------------------------------
1267 // wxAMMediaBackend::Load (URL Version)
1268 //
1269 // Loads media from a URL. Interestingly enough DirectShow
1270 // appears (?) to escape the URL for us, at least on normal
1271 // files
1272 //---------------------------------------------------------------------------
1273 bool wxAMMediaBackend::Load(const wxURI& location)
1274 {
1275 return Load(location.BuildUnescapedURI());
1276 }
1277
1278 //---------------------------------------------------------------------------
1279 // wxAMMediaBackend::Play
1280 //
1281 // Plays the stream. If it is non-seekable, it will restart it (implicit).
1282 //
1283 // Note that we use SUCCEEDED here because run/pause/stop tend to be overly
1284 // picky and return warnings on pretty much every call
1285 //---------------------------------------------------------------------------
1286 bool wxAMMediaBackend::Play()
1287 {
1288 wxCriticalSectionLocker lock(m_rendercs);
1289
1290 if( SUCCEEDED(m_pMC->Run()) )
1291 {
1292 m_state = wxMEDIASTATE_PLAYING;
1293 m_ctrl->Refresh(); //videoless control finicky about refreshing
1294 return true;
1295 }
1296
1297 return false;
1298 }
1299
1300 //---------------------------------------------------------------------------
1301 // wxAMMediaBackend::Pause
1302 //
1303 // Pauses the stream.
1304 //---------------------------------------------------------------------------
1305 bool wxAMMediaBackend::Pause()
1306 {
1307 wxCriticalSectionLocker lock(m_rendercs);
1308
1309 if( SUCCEEDED(m_pMC->Pause()) )
1310 {
1311 m_state = wxMEDIASTATE_PAUSED;
1312 return true;
1313 }
1314
1315 return false;
1316 }
1317
1318 //---------------------------------------------------------------------------
1319 // wxAMMediaBackend::Stop
1320 //
1321 // Stops the stream.
1322 //---------------------------------------------------------------------------
1323 bool wxAMMediaBackend::Stop()
1324 {
1325 wxCriticalSectionLocker lock(m_rendercs);
1326
1327 if( SUCCEEDED(m_pMC->Stop()) )
1328 {
1329 //We don't care if it can't get to the beginning in directshow -
1330 //it could be a non-seeking filter (wince midi) in which case playing
1331 //starts all over again
1332 wxAMMediaBackend::SetPosition(0);
1333
1334 m_state = wxMEDIASTATE_STOPPED;
1335 return true;
1336 }
1337
1338 return false;
1339 }
1340
1341 //---------------------------------------------------------------------------
1342 // wxAMMediaBackend::SetPosition
1343 //
1344 // 1) Translates the current position's time to directshow time,
1345 // which is in a scale of 1 second (in a double)
1346 // 2) Sets the play position of the IMediaSeeking interface -
1347 // passing NULL as the stop position means to keep the old
1348 // stop position
1349 //---------------------------------------------------------------------------
1350 bool wxAMMediaBackend::SetPosition(wxLongLong where)
1351 {
1352 return SUCCEEDED( m_pMS->put_CurrentPosition(
1353 ((LONGLONG)where.GetValue()) / 1000.0
1354 ) );
1355 }
1356
1357 //---------------------------------------------------------------------------
1358 // wxAMMediaBackend::GetPosition
1359 //
1360 // 1) Obtains the current play and stop positions from IMediaSeeking
1361 // 2) Returns the play position translated to our time base
1362 //---------------------------------------------------------------------------
1363 wxLongLong wxAMMediaBackend::GetPosition()
1364 {
1365 double outCur;
1366 wxAMVERIFY( m_pMS->get_CurrentPosition(&outCur) );
1367
1368 //h,m,s,milli - outdur is in 1 second (double)
1369 outCur *= 1000;
1370 wxLongLong ll;
1371 ll.Assign(outCur);
1372
1373 return ll;
1374 }
1375
1376 //---------------------------------------------------------------------------
1377 // wxAMMediaBackend::SetVolume
1378 //
1379 // Sets the volume through the IBasicAudio interface -
1380 // value ranges from 0 (MAX volume) to -10000 (minimum volume).
1381 // -100 per decibel.
1382 //---------------------------------------------------------------------------
1383 bool wxAMMediaBackend::SetVolume(double dVolume)
1384 {
1385 return SUCCEEDED(m_pBA->put_Volume( (long) ((dVolume-1.0) * 10000.0) ) );
1386 }
1387
1388 //---------------------------------------------------------------------------
1389 // wxAMMediaBackend::GetVolume
1390 //
1391 // Gets the volume through the IBasicAudio interface -
1392 // value ranges from 0 (MAX volume) to -10000 (minimum volume).
1393 // -100 per decibel.
1394 //---------------------------------------------------------------------------
1395 double wxAMMediaBackend::GetVolume()
1396 {
1397 long lVolume;
1398 if ( SUCCEEDED( m_pBA->get_Volume(&lVolume) ) )
1399 return (((double)(lVolume + 10000)) / 10000.0);
1400 return 0.0;
1401 }
1402
1403 //---------------------------------------------------------------------------
1404 // wxAMMediaBackend::GetDuration
1405 //
1406 // 1) Obtains the duration of the media from IAMMultiMediaStream
1407 // 2) Converts that value to our time base, and returns it
1408 //
1409 // NB: With VBR MP3 files the default DirectShow MP3 render does not
1410 // read the Xing header correctly, resulting in skewed values for duration
1411 // and seeking
1412 //---------------------------------------------------------------------------
1413 wxLongLong wxAMMediaBackend::GetDuration()
1414 {
1415 double outDuration;
1416 wxAMVERIFY( m_pMS->get_Duration(&outDuration) );
1417
1418 //h,m,s,milli - outdur is in 1 second (double)
1419 outDuration *= 1000;
1420 wxLongLong ll;
1421 ll.Assign(outDuration);
1422
1423 return ll;
1424 }
1425
1426 //---------------------------------------------------------------------------
1427 // wxAMMediaBackend::GetState
1428 //
1429 // Returns the cached state
1430 //---------------------------------------------------------------------------
1431 wxMediaState wxAMMediaBackend::GetState()
1432 {
1433 return m_state;
1434 }
1435
1436 //---------------------------------------------------------------------------
1437 // wxAMMediaBackend::GetPlaybackRate
1438 //
1439 // Pretty simple way of obtaining the playback rate from
1440 // the IMediaSeeking interface
1441 //---------------------------------------------------------------------------
1442 double wxAMMediaBackend::GetPlaybackRate()
1443 {
1444 double dRate;
1445 wxAMVERIFY( m_pMS->get_Rate(&dRate) );
1446 return dRate;
1447 }
1448
1449 //---------------------------------------------------------------------------
1450 // wxAMMediaBackend::SetPlaybackRate
1451 //
1452 // Sets the playback rate of the media - DirectShow is pretty good
1453 // about this, actually
1454 //---------------------------------------------------------------------------
1455 bool wxAMMediaBackend::SetPlaybackRate(double dRate)
1456 {
1457 return m_pMS->put_Rate(dRate) == 0;
1458 }
1459
1460 //---------------------------------------------------------------------------
1461 // wxAMMediaBackend::Cleanup
1462 //
1463 // Releases all the directshow interfaces we use
1464 // TODO: Maybe only create one instance of IAMMultiMediaStream and reuse it
1465 // rather than recreating it each time?
1466 //---------------------------------------------------------------------------
1467 void wxAMMediaBackend::Cleanup()
1468 {
1469 m_pThread->Delete();
1470 m_pThread = NULL;
1471
1472 // Release and zero DirectShow interfaces
1473 SAFE_RELEASE(m_pMC);
1474 SAFE_RELEASE(m_pME);
1475 SAFE_RELEASE(m_pMS);
1476 SAFE_RELEASE(m_pBA);
1477 SAFE_RELEASE(m_pGB);
1478 SAFE_RELEASE(m_pVMC);
1479 }
1480
1481
1482 //---------------------------------------------------------------------------
1483 // wxAMMediaBackend::GetVideoSize
1484 //
1485 // Obtains the cached original video size
1486 //---------------------------------------------------------------------------
1487 wxSize wxAMMediaBackend::GetVideoSize() const
1488 {
1489 return m_bestSize;
1490 }
1491
1492 //---------------------------------------------------------------------------
1493 // wxAMMediaBackend::Move
1494 //
1495 // We take care of this in our redrawing
1496 //---------------------------------------------------------------------------
1497 void wxAMMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y),
1498 int w, int h)
1499 {
1500 if(m_pVMC && m_bVideo)
1501 {
1502 RECT srcRect, destRect;
1503
1504 //portion of video to display in window
1505 srcRect.top = 0; srcRect.left = 0;
1506 srcRect.bottom = m_bestSize.y; srcRect.right = m_bestSize.x;
1507
1508 //it happens.
1509 if (w < 0)
1510 {
1511 w = 0;
1512 }
1513 if (h < 0)
1514 {
1515 h = 0;
1516 }
1517
1518 //position in window client coordinates to display and stretch to
1519 destRect.top = 0; destRect.left = 0;
1520 destRect.bottom = h; destRect.right = w;
1521
1522 //set the windowless control positions
1523 if( m_pVMC->SetVideoPosition(&srcRect, &destRect) != 0 )
1524 {
1525 wxASSERT_MSG(false, wxT("Could not set video position!"));
1526 }
1527
1528 /*
1529 //oddly enough, it doesn't redraw the frame after moving...
1530 //TODO: Use wxClientDC?
1531 HDC hdc = ::GetDC((HWND)m_ctrl->GetHandle());
1532 if( m_pVMC->RepaintVideo((HWND)m_ctrl->GetHandle(),
1533 hdc) != 0 )
1534 {
1535 wxASSERT(false);
1536 }
1537 ::ReleaseDC((HWND)m_ctrl->GetHandle(), hdc);
1538 */
1539 }
1540 }
1541
1542 //---------------------------------------------------------------------------
1543 // wxAMMediaEvtHandler::OnMove
1544 //
1545 // Oddly enough Move isn't called on MSW when the parent moves
1546 // and the child (us) doesn't, so we have to do it twice I guess :(
1547 //---------------------------------------------------------------------------
1548 void wxAMMediaEvtHandler::OnMove(wxMoveEvent& evt)
1549 {
1550 wxAMMediaBackend* pThis = (wxAMMediaBackend*) this;
1551 pThis->Move(pThis->m_ctrl->GetPosition().x,
1552 pThis->m_ctrl->GetPosition().y,
1553 pThis->m_ctrl->GetSize().x,
1554 pThis->m_ctrl->GetSize().y );
1555 evt.Skip();
1556 }
1557 void wxAMMediaEvtHandler::OnSize(wxSizeEvent& evt)
1558 {
1559 wxAMMediaBackend* pThis = (wxAMMediaBackend*) this;
1560 pThis->Move(pThis->m_ctrl->GetPosition().x,
1561 pThis->m_ctrl->GetPosition().y,
1562 pThis->m_ctrl->GetSize().x,
1563 pThis->m_ctrl->GetSize().y );
1564 evt.Skip();
1565 }
1566
1567 //---------------------------------------------------------------------------
1568 // wxAMMediaThread::Entry
1569 //
1570 // Render the current movie frame
1571 //---------------------------------------------------------------------------
1572 wxThread::ExitCode wxAMMediaThread::Entry()
1573 {
1574 while(!TestDestroy())
1575 {
1576 LONG evCode,
1577 evParam1,
1578 evParam2;
1579
1580 //
1581 // DirectShow keeps a list of queued events, and we need
1582 // to go through them one by one, stopping at (Hopefully only one)
1583 // EC_COMPLETE message
1584 //
1585 while( pThis->m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
1586 (LONG_PTR *) &evParam2, 0) == 0 )
1587 {
1588 // Cleanup memory that GetEvent allocated
1589 if( pThis->m_pME->FreeEventParams(evCode, evParam1, evParam2) != 0 )
1590 {
1591 wxASSERT(false);
1592 }
1593
1594 // If this is the end of the clip, notify handler
1595 if(1 == evCode) //EC_COMPLETE
1596 {
1597 pThis->OnStop();
1598 }
1599 }
1600
1601 Sleep(10);
1602 }
1603
1604 return NULL;
1605 }
1606
1607
1608 //---------------------------------------------------------------------------
1609 // wxAMMediaBackend::OnStop
1610 //
1611 // Handle stopping when the stream ends
1612 //---------------------------------------------------------------------------
1613 void wxAMMediaBackend::OnStop()
1614 {
1615 //send the event to our child
1616 wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId());
1617 m_ctrl->ProcessEvent(theEvent);
1618
1619 //if the user didn't veto it, stop the stream
1620 if (theEvent.IsAllowed())
1621 {
1622 //Interestingly enough, DirectShow does not actually stop
1623 //the filters - even when it reaches the end!
1624 wxVERIFY( Stop() );
1625
1626 //send the event to our child
1627 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
1628 m_ctrl->GetId());
1629 m_ctrl->ProcessEvent(theEvent);
1630 }
1631 }
1632
1633 //---------------------------------------------------------------------------
1634 // wxAMMediaEvtHandler::OnEraseBackground
1635 //
1636 // Tell WX not to erase the background of our control window
1637 //---------------------------------------------------------------------------
1638 void wxAMMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt)
1639 {
1640 wxAMMediaBackend* pThis = (wxAMMediaBackend*) this;
1641 if(pThis->m_pVMC && pThis->m_bVideo)
1642 {
1643 //TODO: Use wxClientDC?
1644 HDC hdc = ::GetDC((HWND)pThis->m_ctrl->GetHandle());
1645 if( pThis->m_pVMC->RepaintVideo((HWND)pThis->m_ctrl->GetHandle(),
1646 hdc) != 0 )
1647 {
1648 wxASSERT(false);
1649 }
1650 ::ReleaseDC((HWND)pThis->m_ctrl->GetHandle(), hdc);
1651 }
1652 else
1653 {
1654 evt.Skip();
1655 }
1656 }
1657
1658 //---------------------------------------------------------------------------
1659 // wxAMMediaEvtHandler::OnPaint
1660 //
1661 // Handle redrawing
1662 //---------------------------------------------------------------------------
1663 void wxAMMediaEvtHandler::OnPaint(wxPaintEvent& WXUNUSED(evt))
1664 {
1665 wxAMMediaBackend* pThis = (wxAMMediaBackend*) this;
1666 wxPaintDC dc(pThis->m_ctrl);
1667 if( pThis->m_pVMC && pThis->m_bVideo)
1668 {
1669 if( pThis->m_pVMC->RepaintVideo((HWND)pThis->m_ctrl->GetHandle(),
1670 (HDC)dc.GetHDC()) != 0 )
1671 {
1672 wxASSERT(false);
1673 }
1674 }
1675 }
1676
1677 //---------------------------------------------------------------------------
1678 // End of wxAMMediaBackend
1679 //---------------------------------------------------------------------------
1680
1681 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1682 //
1683 // wxMCIMediaBackend
1684 //
1685 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1686
1687 IMPLEMENT_DYNAMIC_CLASS(wxMCIMediaBackend, wxMediaBackend);
1688
1689 //---------------------------------------------------------------------------
1690 // Usual debugging macros for MCI returns
1691 //---------------------------------------------------------------------------
1692
1693 #ifdef __WXDEBUG__
1694 #define wxMCIVERIFY(arg) \
1695 { \
1696 DWORD nRet; \
1697 if ( (nRet = (arg)) != 0) \
1698 { \
1699 TCHAR sz[5000]; \
1700 mciGetErrorString(nRet, sz, 5000); \
1701 wxFAIL_MSG(wxString::Format(_T("MCI Error:%s"), sz)); \
1702 } \
1703 }
1704 #else
1705 #define wxMCIVERIFY(arg) (arg);
1706 #endif
1707
1708 //---------------------------------------------------------------------------
1709 // Simulation for <digitalv.h>
1710 //
1711 // Mingw and possibly other compilers don't have the digitalv.h header
1712 // that is needed to have some essential features of mci work with
1713 // windows - so we provide the declarations for the types we use here
1714 //---------------------------------------------------------------------------
1715
1716 typedef struct {
1717 DWORD_PTR dwCallback;
1718 #ifdef MCI_USE_OFFEXT
1719 POINT ptOffset;
1720 POINT ptExtent;
1721 #else
1722 RECT rc;
1723 #endif
1724 } MCI_DGV_RECT_PARMS;
1725
1726 typedef struct {
1727 DWORD_PTR dwCallback;
1728 HWND hWnd;
1729 #ifndef _WIN32
1730 WORD wReserved1;
1731 #endif
1732 UINT nCmdShow;
1733 #ifndef _WIN32
1734 WORD wReserved2;
1735 #endif
1736 wxChar* lpstrText;
1737 } MCI_DGV_WINDOW_PARMS;
1738
1739 typedef struct {
1740 DWORD_PTR dwCallback;
1741 DWORD dwTimeFormat;
1742 DWORD dwAudio;
1743 DWORD dwFileFormat;
1744 DWORD dwSpeed;
1745 } MCI_DGV_SET_PARMS;
1746
1747 typedef struct {
1748 DWORD_PTR dwCallback;
1749 DWORD dwItem;
1750 DWORD dwValue;
1751 DWORD dwOver;
1752 wxChar* lpstrAlgorithm;
1753 wxChar* lpstrQuality;
1754 } MCI_DGV_SETAUDIO_PARMS;
1755
1756 //---------------------------------------------------------------------------
1757 // wxMCIMediaBackend Constructor
1758 //
1759 // Here we don't need to do much except say we don't have any video :)
1760 //---------------------------------------------------------------------------
1761 wxMCIMediaBackend::wxMCIMediaBackend() : m_hNotifyWnd(NULL), m_bVideo(false)
1762 {
1763 }
1764
1765 //---------------------------------------------------------------------------
1766 // wxMCIMediaBackend Destructor
1767 //
1768 // We close the mci device - note that there may not be an mci device here,
1769 // or it may fail - but we don't really care, since we're destructing
1770 //---------------------------------------------------------------------------
1771 wxMCIMediaBackend::~wxMCIMediaBackend()
1772 {
1773 if(m_hNotifyWnd)
1774 {
1775 mciSendCommand(m_hDev, MCI_CLOSE, 0, 0);
1776 DestroyWindow(m_hNotifyWnd);
1777 m_hNotifyWnd = NULL;
1778 }
1779 }
1780
1781 //---------------------------------------------------------------------------
1782 // wxMCIMediaBackend::Create
1783 //
1784 // Here we just tell wxMediaCtrl that mci does exist (which it does, on all
1785 // msw systems, at least in some form dating back to win16 days)
1786 //---------------------------------------------------------------------------
1787 bool wxMCIMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
1788 wxWindowID id,
1789 const wxPoint& pos,
1790 const wxSize& size,
1791 long style,
1792 const wxValidator& validator,
1793 const wxString& name)
1794 {
1795 //
1796 // Create window
1797 // By default wxWindow(s) is created with a border -
1798 // so we need to get rid of those, and create with
1799 // wxCLIP_CHILDREN, so that if the driver/backend
1800 // is a child window, it refereshes properly
1801 //
1802 if ( !ctrl->wxControl::Create(parent, id, pos, size,
1803 (style & ~wxBORDER_MASK) | wxBORDER_NONE | wxCLIP_CHILDREN,
1804 validator, name) )
1805 return false;
1806
1807 m_ctrl = ctrl;
1808 return true;
1809 }
1810
1811 //---------------------------------------------------------------------------
1812 // wxMCIMediaBackend::Load (file version)
1813 //
1814 // Here we have MCI load a file and device, set the time format to our
1815 // default (milliseconds), and set the video (if any) to play in the control
1816 //---------------------------------------------------------------------------
1817 bool wxMCIMediaBackend::Load(const wxString& fileName)
1818 {
1819 //
1820 //if the user already called load close the previous MCI device
1821 //
1822 if(m_hNotifyWnd)
1823 {
1824 mciSendCommand(m_hDev, MCI_CLOSE, 0, 0);
1825 DestroyWindow(m_hNotifyWnd);
1826 m_hNotifyWnd = NULL;
1827 }
1828
1829 //
1830 //Opens a file and has MCI select a device. Normally you'd put
1831 //MCI_OPEN_TYPE in addition to MCI_OPEN_ELEMENT - however if you
1832 //omit this it tells MCI to select the device instead. This is
1833 //good because we have no reliable way of "enumerating" the devices
1834 //in MCI
1835 //
1836 MCI_OPEN_PARMS openParms;
1837 openParms.lpstrElementName = (wxChar*) fileName.c_str();
1838
1839 if ( mciSendCommand(0, MCI_OPEN, MCI_OPEN_ELEMENT,
1840 (DWORD)(LPVOID)&openParms) != 0)
1841 return false;
1842
1843 m_hDev = openParms.wDeviceID;
1844
1845 //
1846 //Now set the time format for the device to milliseconds
1847 //
1848 MCI_SET_PARMS setParms;
1849 setParms.dwCallback = 0;
1850 setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
1851
1852 if (mciSendCommand(m_hDev, MCI_SET, MCI_SET_TIME_FORMAT,
1853 (DWORD)(LPVOID)&setParms) != 0)
1854 return false;
1855
1856 //
1857 //Now tell the MCI device to display the video in our wxMediaCtrl
1858 //
1859 MCI_DGV_WINDOW_PARMS windowParms;
1860 windowParms.hWnd = (HWND)m_ctrl->GetHandle();
1861
1862 m_bVideo = (mciSendCommand(m_hDev, MCI_WINDOW,
1863 0x00010000L, //MCI_DGV_WINDOW_HWND
1864 (DWORD)(LPVOID)&windowParms) == 0);
1865
1866 //
1867 // Create a hidden window and register to handle
1868 // MCI events
1869 // Note that wxCanvasClassName is already registered
1870 // and used by all wxWindows and normal wxControls
1871 //
1872 m_hNotifyWnd = ::CreateWindow
1873 (
1874 wxCanvasClassName,
1875 NULL,
1876 0, 0, 0, 0,
1877 0,
1878 (HWND) NULL,
1879 (HMENU)NULL,
1880 wxGetInstance(),
1881 (LPVOID) NULL
1882 );
1883
1884 if(!m_hNotifyWnd)
1885 {
1886 wxLogSysError( wxT("Could not create hidden needed for ")
1887 wxT("registering for DirectShow events!") );
1888
1889 return false;
1890 }
1891
1892 wxSetWindowProc(m_hNotifyWnd, wxMCIMediaBackend::NotifyWndProc);
1893
1894 ::SetWindowLong(m_hNotifyWnd, GWL_USERDATA,
1895 (LONG) this);
1896
1897 //
1898 //Here, if the parent of the control has a sizer - we
1899 //tell it to recalculate the size of this control since
1900 //the user opened a separate media file
1901 //
1902 m_ctrl->InvalidateBestSize();
1903 m_ctrl->GetParent()->Layout();
1904 m_ctrl->GetParent()->Refresh();
1905 m_ctrl->GetParent()->Update();
1906 m_ctrl->SetSize(m_ctrl->GetSize());
1907
1908 return true;
1909 }
1910
1911 //---------------------------------------------------------------------------
1912 // wxMCIMediaBackend::Load (URL version)
1913 //
1914 // MCI doesn't support URLs directly (?)
1915 //
1916 // TODO: Use wxURL/wxFileSystem and mmioInstallProc
1917 //---------------------------------------------------------------------------
1918 bool wxMCIMediaBackend::Load(const wxURI& WXUNUSED(location))
1919 {
1920 return false;
1921 }
1922
1923 //---------------------------------------------------------------------------
1924 // wxMCIMediaBackend::Play
1925 //
1926 // Plays/Resumes the MCI device... a couple notes:
1927 // 1) Certain drivers will crash and burn if we don't pass them an
1928 // MCI_PLAY_PARMS, despite the documentation that says otherwise...
1929 // 2) There is a MCI_RESUME command, but MCI_PLAY does the same thing
1930 // and will resume from a stopped state also, so there's no need to
1931 // call both, for example
1932 //---------------------------------------------------------------------------
1933 bool wxMCIMediaBackend::Play()
1934 {
1935 MCI_PLAY_PARMS playParms;
1936 playParms.dwCallback = (DWORD)m_hNotifyWnd;
1937
1938 bool bOK = ( mciSendCommand(m_hDev, MCI_PLAY, MCI_NOTIFY,
1939 (DWORD)(LPVOID)&playParms) == 0 );
1940
1941 if(bOK)
1942 m_ctrl->Show(m_bVideo);
1943
1944 return bOK;
1945 }
1946
1947 //---------------------------------------------------------------------------
1948 // wxMCIMediaBackend::Pause
1949 //
1950 // Pauses the MCI device - nothing special
1951 //---------------------------------------------------------------------------
1952 bool wxMCIMediaBackend::Pause()
1953 {
1954 return (mciSendCommand(m_hDev, MCI_PAUSE, MCI_WAIT, 0) == 0);
1955 }
1956
1957 //---------------------------------------------------------------------------
1958 // wxMCIMediaBackend::Stop
1959 //
1960 // Stops the MCI device & seeks to the beginning as wxMediaCtrl docs outline
1961 //---------------------------------------------------------------------------
1962 bool wxMCIMediaBackend::Stop()
1963 {
1964 return (mciSendCommand(m_hDev, MCI_STOP, MCI_WAIT, 0) == 0) &&
1965 (mciSendCommand(m_hDev, MCI_SEEK, MCI_SEEK_TO_START, 0) == 0);
1966 }
1967
1968 //---------------------------------------------------------------------------
1969 // wxMCIMediaBackend::GetState
1970 //
1971 // Here we get the state and convert it to a wxMediaState -
1972 // since we use direct comparisons with MCI_MODE_PLAY and
1973 // MCI_MODE_PAUSE, we don't care if the MCI_STATUS call
1974 // fails or not
1975 //---------------------------------------------------------------------------
1976 wxMediaState wxMCIMediaBackend::GetState()
1977 {
1978 MCI_STATUS_PARMS statusParms;
1979 statusParms.dwItem = MCI_STATUS_MODE;
1980
1981 mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
1982 (DWORD)(LPVOID)&statusParms);
1983
1984 if(statusParms.dwReturn == MCI_MODE_PAUSE)
1985 return wxMEDIASTATE_PAUSED;
1986 else if(statusParms.dwReturn == MCI_MODE_PLAY)
1987 return wxMEDIASTATE_PLAYING;
1988 else
1989 return wxMEDIASTATE_STOPPED;
1990 }
1991
1992 //---------------------------------------------------------------------------
1993 // wxMCIMediaBackend::SetPosition
1994 //
1995 // Here we set the position of the device in the stream.
1996 // Note that MCI actually stops the device after you seek it if the
1997 // device is playing/paused, so we need to play the file after
1998 // MCI seeks like normal APIs would
1999 //---------------------------------------------------------------------------
2000 bool wxMCIMediaBackend::SetPosition(wxLongLong where)
2001 {
2002 MCI_SEEK_PARMS seekParms;
2003 seekParms.dwCallback = 0;
2004 #if wxUSE_LONGLONG_NATIVE && !wxUSE_LONGLONG_WX
2005 seekParms.dwTo = (DWORD)where.GetValue();
2006 #else /* wxUSE_LONGLONG_WX */
2007 /* no way to return it in one piece */
2008 wxASSERT( where.GetHi()==0 );
2009 seekParms.dwTo = (DWORD)where.GetLo();
2010 #endif /* wxUSE_LONGLONG_* */
2011
2012 //device was playing?
2013 bool bReplay = GetState() == wxMEDIASTATE_PLAYING;
2014
2015 if( mciSendCommand(m_hDev, MCI_SEEK, MCI_TO,
2016 (DWORD)(LPVOID)&seekParms) != 0)
2017 return false;
2018
2019 //If the device was playing, resume it
2020 if (bReplay)
2021 return Play();
2022 else
2023 return true;
2024 }
2025
2026 //---------------------------------------------------------------------------
2027 // wxMCIMediaBackend::GetPosition
2028 //
2029 // Gets the position of the device in the stream using the current
2030 // time format... nothing special here...
2031 //---------------------------------------------------------------------------
2032 wxLongLong wxMCIMediaBackend::GetPosition()
2033 {
2034 MCI_STATUS_PARMS statusParms;
2035 statusParms.dwItem = MCI_STATUS_POSITION;
2036
2037 if (mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
2038 (DWORD)(LPSTR)&statusParms) != 0)
2039 return 0;
2040
2041 return statusParms.dwReturn;
2042 }
2043
2044 //---------------------------------------------------------------------------
2045 // wxMCIMediaBackend::GetVolume
2046 //
2047 // Gets the volume of the current media via the MCI_DGV_STATUS_VOLUME
2048 // message. Value ranges from 0 (minimum) to 1000 (maximum volume).
2049 //---------------------------------------------------------------------------
2050 double wxMCIMediaBackend::GetVolume()
2051 {
2052 MCI_STATUS_PARMS statusParms;
2053 statusParms.dwCallback = 0;
2054 statusParms.dwItem = 0x4019; //MCI_DGV_STATUS_VOLUME
2055
2056 if (mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
2057 (DWORD)(LPSTR)&statusParms) != 0)
2058 return 0;
2059
2060 return ((double)statusParms.dwReturn) / 1000.0;
2061 }
2062
2063 //---------------------------------------------------------------------------
2064 // wxMCIMediaBackend::SetVolume
2065 //
2066 // Sets the volume of the current media via the MCI_DGV_SETAUDIO_VOLUME
2067 // message. Value ranges from 0 (minimum) to 1000 (maximum volume).
2068 //---------------------------------------------------------------------------
2069 bool wxMCIMediaBackend::SetVolume(double dVolume)
2070 {
2071 MCI_DGV_SETAUDIO_PARMS audioParms;
2072 audioParms.dwCallback = 0;
2073 audioParms.dwItem = 0x4002; //MCI_DGV_SETAUDIO_VOLUME
2074 audioParms.dwValue = (DWORD) (dVolume * 1000.0);
2075 audioParms.dwOver = 0;
2076 audioParms.lpstrAlgorithm = NULL;
2077 audioParms.lpstrQuality = NULL;
2078
2079 if (mciSendCommand(m_hDev, 0x0873, //MCI_SETAUDIO
2080 0x00800000L | 0x01000000L, //MCI_DGV_SETAUDIO+(_ITEM | _VALUE)
2081 (DWORD)(LPSTR)&audioParms) != 0)
2082 return false;
2083 return true;
2084 }
2085
2086 //---------------------------------------------------------------------------
2087 // wxMCIMediaBackend::GetDuration
2088 //
2089 // Gets the duration of the stream... nothing special
2090 //---------------------------------------------------------------------------
2091 wxLongLong wxMCIMediaBackend::GetDuration()
2092 {
2093 MCI_STATUS_PARMS statusParms;
2094 statusParms.dwItem = MCI_STATUS_LENGTH;
2095
2096 if (mciSendCommand(m_hDev, MCI_STATUS, MCI_STATUS_ITEM,
2097 (DWORD)(LPSTR)&statusParms) != 0)
2098 return 0;
2099
2100 return statusParms.dwReturn;
2101 }
2102
2103 //---------------------------------------------------------------------------
2104 // wxMCIMediaBackend::Move
2105 //
2106 // Moves the window to a location
2107 //---------------------------------------------------------------------------
2108 void wxMCIMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y),
2109 int w, int h)
2110 {
2111 if (m_hNotifyWnd && m_bVideo)
2112 {
2113 MCI_DGV_RECT_PARMS putParms; //ifdefed MCI_DGV_PUT_PARMS
2114 memset(&putParms, 0, sizeof(MCI_DGV_RECT_PARMS));
2115 putParms.rc.bottom = h;
2116 putParms.rc.right = w;
2117
2118 //wxStackWalker will crash and burn here on assert
2119 //and mci doesn't like 0 and 0 for some reason (out of range )
2120 //so just don't it in that case
2121 if(w || h)
2122 {
2123 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_PUT,
2124 0x00040000L, //MCI_DGV_PUT_DESTINATION
2125 (DWORD)(LPSTR)&putParms) );
2126 }
2127 }
2128 }
2129
2130 //---------------------------------------------------------------------------
2131 // wxMCIMediaBackend::GetVideoSize
2132 //
2133 // Gets the original size of the movie for sizers
2134 //---------------------------------------------------------------------------
2135 wxSize wxMCIMediaBackend::GetVideoSize() const
2136 {
2137 if(m_bVideo)
2138 {
2139 MCI_DGV_RECT_PARMS whereParms; //ifdefed MCI_DGV_WHERE_PARMS
2140
2141 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_WHERE,
2142 0x00020000L, //MCI_DGV_WHERE_SOURCE
2143 (DWORD)(LPSTR)&whereParms) );
2144
2145 return wxSize(whereParms.rc.right, whereParms.rc.bottom);
2146 }
2147 return wxSize(0,0);
2148 }
2149
2150 //---------------------------------------------------------------------------
2151 // wxMCIMediaBackend::GetPlaybackRate
2152 //
2153 // TODO
2154 //---------------------------------------------------------------------------
2155 double wxMCIMediaBackend::GetPlaybackRate()
2156 {
2157 return 1.0;
2158 }
2159
2160 //---------------------------------------------------------------------------
2161 // wxMCIMediaBackend::SetPlaybackRate
2162 //
2163 // TODO
2164 //---------------------------------------------------------------------------
2165 bool wxMCIMediaBackend::SetPlaybackRate(double WXUNUSED(dRate))
2166 {
2167 /*
2168 MCI_WAVE_SET_SAMPLESPERSEC
2169 MCI_DGV_SET_PARMS setParms;
2170 setParms.dwSpeed = (DWORD) (dRate * 1000.0);
2171
2172 return (mciSendCommand(m_hDev, MCI_SET,
2173 0x00020000L, //MCI_DGV_SET_SPEED
2174 (DWORD)(LPSTR)&setParms) == 0);
2175 */
2176 return false;
2177 }
2178
2179 //---------------------------------------------------------------------------
2180 // [static] wxMCIMediaBackend::MSWWindowProc
2181 //
2182 // Here we process a message when MCI reaches the stopping point
2183 // in the stream
2184 //---------------------------------------------------------------------------
2185 LRESULT CALLBACK wxMCIMediaBackend::NotifyWndProc(HWND hWnd, UINT nMsg,
2186 WPARAM wParam,
2187 LPARAM lParam)
2188 {
2189 wxMCIMediaBackend* backend = (wxMCIMediaBackend*)
2190 #ifdef _WIN32
2191 ::GetWindowLong(hWnd, GWL_USERDATA);
2192 #else
2193 ::GetWindowLongPtr(hWnd, GWLP_USERDATA);
2194 #endif
2195 wxASSERT(backend);
2196
2197 return backend->OnNotifyWndProc(hWnd, nMsg, wParam, lParam);
2198 }
2199
2200 LRESULT CALLBACK wxMCIMediaBackend::OnNotifyWndProc(HWND hWnd, UINT nMsg,
2201 WPARAM wParam,
2202 LPARAM lParam)
2203 {
2204 if(nMsg == MM_MCINOTIFY)
2205 {
2206 wxASSERT(lParam == (LPARAM) m_hDev);
2207 if(wParam == MCI_NOTIFY_SUCCESSFUL && lParam == (LPARAM)m_hDev)
2208 {
2209 wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId());
2210 m_ctrl->ProcessEvent(theEvent);
2211
2212 if(theEvent.IsAllowed())
2213 {
2214 wxMCIVERIFY( mciSendCommand(m_hDev, MCI_SEEK,
2215 MCI_SEEK_TO_START, 0) );
2216
2217 //send the event to our child
2218 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
2219 m_ctrl->GetId());
2220 m_ctrl->ProcessEvent(theEvent);
2221 }
2222 }
2223 }
2224 return DefWindowProc(hWnd, nMsg, wParam, lParam);
2225 }
2226 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2227 //
2228 // wxQTMediaBackend
2229 //
2230 // TODO: Use a less cludgy way to pause/get state/set state
2231 // TODO: Dynamically load from qtml.dll
2232 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2233
2234 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
2235
2236 //Time between timer calls
2237 #define MOVIE_DELAY 100
2238
2239 #include "wx/timer.h"
2240
2241 // --------------------------------------------------------------------------
2242 // wxQTTimer - Handle Asyncronous Playing
2243 // --------------------------------------------------------------------------
2244 class _wxQTTimer : public wxTimer
2245 {
2246 public:
2247 _wxQTTimer(Movie movie, wxQTMediaBackend* parent, wxQuickTimeLibrary* pLib) :
2248 m_movie(movie), m_bPaused(false), m_parent(parent), m_pLib(pLib)
2249 {
2250 }
2251
2252 ~_wxQTTimer()
2253 {
2254 }
2255
2256 bool GetPaused() {return m_bPaused;}
2257 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
2258
2259 //-----------------------------------------------------------------------
2260 // _wxQTTimer::Notify
2261 //
2262 // 1) Checks to see if the movie is done, and if not continues
2263 // streaming the movie
2264 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
2265 // the movie.
2266 //-----------------------------------------------------------------------
2267 void Notify()
2268 {
2269 if (!m_bPaused)
2270 {
2271 if(!m_pLib->IsMovieDone(m_movie))
2272 m_pLib->MoviesTask(m_movie, MOVIE_DELAY);
2273 else
2274 {
2275 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
2276 m_parent->m_ctrl->GetId());
2277 m_parent->m_ctrl->ProcessEvent(theEvent);
2278
2279 if(theEvent.IsAllowed())
2280 {
2281 Stop();
2282 m_parent->Stop();
2283 wxASSERT(m_pLib->GetMoviesError() == noErr);
2284
2285 //send the event to our child
2286 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
2287 m_parent->m_ctrl->GetId());
2288 m_parent->m_ctrl->ProcessEvent(theEvent);
2289 }
2290 }
2291 }
2292 }
2293
2294 protected:
2295 Movie m_movie; //Our movie instance
2296 bool m_bPaused; //Whether we are paused or not
2297 wxQTMediaBackend* m_parent; //Backend pointer
2298 wxQuickTimeLibrary* m_pLib; //Interfaces
2299 };
2300
2301 //---------------------------------------------------------------------------
2302 // wxQTMediaBackend Destructor
2303 //
2304 // Sets m_timer to NULL signifying we havn't loaded anything yet
2305 //---------------------------------------------------------------------------
2306 wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
2307 {
2308 }
2309
2310 //---------------------------------------------------------------------------
2311 // wxQTMediaBackend Destructor
2312 //
2313 // 1) Cleans up the QuickTime movie instance
2314 // 2) Decrements the QuickTime reference counter - if this reaches
2315 // 0, QuickTime shuts down
2316 // 3) Decrements the QuickTime Windows Media Layer reference counter -
2317 // if this reaches 0, QuickTime shuts down the Windows Media Layer
2318 //---------------------------------------------------------------------------
2319 wxQTMediaBackend::~wxQTMediaBackend()
2320 {
2321 if(m_timer)
2322 Cleanup();
2323
2324 if(m_lib.IsOk())
2325 {
2326 //Note that ExitMovies() is not necessary, but
2327 //the docs are fuzzy on whether or not TerminateQTML is
2328 m_lib.ExitMovies();
2329 m_lib.TerminateQTML();
2330 }
2331 }
2332
2333 //---------------------------------------------------------------------------
2334 // wxQTMediaBackend::CreateControl
2335 //
2336 // 1) Intializes QuickTime
2337 // 2) Creates the control window
2338 //---------------------------------------------------------------------------
2339 bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
2340 wxWindowID id,
2341 const wxPoint& pos,
2342 const wxSize& size,
2343 long style,
2344 const wxValidator& validator,
2345 const wxString& name)
2346 {
2347 if(!m_lib.Initialize())
2348 return false;
2349
2350 int nError = m_lib.InitializeQTML(0);
2351 if (nError != noErr) //-2093 no dll
2352 {
2353 wxFAIL_MSG(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
2354 return false;
2355 }
2356 m_lib.EnterMovies();
2357
2358 //
2359 // Create window
2360 // By default wxWindow(s) is created with a border -
2361 // so we need to get rid of those
2362 //
2363 // Since we don't have a child window like most other
2364 // backends, we don't need wxCLIP_CHILDREN
2365 //
2366 if ( !ctrl->wxControl::Create(parent, id, pos, size,
2367 (style & ~wxBORDER_MASK) | wxBORDER_NONE,
2368 validator, name) )
2369 return false;
2370
2371 m_ctrl = ctrl;
2372 return true;
2373 }
2374
2375 //---------------------------------------------------------------------------
2376 // wxQTMediaBackend::Load (file version)
2377 //
2378 // 1) Get an FSSpec from the Windows path name
2379 // 2) Open the movie
2380 // 3) Obtain the movie instance from the movie resource
2381 // 4)
2382 //---------------------------------------------------------------------------
2383 bool wxQTMediaBackend::Load(const wxString& fileName)
2384 {
2385 if(m_timer)
2386 Cleanup();
2387
2388 short movieResFile = 0; //= 0 because of annoying VC6 warning
2389 FSSpec sfFile;
2390
2391 if (m_lib.NativePathNameToFSSpec ((char*) (const char*) fileName.mb_str(),
2392 &sfFile, 0) != noErr)
2393 return false;
2394
2395 if (m_lib.OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
2396 return false;
2397
2398 short movieResID = 0;
2399 Str255 movieName;
2400
2401 OSErr err = m_lib.NewMovieFromFile (
2402 &m_movie,
2403 movieResFile,
2404 &movieResID,
2405 movieName,
2406 newMovieActive,
2407 NULL
2408 ); //wasChanged
2409
2410 m_lib.CloseMovieFile (movieResFile);
2411
2412 if (err != noErr)
2413 return false;
2414
2415 FinishLoad();
2416
2417 return m_lib.GetMoviesError() == noErr;
2418 }
2419
2420 //---------------------------------------------------------------------------
2421 // wxQTMediaBackend::Move
2422 //
2423 // TODO
2424 //---------------------------------------------------------------------------
2425 bool wxQTMediaBackend::Load(const wxURI& location)
2426 {
2427 if(m_timer)
2428 Cleanup();
2429
2430 wxString theURI = location.BuildURI();
2431
2432 Handle theHandle = m_lib.NewHandleClear(theURI.length() + 1);
2433 wxASSERT(theHandle);
2434
2435 m_lib.BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
2436
2437 //create the movie from the handle that refers to the URI
2438 OSErr err = m_lib.NewMovieFromDataRef(&m_movie, newMovieActive,
2439 NULL, theHandle,
2440 URLDataHandlerSubType);
2441
2442 m_lib.DisposeHandle(theHandle);
2443
2444 if (err != noErr)
2445 return false;
2446
2447 //preroll movie for streaming
2448 //TODO:Async this?
2449 /*
2450 TimeValue timeNow;
2451 Fixed playRate;
2452 timeNow = GetMovieTime(m_movie, NULL);
2453 playRate = GetMoviePreferredRate(m_movie);
2454 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
2455 PrerollMovie(m_movie, timeNow, playRate);
2456 m_lib.SetMovieRate(m_movie, playRate);
2457 */
2458
2459 FinishLoad();
2460
2461 return m_lib.GetMoviesError() == noErr;
2462 }
2463
2464 //---------------------------------------------------------------------------
2465 // wxQTMediaBackend::Move
2466 //
2467 // TODO
2468 //---------------------------------------------------------------------------
2469 void wxQTMediaBackend::FinishLoad()
2470 {
2471 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this, &m_lib);
2472 wxASSERT(m_timer);
2473
2474 //get the real size of the movie
2475 Rect outRect;
2476 memset(&outRect, 0, sizeof(Rect)); //for annoying VC6 warning
2477 m_lib.GetMovieNaturalBoundsRect (m_movie, &outRect);
2478 wxASSERT(m_lib.GetMoviesError() == noErr);
2479
2480 m_bestSize.x = outRect.right - outRect.left;
2481 m_bestSize.y = outRect.bottom - outRect.top;
2482
2483 //reparent movie/*AudioMediaCharacteristic*/
2484 if(m_lib.GetMovieIndTrackType(m_movie, 1,
2485 VisualMediaCharacteristic,
2486 (1 << 1) //movieTrackCharacteristic
2487 | (1 << 2) //movieTrackEnabledOnly
2488 ) != NULL)
2489 {
2490 m_lib.CreatePortAssociation(m_ctrl->GetHWND(), NULL, 0L);
2491
2492 m_lib.SetMovieGWorld(m_movie,
2493 (CGrafPtr) m_lib.GetNativeWindowPort(m_ctrl->GetHWND()),
2494 NULL);
2495 }
2496
2497 //we want millisecond precision
2498 m_lib.SetMovieTimeScale(m_movie, 1000);
2499 wxASSERT(m_lib.GetMoviesError() == noErr);
2500
2501 //
2502 //Here, if the parent of the control has a sizer - we
2503 //tell it to recalculate the size of this control since
2504 //the user opened a separate media file
2505 //
2506 m_ctrl->InvalidateBestSize();
2507 m_ctrl->GetParent()->Layout();
2508 m_ctrl->GetParent()->Refresh();
2509 m_ctrl->GetParent()->Update();
2510 }
2511
2512 //---------------------------------------------------------------------------
2513 // wxQTMediaBackend::Move
2514 //
2515 // TODO
2516 //---------------------------------------------------------------------------
2517 bool wxQTMediaBackend::Play()
2518 {
2519 m_lib.StartMovie(m_movie);
2520 m_timer->SetPaused(false);
2521 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
2522 return m_lib.GetMoviesError() == noErr;
2523 }
2524
2525 //---------------------------------------------------------------------------
2526 // wxQTMediaBackend::Move
2527 //
2528 // TODO
2529 //---------------------------------------------------------------------------
2530 bool wxQTMediaBackend::Pause()
2531 {
2532 m_lib.StopMovie(m_movie);
2533 m_timer->SetPaused(true);
2534 m_timer->Stop();
2535 return m_lib.GetMoviesError() == noErr;
2536 }
2537
2538 //---------------------------------------------------------------------------
2539 // wxQTMediaBackend::Move
2540 //
2541 // TODO
2542 //---------------------------------------------------------------------------
2543 bool wxQTMediaBackend::Stop()
2544 {
2545 m_timer->SetPaused(false);
2546 m_timer->Stop();
2547
2548 m_lib.StopMovie(m_movie);
2549 if(m_lib.GetMoviesError() != noErr)
2550 return false;
2551
2552 m_lib.GoToBeginningOfMovie(m_movie);
2553 return m_lib.GetMoviesError() == noErr;
2554 }
2555
2556 //---------------------------------------------------------------------------
2557 // wxQTMediaBackend::Move
2558 //
2559 // TODO
2560 //---------------------------------------------------------------------------
2561 double wxQTMediaBackend::GetPlaybackRate()
2562 {
2563 return ( ((double)m_lib.GetMovieRate(m_movie)) / 0x10000);
2564 }
2565
2566 //---------------------------------------------------------------------------
2567 // wxQTMediaBackend::Move
2568 //
2569 // TODO
2570 //---------------------------------------------------------------------------
2571 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
2572 {
2573 m_lib.SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
2574 return m_lib.GetMoviesError() == noErr;
2575 }
2576
2577 //---------------------------------------------------------------------------
2578 // wxQTMediaBackend::Move
2579 //
2580 // TODO
2581 //---------------------------------------------------------------------------
2582 bool wxQTMediaBackend::SetPosition(wxLongLong where)
2583 {
2584 TimeRecord theTimeRecord;
2585 memset(&theTimeRecord, 0, sizeof(TimeRecord));
2586 theTimeRecord.value.lo = where.GetLo();
2587 theTimeRecord.scale = m_lib.GetMovieTimeScale(m_movie);
2588 theTimeRecord.base = m_lib.GetMovieTimeBase(m_movie);
2589 m_lib.SetMovieTime(m_movie, &theTimeRecord);
2590
2591 if (m_lib.GetMoviesError() != noErr)
2592 return false;
2593
2594 return true;
2595 }
2596
2597 //---------------------------------------------------------------------------
2598 // wxQTMediaBackend::GetPosition
2599 //
2600 // 1) Calls GetMovieTime to get the position we are in in the movie
2601 // in milliseconds (we called
2602 //---------------------------------------------------------------------------
2603 wxLongLong wxQTMediaBackend::GetPosition()
2604 {
2605 return m_lib.GetMovieTime(m_movie, NULL);
2606 }
2607
2608 //---------------------------------------------------------------------------
2609 // wxQTMediaBackend::GetVolume
2610 //
2611 // Gets the volume through GetMovieVolume - which returns a 16 bit short -
2612 //
2613 // +--------+--------+
2614 // + (1) + (2) +
2615 // +--------+--------+
2616 //
2617 // (1) first 8 bits are value before decimal
2618 // (2) second 8 bits are value after decimal
2619 //
2620 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
2621 // 1 (full gain and sound)
2622 //---------------------------------------------------------------------------
2623 double wxQTMediaBackend::GetVolume()
2624 {
2625 short sVolume = m_lib.GetMovieVolume(m_movie);
2626
2627 if(sVolume & (128 << 8)) //negative - no sound
2628 return 0.0;
2629
2630 return (sVolume & (127 << 8)) ? 1.0 : ((double)(sVolume & 255)) / 255.0;
2631 }
2632
2633 //---------------------------------------------------------------------------
2634 // wxQTMediaBackend::SetVolume
2635 //
2636 // Sets the volume through SetMovieVolume - which takes a 16 bit short -
2637 //
2638 // +--------+--------+
2639 // + (1) + (2) +
2640 // +--------+--------+
2641 //
2642 // (1) first 8 bits are value before decimal
2643 // (2) second 8 bits are value after decimal
2644 //
2645 // Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
2646 // 1 (full gain and sound)
2647 //---------------------------------------------------------------------------
2648 bool wxQTMediaBackend::SetVolume(double dVolume)
2649 {
2650 short sVolume = (short) (dVolume >= .9999 ? 1 << 8 : (dVolume * 255) );
2651 m_lib.SetMovieVolume(m_movie, sVolume);
2652 return true;
2653 }
2654
2655 //---------------------------------------------------------------------------
2656 // wxQTMediaBackend::Move
2657 //
2658 // TODO
2659 //---------------------------------------------------------------------------
2660 wxLongLong wxQTMediaBackend::GetDuration()
2661 {
2662 return m_lib.GetMovieDuration(m_movie);
2663 }
2664
2665 //---------------------------------------------------------------------------
2666 // wxQTMediaBackend::Move
2667 //
2668 // TODO
2669 //---------------------------------------------------------------------------
2670 wxMediaState wxQTMediaBackend::GetState()
2671 {
2672 if ( !m_timer || (m_timer->IsRunning() == false &&
2673 m_timer->GetPaused() == false) )
2674 return wxMEDIASTATE_STOPPED;
2675
2676 if( m_timer->IsRunning() == true )
2677 return wxMEDIASTATE_PLAYING;
2678 else
2679 return wxMEDIASTATE_PAUSED;
2680 }
2681
2682 //---------------------------------------------------------------------------
2683 // wxQTMediaBackend::Move
2684 //
2685 // TODO
2686 //---------------------------------------------------------------------------
2687 void wxQTMediaBackend::Cleanup()
2688 {
2689 delete m_timer;
2690 m_timer = NULL;
2691
2692 m_lib.StopMovie(m_movie);
2693 m_lib.DisposeMovie(m_movie);
2694 }
2695
2696 //---------------------------------------------------------------------------
2697 // wxQTMediaBackend::Move
2698 //
2699 // TODO
2700 //---------------------------------------------------------------------------
2701 wxSize wxQTMediaBackend::GetVideoSize() const
2702 {
2703 return m_bestSize;
2704 }
2705
2706 //---------------------------------------------------------------------------
2707 // wxQTMediaBackend::Move
2708 //
2709 // TODO
2710 //---------------------------------------------------------------------------
2711 void wxQTMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y), int w, int h)
2712 {
2713 if(m_timer)
2714 {
2715 Rect theRect = {0, 0, (short)h, (short)w};
2716
2717 m_lib.SetMovieBox(m_movie, &theRect);
2718 wxASSERT(m_lib.GetMoviesError() == noErr);
2719 }
2720 }
2721
2722 //---------------------------------------------------------------------------
2723 // End QT Backend
2724 //---------------------------------------------------------------------------
2725
2726 //in source file that contains stuff you don't directly use
2727 #include <wx/html/forcelnk.h>
2728 FORCE_LINK_ME(basewxmediabackends);
2729
2730 //---------------------------------------------------------------------------
2731 // End wxMediaCtrl Compilation Guard and this file
2732 //---------------------------------------------------------------------------
2733 #endif //wxUSE_MEDIACTRL
2734
2735