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