]>
Commit | Line | Data |
---|---|---|
4ad7af2b RN |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: msw/moviectrl.cpp | |
3 | // Purpose: wxMovieCtrl MSW | |
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 | //#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) | |
13 | //#pragma implementation "moviectrl.h" | |
14 | //#endif | |
15 | ||
16 | // For compilers that support precompilation, includes "wx.h". | |
17 | #include "wx/wxprec.h" | |
18 | ||
19 | #ifdef __BORLANDC__ | |
20 | #pragma hdrstop | |
21 | #endif | |
22 | ||
bcac1ec2 RN |
23 | #define wxUSE_MOVIECTRL 1 |
24 | ||
4ad7af2b RN |
25 | #if wxUSE_MOVIECTRL |
26 | ||
27 | #include "wx/moviectrl.h" | |
28 | #include "wx/msw/ole/oleutils.h" //for wxBasicString | |
29 | ||
30 | #include <dshow.h> | |
31 | ||
32 | IMPLEMENT_CLASS(wxMovieCtrl, wxControl); | |
0568fd59 RN |
33 | IMPLEMENT_DYNAMIC_CLASS(wxMovieEvent, wxEvent); |
34 | DEFINE_EVENT_TYPE(wxEVT_MOVIE_FINISHED); | |
4ad7af2b RN |
35 | |
36 | #define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; } | |
37 | ||
38 | #define WM_GRAPHNOTIFY WM_USER+13 | |
39 | ||
40 | #ifdef __WXDEBUG__ | |
41 | #define wxDSVERIFY(x) wxASSERT( SUCCEEDED ((x)) ) | |
42 | #else | |
43 | #define wxDSVERIFY(x) (x) | |
44 | #endif | |
45 | ||
4ad7af2b RN |
46 | //it's there someplace :) |
47 | extern "C" WXDLLIMPEXP_BASE HWND | |
48 | wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc); | |
49 | ||
50 | bool wxMovieCtrl::Create(wxWindow* parent, wxWindowID id, const wxString& fileName, | |
51 | const wxString& label, const wxPoint& pos, const wxSize& size, | |
52 | long style, const wxString& name) | |
53 | { | |
3a774635 RN |
54 | //do some window stuff - ORDER IS IMPORTANT |
55 | //base create | |
56 | if ( !wxControl::Create(parent, id, pos, size, wxNO_BORDER | wxCLIP_CHILDREN, wxDefaultValidator, name) ) | |
57 | return false; | |
58 | ||
59 | //Set our background color to black by default | |
60 | SetBackgroundColour(*wxBLACK); | |
61 | ||
62 | if(!fileName.empty()) | |
63 | { | |
64 | if (!Load(fileName)) | |
65 | return false; | |
66 | ||
67 | SetLabel(label); | |
68 | ||
69 | if(!Play()) | |
70 | return false; | |
71 | } | |
72 | else | |
73 | wxControl::SetLabel(label); | |
74 | ||
75 | return true; | |
76 | } | |
77 | ||
78 | bool wxMovieCtrl::Load(const wxString& fileName) | |
79 | { | |
80 | if(m_bLoaded) | |
81 | Cleanup(); | |
82 | ||
4ad7af2b RN |
83 | //cast helpers |
84 | IGraphBuilder*& pGB = (IGraphBuilder*&) m_pGB; | |
85 | IMediaControl*& pMC = (IMediaControl*&) m_pMC; | |
86 | IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; | |
87 | IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; | |
88 | IBasicAudio*& pBA = (IBasicAudio*&) m_pBA; | |
89 | IBasicVideo*& pBV = (IBasicVideo*&) m_pBV; | |
90 | IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; | |
91 | ||
92 | //create our filter graph | |
93 | CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, | |
94 | IID_IGraphBuilder, (void**)&pGB); | |
95 | ||
96 | //load the graph & render | |
97 | if (FAILED(pGB->RenderFile(fileName.wc_str(wxConvLocal), NULL))) | |
98 | { | |
99 | wxFAIL_MSG("Could not load movie!"); | |
100 | return false; | |
101 | } | |
102 | ||
103 | //get the interfaces, all of them | |
104 | wxDSVERIFY( pGB->QueryInterface(IID_IMediaControl, (void**)&pMC) ); | |
105 | wxDSVERIFY( pGB->QueryInterface(IID_IMediaEventEx, (void**)&pME) ); | |
106 | wxDSVERIFY( pGB->QueryInterface(IID_IMediaSeeking, (void**)&pMS) ); | |
107 | wxDSVERIFY( pGB->QueryInterface(IID_IVideoWindow, (void**)&pVW) ); | |
108 | wxDSVERIFY( pGB->QueryInterface(IID_IBasicAudio, (void**)&pBA) ); | |
109 | wxDSVERIFY( pGB->QueryInterface(IID_IBasicVideo, (void**)&pBV) ); | |
110 | ||
111 | //get the _actual_ size of the movie & remember it | |
112 | long nX, nY, nSX, nSY; | |
0568fd59 RN |
113 | if (FAILED(pVW->GetWindowPosition(&nX,&nY,&nSX,&nSY))) |
114 | m_bVideo = false; | |
115 | else | |
116 | { | |
117 | m_bVideo = true; | |
118 | ||
119 | this->Connect( wxID_ANY, | |
120 | wxEVT_SIZE, | |
121 | (wxObjectEventFunction) (wxEventFunction) (wxSizeEventFunction) &wxMovieCtrl::OnSize ); | |
122 | } | |
4ad7af2b RN |
123 | |
124 | m_bestSize.x = nSX; | |
125 | m_bestSize.y = nSY; | |
126 | ||
0568fd59 RN |
127 | if (m_bVideo) |
128 | { | |
129 | wxDSVERIFY( pVW->put_Owner((OAHWND)this->GetHandle()) ); | |
130 | wxDSVERIFY( pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS) ); | |
131 | // wxDSVERIFY( pME->SetNotifyWindow((OAHWND)this->GetHandle(), WM_GRAPHNOTIFY, 0) ); | |
132 | wxDSVERIFY( pVW->put_Visible(OATRUE) ); //OATRUE actually == -1 :) | |
133 | } | |
4ad7af2b RN |
134 | |
135 | //set the time format | |
136 | wxDSVERIFY( pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME) ); | |
137 | ||
3a774635 | 138 | m_bLoaded = true; |
4ad7af2b RN |
139 | return true; |
140 | } | |
141 | ||
142 | void wxMovieCtrl::SetLabel(const wxString& label) | |
143 | { | |
144 | wxControl::SetLabel(label); | |
145 | ||
146 | IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; | |
147 | ||
148 | //wxBasicString will have a null string on an | |
149 | //empty wxString - gotta love those workarounds!! | |
0568fd59 | 150 | if(!label.empty() && m_bVideo) |
4ad7af2b RN |
151 | { |
152 | wxBasicString theBasicString(label.mb_str()); | |
153 | wxDSVERIFY( pVW->put_Caption(theBasicString.Get()) ); | |
154 | } | |
155 | } | |
156 | ||
157 | bool wxMovieCtrl::Play() | |
158 | { | |
159 | return SUCCEEDED( ((IMediaControl*&)m_pMC)->Run() ); | |
160 | } | |
161 | ||
162 | bool wxMovieCtrl::Pause() | |
163 | { | |
164 | return SUCCEEDED( ((IMediaControl*&)m_pMC)->Pause() ); | |
165 | } | |
166 | ||
167 | bool wxMovieCtrl::Stop() | |
168 | { | |
169 | return SUCCEEDED( ((IMediaControl*&)m_pMC)->Stop() ); | |
170 | } | |
171 | ||
172 | #if wxUSE_DATETIME | |
173 | ||
174 | bool wxMovieCtrl::Seek(const wxTimeSpan& where) | |
175 | { | |
176 | //DS uses 100 nanos - so we need a 10 mult | |
177 | LONGLONG pos = ((size_t)where.GetMilliseconds().ToLong()) * 10; | |
178 | ||
179 | return SUCCEEDED( ((IMediaSeeking*&)m_pMS)->SetPositions( | |
180 | &pos, | |
181 | AM_SEEKING_AbsolutePositioning, | |
182 | NULL, | |
183 | AM_SEEKING_NoPositioning | |
184 | ) ); | |
185 | } | |
186 | ||
0568fd59 RN |
187 | wxTimeSpan wxMovieCtrl::Tell() |
188 | { | |
189 | LONGLONG outCur, outStop; | |
190 | wxDSVERIFY( ((IMediaSeeking*&)m_pMS)->GetPositions(&outCur, &outStop) ); | |
191 | ||
192 | return outCur; | |
193 | } | |
194 | ||
195 | wxTimeSpan wxMovieCtrl::Length() | |
196 | { | |
197 | LONGLONG outDuration; | |
198 | wxDSVERIFY( ((IMediaSeeking*&)m_pMS)->GetDuration(&outDuration) ); | |
199 | ||
200 | return outDuration; | |
201 | } | |
202 | ||
4ad7af2b RN |
203 | #endif // wxUSE_DATETIME |
204 | ||
205 | wxMovieCtrlState wxMovieCtrl::GetState() | |
206 | { | |
207 | HRESULT hr; | |
208 | OAFilterState theState; | |
209 | hr = ((IMediaControl*&)m_pMC)->GetState(INFINITE, &theState); | |
210 | ||
211 | wxASSERT( SUCCEEDED(hr) ); | |
212 | ||
213 | //MSW state is the same as ours | |
214 | //State_Stopped = 0, | |
215 | //State_Paused = State_Stopped + 1, | |
216 | //State_Running = State_Paused + 1 | |
217 | ||
218 | return (wxMovieCtrlState) theState; | |
219 | } | |
220 | ||
0568fd59 RN |
221 | double wxMovieCtrl::GetPlaybackRate() |
222 | { | |
223 | double dRate; | |
224 | wxDSVERIFY( ((IMediaSeeking*&)m_pMS)->GetRate(&dRate) ); | |
225 | return dRate; | |
226 | } | |
227 | ||
228 | bool wxMovieCtrl::SetPlaybackRate(double dRate) | |
229 | { | |
230 | return SUCCEEDED( ((IMediaSeeking*&)m_pMS)->SetRate(dRate) ); | |
231 | } | |
232 | ||
233 | WXLRESULT wxMovieCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) | |
234 | { | |
4ad7af2b | 235 | //cast helpers |
0568fd59 | 236 | // IMediaControl*& pMC = (IMediaControl*&) m_pMC; |
4ad7af2b | 237 | IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; |
0568fd59 | 238 | // IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; |
4ad7af2b RN |
239 | |
240 | if (nMsg == WM_GRAPHNOTIFY) | |
241 | { | |
242 | LONG evCode, evParam1, evParam2; | |
243 | HRESULT hr=S_OK; | |
244 | ||
4ad7af2b RN |
245 | // Process all queued events |
246 | while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, | |
247 | (LONG_PTR *) &evParam2, 0) | |
248 | ) | |
249 | ) | |
250 | { | |
251 | // Free memory associated with callback, since we're not using it | |
252 | hr = pME->FreeEventParams(evCode, evParam1, evParam2); | |
253 | ||
0568fd59 | 254 | // If this is the end of the clip, notify handler |
4ad7af2b RN |
255 | if(EC_COMPLETE == evCode) |
256 | { | |
0568fd59 RN |
257 | wxMovieEvent theEvent(wxEVT_MOVIE_FINISHED, this->GetId()); |
258 | GetParent()->ProcessEvent(theEvent); | |
259 | /* | |
4ad7af2b RN |
260 | LONGLONG pos=0; |
261 | ||
262 | // Reset to first frame of movie | |
263 | hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning , | |
264 | NULL, AM_SEEKING_NoPositioning); | |
265 | if (FAILED(hr)) | |
266 | { | |
267 | hr = pMC->Stop(); | |
268 | // for filters that don't support seeking do a rewind | |
269 | if (FAILED(hr)) | |
270 | { | |
271 | wxFAIL_MSG( | |
272 | wxString::Format(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr) | |
273 | ); | |
274 | break; | |
275 | } | |
276 | ||
277 | hr = hr = pMC->Run(); | |
278 | ||
279 | if (FAILED(hr)) | |
280 | { | |
281 | wxFAIL_MSG( | |
282 | wxString::Format(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr) | |
283 | ); | |
284 | break; | |
285 | } | |
286 | } | |
0568fd59 | 287 | */ |
4ad7af2b RN |
288 | } |
289 | } | |
290 | return wxControl::MSWDefWindowProc(nMsg, wParam, lParam); | |
291 | } | |
4ad7af2b | 292 | //pass the event to our parent |
0568fd59 RN |
293 | return wxControl::MSWWindowProc(nMsg, wParam, lParam); |
294 | } | |
4ad7af2b | 295 | |
3a774635 | 296 | void wxMovieCtrl::Cleanup() |
4ad7af2b | 297 | { |
3a774635 RN |
298 | if(m_bVideo) |
299 | this->Disconnect( wxID_ANY, | |
300 | wxEVT_SIZE, | |
301 | (wxObjectEventFunction) (wxEventFunction) (wxSizeEventFunction) &wxMovieCtrl::OnSize ); | |
302 | ||
4ad7af2b RN |
303 | //cast helpers |
304 | IGraphBuilder*& pGB = (IGraphBuilder*&) m_pGB; | |
305 | IMediaControl*& pMC = (IMediaControl*&) m_pMC; | |
306 | IMediaEventEx*& pME = (IMediaEventEx*&) m_pME; | |
307 | IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; | |
308 | IBasicAudio*& pBA = (IBasicAudio*&) m_pBA; | |
309 | IBasicVideo*& pBV = (IBasicVideo*&) m_pBV; | |
310 | IMediaSeeking*& pMS = (IMediaSeeking*&) m_pMS; | |
311 | ||
312 | // Hide then disown the window | |
313 | if(pVW) | |
314 | { | |
315 | pVW->put_Visible(OAFALSE); //OSFALSE == 0 | |
316 | pVW->put_Owner(NULL); | |
317 | } | |
318 | ||
319 | // Release and zero DirectShow interfaces | |
320 | SAFE_RELEASE(pME); | |
321 | SAFE_RELEASE(pMS); | |
322 | SAFE_RELEASE(pMC); | |
323 | SAFE_RELEASE(pBA); | |
324 | SAFE_RELEASE(pBV); | |
325 | SAFE_RELEASE(pVW); | |
326 | SAFE_RELEASE(pGB); | |
327 | } | |
328 | ||
3a774635 RN |
329 | wxMovieCtrl::~wxMovieCtrl() |
330 | { | |
331 | if (m_bLoaded) | |
332 | Cleanup(); | |
333 | } | |
334 | ||
4ad7af2b RN |
335 | wxSize wxMovieCtrl::DoGetBestSize() const |
336 | { | |
337 | return m_bestSize; | |
338 | } | |
339 | ||
4ad7af2b RN |
340 | void wxMovieCtrl::OnSize(wxSizeEvent& evt) |
341 | { | |
342 | IVideoWindow*& pVW = (IVideoWindow*&) m_pVW; | |
343 | wxDSVERIFY( pVW->SetWindowPosition(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight()) ); | |
344 | ||
345 | evt.Skip(); | |
346 | } | |
4ad7af2b RN |
347 | |
348 | #endif //wxUSE_MOVIECTRL |