问题描述
我有一个通过 directshow 播放文件 wmv 的 dll。
这里的主要功能: 玩家:
include "common.h"
#include "dshow.player.h"
#include "dshow.playback.h"
class DShowWindow
{
public:
DShowWindow(std::wstring name);
~DShowWindow();
public:
static HRESULT PlayVideo(HWND owner,std::wstring name,std::wstring filename);
private:
static LRESULT CALLBACK WndProc(HWND hwnd,UINT uMsg,WParaM wParam,LParaM lParam);
LRESULT HandleMessages(HWND hwnd,LParaM lParam);
static void CALLBACK GraphEventProc(HWND hwnd,long evCode,LONG_PTR param1,LONG_PTR param2);
void OnGraphEvent(HWND hwnd,LONG_PTR param2);
void OnPaint(HWND hwnd);
void OnSize(HWND hwnd);
HRESULT Play(HWND owner,std::wstring filename);
private:
ATOM classAtom;
HWND windowHandle;
std::unique_ptr<DShowPlayer> player;
bool continuePlayback;
bool userInterrupted;
};
DShowWindow::DShowWindow(std::wstring name)
{
this->userInterrupted = false;
WNDCLASS wc{};
wc.lpfnWndProc = DShowWindow::WndProc;
wc.lpszClassName = name.c_str();
this->classAtom = RegisterClass(&wc);
if (!this->classAtom)
{
return;
}
this->windowHandle = CreateWindowEx(
0,(LPWSTR)this->classAtom,L"DirectShow Playback",WS_POPUP | WS_EX_TOPMOST,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),nullptr,nullptr);
if (this->windowHandle == nullptr)
{
return;
}
SetwindowLongPtr(this->windowHandle,GWLP_USERDATA,reinterpret_cast<LONG_PTR>(this));
}
DShowWindow::~DShowWindow()
{
if (this->windowHandle)
{
DestroyWindow(this->windowHandle);
}
if (this->classAtom)
{
UnregisterClass((LPWSTR)this->classAtom,nullptr);
}
}
HRESULT DShowWindow::PlayVideo(HWND owner,std::wstring filename)
{
DShowWindow window{ name };
HRESULT hr = window.Play(owner,filename);
if (Failed(hr))
{
return hr;
}
return window.userInterrupted ? S_FALSE : S_OK;
}
LRESULT CALLBACK DShowWindow::WndProc(HWND hwnd,LParaM lParam)
{
LONG_PTR ptr = getwindowlongPtr(hwnd,GWLP_USERDATA);
if (ptr == 0)
{
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return reinterpret_cast<DShowWindow*>(ptr)->HandleMessages(hwnd,lParam);
}
LRESULT DShowWindow::HandleMessages(HWND hwnd,LParaM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
this->continuePlayback = false;
this->userInterrupted = true;
return 0;
case WM_disPLAYCHANGE:
if (this->player)
{
this->player->displayModeChanged();
}
break;
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
OnPaint(hwnd);
return 0;
case WM_SIZE:
OnSize(hwnd);
return 0;
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
case VK_SPACE:
case VK_RETURN:
case VK_BACK:
this->continuePlayback = false;
this->userInterrupted = true;
break;
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
this->continuePlayback = false;
this->userInterrupted = true;
break;
case WM_GRAPH_EVENT:
if (this->player)
{
this->player->HandleGraphEvent(DShowWindow::GraphEventProc);
}
return 0;
}
return DefWindowProc(hwnd,lParam);
}
void CALLBACK DShowWindow::GraphEventProc(HWND hwnd,LONG_PTR param2)
{
LONG_PTR ptr = getwindowlongPtr(hwnd,GWLP_USERDATA);
if (ptr == 0)
{
return;
}
return reinterpret_cast<DShowWindow*>(ptr)->OnGraphEvent(hwnd,evCode,param1,param2);
}
void DShowWindow::OnGraphEvent(HWND hwnd,LONG_PTR param2)
{
switch (evCode)
{
case EC_COMPLETE:
case EC_ERRORABORT:
this->player->Stop();
this->continuePlayback = false;
break;
case EC_USERABORT:
this->player->Stop();
this->continuePlayback = false;
this->userInterrupted = true;
break;
}
}
void DShowWindow::OnPaint(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hwnd,&ps);
if (this->player->State() != STATE_NO_GRAPH && this->player->HasVideo())
{
this->player->Repaint(hdc);
}
else
{
FillRect(hdc,&ps.rcPaint,(HBrush)GetStockObject(BLACK_Brush));
}
EndPaint(hwnd,&ps);
}
void DShowWindow::OnSize(HWND hwnd)
{
if (this->player)
{
RECT rc;
GetClientRect(hwnd,&rc);
this->player->UpdateVideoWindow(&rc);
}
}
HRESULT DShowWindow::Play(HWND owner,std::wstring filename)
{
this->continuePlayback = true;
this->userInterrupted = false;
if (!this->windowHandle)
{
return E_FAIL;
}
this->player = std::make_unique<DShowPlayer>(this->windowHandle);
if (this->player == nullptr)
{
return E_FAIL;
}
HRESULT hr;
hr = this->player->OpenFile(filename.c_str());
if (Failed(hr))
{
return hr;
}
InvalidateRect(this->windowHandle,NULL,FALSE);
this->OnSize(this->windowHandle);
ShowWindow(this->windowHandle,SW_MAXIMIZE);
while (ShowCursor(FALSE) >= 0);
hr = this->player->Play();
if (Failed(hr))
{
return hr;
}
MSG msg{};
while (this->continuePlayback && GetMessage(&msg,this->windowHandle,0))
{
TranslateMessage(&msg);
dispatchMessage(&msg);
}
this->player.reset();
return S_OK;
}
int DShowPlayVideo(std::wstring filename)
{
HWND xwaWindow = *(HWND*)0x9F701A;
LPDIRECTDRAW xwaDirectDraw = *(LPDIRECTDRAW*)0x9F7026;
int& xwaUserInterrupted = *(int*)0x9F4B40;
xwaDirectDraw->RestoredisplayMode();
// Free DirectDraw resources
((void(*)())0x5407F0)();
int returnValue = 0;
HRESULT hr;
if (Failed(hr = DShowWindow::PlayVideo(xwaWindow,L"tgsmushPlayer",filename)))
{
wchar_t error[MAX_ERROR_TEXT_LEN];
AMGetErrorText(hr,error,MAX_ERROR_TEXT_LEN);
OutputDebugString(error);
}
else if (hr == S_FALSE)
{
xwaUserInterrupted = 1;
returnValue = 1;
}
SetForegroundWindow(xwaWindow);
ShowWindow(xwaWindow,SW_MAXIMIZE);
// Init DirectDraw resources
((void(*)())0x540370)();
return returnValue;
}
回放:
DShowPlayer::DShowPlayer(HWND hwnd)
:m_state(STATE_NO_GRAPH),m_hwnd(hwnd)
{
}
DShowPlayer::~DShowPlayer()
{
// Stop sending event messages
if (m_pEvent)
{
m_pEvent->SetNotifyWindow((OAHWND)NULL,NULL);
}
}
// Open a media file for playback.
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
HRESULT hr;
ComPtr<IBaseFilter> pSource;
// Create a new filter graph. (This also closes the old one,if any.)
hr = InitializeGraph();
if (SUCCEEDED(hr))
{
// Add the source filter to the graph.
hr = m_pGraph->AddSourceFilter(pszFileName,&pSource);
}
if (SUCCEEDED(hr))
{
// Try to render the streams.
hr = RenderStreams(pSource);
}
return hr;
}
// Respond to a graph event.
//
// The owning window should call this method when it receives the window
// message that the application specified when it called SetEventwindow.
//
// Caution: Do not tear down the graph from inside the callback.
HRESULT DShowPlayer::HandleGraphEvent(GraphEventFN pfnOnGraphEvent)
{
if (!m_pEvent)
{
return E_UNEXPECTED;
}
long evCode = 0;
LONG_PTR param1 = 0,param2 = 0;
HRESULT hr = S_OK;
// Get the events from the queue.
while (SUCCEEDED(m_pEvent->GetEvent(&evCode,¶m1,¶m2,0)))
{
// Invoke the callback.
pfnOnGraphEvent(m_hwnd,param2);
// Free the event data.
hr = m_pEvent->FreeEventParams(evCode,param2);
if (Failed(hr))
{
break;
}
}
return hr;
}
HRESULT DShowPlayer::Play()
{
if (m_state != STATE_PAUSED && m_state != STATE_STOPPED)
{
return VFW_E_WRONG_STATE;
}
HRESULT hr = m_pControl->Run();
if (SUCCEEDED(hr))
{
m_state = STATE_RUNNING;
}
return hr;
}
HRESULT DShowPlayer::Pause()
{
if (m_state != STATE_RUNNING)
{
return VFW_E_WRONG_STATE;
}
HRESULT hr = m_pControl->Pause();
if (SUCCEEDED(hr))
{
m_state = STATE_PAUSED;
}
return hr;
}
HRESULT DShowPlayer::Stop()
{
if (m_state != STATE_RUNNING && m_state != STATE_PAUSED)
{
return VFW_E_WRONG_STATE;
}
HRESULT hr = m_pControl->Stop();
if (SUCCEEDED(hr))
{
m_state = STATE_STOPPED;
}
return hr;
}
// EVR/VMR functionality
BOOL DShowPlayer::HasVideo() const
{
return (m_pVideo && m_pVideo->HasVideo());
}
// Sets the destination rectangle for the video.
HRESULT DShowPlayer::UpdateVideoWindow(const LPRECT prc)
{
if (m_pVideo)
{
return m_pVideo->UpdateVideoWindow(m_hwnd,prc);
}
else
{
return S_OK;
}
}
// Repaints the video. Call this method when the application receives WM_PAINT.
HRESULT DShowPlayer::Repaint(HDC hdc)
{
if (m_pVideo)
{
return m_pVideo->Repaint(m_hwnd,hdc);
}
else
{
return S_OK;
}
}
// Notifies the video renderer that the display mode changed.
//
// Call this method when the application receives WM_disPLAYCHANGE.
HRESULT DShowPlayer::displayModeChanged()
{
if (m_pVideo)
{
return m_pVideo->displayModeChanged();
}
else
{
return S_OK;
}
}
// Graph building
// Create a new filter graph.
HRESULT DShowPlayer::InitializeGraph()
{
HRESULT hr;
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_Filtergraph,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&m_pGraph));
if (Failed(hr))
{
return hr;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
if (Failed(hr))
{
return hr;
}
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
if (Failed(hr))
{
return hr;
}
// Set up event notification.
hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd,WM_GRAPH_EVENT,NULL);
if (Failed(hr))
{
return hr;
}
m_state = STATE_STOPPED;
return hr;
}
HRESULT DShowPlayer::CreateVideoRenderer()
{
HRESULT hr = E_FAIL;
enum { Try_EVR,Try_VMR9,Try_VMR7 };
for (DWORD i = Try_EVR; i <= Try_VMR7; i++)
{
switch (i)
{
case Try_EVR:
m_pVideo = std::make_unique<CEVR>();
break;
case Try_VMR9:
m_pVideo = std::make_unique<CVMR9>();
break;
case Try_VMR7:
m_pVideo = std::make_unique<CVMR7>();
break;
}
if (m_pVideo == nullptr)
{
hr = E_OUTOFMEMORY;
break;
}
hr = m_pVideo->AddToGraph(m_pGraph,m_hwnd);
if (SUCCEEDED(hr))
{
break;
}
m_pVideo.reset();
}
return hr;
}
// Render the streams from a source filter.
HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
HRESULT hr;
BOOL bRenderedAnyPin = FALSE;
ComPtr<IFiltergraph2> pGraph2;
ComPtr<IEnumPins> pEnum;
ComPtr<IBaseFilter> pAudioRenderer;
hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
if (Failed(hr))
{
return hr;
}
// Add the video renderer to the graph
hr = CreateVideoRenderer();
if (Failed(hr))
{
return hr;
}
// Add the DSound Renderer to the graph.
hr = AddFilterByCLSID(m_pGraph,CLSID_DSoundRender,&pAudioRenderer,L"Audio Renderer");
if (Failed(hr))
{
return hr;
}
// Enumerate the pins on the source filter.
hr = pSource->EnumPins(&pEnum);
if (Failed(hr))
{
return hr;
}
// Loop through all the pins
ComPtr<IPin> pPin;
while (pEnum->Next(1,&pPin,nullptr) == S_OK)
{
// Try to render this pin.
// It's OK if we fail some pins,if at least one pin renders.
HRESULT hr2 = pGraph2->RenderEx(pPin,AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL);
if (SUCCEEDED(hr2))
{
bRenderedAnyPin = TRUE;
}
}
hr = m_pVideo->FinalizeGraph(m_pGraph);
if (Failed(hr))
{
return hr;
}
// Remove the audio renderer,if not used.
BOOL bRemoved;
hr = RemoveUnconnectedRenderer(m_pGraph,pAudioRenderer,&bRemoved);
// If we succeeded to this point,make sure we rendered at least one
// stream.
if (SUCCEEDED(hr))
{
if (!bRenderedAnyPin)
{
hr = VFW_E_CANNOT_RENDER;
}
}
return hr;
}
这个 dll 是一个钩子,用于添加对高分辨率视频文件的支持。
但遗憾的是,在完成视频后,出现了 1 秒或更短的黑屏。
DLL 包含其他方法,可以播放 avi 文件而不会出现任何黑屏,但无法读取 60 FPS 文件(播放限制为 15 FPS)。
所以实际上最好的折衷方案是这 2 个运行良好的 cpp 文件,但在完成播放时添加了这个简短的黑屏。
我已尝试重新整理这些行:
// MSG msg{};
// while (this->continuePlayback && GetMessage(&msg,0))
// {
// TranslateMessage(&msg);
// dispatchMessage(&msg);
// }
// this->player.reset();
并且没有黑屏但不能播放视频。
我怀疑在收到消息时会产生这种短黑屏。
但我没有任何直播播放经验。
你能帮我吗?
谢谢!
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)