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