www.pudn.com > MoviePlay.zip > MovieView.cpp
//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1994 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; // // MovieView.cpp : implementation of the CMovieView class // aze // (c) Stephane Rodriguez 98 #include "stdafx.h" #include "MovieApp.h" #include "MovieDoc.h" #include// Multimedia stream interfaces #include #include // DirectShow multimedia stream interfaces #include // Defines DEFINE_GUID macro and enables GUID initialization #include "MovieView.h" #include CComModule _Module; #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CMovieView IMPLEMENT_DYNCREATE(CMovieView, CView) BEGIN_MESSAGE_MAP(CMovieView, CView) //{{AFX_MSG_MAP(CMovieView) // NOTE - the ClassWizard will add and remove mapping macros here. ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_COMMAND(ID_FILE_OPEN, OnFileOpen) ON_COMMAND(IDM_START, OnStart) ON_COMMAND(IDM_PAUSE, OnPause) ON_COMMAND(IDM_STOP, OnStop) // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMovieView construction/destruction CMovieView::CMovieView() { m_bAppactive=FALSE; m_bFileLoaded = FALSE; m_bPaused=FALSE; // MultiMedia streaming interfaces m_pMMStream=NULL; m_pPrimaryVidStream=NULL; m_pDDStream=NULL; m_pSample=NULL; //DirectDrawEx interfaces m_pDD=NULL; m_pDD3=NULL; m_pDDF=NULL; m_pPrimarySurface=NULL; m_pDDSOffscreen=NULL; m_pDDSOffscreen2=NULL; m_pDDClipper=NULL; // attach the view to the app (to easier interfacing) ((CMovieApp*)AfxGetApp())->OnViewChildCreated(this); } CMovieView::~CMovieView() { ExitCode(); } BOOL CMovieView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return CView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // CMovieView drawing void CMovieView::OnDraw(CDC* pDC) { CMovieDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // draw a black background when the video is not yet loaded // so we are dealing with a video application and not a simple // MFC MDI type. if (!m_bFileLoaded) { CBrush brush; brush.CreateSolidBrush(RGB(0,0,0)); CRect rect; GetClientRect(&rect); pDC->FillRect(&rect,&brush); } } BOOL CMovieView::OnEraseBkgnd( CDC* pDC ) { return TRUE; } HRESULT CMovieView::InitDDrawEx() { HRESULT hr=NOERROR; DDSURFACEDESC ddsd, ddsd2, ddsd3; CoInitialize(NULL); //Create a DirectDrawFactory object hr = CoCreateInstance( CLSID_DirectDrawFactory, NULL, CLSCTX_INPROC_SERVER, IID_IDirectDrawFactory, (void **)&m_pDDF); if (FAILED(hr)) { AfxMessageBox(_T("Couldn't create DirectDrawFactory")); return E_FAIL; } //Call the IDirectDrawFactory::CreateDirectDraw method to create the //DirectDraw object, set the cooperative level, and get the address //of an IDirectDraw interface pointer hr = (m_pDDF->CreateDirectDraw(NULL, ::GetDesktopWindow(), DDSCL_NORMAL, NULL, NULL, &m_pDD)); if (FAILED(hr)) { AfxMessageBox(_T("Couldn't create DirectDraw object")); return E_FAIL; } //Now query for the new IDirectDraw3 interface hr =(m_pDD->QueryInterface(IID_IDirectDraw3, (LPVOID*)&m_pDD3)); if (FAILED(hr)) { AfxMessageBox(_T("Couldn't get IDirectDraw3")); return E_FAIL; } //Initialize the DDSURFACEDESC structure for the primary surface ::ZeroMemory(&ddsd, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; hr = m_pDD3->CreateSurface(&ddsd, &m_pPrimarySurface, NULL); if(FAILED(hr)) { AfxMessageBox(_T("Couldn't create Primary Surface")); return E_FAIL; } // Now, do the same for the offscreen surfaces. // The offscreen surface needs to use the same pixel format as the primary. // Query the primary surface to for its pixel format. hr = m_pPrimarySurface->GetSurfaceDesc(&ddsd); if(FAILED(hr)) { AfxMessageBox(_T("Couldn't GetSurfaceDesc")); return E_FAIL; } // Now, set the info for the offscreen surface #1, using the primary's pixel format. ::ZeroMemory(&ddsd2, sizeof(ddsd2)); ddsd2.dwSize = sizeof(ddsd2); ddsd2.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd2.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; ddsd2.dwHeight = ddsd.dwHeight; //set the height of the surfaces equal ddsd2.dwWidth = ddsd.dwWidth; //set the width of the surfaces equal ddsd2.ddpfPixelFormat = ddsd.ddpfPixelFormat; //set the pixel formats equal // Now, create the offscreen surface #1 and query for the latest interface. hr = m_pDD3->CreateSurface(&ddsd2, &m_pDDSOffscreen, NULL); if(FAILED(hr)) { AfxMessageBox(_T("Couldn't create Offscreen Surface")); return E_FAIL; } // Now, set the info for the offscreen surface #2, using the primary's pixel format. ::ZeroMemory(&ddsd3, sizeof(ddsd3)); ddsd3.dwSize = sizeof(ddsd3); ddsd3.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd3.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; ddsd3.dwHeight = ddsd.dwHeight; //set the height of the surfaces equal ddsd3.dwWidth = ddsd.dwWidth; //set the width of the surfaces equal ddsd3.ddpfPixelFormat = ddsd.ddpfPixelFormat; //set the pixel formats equal // Now, create the offscreen surface #2 and query for the latest interface. hr = m_pDD3->CreateSurface(&ddsd3, &m_pDDSOffscreen2, NULL); if(FAILED(hr)) { AfxMessageBox(_T("Couldn't create Offscreen Surface2")); return E_FAIL; } //Add code for Clipper hr = m_pDD3->CreateClipper(0, &m_pDDClipper, NULL); if(FAILED(hr)) { AfxMessageBox(_T("Couldn't create Clipper")); return E_FAIL; } hr = m_pPrimarySurface->SetClipper(m_pDDClipper); if(FAILED(hr)) { AfxMessageBox(_T("Call to SetClipper failed")); return E_FAIL; } hr = m_pDDClipper->SetHWnd(0, AfxGetMainWnd()->m_hWnd); if(FAILED(hr)) { AfxMessageBox(_T("Call to SetHWnd failed")); return E_FAIL; } return NOERROR; } //Renders a file to a multimedia stream HRESULT CMovieView::RenderFileToMMStream(LPCTSTR szFilename) //IMultiMediaStream **ppMMStream { HRESULT hr; CComPtr pAMStream; //Convert filename to Unicode WCHAR wFile[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, szFilename, -1, wFile, sizeof(wFile)/sizeof(wFile[0])); //Create the AMMultiMediaStream object hr =CoCreateInstance( CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAMStream); if (FAILED(hr)) { AfxMessageBox(_T("Could not create a CLSID_MultiMediaStream object")); return E_FAIL; } //Initialize stream hr = pAMStream->Initialize(STREAMTYPE_READ, 0, NULL); if (FAILED(hr)) { AfxMessageBox(_T("Initialize failed.")); return E_FAIL; } //Add primary video stream hr = pAMStream->AddMediaStream(m_pDD3, &MSPID_PrimaryVideo, 0, NULL); if (FAILED(hr)) { AfxMessageBox(_T("AddMediaStream failed.")); return E_FAIL; } //Add primary audio stream hr = pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, AMMSF_ADDDEFAULTRENDERER, NULL); if (FAILED(hr)) { AfxMessageBox(_T("AddMediaStream failed.")); return E_FAIL; } //Opens and automatically creates a filter graph for the specified media file hr = pAMStream->OpenFile(wFile, 0); if (FAILED(hr)) { AfxMessageBox(_T("File format not supported.")); return E_FAIL; } //save the local stream to the global variable m_pMMStream = pAMStream; // Add a reference to the file pAMStream->AddRef(); return NOERROR; } //Create the stream sample which will be used to call updates on the video HRESULT CMovieView::InitRenderToSurface() { HRESULT hr; DDSURFACEDESC ddsd; CRect rect; //Use the multimedia stream to get the primary video media stream hr = m_pMMStream->GetMediaStream(MSPID_PrimaryVideo, &m_pPrimaryVidStream); if (FAILED(hr)) { goto Exit; } //Use the media stream to get the IDirectDrawMediaStream hr = m_pPrimaryVidStream->QueryInterface(IID_IDirectDrawMediaStream, (void **)&m_pDDStream); if (FAILED(hr)) { goto Exit; } //Must set dwSize before calling GetFormat ddsd.dwSize = sizeof(ddsd); hr = m_pDDStream->GetFormat(&ddsd, NULL, NULL, NULL); if (FAILED(hr)) { goto Exit; } rect.top = rect.left = 0; rect.bottom = ddsd.dwHeight; m_video_height=(WORD)rect.bottom; rect.right = ddsd.dwWidth; m_video_width=(WORD)rect.right; //Create the stream sample in offscreen surface #2 (attachment) hr = m_pDDStream->CreateSample(m_pDDSOffscreen2, &rect, 0, &m_pSample); if (FAILED(hr)) { goto Exit; } Exit: if (FAILED(hr)) { AfxMessageBox(_T("Initialization failure in InitRenderToSurface")); return E_FAIL; } return NOERROR; } //Perform frame by frame updates and blits. Set the stream //state to STOP if there are no more frames to update. void CMovieView::RenderToSurface(BOOL bSingleBlit) { HRESULT hr; POINT point; CRect rect, rect2; if (!m_bFileLoaded || !m_bAppactive) return; if (!m_bPaused) { //update each frame if (m_pSample->Update(0, NULL, NULL, 0) != S_OK) { m_bAppactive = FALSE; m_pMMStream->SetState(STREAMSTATE_STOP); } } rect.top = rect.left = 0; rect.bottom = m_video_height; rect.right = m_video_width; if (!bSingleBlit) { // performs a couple of operations including a blit. //blit from the offscreen surface #2 (video sample-backbuffer) // to the offscreen surface #1 (copy-backbuffer) hr = m_pDDSOffscreen->Blt(&rect, m_pDDSOffscreen2, &rect, DDBLT_WAIT, NULL); if(FAILED(hr)) { AfxMessageBox("Blt failed"); ExitCode(); } } //stretchblit from the offscreen surface #1 to the primary surface //get window coordinates to blit into GetClientRect(&rect2); // rectangle for the primary surface point.x = rect2.top; // transfrom rect into screen coordinates point.y = rect2.left; ClientToScreen(&point); rect2.left = point.x; rect2.top = point.y; point.x = rect2.right; point.y = rect2.bottom; ClientToScreen(&point); rect2.right = point.x; rect2.bottom= point.y; // In single blit mode, choose the sample-backbuffer to stretch-blit from // Otherwise, blit from the copy-backbuffer IDirectDrawSurface *surface= bSingleBlit ? m_pDDSOffscreen2 : m_pDDSOffscreen; hr = m_pPrimarySurface->Blt(&rect2, surface, &rect, DDBLT_WAIT, NULL); if(FAILED(hr)) { AfxMessageBox(_T("Blt failed")); ExitCode(); } } void CMovieView::DestroyObjects() { //Release MultiMedia streaming Objects if (m_pMMStream != NULL) { m_pMMStream->Release(); m_pMMStream= NULL; } if (m_pSample != NULL) { m_pSample->Release(); m_pSample = NULL; } if (m_pDDStream != NULL) { m_pDDStream->Release(); m_pDDStream= NULL; } if (m_pPrimaryVidStream != NULL) { m_pPrimaryVidStream->Release(); m_pPrimaryVidStream= NULL; } // Release DirectDrawEx interfaces if (m_pDDSOffscreen != NULL) { m_pDDSOffscreen->Release(); m_pDDSOffscreen= NULL; } if (m_pDDSOffscreen2 != NULL) { m_pDDSOffscreen2->Release(); m_pDDSOffscreen2= NULL; } if (m_pDDClipper != NULL) { m_pDDClipper->Release(); m_pDDClipper= NULL; } if (m_pPrimarySurface != NULL) { m_pPrimarySurface->Release(); m_pPrimarySurface= NULL; } if (m_pDD3 != NULL) { m_pDD3->Release(); m_pDD3= NULL; } if (m_pDD != NULL) { m_pDD->Release(); m_pDD= NULL; } if (m_pDDF != NULL) { m_pDDF->Release(); m_pDDF= NULL; } } void CMovieView::ExitCode() { DestroyObjects(); ::PostQuitMessage(0); CoUninitialize(); } afx_msg void CMovieView::OnFileOpen() { //If a file is already open - call STOP first if (m_bAppactive && m_bFileLoaded) { m_pMMStream->SetState(STREAMSTATE_STOP); // as the routine OnFileOpen can be called a couple of times, each time // the multimedia stream and directDraw surfaces should be released before // we start the whole story once again... DestroyObjects(); } if (m_pDD==NULL) { HRESULT hr = InitDDrawEx(); // initialize DirectDrawEx if (FAILED(hr)) ExitCode(); } static BOOL bOpen = TRUE; GetOpenMovieFile(m_szFilename); if (bOpen) { HRESULT hr = RenderFileToMMStream(m_szFilename); if (FAILED(hr)) { // file format not supported // hence destroy stream DestroyObjects(); m_bAppactive = m_bFileLoaded = FALSE; } else { hr = InitRenderToSurface(); if (FAILED(hr)) { ExitCode(); } m_bAppactive = m_bFileLoaded = TRUE; m_bPaused = FALSE; //Take care of any old pauses //Now set the multimedia stream to RUN hr = m_pMMStream->SetState(STREAMSTATE_RUN); if (FAILED(hr)) { ExitCode(); } } } // end if (bOpen) } // Display the open dialog box to retrieve the user-selected movie file BOOL CMovieView::GetOpenMovieFile(LPSTR szName)//LPSTR szName { OPENFILENAME ofn; ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = AfxGetMainWnd()->m_hWnd; ofn.lpstrFilter = NULL; ofn.lpstrFilter = "Video (*.avi;*.mov;*.mpg;*.mpeg)\0*.avi;*.mov;*.mpg;*.mpeg\0All Files (*.*)\0*.*\0"; ofn.lpstrCustomFilter = NULL; ofn.nFilterIndex = 1; *szName = 0; ofn.lpstrFile = szName; ofn.nMaxFile = MAX_PATH; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = NULL; ofn.lpstrFileTitle = NULL; ofn.lpstrDefExt = NULL; ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST; return ::GetOpenFileName((LPOPENFILENAME)&ofn); } afx_msg void CMovieView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ) { if (nChar==VK_SPACE) switchPlayPause(); } afx_msg void CMovieView::OnLButtonDown( UINT nFlags, CPoint point ) { switchPlayPause(); } void CMovieView::switchPlayPause() { // pause if (!m_bPaused && m_bFileLoaded) { m_pMMStream->GetTime(&m_StreamTime); m_pMMStream->SetState(STREAMSTATE_STOP); //m_bAppactive = FALSE; m_bPaused = TRUE; } else // play { if (m_bPaused) { // If its in a paused state, seek and run m_pMMStream->Seek(m_StreamTime); m_pMMStream->SetState(STREAMSTATE_RUN); m_bAppactive = TRUE; m_bPaused = FALSE; } } } afx_msg void CMovieView::OnStart() { if (m_bFileLoaded) { if (!m_bAppactive) // Restart the stream m_StreamTime=0; if (!m_bAppactive || m_bPaused) // seek to restart or to end the pause state { m_pMMStream->Seek(m_StreamTime); m_pMMStream->SetState(STREAMSTATE_RUN); // run } m_bAppactive = TRUE; m_bPaused = FALSE; } else { AfxMessageBox(_T("Please select a movie file first.")); } } afx_msg void CMovieView::OnPause() { // Pause if not already in a paused state and you have a file loaded if (!m_bPaused && m_bFileLoaded) { m_pMMStream->GetTime(&m_StreamTime); m_pMMStream->SetState(STREAMSTATE_STOP); m_bAppactive = FALSE; m_bPaused = TRUE; } } afx_msg void CMovieView::OnStop() { if (m_bFileLoaded) { m_pMMStream->SetState(STREAMSTATE_STOP); m_StreamTime = 0; // Reset the stream time to 0 m_pMMStream->Seek(m_StreamTime); //Run one frame to reset video m_pMMStream->SetState(STREAMSTATE_RUN); RenderToSurface(); m_pMMStream->SetState(STREAMSTATE_STOP); // Stop for real this time m_StreamTime = 0; } m_bAppactive = FALSE; } ///////////////////////////////////////////////////////////////////////////// // CMovieView diagnostics #ifdef _DEBUG void CMovieView::AssertValid() const { CView::AssertValid(); } void CMovieView::Dump(CDumpContext& dc) const { CView::Dump(dc); } CMovieDoc* CMovieView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMovieDoc))); return (CMovieDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CMovieView message handlers