X64 transport [Part 5] (Update plugins.cpp)
[xy_vsfilter.git] / src / apps / mplayerc / QuicktimeGraph.cpp
blob047e0e53cbfdd6c9ef897ddc140e6d14807085ef
1 /*
2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "stdafx.h"
23 #include <math.h>
24 #include "QuicktimeGraph.h"
25 #include "IQTVideoSurface.h"
26 #include "mplayerc.h"
27 #include "..\..\DSUtil\DSUtil.h"
30 // CQuicktimeGraph
33 #pragma warning(disable:4355) // 'this' : used in base member initializer list
35 using namespace QT;
37 CQuicktimeGraph::CQuicktimeGraph(HWND hWndParent, HRESULT& hr)
38 : CBaseGraph()
39 , m_wndDestFrame(this)
40 , m_fQtInitialized(false)
42 hr = S_OK;
44 DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
46 AppSettings& s = AfxGetAppSettings();
48 if(s.iQTVideoRendererType == VIDRNDT_QT_DX7)
50 if(SUCCEEDED(CreateAP7(CLSID_QT7AllocatorPresenter, hWndParent, &m_pQTAP)))
51 dwStyle &= ~WS_VISIBLE;
53 else if(s.iQTVideoRendererType == VIDRNDT_QT_DX9)
55 if(SUCCEEDED(CreateAP9(CLSID_QT9AllocatorPresenter, hWndParent, &m_pQTAP)))
56 dwStyle &= ~WS_VISIBLE;
59 m_fQtInitialized = false;
60 if(InitializeQTML(0) != 0) {hr = E_FAIL; return;}
61 if(EnterMovies() != 0) {TerminateQTML(); hr = E_FAIL; return;}
62 m_fQtInitialized = true;
64 if(!m_wndWindowFrame.CreateEx(WS_EX_NOPARENTNOTIFY, NULL, NULL, dwStyle, CRect(0, 0, 0, 0), CWnd::FromHandle(hWndParent), 0))
66 hr = E_FAIL;
67 return;
70 if(!m_wndDestFrame.Create(NULL, NULL, dwStyle, CRect(0, 0, 0, 0), &m_wndWindowFrame, 0))
72 hr = E_FAIL;
73 return;
77 CQuicktimeGraph::~CQuicktimeGraph()
79 m_wndDestFrame.DestroyWindow();
80 m_wndWindowFrame.DestroyWindow();
82 if(m_fQtInitialized)
84 ExitMovies();
85 TerminateQTML();
89 STDMETHODIMP CQuicktimeGraph::NonDelegatingQueryInterface(REFIID riid, void** ppv)
91 CheckPointer(ppv, E_POINTER);
93 return
94 QI(IVideoFrameStep)
95 (m_pQTAP && (riid == __uuidof(ISubPicAllocatorPresenter) || riid == __uuidof(IQTVideoSurface))) ? m_pQTAP->QueryInterface(riid, ppv) :
96 __super::NonDelegatingQueryInterface(riid, ppv);
99 // IGraphBuilder
100 STDMETHODIMP CQuicktimeGraph::RenderFile(LPCWSTR lpcwstrFile, LPCWSTR lpcwstrPlayList)
102 bool fRet = m_wndDestFrame.OpenMovie(CString(lpcwstrFile));
104 if(fRet)
106 for(int i = 1, cnt = GetMovieTrackCount(m_wndDestFrame.theMovie); i <= cnt; i++)
108 Track aTrack = GetMovieIndTrack(m_wndDestFrame.theMovie, i);
109 Media aMedia = GetTrackMedia(aTrack);
111 OSType aTrackType;
112 GetMediaHandlerDescription(aMedia, &aTrackType, 0, 0);
113 if(aTrackType == SoundMediaType)
115 SampleDescriptionHandle aDesc = (SampleDescriptionHandle)NewHandle(sizeof(aDesc));
116 GetMediaSampleDescription(aMedia, 1, aDesc);
117 if(GetMoviesError() == noErr)
119 SoundDescription& desc = **(SoundDescriptionHandle)aDesc;
120 NotifyEvent(EC_BG_AUDIO_CHANGED, desc.numChannels, 0);
121 i = cnt;
123 DisposeHandle((Handle)aDesc);
128 return fRet ? S_OK : E_FAIL;
131 // IMediaControl
132 STDMETHODIMP CQuicktimeGraph::Run()
134 m_wndDestFrame.Run();
135 return S_OK;
137 STDMETHODIMP CQuicktimeGraph::Pause()
139 m_wndDestFrame.Pause();
140 return S_OK;
142 STDMETHODIMP CQuicktimeGraph::Stop()
144 m_wndDestFrame.Stop();
145 return S_OK;
147 STDMETHODIMP CQuicktimeGraph::GetState(LONG msTimeout, OAFilterState* pfs)
149 // TODO: this seems to deadlock when opening from the net
150 return pfs ? *pfs = m_wndDestFrame.GetState(), S_OK : E_POINTER;
153 // IMediaSeeking
154 STDMETHODIMP CQuicktimeGraph::GetDuration(LONGLONG* pDuration)
156 CheckPointer(pDuration, E_POINTER);
158 *pDuration = 0;
160 if(!m_wndDestFrame.theMovie) return E_UNEXPECTED;
162 TimeScale ts = GetMovieTimeScale(m_wndDestFrame.theMovie);
163 if(ts == 0) return E_FAIL;
165 *pDuration = 10000i64*GetMovieDuration(m_wndDestFrame.theMovie)/ts*1000;
167 return S_OK;
169 STDMETHODIMP CQuicktimeGraph::GetCurrentPosition(LONGLONG* pCurrent)
171 CheckPointer(pCurrent, E_POINTER);
173 *pCurrent = 0;
175 if(!m_wndDestFrame.theMovie) return E_UNEXPECTED;
177 TimeScale ts = GetMovieTimeScale(m_wndDestFrame.theMovie);
178 if(ts == 0) return E_FAIL;
180 TimeRecord tr;
181 *pCurrent = 10000i64*GetMovieTime(m_wndDestFrame.theMovie, &tr)/ts*1000;
183 return S_OK;
185 STDMETHODIMP CQuicktimeGraph::SetPositions(LONGLONG* pCurrent, DWORD dwCurrentFlags, LONGLONG* pStop, DWORD dwStopFlags)
187 CheckPointer(pCurrent, E_POINTER);
189 if(!(dwCurrentFlags&AM_SEEKING_AbsolutePositioning)) return E_INVALIDARG;
191 if(!m_wndDestFrame.theMovie) return E_UNEXPECTED;
193 TimeScale ts = GetMovieTimeScale(m_wndDestFrame.theMovie);
194 if(ts == 0) return E_FAIL;
196 SetMovieTimeValue(m_wndDestFrame.theMovie, (TimeValue)(*pCurrent*ts/1000/10000i64));
198 if(!m_wndDestFrame.theMC)
200 UpdateMovie(m_wndDestFrame.theMovie);
201 MoviesTask(m_wndDestFrame.theMovie, 0L);
204 return S_OK;
206 STDMETHODIMP CQuicktimeGraph::SetRate(double dRate)
208 return m_wndDestFrame.theMovie ? SetMovieRate(m_wndDestFrame.theMovie, (Fixed)(dRate * 0x10000)), S_OK : E_UNEXPECTED;
210 STDMETHODIMP CQuicktimeGraph::GetRate(double* pdRate)
212 CheckPointer(pdRate, E_POINTER);
213 *pdRate = 1.0;
214 return m_wndDestFrame.theMovie ? *pdRate = (double)GetMovieRate(m_wndDestFrame.theMovie) / 0x10000, S_OK : E_UNEXPECTED;
217 // IVideoWindow
218 STDMETHODIMP CQuicktimeGraph::SetWindowPosition(long Left, long Top, long Width, long Height)
220 if(IsWindow(m_wndWindowFrame.m_hWnd))
221 m_wndWindowFrame.MoveWindow(Left, Top, Width, Height);
223 return S_OK;
226 // IBasicVideo
227 STDMETHODIMP CQuicktimeGraph::SetDestinationPosition(long Left, long Top, long Width, long Height)// {return E_NOTIMPL;}
229 if(!m_pQTAP && IsWindow(m_wndDestFrame.m_hWnd))
231 m_wndDestFrame.MoveWindow(Left, Top, Width, Height);
233 if(m_wndDestFrame.theMC)
235 Rect bounds = {0,0,(short)Height,(short)Width};
236 MCPositionController(m_wndDestFrame.theMC, &bounds, NULL, mcTopLeftMovie|mcScaleMovieToFit);
240 return S_OK;
242 STDMETHODIMP CQuicktimeGraph::GetVideoSize(long* pWidth, long* pHeight)
244 if(!pWidth || !pHeight) return E_POINTER;
246 *pWidth = m_wndDestFrame.m_size.cx;
247 *pHeight = m_wndDestFrame.m_size.cy;
249 return S_OK;
252 // IBasicAudio
253 STDMETHODIMP CQuicktimeGraph::put_Volume(long lVolume)
255 if(m_wndDestFrame.theMovie)
257 lVolume = (lVolume == -10000) ? 0 : (int)pow(10.0, (double)lVolume/4152.41 + 2.41);
258 SetMovieVolume(m_wndDestFrame.theMovie, (short)max(min(lVolume, 256), 0));
259 return S_OK;
262 return E_UNEXPECTED;
264 STDMETHODIMP CQuicktimeGraph::get_Volume(long* plVolume)
266 CheckPointer(plVolume, E_POINTER);
268 if(m_wndDestFrame.theMovie)
270 long lVolume = (long)GetMovieVolume(m_wndDestFrame.theMovie);
271 *plVolume = (int)((log10(1.0*lVolume)-2.41)*4152.41);
272 *plVolume = max(min(*plVolume, 0), -10000);
273 return S_OK;
276 return E_UNEXPECTED;
279 // IVideoFrameStep
280 STDMETHODIMP CQuicktimeGraph::Step(DWORD dwFrames, IUnknown* pStepObject)
282 if(pStepObject) return E_INVALIDARG;
283 if(dwFrames == 0) return S_OK;
284 if(!m_wndDestFrame.theMovie) return E_UNEXPECTED;
286 // w/o m_wndDestFrame.theMC
288 OSType myTypes[] = {VisualMediaCharacteristic};
289 TimeValue myCurrTime = GetMovieTime(m_wndDestFrame.theMovie, NULL);
290 Fixed theRate = (int)dwFrames > 0 ? 0x00010000 : 0xffff0000;
292 for(int nSteps = abs((int)dwFrames); nSteps > 0; nSteps--)
294 TimeValue myNextTime;
295 GetMovieNextInterestingTime(m_wndDestFrame.theMovie, nextTimeStep, 1, myTypes, myCurrTime, theRate, &myNextTime, NULL);
296 if(GetMoviesError() != noErr) return E_FAIL;
297 myCurrTime = myNextTime;
300 if(myCurrTime >= 0 && myCurrTime < GetMovieDuration(m_wndDestFrame.theMovie))
302 SetMovieTimeValue(m_wndDestFrame.theMovie, myCurrTime);
303 if(GetMoviesError() != noErr) return E_FAIL;
304 // the rest is not needed when we also have m_wndDestFrame.theMC:
305 UpdateMovie(m_wndDestFrame.theMovie);
306 if(GetMoviesError() != noErr) return E_FAIL;
307 MoviesTask(m_wndDestFrame.theMovie, 0L);
310 NotifyEvent(EC_STEP_COMPLETE);
312 return S_OK;
315 // w/ m_wndDestFrame.theMC
317 short myStep = (short)(long)dwFrames;
318 return noErr == MCDoAction(m_wndDestFrame.theMC, mcActionStep, (Ptr)myStep)
319 ? NotifyEvent(EC_STEP_COMPLETE), S_OK : E_FAIL;
322 STDMETHODIMP CQuicktimeGraph::CanStep(long bMultiple, IUnknown* pStepObject)
324 return m_wndDestFrame.theMovie ? S_OK : S_FALSE;
326 STDMETHODIMP CQuicktimeGraph::CancelStep()
328 return E_NOTIMPL;
331 // IGraphEngine
332 STDMETHODIMP_(engine_t) CQuicktimeGraph::GetEngine() {return QuickTime;}
335 // CQuicktimeWindow
338 CQuicktimeWindow::CQuicktimeWindow(CQuicktimeGraph* pGraph)
339 : m_pGraph(pGraph)
340 , theMovie(NULL)
341 , theMC(NULL)
342 , m_size(0, 0)
343 , m_idEndPoller(0)
344 , m_fs(State_Stopped)
345 , m_offscreenGWorld(NULL)
349 void CQuicktimeWindow::ProcessMovieEvent(unsigned int message, unsigned int wParam, long lParam)
351 if(message >= WM_MOUSEFIRST && message <= WM_MOUSELAST
352 || message >= WM_KEYFIRST && message <= WM_KEYLAST)
353 return;
355 // Convert the Windows event to a QTML event
356 MSG theMsg;
357 EventRecord macEvent;
358 LONG thePoints = GetMessagePos();
360 theMsg.hwnd = m_hWnd;
361 theMsg.message = message;
362 theMsg.wParam = wParam;
363 theMsg.lParam = lParam;
364 theMsg.time = GetMessageTime();
365 theMsg.pt.x = LOWORD(thePoints);
366 theMsg.pt.y = HIWORD(thePoints);
368 // tranlate a windows event to a mac event
369 WinEventToMacEvent(&theMsg, &macEvent);
371 // Pump messages as mac event
372 MCIsPlayerEvent(theMC, (const EventRecord*)&macEvent);
375 LRESULT CQuicktimeWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
377 if(message == WM_ERASEBKGND)
379 LRESULT theResult = __super::WindowProc(message, wParam, lParam);
380 ProcessMovieEvent(message, wParam, lParam);
381 return theResult;
383 else
385 ProcessMovieEvent(message, wParam, lParam);
386 return __super::WindowProc(message, wParam, lParam);
390 OSErr CQuicktimeWindow::MyMovieDrawingCompleteProc(Movie theMovie, long refCon)
392 CQuicktimeWindow* pQW = (CQuicktimeWindow*)refCon;
393 if(!pQW) return noErr;
395 CQuicktimeGraph* pGraph = pQW->m_pGraph;
396 if(!pGraph) return noErr;
398 if(CComQIPtr<IQTVideoSurface> pQTVS = (IUnknown*)(INonDelegatingUnknown*)pGraph)
400 BITMAP bm;
401 pQW->m_bm.GetObject(sizeof(bm), &bm);
402 pQTVS->DoBlt(bm);
405 else
407 pQW->Invalidate();
410 return(noErr);
413 bool CQuicktimeWindow::OpenMovie(CString fn)
415 CloseMovie();
417 CComQIPtr<IQTVideoSurface> pQTVS = (IUnknown*)(INonDelegatingUnknown*)m_pGraph;
419 if(!pQTVS)
421 // Set the port
422 SetGWorld((CGrafPtr)GetHWNDPort(m_hWnd), NULL);
425 if(fn.Find(_T("://")) > 0)
427 Handle myHandle = NULL;
428 Size mySize = fn.GetLength()+1;
430 if(!(myHandle = NewHandleClear(mySize)))
431 return(false);
433 BlockMove((LPSTR)(LPCSTR)CStringA(fn), *myHandle, mySize);
435 OSErr err = NewMovieFromDataRef(&theMovie, newMovieActive, NULL, myHandle, URLDataHandlerSubType);
437 DisposeHandle(myHandle);
439 if(err != noErr) return(false);
441 else
443 if(!(fn.GetLength() > 0 && fn.GetLength() < 255))
444 return(false);
446 CHAR buff[MAX_PATH] = {0, 0};
447 #ifdef UNICODE
448 WideCharToMultiByte(GetACP(), 0, fn, -1, buff+1, MAX_PATH-1, 0, 0);
449 #else
450 strcpy(buff+1, fn);
451 #endif
452 buff[0] = strlen(buff+1);
454 // Make a FSSpec with a pascal string filename
455 FSSpec sfFile;
456 FSMakeFSSpec(0, 0L, (BYTE*)buff, &sfFile);
458 // Open the movie file
459 short movieResFile;
460 OSErr err = OpenMovieFile(&sfFile, &movieResFile, fsRdPerm);
461 if(err == noErr)
463 err = NewMovieFromFile(&theMovie, movieResFile, 0, 0, newMovieActive, 0);
464 CloseMovieFile(movieResFile);
466 if(err != noErr) return(false);
469 Rect rect;
470 GetMovieBox(theMovie, &rect);
471 MacOffsetRect(&rect, -rect.left, -rect.top);
472 SetMovieBox(theMovie, &rect);
473 m_size.SetSize(rect.right - rect.left, rect.bottom - rect.top);
475 Rect nrect;
476 GetMovieNaturalBoundsRect(theMovie, &nrect);
478 if(!pQTVS)
480 theMC = NewMovieController(theMovie, &rect, mcTopLeftMovie|mcNotVisible);
482 else if(m_size.cx > 0 && m_size.cy > 0)
484 SetMovieDrawingCompleteProc(theMovie,
485 movieDrawingCallWhenChanged,//|movieDrawingCallAlways,
486 MyMovieDrawingCompleteProc, (long)this);
488 if(CDC* pDC = GetDC())
490 m_dc.CreateCompatibleDC(pDC);
491 ReleaseDC(pDC);
493 struct
495 BITMAPINFOHEADER bmiHeader;
496 long bmiColors[256];
497 } bmi;
499 memset(&bmi, 0, sizeof(bmi));
501 int bpp = m_dc.GetDeviceCaps(BITSPIXEL);
503 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
504 bmi.bmiHeader.biCompression = BI_BITFIELDS/*BI_RGB*/;
505 bmi.bmiHeader.biWidth = m_size.cx;
506 bmi.bmiHeader.biHeight = -m_size.cy;
507 bmi.bmiHeader.biPlanes = 1;
508 bmi.bmiHeader.biBitCount = 32/*bpp*/;
510 bmi.bmiColors[0] = /*bpp == 16 ? 0xf800 :*/ 0xff0000;
511 bmi.bmiColors[1] = /*bpp == 16 ? 0x07e0 :*/ 0x00ff00;
512 bmi.bmiColors[2] = /*bpp == 16 ? 0x001f :*/ 0x0000ff;
514 void* bits;
515 m_bm.Attach(CreateDIBSection(m_dc, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, &bits, NULL, 0));
517 QDErr err = NewGWorldFromHBITMAP(&m_offscreenGWorld, NULL, NULL, 0, m_bm.m_hObject, m_dc.m_hDC);
519 SetMovieGWorld(theMovie, m_offscreenGWorld, GetGWorldDevice(m_offscreenGWorld));
521 BITMAP bm;
522 m_bm.GetObject(sizeof(bm), &bm);
523 pQTVS->BeginBlt(bm);
527 return(theMovie != NULL);
530 void CQuicktimeWindow::CloseMovie()
532 if(theMC) DisposeMovieController(theMC), theMC = NULL;
533 if(theMovie) DisposeMovie(theMovie), theMovie = NULL;
534 m_size.SetSize(0, 0);
535 m_fs = State_Stopped;
537 if(m_offscreenGWorld) DisposeGWorld(m_offscreenGWorld), m_offscreenGWorld = NULL;
538 m_dc.DeleteDC();
539 m_bm.DeleteObject();
542 void CQuicktimeWindow::Run()
544 if(theMovie)
546 StartMovie(theMovie);
547 if(!m_idEndPoller) m_idEndPoller = SetTimer(1, 10, NULL); // 10ms -> 100fps max
550 m_fs = State_Running;
553 void CQuicktimeWindow::Pause()
555 if(theMovie)
557 StopMovie(theMovie);
558 if(m_idEndPoller) KillTimer(m_idEndPoller), m_idEndPoller = 0;
561 m_fs = State_Paused;
564 void CQuicktimeWindow::Stop()
566 if(theMovie)
568 StopMovie(theMovie);
569 GoToBeginningOfMovie(theMovie);
570 if(m_idEndPoller) KillTimer(m_idEndPoller), m_idEndPoller = 0;
573 m_fs = State_Stopped;
576 FILTER_STATE CQuicktimeWindow::GetState()
578 return m_fs;
581 BEGIN_MESSAGE_MAP(CQuicktimeWindow, CPlayerWindow)
582 ON_WM_CREATE()
583 ON_WM_DESTROY()
584 ON_WM_ERASEBKGND()
585 ON_WM_TIMER()
586 END_MESSAGE_MAP()
588 int CQuicktimeWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
590 if(__super::OnCreate(lpCreateStruct) == -1)
591 return -1;
593 CComQIPtr<IQTVideoSurface> pQTVS = (IUnknown*)(INonDelegatingUnknown*)m_pGraph;
595 if(!pQTVS)
597 // Create GrafPort <-> HWND association
598 CreatePortAssociation(m_hWnd, NULL, 0);
601 return 0;
604 void CQuicktimeWindow::OnDestroy()
606 CPlayerWindow::OnDestroy();
608 // close any movies before destroying PortAssocation
609 CloseMovie();
611 CComQIPtr<IQTVideoSurface> pQTVS = (IUnknown*)(INonDelegatingUnknown*)m_pGraph;
613 if(!pQTVS)
615 // Destroy the view's GrafPort <-> HWND association
616 if(m_hWnd)
617 if(CGrafPtr windowPort = (CGrafPtr)GetHWNDPort(m_hWnd))
618 DestroyPortAssociation(windowPort);
622 BOOL CQuicktimeWindow::OnEraseBkgnd(CDC* pDC)
624 return m_fs != State_Stopped && theMovie ? TRUE : __super::OnEraseBkgnd(pDC);
627 void CQuicktimeWindow::OnTimer(UINT nIDEvent)
629 if(nIDEvent == m_idEndPoller && theMovie)
631 if(IsMovieDone(theMovie))
633 Pause();
634 m_pGraph->NotifyEvent(EC_COMPLETE);
636 else if(CComQIPtr<IQTVideoSurface> pQTVS = (IUnknown*)(INonDelegatingUnknown*)m_pGraph)
638 MoviesTask(theMovie, 0);
640 long duration = 0, scale = 1000;
641 OSErr err = QTGetTimeUntilNextTask(&duration, scale);
643 // err is 0 but still doesn't seem to work... returns duration=0 always
644 TRACE(_T("%d\n"), duration);
645 KillTimer(m_idEndPoller);
646 m_idEndPoller = SetTimer(m_idEndPoller, duration, NULL);
651 __super::OnTimer(nIDEvent);