📄 ticker.cpp
字号:
//------------------------------------------------------------------------------
// File: Ticker.cpp
//
// Desc: DirectShow sample code - a simple static image display app.
// Using the DirectX 9 Video Mixing Renderer, a static image is
// alpha blended with the image in the corner of the screen.
// The image moves from right to left near the bottom of the screen.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include <dshow.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>
#include "ticker.h"
#include "bitmap.h"
#include "vmrutil.h"
// An application can advertise the existence of its filter graph
// by registering the graph with a global Running Object Table (ROT).
// The GraphEdit application can detect and remotely view the running
// filter graph, allowing you to 'spy' on the graph with GraphEdit.
//
// To enable registration in this sample, define REGISTER_FILTERGRAPH.
//
#define REGISTER_FILTERGRAPH
//
// Global data
//
HWND ghApp=0;
HMENU ghMenu=0;
HINSTANCE ghInst=0;
TCHAR g_szFileName[MAX_PATH]={0};
DWORD g_dwGraphRegister=0;
RECT g_rcDest={0};
// DirectShow interfaces
IGraphBuilder *pGB = NULL;
IMediaControl *pMC = NULL;
IMediaEventEx *pME = NULL;
IMediaSeeking *pMS = NULL;
IVMRWindowlessControl9 *pWC = NULL;
HRESULT PlayMovieInWindow(LPTSTR szFile)
{
USES_CONVERSION;
WCHAR wFile[MAX_PATH];
HRESULT hr;
// Check input string
if (szFile == NULL)
return E_POINTER;
// Clear open dialog remnants before calling RenderFile()
UpdateWindow(ghApp);
// Convert filename to wide character string
wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
wFile[MAX_PATH-1] = 0;
// Get the interface for DirectShow's GraphBuilder
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGB));
if(SUCCEEDED(hr))
{
CComPtr <IBaseFilter> pVmr;
// Create the Video Mixing Renderer and add it to the graph
JIF(InitializeWindowlessVMR(&pVmr));
// Render the file programmatically to use the VMR9 as renderer.
// We pass a pointer to the VMR9 so that it will be used as the
// video renderer. Pass TRUE to create an audio renderer also.
if (FAILED(hr = RenderFileToVMR9(pGB, wFile, pVmr, TRUE)))
return hr;
// QueryInterface for DirectShow interfaces
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
// Is this an audio-only file (no video component)?
if (CheckVideoVisibility())
{
JIF(InitVideoWindow(1, 1));
}
else
{
// This sample requires a video clip to be loaded
Msg(TEXT("This sample requires media with a video component. ")
TEXT("Please select another file."));
return E_FAIL;
}
// Have the graph signal event via window callbacks for performance
JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));
// Add the bitmap (static image or dynamic text) to the VMR's input
if (g_dwTickerFlags & MARK_STATIC_IMAGE)
{
hr = BlendApplicationImage(ghApp);
if (FAILED(hr))
PostMessage(ghApp, WM_CLOSE, 0, 0);
CheckMenuItem(ghMenu, ID_TICKER_STATIC_IMAGE, MF_CHECKED);
CheckMenuItem(ghMenu, ID_TICKER_DYNAMIC_TEXT, MF_UNCHECKED);
}
else // MARK_DYNAMIC_TEXT
{
if (!g_hFont)
g_hFont = SetTextFont(FALSE); // Don't display the Windows Font Select dialog
// If the initial blend fails, post a close message to exit the app
hr = BlendApplicationText(ghApp, g_szAppText);
if (FAILED(hr))
PostMessage(ghApp, WM_CLOSE, 0, 0);
CheckMenuItem(ghMenu, ID_TICKER_STATIC_IMAGE, MF_UNCHECKED);
CheckMenuItem(ghMenu, ID_TICKER_DYNAMIC_TEXT, MF_CHECKED);
}
// Complete the window setup
ShowWindow(ghApp, SW_SHOWNORMAL);
UpdateWindow(ghApp);
SetForegroundWindow(ghApp);
SetFocus(ghApp);
#ifdef REGISTER_FILTERGRAPH
if (FAILED(AddGraphToRot(pGB, &g_dwGraphRegister)))
{
Msg(TEXT("Failed to register filter graph with ROT!"));
g_dwGraphRegister = 0;
}
#endif
// Run the graph to play the media file
JIF(pMC->Run());
// Start animation by default
PostMessage(ghApp, WM_COMMAND, ID_SLIDE, 0);
}
return hr;
}
HRESULT InitVideoWindow(int nMultiplier, int nDivider)
{
LONG lHeight, lWidth;
HRESULT hr = S_OK;
if (!pWC)
return S_OK;
// Read the default video size
hr = pWC->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
if (hr == E_NOINTERFACE)
return S_OK;
// Account for requests of normal, half, or double size
lWidth = lWidth * nMultiplier / nDivider;
lHeight = lHeight * nMultiplier / nDivider;
int nTitleHeight = GetSystemMetrics(SM_CYCAPTION);
int nBorderWidth = GetSystemMetrics(SM_CXBORDER);
int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
// Account for size of title bar and borders for exact match
// of window client area to default video size
SetWindowPos(ghApp, NULL, 0, 0, lWidth + 2*nBorderWidth,
lHeight + nTitleHeight + 2*nBorderHeight,
SWP_NOMOVE | SWP_NOOWNERZORDER);
GetClientRect(ghApp, &g_rcDest);
hr = pWC->SetVideoPosition(NULL, &g_rcDest);
if (FAILED(hr))
Msg(TEXT("SetVideoPosition FAILED! hr=0x%x\r\n"), hr);
return hr;
}
HRESULT InitPlayerWindow(void)
{
// Reset to a default size for audio and after closing a clip
SetWindowPos(ghApp, NULL, 0, 0,
DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT,
SWP_NOMOVE | SWP_NOOWNERZORDER);
return S_OK;
}
void MoveVideoWindow(void)
{
HRESULT hr;
// Track the movement of the container window and resize as needed
if(pWC)
{
GetClientRect(ghApp, &g_rcDest);
hr = pWC->SetVideoPosition(NULL, &g_rcDest);
if (FAILED(hr))
Msg(TEXT("SetVideoPosition FAILED! hr=0x%x\r\n"), hr);
}
}
BOOL CheckVideoVisibility(void)
{
HRESULT hr;
LONG lWidth=0, lHeight=0;
//
// Because this sample explicitly loads the VMR9 into the filter graph
// before rendering a file, the IVMRWindowlessControl interface will exist
// for all properly rendered files. As a result, we can't depend on the
// existence of the pWC interface to determine whether the media file has
// a video component. Instead, check the width and height values.
//
if (!pWC)
{
// Audio-only files have no video interfaces. This might also
// be a file whose video component uses an unknown video codec.
return FALSE;
}
hr = pWC->GetNativeVideoSize(&lWidth, &lHeight, 0, 0);
if (FAILED(hr))
{
// If this video is encoded with an unsupported codec,
// we won't see any video, although the audio will work if it is
// of a supported format.
return FALSE;
}
// If this is an audio-only clip, width and height will be 0.
if ((lWidth == 0) && (lHeight == 0))
return FALSE;
// Assume that this media file contains a video component
return TRUE;
}
void OpenClip()
{
HRESULT hr;
// If no filename specified by command line, show file open dialog
if(g_szFileName[0] == L'\0')
{
TCHAR szFilename[MAX_PATH];
InitPlayerWindow();
SetForegroundWindow(ghApp);
if (! GetClipFileName(szFilename))
{
DWORD dwDlgErr = CommDlgExtendedError();
// Don't show output if user cancelled the selection (no dlg error)
if (dwDlgErr)
{
Msg(TEXT("GetClipFileName Failed! Error=0x%x\r\n"), GetLastError());
}
return;
}
// This sample does not support playback of ASX playlists.
// Since this could be confusing to a user, display a warning
// message if an ASX file was opened.
if (_tcsstr((_tcslwr(szFilename)), TEXT(".asx")))
{
Msg(TEXT("ASX Playlists are not supported by this application.\n\n")
TEXT("Please select a valid media file.\0"));
return;
}
lstrcpyn(g_szFileName, szFilename, NUMELMS(g_szFileName));
}
EnableTickerMenu(TRUE);
// Start playing the media file
hr = PlayMovieInWindow(g_szFileName);
// If we couldn't play the clip, clean up
if (FAILED(hr))
CloseClip();
}
BOOL GetClipFileName(LPTSTR szName)
{
static OPENFILENAME ofn={0};
static BOOL bSetInitialDir = FALSE;
// Reset filename
*szName = 0;
// Fill in standard structure fields
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = ghApp;
ofn.lpstrFilter = NULL;
ofn.lpstrFilter = FILE_FILTER_TEXT;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szName;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrTitle = TEXT("Open Image File...\0");
ofn.lpstrFileTitle = NULL;
ofn.lpstrDefExt = TEXT("*\0");
ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;
// Remember the path of the first selected file
if (bSetInitialDir == FALSE)
{
ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH;
bSetInitialDir = TRUE;
}
else
ofn.lpstrInitialDir = NULL;
// Create the standard file open dialog and return its result
return GetOpenFileName((LPOPENFILENAME)&ofn);
}
void CloseClip()
{
HRESULT hr;
// Stop media playback
if(pMC)
hr = pMC->Stop();
// Free DirectShow interfaces
CloseInterfaces();
// Clear file name to allow selection of new file with open dialog
g_szFileName[0] = L'\0';
// Reset the player window
RECT rect;
GetClientRect(ghApp, &rect);
InvalidateRect(ghApp, &rect, TRUE);
InitPlayerWindow();
EnableTickerMenu(FALSE);
}
void CloseInterfaces(void)
{
#ifdef REGISTER_FILTERGRAPH
if (g_dwGraphRegister)
{
RemoveGraphFromRot(g_dwGraphRegister);
g_dwGraphRegister = 0;
}
#endif
// Clear ticker state and timer settings
ClearTickerState();
// Release and zero DirectShow interfaces
SAFE_RELEASE(pME);
SAFE_RELEASE(pMS);
SAFE_RELEASE(pMC);
SAFE_RELEASE(pWC);
SAFE_RELEASE(pBMP);
SAFE_RELEASE(pGB);
}
HRESULT HandleGraphEvent(void)
{
LONG evCode, evParam1, evParam2;
HRESULT hr=S_OK;
// Make sure that we don't access the media event interface
// after it has already been released.
if (!pME)
return S_OK;
// Process all queued events
while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
(LONG_PTR *) &evParam2, 0)))
{
// Free memory associated with callback, since we're not using it
hr = pME->FreeEventParams(evCode, evParam1, evParam2);
// If this is the end of the clip, reset to beginning
if(EC_COMPLETE == evCode)
{
LONGLONG pos=0;
// Reset to first frame of movie
hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
NULL, AM_SEEKING_NoPositioning);
if (FAILED(hr))
{
// If seeking failed, just stop and restart playback
hr = pMC->Stop();
hr = pMC->Run();
}
}
}
return hr;
}
#ifdef REGISTER_FILTERGRAPH
HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
IMoniker * pMoniker;
IRunningObjectTable *pROT;
if (FAILED(GetRunningObjectTable(0, &pROT)))
{
return E_FAIL;
}
WCHAR wsz[128];
wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph,
GetCurrentProcessId());
HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
if (SUCCEEDED(hr))
{
// Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
// to the object. Using this flag will cause the object to remain
// registered until it is explicitly revoked with the Revoke() method.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -