The example program provided here to run a video file. I am using "avi" format file for playing with DShow APIs in Visual Studio 2015.
Refer to the complete code:
#include<dshow.h>
#include<iostream>
using namespace std;
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
cout << "ERROR - Could not initialize COM library"<<endl;
return -1;
}
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr))
{
cout << "EROR - Could not create the Filter Graph Manager";
return -1 ;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void**)&pEvent);
hr = pGraph->RenderFile(L"C:\\Users\\sunil\\Documents\\Ramp\\output.avi", NULL);
if (SUCCEEDED(hr))
{
hr = pControl->Run();
if (SUCCEEDED(hr))
{
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
return 0;
}
The build is successful but when I run it a windows pops up with the title "ActiveMovie window".
But there is no video in the window.
I referred to the comments on the same page and many others faced the same issue. However a few were able to successfully run the program.
What is it that I am doing wrong?
My question is taken from another similar question. I could not comment in that question coz I am new to Stack Overflow.
The code is about right (plays for me just as is). Typical problems with the code include:
there is a codec issue, you are trying to play a file with specific encoding, which is not picked up or handling is overridden by another third party software (which might be okay on its own but creates a problem together with #2 below).
you are ignoring the fact that WaitForCompletion call is blocking execution on the thread and you are in the same time responsible for dispatching window messages there because you initialized COM as STA.
An easy way to find out if #2 is the problem is placing MessageBox call between Run and WaitForCompletion. MessageBox dispatches messages for you and as long as you keep the box open, video plays as well (or start playing well and keeps playing even after you close the box). Proper solution is to wait and displach messages in the same time (WaitDispatchingMessages, this SO question or similar).
Related
I have learned C/C++ Basics and practiced , but I have hard time understanding
Microsoft documentation and find it confusing Documention example
for example : I try to create command line program that should let the user open
folder dialog and choose folder , as result the folders path should be stored in variable
did research and found that there is many ways to achieve this goal but the best way is using IFileDialog::GetFolder method (shobjidl_core.h)
what the difference between file dialogs?
The main question :
How to get folders path as string variable based on user choice from file dialog?
There is c++ resources with practical tutorials ?
I try to understand how I use the following dialog :
Folder dialog
it refernces me to:
BROWSEINFOA structure
Would be very helpful if someone could explain how I can use this folder dialog or something better
any great tutorial of windows/linux file system handling
I used to wcout to print out path
TCHAR path[260];
BROWSEINFO bi = { 0 };
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
SHGetPathFromIDList(pidl, path);
wcout << path << '\n';
We can use com interface as well :
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow);
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOpenDialog* pFileOpen;
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
// if object created ( that's com object )
if (SUCCEEDED(hr))
{
// Show the Open dialog box.
hr = pFileOpen->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem* pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
PWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
// Display the file name to the user.
if (SUCCEEDED(hr))
{
MessageBoxW(NULL, pszFilePath, L"File Path", MB_OK);
CoTaskMemFree(pszFilePath);
cout << pszFilePath;
std::string stringtoconvert;
std::wstring temp = std::wstring(stringtoconvert.begin(), stringtoconvert.end());
LPCWSTR lpcwstr = temp.c_str();
}
pItem->Release();
}
}
pFileOpen->Release();
}
CoUninitialize();
}
return 0;
}
Important to mention :
Open and Save As common dialog boxes ( commdlg.h ) have been superseded by the Common Item Dialog
For linux and other platforms there is cross platforms libraries like Qt and more , here is a link for UI solutions
and about Microsoft documentation : I think there should be simpler examples and explanations its complicated & frustrating to learn this way.
I need to write a program that can play .ogg Vorbis file with the help of IGraphBuilder or any other windows API directly (in C++/win32 API)?
I tried with IGraphBuilder but that is not working for me.
Sample code:
IMediaControl *pControl = NULL;
IGraphBuilder *pGraph= NULL;
IMediaEventEx *pEvent= NULL;
IMediaPosition *pMediaPosition= NULL;
hr = ::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr)) {
return false;
}
hr = pGraph->AddFilter(pFilter, L"Out");
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
hr = pGraph->QueryInterface(IID_IMediaPosition, (void**)&pMediaPosition);
// Build the graph.
hr = pGraph->RenderFile(mFilePath.c_str()/*"C:\\sample.ogg file"*/, NULL);
/* here hr = 0x80040265 so SUCCEEDED(hr) didnt allow it to enter in if condition*/
if (SUCCEEDED(hr)) {
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr)) {
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
}
}
// Clean up in reverse order.
SAFE_RELEASE(pEvent);
SAFE_RELEASE(pControl);
SAFE_RELEASE(pGraph);
pGraph = NULL;
::CoUninitialize();
NOTE:
Above statement hr = pGraph->RenderFile() returned the hr = 0x80040265 and condition if (SUCCEEDED(hr)) doesnt allow to play it.
If I dont use this condition then pControl->Run() gets executed with return ID_OK. But nothing played with speaker.
Please suggest the solution/method.
Pay attention to HRESULT error codes; they mean something. MSDN is often helpful with function-specific error codes like the one you got. (With enough COM programming you'll be able to recognize common ones like E_INVALIDARG by sight.) If not, you can use your header files to pinpoint potential error codes. HRESULTs have a specific format; learn it!
In the case of IGraphBuilder::RenderFile(), that HRESULT maps to VFW_E_UNSUPPORTED_STREAM, which basically means your setup does not support playing Ogg Vorbis files. You will need to install a filter that allows DirectShow to play Ogg Vorbis files, such as the official one from Xiph.Org.
I was referring to the example program provided here to run a video file, in my case mp4 format using DShow.
Refer to the complete code:
#include <dshow.h>
#pragma comment (lib, "strmiids.lib")
void main(void)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
// Initialize the COM library.
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr))
{
printf("ERROR - Could not create the Filter Graph Manager.");
return;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph. IMPORTANT: Change this string to a file on your system.
hr = pGraph->RenderFile(L"C:\\Example.avi", NULL);
if (SUCCEEDED(hr))
{
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}
However when I build the program, the build is sucessful but when I run it, the console window pops up and disappears within a second.
I referred to the comments on the same page and many others faced the same issue. However a few were able to successfully run the program.
What is it that I am doing wrong?
Is it the type of project I am selecting? I am selecting win32 console application. Should I select something else? Or is there something else that I am doing wrong?
That's the actual main code, preview works fine:
int main()
{
HRESULT hr = CoInitialize(NULL);
ICaptureGraphBuilder2 *pBuild;
IGraphBuilder *pGraph;
IMoniker *pMoniker;
IMediaControl *pControl;
IMediaEvent *pEvent;
InitCaptureGraphBuilder(&pGraph, &pBuild);
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
IBaseFilter *pCap; // Video capture filter
IEnumMoniker *pEnum;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
DisplayDeviceInformation(pEnum, &pMoniker);
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pCap, L"Capture Filter");
}
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);
hr = pControl->Run();
_getch();
pControl->Release();
pCap->Release();
pGraph->Release();
pBuild->Release();
CoUninitialize();
return 0;
}
Now, I know that for recording I need this piece of code:
IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"D:\\test.avi", &pMux, NULL);
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL, pMux);
If I replace this to the preview code, it actually create the AVI file (a very big one), but it's empty, no video.
I mean I'm replacing the:
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);
code, with the one above.
What I'm doing wrong, or better, what I'm missing?
RenderStream is high level method that internally embeds other calls through public documented APIs, typically for the ease of use. While it looks simple, it's not so easy to troubleshoot in case something does not work well and as expected. Even harder to tell inspecting just code visually. It also is not the most efficient because there is something you can do yourself to get closer to the solution, which is:
Your further steps are along either of the two:
You take working sample code and compare to yours looking at differences and locating the source of the problem.
You inspect the resulting filter graph topology putting your graph onto ROT, and checking using GraphEdit or a similar tool to ensure the topology is matching your expectations.
You also certainly need to check HRESULT codes, what you already seem to be doing.
[Revised yet again for clarity]
I have a C++ program which interacts with a website. The site is IE-specific, and so is my program.
I'm connecting to the running instance of IE in an ordinary way (out of process -- see code). Once I get the IWebBrowser2, I have no problem getting the IHTMLDocument2 and interacting with the individual IHTMLElement objects, filling in fields and clicking buttons.
But if the web page has javascript that calls window.showModalDialog, I'm stuck: I need to interact with the HTML elements in the popup, just like the other pages; but I can't seem to get its IWebBrowser2.
The popup is always titled "Web Page Dialog", and is a window of type Internet Explorer_TridentDlgFrame containing an Internet Explorer_Server. But I can't get the IWebBrowser2 from the Internet Explorer_Server window the way I can when it's a normal IE instance.
I can get the IHTMLDocument2Ptr, but when I try to get the IWebBrowser2 I get an HRESULT of E_NOINTERFACE.
The code is pretty standard stuff, and works fine if it's a 'normal' IE window
IHTMLDocument2Ptr pDoc;
LRESULT lRes;
/* hWndChild is an instance of class "Internet Explorer_Server" */
UINT nMsg = ::RegisterWindowMessage( "WM_HTML_GETOBJECT" );
::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000,
(DWORD*)&lRes );
LPFNOBJECTFROMLRESULT pfObjectFromLresult =
(LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, "ObjectFromLresult" );
if ( pfObjectFromLresult != NULL )
{
HRESULT hr;
hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&pDoc );
if ( SUCCEEDED(hr) ) {
IServiceProvider *pService;
hr = pDoc->QueryInterface(IID_IServiceProvider, (void **) &pService);
if ( SUCCEEDED(hr) )
{
hr = pService->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2, (void **) &pBrowser);
// This is where the problem occurs:
// hr == E_NOINTERFACE
}
}
}
In case it matters, this is Vista and IE8. (I emphasize this because both of these introduced breaking changes in my codebase, which had worked fine with XP/IE7.)
Once again, my goal is to get each IHTMLElement and interact with it. I don't have access to the source code of the application which I'm automating.
I'm considering sending keystrokes blindly to the Internet Explorer_Server window, but would rather not.
Edited to add:
Someone suggested getting the child windows and sending them messages, but I'm pretty sure that doesn't work with Internet Explorer_Server; according to Spy++, there's aren't any child windows. (This is not IE-specific. Java applets don't seem to have child windows, either.)
Update
In the comments, Simon Maurer said the above code worked for him, and just to make sure there were no typos he very generously posted a complete standalone app on pastebin. When I used his code, it failed in the same way in the same place, and I realized he thought I wanted to connect to the underlying page, not the popup. So I've edited the text above to remove that ambiguity.
I don't know why you want to get the IServiceProvider or IWebBrowser2 if you just want the IHTMLElement's. You can get them by calling the IHTMLDocument's get_all() method.
This code snippet shows you how this works:
#include <Windows.h>
#include <mshtml.h>
#include <Exdisp.h>
#include <atlbase.h>
#include <SHLGUID.h>
#include <oleacc.h>
#include <comdef.h>
#include <tchar.h>
HRESULT EnumElements(HINSTANCE hOleAccInst, HWND child)
{
HRESULT hr;
UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
LRESULT lRes = 0;
::SendMessageTimeout(child, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes);
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hOleAccInst, "ObjectFromLresult");
if (pfObjectFromLresult == NULL)
return S_FALSE;
CComPtr<IHTMLDocument2> spDoc;
hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc);
if (FAILED(hr)) return hr;
CComPtr<IHTMLElementCollection> spElementCollection;
hr = spDoc->get_all(&spElementCollection);
if (FAILED(hr)) return hr;
CComBSTR url;
spDoc->get_URL(&url);
printf("URL: %ws\n", url);
long lElementCount;
hr = spElementCollection->get_length(&lElementCount);
if (FAILED(hr)) return hr;
printf("Number of elements: %d", lElementCount);
VARIANT vIndex; vIndex.vt = VT_I4;
VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0;
for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++)
{
CComPtr<IDispatch> spDispatchElement;
if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement)))
continue;
CComPtr<IHTMLElement> spElement;
if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement)))
continue;
CComBSTR tagName;
if (SUCCEEDED(spElement->get_tagName(&tagName)))
{
printf("%ws\n", tagName);
}
}
return S_OK;
}
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL"));
if (hInst != NULL)
{
HRESULT hr = EnumElements(hInst, (HWND)0x000F05E4); // Handle to Internet Explorer_Server determined with Spy++ :)
::FreeLibrary(hInst);
}
::CoUninitialize();
return 0;
}
Above code works on both: a normal window or a modal window, just pass the correct HWND to the SendMessageTimeout function.
WARNING I use a hard-coded HWND value in this example, if you want to test it you should start an IE instance and get the HWND of the Internet Explorer_Server window using Spy++.
I also advise you to use CComPtr to avoid memory leaks.