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