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