I built a little test video player and put my DS code inside a MFC application under Windows 7. The filter graph is built automatically with CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&m_pGraph);
I defined an IDC_STATIC area inside the window which contains the video:
HRESULT hr = m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&m_pVideo); RECT plane; GetDlgItem(IDC_STATIC_VIDEOPLANE)->GetWindowRect(&plane); ScreenToClient(&plane); m_pVideo->put_Owner((OAHWND)GetSafeHwnd()); m_pVideo->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS); m_pVideo->put_Visible(OATRUE); ...
I turn on and off fullscreen mode with this method:
void CDirectShowPlayerDlg::Fullscreen(bool enabled) {
if (m_pGraph) { IVideoWindow *pVideo = NULL; m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVideo); pVideo->put_FullScreenMode(enabled?OATRUE:OAFALSE); pVideo->Release(); } }
So, if I start the video in windowed mode in runs properly. Then, if I switch to fullscreen the video (with audio) runs nice in fullscreen mode. But if I switch back to windowed mode, my video area is just black, but audio continues to play. :-( If I then switch back to fullscreen again, the video runs properly again!
So, I am bit helpless why this happens. Any ideas?
Thanks a lot in advance!
25 Answers Found
Hallo John Archer!
I have same problem under Windows7 (64bit). I have tested three video renderers VMR9, VMR7 and old video renderer (CLSID_VideoRenderer). Both mixing video renderers work not properly, not how in Windows 2000/XP/Vista. They cannot return from the full screen mode with calling the put_FullScreenMode(OAFALSE) method. The old video renderer work properly but it has not all required features.
All attempts to set old settings return with S_OK, but make not any effect. An idea to work only in windowless mode is not acceptable for us, way a lot of our application cannot be remade.
Probably the problem lays in the following(DirectShow SDK, IVMRFilterConfig9::SetRenderingMode):
Remarks
The VMR is in VMRMode_Windowed by default. Use this method only if you are putting the VMR into VMRMode_Windowless or VMRMode_Renderless mode. You cannot change the mode after any pin has been connected and you cannot change the mode from windowless or renderless back to windowed, even before any pins are connected. Therefore, specifying VMRMode_Windowed for Mode has no effect under any circumstances.Regards
Pavel
thanks for your answer. Interesting, that you have the same problem. Maybe we can find a solution.
I encountered a very strange thing: I use a laptop with an second external monitor attached. If I start my player it is shown at the laptop screen and the described full screen dilemma begins. But if I drag the player onto the external screen, go to full screen and then back the video is shown properly! Very strange; I don't know what that means or how it could help us ...
@Roman: Thanks for your quick reply! Yep, I set the video window position correctly (4). But number 5 leads me into (probably noob) problems. As I said I have a MFC app. So I let Visual Studio declare ON_WM_MOVE() in the message map and the OnMove() method. Here it is:
void CDirectShowPlayerDlg::OnMove(int x, int y)
{
CDialog::OnMove(x, y);
if (m_pVideo) {
m_pVideo->NotifyOwnerMessage((OAHWND)this->GetSafeHwnd(), WM_MOVE, 0, 0);
}
}
But if I run that in debug mode I get an unhandled exception, access violation while reading at postion 0xcccccccc ... I am quite sure that this occurs as m_pVideo is not initialized. Sorry, I am a relatively beginner on that. m_pVideo is a class member and I tried to initialize it in the OnInitDialog() with m_pGraph->QueryInterface(IID_IVideoWindow, (void**)&m_pVideo);, but it seems, that OnMove() is called before OnInitDialog(). Any help on that would be cool! :-)
Thanks a lot in advance!
Hence:
use CComPtr templates take care of initialization of your variables
Yep, I know that this initialization is the problem, but I am not sure where to init it correctly. As I said, I though OnInitDialog would be a good place (I also init my filter graph, MediaControl and MediaEventEx there), but OnMove() seems to be called earlier. So what would be a good place to init them? Hm ...
EDIT: Ok, I put all the init code into ON_WM_CREATE() and it seems at least this debug problem is gone. :-)
Let's face the other/real problem ...
Yep, I know that this initialization is the problem, but I am not sure where to init it correctly. As I said, I though OnInitDialog would be a good place (I also init my filter graph, MediaControl and MediaEventEx there), but OnMove() seems to be called earlier. So what would be a good place to init them? Hm ...With CComPtr it's very easy:
class CDirectShowPlayerDlg
{
// ...
CComPtr<IVideoWindow> m_pVideo;
// ...
};
CDirectShowPlayerDlg::OnInitDialog(...)
{
ASSERT(!m_pVideo);
// ...
}
CDirectShowPlayerDlg::OnDestroy()
{
// ...
m_pVideo = NULL;
}
CDirectShowPlayerDlg::OnMove(...)
{
// ...
if(m_pVideo)
m_pVideo->NotifyOwnerMessage((OAHWND)this->GetSafeHwnd(), WM_MOVE, 0, 0);
}
http://alax.info/blog/tag/directshow
Nevertheless, the OnMove works fine now, but the problem with black video plane is still there. :-( On the second screen it works properly (see my post above). Strange ... :-/
I take the PlayWnd sample from DirectShow DSK. It can be found into the DirectX 9.0 SDK Update (February 2005) Extras(www.microsoft.com/DownLoads/details.aspx?familyid=8AF0AFA9-1383-44B4-BC8B-7D6315212323&displaylang=en).
At the moment, I have one (not elegant) solution of full screen problem:
Pause the graph after return from the full screen mode,
Save current play position,
Stop the graph,
Reconnect the input of VMR,
Start the graph again from the saved position.
But it produce a brake into the playback.
@Pavel: Ok, interesting. So I use ATI graphic card and AMD processor, so it seems not to be a hardware problem. Ok, this is a workaround, thanks for that. But I hope this could be fixed more smoothly ... Is DirectShow still bug fixed (assuming this is a bug)?
Thanks a lot in advance!
Thanks for explaining your workaround. This is a possibility but of course it would be better not to simulate full screen but to have real DS supported full screen. It's a bit annoying ...
Thanks for explaining your workaround. This is a possibility but of course it would be better not to simulate full screen but to have real DS supported full screen. It's a bit annoying ...Note that "real" full screen is deprecated, including as a part of VMR windowed mode. So windowless/simulation is the best you can do.
Oh, that is interesting, Roman. Thanks for that info. Can I find that info in the msdn documentary? Could you provide a link? I would need that as an official reference/source for my work. Thanks a lot in advance!http://msdn.microsoft.com/en-us/library/dd407299%28VS.85%29.aspx
To remain backward-compatible with existing applications, the VMR defaults to windowed mode. In windowed mode, the renderer creates its own window to display the video. Typically the application sets the video window to be a child of the application window. The existence of a separate video window causes some problems, however:
[...]
Windowless mode avoids these problems by [...]
http://msdn.microsoft.com/en-us/library/dd373407%28VS.85%29.aspx
The Video Renderer filter supports windowed mode only. The VMR-7 and VMR-9 filters support both modes. They default to windowed mode for backward compatibility, but windowless mode is preferred. The EVR supports windowless mode only.
This is the replacement of ToggleFullScreen() function:
HRESULT ToggleFullScreen(void)
{
HRESULT hr=S_OK;
LONG lMode;
static HWND hDrain=0;
// Don't bother with full-screen for audio-only files
if ((g_bAudioOnly) || (!pVW))
return S_OK;
CComPtr<IBaseFilter> pVideoRender;
LIF(GetVideoRenderer(pGB, pVideoRender));
if (FALSE == g_bFullscreen)
{
// Save current message drain
LIF(pVW->get_MessageDrain((OAHWND *) &hDrain));
//Switch into the FullScren mode.
RECT rcMonitor = {};
LIF(GetCurrentMonitorSize(rcMonitor, pVideoRender));
// Switch to full-screen mode
LIF(pVW->put_Visible(OAFALSE));
LIF(pVW->put_Owner( (OAHWND) NULL));
LIF(pVW->put_WindowStyle(0));
LIF(pVW->put_WindowStyleEx(WS_EX_TOPMOST | WS_POPUP));
LIF(pVW->SetWindowPosition(rcMonitor.left, rcMonitor.top,
rcMonitor.right - rcMonitor.left, rcMonitor.bottom - rcMonitor.top));
LIF(CorrectAspectRatio(pVideoRender));
LIF(pVW->put_Visible(OATRUE));
// Set message drain to application main window
LIF(pVW->put_MessageDrain((OAHWND) ghApp));
g_bFullscreen = TRUE;
}
else
{
// Switch back to windowed mode
LIF(pVW->put_Visible(OAFALSE));
LIF(pVW->put_Owner((OAHWND)ghApp));
LIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS));
LIF(pVW->put_WindowStyleEx(0));
RECT client={};
GetClientRect(ghApp, &client);
LIF(pVW->SetWindowPosition(client.left, client.top, client.right, client.bottom));
LIF(CorrectAspectRatio(pVideoRender));
// Undo change of message drain
LIF(pVW->put_MessageDrain((OAHWND) hDrain));
LIF(pVW->put_Visible(OATRUE));
// Reset video window
LIF(pVW->SetWindowForeground(-1));
// Reclaim keyboard focus for player application
UpdateWindow(ghApp);
SetForegroundWindow(ghApp);
SetFocus(ghApp);
g_bFullscreen = FALSE;
}
return hr;
}
And are help functions:
HRESULT GetVideoRenderer(IGraphBuilder *pGraphBuilder, CComPtr<IBaseFilter>& pVideoRender)
{
pVideoRender.Release();
CheckPointer(pGraphBuilder, E_POINTER);
HRESULT hr = S_FALSE; //The renderer is not found.
CComPtr<IEnumFilters> pEnum;
CComPtr<IBaseFilter> pFilter;
ULONG ulFetched = 0;
// Get filter enumerator
hr = pGraphBuilder->EnumFilters(&pEnum);
if (FAILED(hr)){
return hr;
}
hr = pEnum->Reset();
if (FAILED(hr)){
return hr;
}
// Enumerate all filters in the graph
while( (pEnum->Next(1, &pFilter, &ulFetched) == S_OK))
{
CComPtr<IBasicVideo> pIBasicVideo;
hr = pFilter->QueryInterface(IID_IBasicVideo, reinterpret_cast<void**>(&pIBasicVideo));
if(SUCCEEDED(hr))
{
pVideoRender = pFilter;
return S_OK;
}
pFilter.Release();
}
return E_NOTIMPL;
}
HRESULT GetCurrentMonitorSize(RECT& rcMonitor, CComPtr<IBaseFilter>& pVideoRender)
{
CheckPointer(pVideoRender, E_POINTER);
HRESULT hr = S_OK;
CComPtr<IVMRMonitorConfig9> pVMRMonitorConfig9;
hr = pVideoRender ->QueryInterface(IID_IVMRMonitorConfig9, reinterpret_cast<void**>(&pVMRMonitorConfig9));
if(SUCCEEDED(hr))
{
VMR9MonitorInfo vMonInfo[16] = {}; // Array of VMR9MonitorInfo structures.
DWORD dwNumMonitors = 0; // Number of attached monitors.
UINT dwCutrrentMonitorIndex = 0;
LIF( pVMRMonitorConfig9->GetAvailableMonitors(vMonInfo, 16, &dwNumMonitors) );
rcMonitor = vMonInfo[dwCutrrentMonitorIndex].rcMonitor;
}
else
{
CComPtr<IVMRMonitorConfig> pVMRMonitorConfig;
hr = pVideoRender->QueryInterface(IID_IVMRMonitorConfig, reinterpret_cast<void**>(&pVMRMonitorConfig));
if(FAILED(hr)){
return hr;
}
VMRMONITORINFO vMonInfo[16] = {}; // Array of VMRMONITORINFO structures.
DWORD dwNumMonitors = 0; // Number of attached monitors.
UINT dwCutrrentMonitorIndex = 0;
LIF( pVMRMonitorConfig->GetAvailableMonitors(vMonInfo, 16, &dwNumMonitors) );
rcMonitor = vMonInfo[dwCutrrentMonitorIndex].rcMonitor;
}
return S_OK;
}
HRESULT CorrectAspectRatio(CComPtr<IBaseFilter>& pVideoRender)
{
CheckPointer(pVideoRender, E_POINTER);
HRESULT hr = S_OK;
CComPtr<IVMRAspectRatioControl9> pIVMRAspectRatioControl9;
hr = pVideoRender ->QueryInterface(IID_IVMRAspectRatioControl9, reinterpret_cast<void**>(&pIVMRAspectRatioControl9));
if(SUCCEEDED(hr))
{
LIF( pIVMRAspectRatioControl9->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX) );
}
else
{
CComPtr<IVMRAspectRatioControl> pIVMRAspectRatioControl;
hr = pVideoRender ->QueryInterface(IID_IVMRAspectRatioControl, reinterpret_cast<void**>(&pIVMRAspectRatioControl));
if(FAILED(hr)){
return hr;
}
LIF( pIVMRAspectRatioControl->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX) );
}
return S_OK;
}
Regards
Thanks again for your good help!
finally I had to time to work on that topic again. Unfortunately I cannot run your code. I don't have the PlayWnd sample here. It seems to be part of an DirectX version, but which one? For my studies I have the version of April 2007 installed, but now DirectShow samples are in there. Then I tried to modify the DSPlay sample from the latest Windows SDK (v7.0), but I can't compile it after your changes because of problems in refclock.h (syntax error: CAMSchedule).
Would it be possible to send the whole project to me? I would be very thankful!
Nevertheless, I took more time in investigating the problem. I cam across the fact that this problem also occurs on Windows Vista ( http://social.msdn.microsoft.com/forums/en-US/windowsdirectshowdevelopment/thread/cd515f32-87f4-4726-a07d-f995fa521e1f/ ). It seems to be problem in Aero. So if I deactivate "Enable desktop composition"everything works perfect!!!
Pavel, you write you think VMR7 and VMR9 are obsolete? I can't find information on that. Why do you think this? Well, ok, as VMR7 uses DirectDraw I would go with that.
I think it is also interesting, that EVR (the successor of VMR7/9?) also has a fullscreen method (IMFVideoDisplayControl::SetFullscreen Method) - but much more interesting that this method is already deprecated! See this: http://msdn.microsoft.com/en-us/library/ms701561(VS.85).aspx . Instead you should do the following:
"This method is deprecated. New applications should not call this method. To implement full-screen playback, an application should simply resize the video window to cover the entire area of the monitor. Also set the window to be a topmost window, so that the application receives all mouse-click messages. For more information about topmost windows, see the documentation for the SetWindowPos function in the Windows SDK."
Why this? I think that's not very comfortable. I mean, such a nice method :-) and I should not use it but implement it for myself? Strange ... :-/
Greetings,
Micha
PS: Also interesting, why is IVideoWindow::put_FullScreenMode not deprecated, if it is for EVR?
I don't have the PlayWnd sample here. It seems to be part of an DirectX version, but which one? For my studies I have the version of April 2007 installed, but now DirectShow samples are in there. Then I tried to modify the DSPlay sample from the latest Windows SDK (v7.0), but I can't compile it after your changes because of problems in refclock.h (syntax error: CAMSchedule).
...1. DirectX Summer 2004 SDK has the most samples (recommended for this and other reasons on my site):
Pavel, you write you think VMR7 and VMR9 are obsolete? I can't find information on that. Why do you think this? Well, ok, as VMR7 uses DirectDraw I would go with that.
I think it is also interesting, that EVR (the successor of VMR7/9?) also has a fullscreen method (IMFVideoDisplayControl::SetFullscreen Method) - but much more interesting that this method is already deprecated! See this: http://msdn.microsoft.com/en-us/library/ms701561(VS.85).aspx . Instead you should do the following:
"This method is deprecated. New applications should not call this method. To implement full-screen playback, an application should simply resize the video window to cover the entire area of the monitor. Also set the window to be a topmost window, so that the application receives all mouse-click messages. For more information about topmost windows, see the documentation for the SetWindowPos function in the Windows SDK."
...
I have just returned from Christmas holiday. If the full project is still interesting to you, I can send it to you.
Pavel
John,
I am facing the same exact problem of yours, did you find a solution or is using a (separate stretched window) is the only solution?
I appreciate any help from you, take care
Hi Tamero,
sorry for my late reply ... :-(
Unfortunately, I couldn't work anymore on that project, so I have no other solution and didn't contact Pavel. :-/ I guess, best is you go with Pavels solution. Try his approach and contact him to get the full project.
Let me know whether it works!
Happy coding and good luck!
Greetings