I have such an implementation
void coAudioPlayerSampleGrabber::test(SoundDataType dataType,
unsigned char const * pData,
int64_t dataLen)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
IMFSourceReader *pReader = NULL;
IMFByteStream * spByteStream = NULL;
HRESULT hr = S_OK;
// Initialize the COM library.
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Initialize the Media Foundation platform.
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
}
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
if (FAILED(hr))
{
printf("Error MFCreateMFByteStreamOnStreamEx");
}
IMFAttributes * Atrr = NULL;
hr = MFCreateAttributes(&Atrr, 10);
if (FAILED(hr))
{
printf("Error MFCreateAttributes");
}
hr = Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);
if (FAILED(hr))
{
printf("Error Atrr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)");
}
hr = MFCreateSourceReaderFromByteStream(spByteStream, Atrr, &pReader);
if (FAILED(hr))
{
printf("Error MFCreateSourceReaderFromByteStream");
}
if (FAILED(hr))
{
printf("Error opening input file");
}
IMFMediaType *pAudioType = NULL; // Represents the PCM audio format.
hr = ConfigureAudioStream(dataType, pReader, &pAudioType);
if (FAILED(hr))
{
printf("Error ConfigureAudioStream");
}
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
BYTE *pAudioData = NULL;
DWORD cbBuffer = 0;
std::vector<SampleData> samples_vec;
while (true)
{
DWORD dwFlags = 0;
hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &dwFlags, NULL, &pSample);
if (FAILED(hr)) { break; }
if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
{
printf("Type change - not supported by WAVE file format.\n");
break;
}
if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of input file.\n");
break;
}
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (FAILED(hr)) { break; }
hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
if (FAILED(hr)) { break; }
//Do something with the pAudioData which is an array of unsigned chars of lenth cbBuffer
SampleData tmp;
tmp.pAudioData = new byte[cbBuffer];
memcpy(tmp.pAudioData, pAudioData, cbBuffer);
tmp.cbBuffer = cbBuffer;
samples_vec.push_back(tmp);
// Unlock the buffer.
hr = pBuffer->Unlock();
pAudioData = NULL;
if (FAILED(hr)) { break; }
}
SafeRelease(&pReader);
SafeRelease(&pSample);
SafeRelease(&pBuffer);
SafeRelease(&spByteStream);
SafeRelease(&Atrr);
// Shut down Media Foundation.
MFShutdown();
CoUninitialize();
}
So as you can see I have pointer to data and size this is actually my data I need to decode it. Problem is that here
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
I got an error access violation and as far as I see this is because I try to convert pData to IUnknown*. Question is - How to convert it right?
You can't cut corners like this:
unsigned char const * pData;
...
hr = MFCreateMFByteStreamOnStreamEx((IUnknown*)pData, &spByteStream);
IUnknown is not yet another fancy alias for a byte. You are supposed to literally supply interface pointer representing stream, as documented.
Media Foundation does offer you means to read from memory bytes. You need to create a create a real stream, IStream or IRandomAccessStream or IMFByteStream per docuemntation. Also supply IMFAttributes you created with proper attributes to specify data type (which otherwise in the case of a file are derived from extension or MIME type) and then Source Reader API would be able to process memory bytes as source of media file data, and suitable decoder would decode audio into PCM data (similar to this).
Something you can do real quick: CreateStreamOnHGlobal to create IStream implementation and copy your bytes into underlying buffer (see docs). Then MFCreateMFByteStreamOnStream would create a IMFByteStream wrappr over it, and you can use this wrapper as MFCreateSourceReaderFromByteStream argument.
I used to use a VectorStream class for such stuff. Some functions are not implemented, but you should get the basic idea.
class VectorStream : public IStream
{
public:
bool ReadOnly = false;
ULONG r = 1;
std::vector<char> d;
size_t p = 0;
VectorStream()
{
}
void Clear()
{
d.clear();
p = 0;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IStream))
{
*ppvObject = (IStream*)this;
r++;
return S_OK;
}
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return ++r;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
return --r;
}
HRESULT __stdcall Clone(
IStream** ppstm
)
{
return E_NOTIMPL;
}
HRESULT __stdcall Commit(
DWORD grfCommitFlags
)
{
return S_OK;
}
HRESULT __stdcall CopyTo(
IStream* pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER* pcbRead,
ULARGE_INTEGER* pcbWritten
)
{
return E_NOINTERFACE;
}
HRESULT __stdcall LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType
)
{
return S_OK;
}
HRESULT __stdcall Revert()
{
return E_NOTIMPL;
}
HRESULT __stdcall Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER* plibNewPosition
)
{
LARGE_INTEGER lo = { 0 };
if (dwOrigin == STREAM_SEEK_SET)
{
p = dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_CUR)
{
p += dlibMove.QuadPart;
}
if (dwOrigin == STREAM_SEEK_END)
{
p = d.size() - dlibMove.QuadPart;
}
if (p >= d.size())
p = d.size();
if (plibNewPosition)
plibNewPosition->QuadPart = p;
return S_OK;
}
HRESULT __stdcall SetSize(
ULARGE_INTEGER libNewSize
)
{
d.resize(libNewSize.QuadPart);
return S_OK;
}
int eb = 0;
HRESULT __stdcall Stat(
STATSTG* pstatstg,
DWORD grfStatFlag
)
{
pstatstg->type = STGTY_STREAM;
pstatstg->cbSize.QuadPart = d.size();
pstatstg->grfLocksSupported = true;
return S_OK;
}
unsigned long long readbytes = 0;
HRESULT __stdcall Read(
void* pv,
ULONG cb,
ULONG* pcbRead
)
{
auto av = d.size() - p;
if (cb < av)
av = cb;
memcpy(pv, d.data() + p, av);
p += av;
if (pcbRead)
*pcbRead = (ULONG)av;
// if (av < cb)
// return S_FALSE;
return S_OK;
}
HRESULT __stdcall Write(
const void* pv,
ULONG cb,
ULONG* pcbWritten
)
{
if (ReadOnly)
return STG_E_ACCESSDENIED;
if (d.size() < (p + cb))
{
auto exc = (p + cb) - d.size();
d.resize(d.size() + exc);
}
memcpy(d.data() + p, pv, cb);
p += cb;
if (pcbWritten)
*pcbWritten = cb;
return S_OK;
}
};
It encapsulates std::vector<> in a IStream.
Related
I am having a big issue getting raw audio data from the SampleGrabber GraphFilter and writing it to an audio file in the proper format.
What i'm trying to achieve is:
grabbing the data from: Microphone->SampleGrabber->Null Rendrer
Write the raw data captured to a proper audio file (MP3\AVI\WAVE)
Some questions:
Am I setting the proper MediaType for the SampleGrabber?
How can you compress the audio data to a proper audio file?
My current code below:
struct sRecortdingData {
std::vector<BYTE> recordingData;
double totalRecordingTime;
};
class CFakeCallback : public ISampleGrabberCB
{
public:
sRecortdingData sRecording;
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
STDMETHODIMP_(HRESULT __stdcall) QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
{
*ppv = (void*) static_cast<ISampleGrabberCB*>(this);
return NOERROR;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(HRESULT __stdcall) SampleCB(double SampleTime, IMediaSample* pSample)
{
return S_OK;
}
STDMETHODIMP_(HRESULT __stdcall) BufferCB(double SampleTime, BYTE* pBuffer, long BufferLen)
{
sRecording.recordingData.push_back(*pBuffer);
sRecording.totalRecordingTime = SampleTime;
return S_OK;
}
};
void Microphone::RecordAudio()
{
HRESULT hr;
AM_MEDIA_TYPE mt;
long recordingEventCode;
IMediaControl* pMediaControl = NULL;
ISampleGrabber* pISampleGrabber = NULL;
IBaseFilter* pSampleGrabberFilter;
IGraphBuilder* pGraphBuilder = NULL;
IMediaEventEx* pEvent = NULL;
IBaseFilter* pMicrophoneFilter = NULL;
ISampleGrabber* pSampleGrabber = NULL;
ICreateDevEnum* pDeviceEnum = NULL;
IBaseFilter* pNullRender = NULL;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Filter graph
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraphBuilder));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Device enum
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnum));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Null renderer
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pNullRender));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Get the even control
hr = pGraphBuilder->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Sample Grabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pSampleGrabberFilter));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Get Media Control
hr = pGraphBuilder->QueryInterface(IID_PPV_ARGS(&pMediaControl));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Setup input device filter
pMicrophoneFilter = Microphone::SetupDeviceFilter(CLSID_AudioInputDeviceCategory, pGraphBuilder, pDeviceEnum,
Utils::s2ws("My Microphone");
if (pMicrophoneFilter == NULL) {
HR_Failed(hr);
return;
}
// Setup sample grabber filter
pSampleGrabberFilter = Microphone::SetupDeviceFilter(CLSID_LegacyAmFilterCategory,
pGraphBuilder, pDeviceEnum, L"SampleGrabber");
if (pSampleGrabberFilter == NULL) {
HR_Failed(hr);
return;
}
// Connect both pins together
Device_Connect(pMicrophoneFilter, pSampleGrabberFilter);
// Setup null renderer filter
pNullRender = Microphone::SetupDeviceFilter(CLSID_LegacyAmFilterCategory,
pGraphBuilder, pDeviceEnum, L"Null Renderer");
if (pNullRender == NULL) {
HR_Failed(hr);
return;
}
// Connect both pins together
Device_Connect(pSampleGrabberFilter, pNullRender);
// Get the ISampleGranner interface
hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pISampleGrabber);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
hr = pISampleGrabber->SetCallback(&CB, 1);
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
// Set the media type
hr = pISampleGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
pISampleGrabber->SetBufferSamples(FALSE);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
hr = pMediaControl->Run();
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
DWORD recordingTime = 20000;
hr = pEvent->WaitForCompletion(recordingTime, &recordingEventCode);
std::string str(CB.sRecording.recordingData.begin(), CB.sRecording.recordingData.end());
// Do something with the data from callback and write it to audio file
}
Here's my code:
// Defines an event handler for general UI Automation events. It listens for
// tooltip and window creation and destruction events.
#include <windows.h>
#include <stdio.h>
#include <UIAutomation.h>
class EventHandler :
public IUIAutomationEventHandler
{
private:
LONG _refCount;
public:
int _eventCount;
// Constructor.
EventHandler() : _refCount(1), _eventCount(0)
{
}
// IUnknown methods.
ULONG STDMETHODCALLTYPE AddRef()
{
ULONG ret = InterlockedIncrement(&_refCount);
return ret;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ret = InterlockedDecrement(&_refCount);
if (ret == 0)
{
delete this;
return 0;
}
return ret;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
{
if (riid == __uuidof(IUnknown))
*ppInterface = static_cast<IUIAutomationEventHandler*>(this);
else if (riid == __uuidof(IUIAutomationEventHandler))
*ppInterface = static_cast<IUIAutomationEventHandler*>(this);
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
this->AddRef();
return S_OK;
}
// IUIAutomationEventHandler methods
HRESULT STDMETHODCALLTYPE HandleAutomationEvent(IUIAutomationElement * pSender, EVENTID eventID)
{
_eventCount++;
switch (eventID)
{
case UIA_AutomationFocusChangedEventId:
wprintf(L">> Event FocusChanged Received! (count: %d)\n", _eventCount);
break;
case UIA_ToolTipOpenedEventId:
wprintf(L">> Event ToolTipOpened Received! (count: %d)\n", _eventCount);
break;
case UIA_ToolTipClosedEventId:
wprintf(L">> Event ToolTipClosed Received! (count: %d)\n", _eventCount);
break;
case UIA_Window_WindowOpenedEventId:
wprintf(L">> Event WindowOpened Received! (count: %d)\n", _eventCount);
break;
case UIA_Window_WindowClosedEventId:
wprintf(L">> Event WindowClosed Received! (count: %d)\n", _eventCount);
break;
default:
wprintf(L">> Event (%d) Received! (count: %d)\n", eventID, _eventCount);
break;
}
return S_OK;
}
};
int main(int argc, char* argv[])
{
HRESULT hr;
int ret = 0;
IUIAutomationElement* pTargetElement = NULL;
EventHandler* pEHTemp = NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IUIAutomation* pAutomation = NULL;
hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&pAutomation);
if (FAILED(hr) || pAutomation == NULL)
{
ret = 1;
goto cleanup;
}
// Use root element for listening to window and tooltip creation and destruction.
hr = pAutomation->GetRootElement(&pTargetElement);
if (FAILED(hr) || pTargetElement == NULL)
{
ret = 1;
goto cleanup;
}
pEHTemp = new EventHandler();
if (pEHTemp == NULL)
{
ret = 1;
goto cleanup;
}
wprintf(L"-Adding Event Handlers.\n");
hr = pAutomation->AddAutomationEventHandler(UIA_ToolTipOpenedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
hr = pAutomation->AddAutomationEventHandler(UIA_ToolTipClosedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
hr = pAutomation->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
hr = pAutomation->AddAutomationEventHandler(UIA_Window_WindowClosedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
// Error is here. hr returns E_INVALIDARG.
hr = pAutomation->AddAutomationEventHandler(UIA_AutomationFocusChangedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
wprintf(L"-Press any key to remove event handlers and exit\n");
getchar();
wprintf(L"-Removing Event Handlers.\n");
cleanup:
// Remove event handlers, release resources, and terminate
if (pAutomation != NULL)
{
hr = pAutomation->RemoveAllEventHandlers();
if (FAILED(hr))
ret = 1;
pAutomation->Release();
}
if (pEHTemp != NULL)
pEHTemp->Release();
if (pTargetElement != NULL)
pTargetElement->Release();
CoUninitialize();
return ret;
}
This code comes from the examples from Microsoft about implementing Event classes for UIAutomation.
I edited the code a little bit so that it can support Focus events but I failed attempting to initialize one line of code.
I don't understand why hr = pAutomation->AddAutomationEventHandler(UIA_AutomationFocusChangedEventId, pTargetElement, TreeScope_Subtree, NULL, (IUIAutomationEventHandler*)pEHTemp) returns E_INVALIDARG.
Tried reading the docs and I can't find a reason why.
Please help.
There is a separate handler for handling FocusChanged, AddFocusChangedEventHandler which should be used for monitor focus changes. Since you are trying to send an EventHandler function pointer to handle FocusChanged, runtime error is coming.
To create that handler, need an instance of class that inherits from IUIAutomationFocusChangedEventHandler.
class FocusChangedEventHandler :
public IUIAutomationFocusChangedEventHandler
{
private:
LONG _refCount;
public:
int _eventCount;
//Constructor.
FocusChangedEventHandler() : _refCount(1), _eventCount(0)
{
}
//IUnknown methods.
ULONG STDMETHODCALLTYPE AddRef()
{
ULONG ret = InterlockedIncrement(&_refCount);
return ret;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ret = InterlockedDecrement(&_refCount);
if (ret == 0)
{
delete this;
return 0;
}
return ret;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
{
if (riid == __uuidof(IUnknown))
*ppInterface = static_cast<IUIAutomationFocusChangedEventHandler*>(this);
else if (riid == __uuidof(IUIAutomationFocusChangedEventHandler))
*ppInterface = static_cast<IUIAutomationFocusChangedEventHandler*>(this);
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
this->AddRef();
return S_OK;
}
// IUIAutomationFocusChangedEventHandler methods.
HRESULT STDMETHODCALLTYPE HandleFocusChangedEvent(IUIAutomationElement * pSender)
{
_eventCount++;
wprintf(L">> FocusChangedEvent Received! (count: %d)\n", _eventCount);
return S_OK;
}
};
In main()
pFHTemp = new FocusChangedEventHandler();
hr = pAutomation->AddFocusChangedEventHandler(NULL, (IUIAutomationFocusChangedEventHandler*)pFHTemp);
if (FAILED(hr))
{
ret = 1;
goto cleanup;
}
Code snippets taken from MSDN link
I want to receive a callback in my application when a photo on a connected mobile phone was shot (WPD_EVENT_OBJECT_ADDED).
I implemented a WPD client and the IPortableDeviceEventCallback like shown in the Windows Dev Center.
Now my problem is that the IPortableDeviceEventCallback::OnEvent() methode get only invoked if I have opened the DCIM/Camera folder on the phone via Windows Explorer and the explorer starts to creating thumbnails for the images. After I did this once, I receive every event from the phone till I dis- and reconnect it. But if I do not, onEvent() never gets called except I disconnect the phone.
Tested with different Android and iOS phones.
Does anyone have an idea whats going on?
DeviceEventsCallback::DeviceEventsCallback(MyPortableDevice* parent) : IPortableDeviceEventCallback(), cRef(1)
{
parentDevice = parent;
}
DeviceEventsCallback::~DeviceEventsCallback()
{
}
HRESULT __stdcall DeviceEventsCallback::QueryInterface(const IID& riid, LPVOID* ppvObj)
{
static const QITAB qitab[] = {
QITABENT(DeviceEventsCallback, IPortableDeviceEventCallback),
{ },
};
return QISearch(this, qitab, riid, ppvObj);
// HRESULT hr = S_OK;
// if (ppvObj == NULL) {
// hr = E_INVALIDARG;
// return hr;
// }
// if ((riid == IID_IUnknown) ||
// (riid == IID_IPortableDeviceEventCallback)) {
// AddRef();
// *ppvObj = this;
// }
// else {
// *ppvObj = NULL;
// hr = E_NOINTERFACE;
// }
// return hr;
}
ULONG __stdcall DeviceEventsCallback::AddRef()
{
InterlockedIncrement((long*) &cRef);
return cRef;
}
ULONG __stdcall DeviceEventsCallback::Release()
{
ULONG refCount = cRef - 1;
long ref = InterlockedDecrement(&cRef);
if (ref == 0) {
delete this;
return 0;
}
return refCount;
}
HRESULT __stdcall DeviceEventsCallback::OnEvent(IPortableDeviceValues* pEventParameters)
{
HRESULT hr = S_OK;
if (pEventParameters == NULL) {
hr = E_POINTER;
return hr;
}
// The pEventParameters collection contains information about the event that was
// fired. We'll at least need the EVENT_ID to figure out which event was fired
// and based on that retrieve additional values from the collection
// Display the event that was fired
GUID EventId;
if (EventId == WPD_EVENT_DEVICE_CAPABILITIES_UPDATED) {
return S_OK;
}
if (hr == S_OK) {
hr = pEventParameters->GetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, &EventId);
}
if (EventId == WPD_EVENT_DEVICE_REMOVED) {
return S_OK;
}
LPWSTR pwszEventId = NULL;
if (hr == S_OK) {
hr = StringFromCLSID(EventId, &pwszEventId);
}
if (pwszEventId != NULL) {
CoTaskMemFree(pwszEventId);
}
// Display the ID of the object that was affected
// We can also obtain WPD_OBJECT_NAME, WPD_OBJECT_PERSISTENT_UNIQUE_ID,
// WPD_OBJECT_PARENT_ID, etc.
LPWSTR pwszObjectId = NULL;
if (hr == S_OK) {
hr = pEventParameters->GetStringValue(WPD_OBJECT_ID, &pwszObjectId);
}
if (parentDevice != nullptr && pwszObjectId != nullptr && EventId == WPD_EVENT_OBJECT_ADDED) {
qDebug() << "invoked method";
QMetaObject::invokeMethod(parentDevice, "onNewFileOnDevice", Qt::DirectConnection, Q_ARG(QString, QString::fromStdWString(pwszObjectId)));
}
if (pwszObjectId != NULL) {
CoTaskMemFree(pwszObjectId);
}
// Note that we intentionally do not call Release on pEventParameters since we
// do not own it
return hr;
}
And in my MTP implementation I register my DeviceEventsCallback() eventNotifier
void MyPortableDevice::registerEventNotification(ComPtr<IPortableDevice> pDevice)
{
HRESULT hr = S_OK;
PWSTR tempEventCookie = nullptr;
if (pwszEventCookie != nullptr || pDevice == nullptr) {
return;
}
eventNotifier = new(std::nothrow) DeviceEventsCallback(this);
if (eventNotifier == nullptr) {
hr = E_OUTOFMEMORY;
}
if (hr == S_OK) {
hr = pDevice->Advise(0, eventNotifier, nullptr, &tempEventCookie);
}
if (hr == S_OK) {
pwszEventCookie = tempEventCookie;
tempEventCookie = nullptr; // relinquish memory to the caller
}
else {
// Free the event registration cookie because some error occurred
CoTaskMemFree(tempEventCookie);
tempEventCookie = nullptr;
}
}
void MyPortableDevice::unregisterEventsNotification(ComPtr<IPortableDevice> pDevice)
{
if (pDevice == nullptr || pwszEventCookie == nullptr) {
return;
}
HRESULT hr = pDevice->Unadvise(pwszEventCookie);
CoTaskMemFree(pwszEventCookie);
pwszEventCookie = nullptr;
}
My open() function looks like this:
bool MyPortableDevice::open(IPortableDevice** ppDevice)
{
retrieveClientInformation(&clientInformation);
IPortableDevice* pDevice = nullptr;
HRESULT hr = CoCreateInstance(CLSID_PortableDeviceFTM, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevice));
if (SUCCEEDED(hr)) {
wchar_t* wID = new wchar_t[ID.length() + 1];
ID.toWCharArray(wID);
wID[ID.length()] = '\0';
hr = pDevice->Open(wID, clientInformation.Get());
if (hr == E_ACCESSDENIED) {
qDebug() << "Failed to Open the device for Read Write access, will open it for Read-only access instead" << hr;
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, GENERIC_READ);
hr = pDevice->Open(wID, clientInformation.Get());
readOnly = true;
}
if (SUCCEEDED(hr)) {
// The device successfully opened, obtain an instance of the Device into
// ppDevice so the caller can be returned an opened IPortableDevice.
hr = pDevice->QueryInterface(IID_IPortableDevice, (VOID**)ppDevice);
if (FAILED(hr)) {
qDebug() << "Failed to QueryInterface the opened IPortableDevice";
return false;
}
}
if (pDevice != nullptr) {
pDevice->Release();
pDevice = nullptr;
}
delete [] wID;
wID = nullptr;
if (clientInformation != nullptr) {
clientInformation.Reset();
clientInformation = nullptr;
}
return true;
}
else {
qDebug() << "! Failed to CoCreateInstance CLSID_PortableDeviceFTM, hr = 0x%lx\n" << hr;
return false;
}
}
I want to make an mp4 file as a sink writer.
The video could be made by providing samples from msdn, but the audio could not be created.
So I did a search and got this source. But this source did not hear any audio either.
Is this a bad source? So where is the wrong part?
Here is search code:
//Audio constants
const GUID AUDIO_MAJOR_TYPE = MFMediaType_Audio; //for audio treatment
const GUID AUDIO_ENCODING_FORMAT = MFAudioFormat_AAC; //encoding format (output)
const GUID AUDIO_INPUT_FORMAT = MFAudioFormat_PCM; //input format
const UINT32 AUDIO_SAMPLES_PER_SECOND = 44100; //samples per second
const UINT32 AUDIO_AVG_BYTES_PER_SECOND = 16000; //average bytes per second
const UINT32 AUDIO_NUM_CHANNELS = 1; //MONO or STEREO
const UINT32 AUDIO_BITS_PER_SAMPLE = 16; //bits per sample
const UINT32 AUDIO_ONE_SECOND = 10; //quantity of buffers per second
const UINT32 AUDIO_BUFFER_LENGTH = AUDIO_BITS_PER_SAMPLE / 8 * AUDIO_NUM_CHANNELS * AUDIO_SAMPLES_PER_SECOND; //max. buffer size
const LONGLONG AUDIO_SAMPLE_DURATION = 10000000; //sample duration
//Creation of a template to release pointers
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
//Creation of the Byte Stream
IMFByteStream* CreateFileByteStream(LPCWSTR FileName)
{
//create file byte stream
IMFByteStream *pByteStream = NULL;
HRESULT hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, FileName, &pByteStream);
if (FAILED(hr))
pByteStream = NULL;
return pByteStream;
}
//Creation of the Video profile (H264)
IMFMediaType* CreateVideoProfile()
//Creation of the Audio profile (AAC)
IMFMediaType* CreateAudioProfile()
{
IMFMediaType *pMediaType = NULL;
HRESULT hr = MFCreateMediaType(&pMediaType);
if (SUCCEEDED(hr))
{
hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, AUDIO_MAJOR_TYPE);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->SetGUID(MF_MT_SUBTYPE, AUDIO_ENCODING_FORMAT);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, AUDIO_BITS_PER_SAMPLE);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, AUDIO_SAMPLES_PER_SECOND);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, AUDIO_NUM_CHANNELS);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, AUDIO_AVG_BYTES_PER_SECOND);
}
if (FAILED(hr))
pMediaType = NULL;
return pMediaType;
}
//Create an aggregate source (both audio and video)
IMFMediaSource* CreateAggregatedSource(IMFMediaSource *pSource1, IMFMediaSource *pSource2, IMFMediaSource *pAggSource)
{
pAggSource = NULL;
IMFCollection *pCollection = NULL;
HRESULT hr = MFCreateCollection(&pCollection);
if (SUCCEEDED(hr))
{
hr = pCollection->AddElement(pSource1);
}
if (SUCCEEDED(hr))
{
hr = pCollection->AddElement(pSource2);
}
if (SUCCEEDED(hr))
{
hr = MFCreateAggregateSource(pCollection, &pAggSource);
}
SafeRelease(&pCollection);
if (FAILED(hr))
pAggSource = NULL;
return pAggSource;
}
//Creation of the MPEG-4 MediaSink
IMFMediaSink* CreateMediaSink(IMFByteStream *pByteStream, IMFMediaType *pVideoMediaType, IMFMediaType *pAudioMediaType)
{
IMFMediaSink *pMediaSink = NULL;
DWORD pdwCharac = NULL;
DWORD pdwStreamCount = NULL;
HRESULT hr = MFCreateMPEG4MediaSink(pByteStream, pVideoMediaType, pAudioMediaType, &pMediaSink);
//// DEBUG ////
pMediaSink->GetCharacteristics(&pdwCharac);
pMediaSink->GetStreamSinkCount(&pdwStreamCount);
if (FAILED(hr))
pMediaSink = NULL;
return pMediaSink;
}
IMFAttributes* CreateAttributesForSinkWriter()
{
IMFAttributes *pMFAttributes = NULL;
HRESULT hr = MFCreateAttributes(&pMFAttributes, 100);
if (SUCCEEDED(hr))
{
hr = pMFAttributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_MPEG4);
}
if (SUCCEEDED(hr))
{
hr = pMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, FALSE); //no hardware encoding
}
if (SUCCEEDED(hr))
{
hr = pMFAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, FALSE); //enable converting formats
}
if (FAILED(hr))
pMFAttributes = NULL;
return pMFAttributes;
}
//Initialization of the Video SinkWriter...
HRESULT InitializeSinkWriterVideo(IMFSinkWriter **ppWriter, DWORD *pStreamIndex, IMFMediaSink *pMediaSink)
//Initialization of the Audio SinkWriter...
HRESULT InitializeSinkWriterAudio(IMFSinkWriter **ppWriter, DWORD *pStreamIndex, IMFMediaSink *pMediaSink)
{
*ppWriter = NULL;
*pStreamIndex = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFMediaType *pMediaTypeOut = NULL;
IMFMediaType *pMediaTypeIn = NULL;
IMFAttributes *pAttrib = NULL;
DWORD streamIndex = 1;
HRESULT hr = MFCreateSinkWriterFromMediaSink(pMediaSink, NULL, &pSinkWriter);
//input : audio
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pMediaTypeIn);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, AUDIO_MAJOR_TYPE);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, AUDIO_INPUT_FORMAT);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, AUDIO_BITS_PER_SAMPLE);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, AUDIO_SAMPLES_PER_SECOND);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, AUDIO_NUM_CHANNELS);
}
if (SUCCEEDED(hr))
{
hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
}
//Tell the Audio SinkWriter to begin data treatment
if (SUCCEEDED(hr))
{
hr = pSinkWriter->BeginWriting();
}
//Possible error codes
if (FAILED(hr))
{
if (hr == MF_E_INVALIDMEDIATYPE)
UINT32 uiShutDown = 0;
if (hr == MF_E_INVALIDSTREAMNUMBER)
UINT32 uiShutDown = 1;
if (hr == MF_E_TOPO_CODEC_NOT_FOUND)
UINT32 uiShutDown = 2;
}
//Returns the pointer of the caller
if (SUCCEEDED(hr))
{
*ppWriter = pSinkWriter;
(*ppWriter)->AddRef();
*pStreamIndex = streamIndex;
}
//Release pointers
SafeRelease(&pSinkWriter);
SafeRelease(&pMediaTypeOut);
SafeRelease(&pMediaTypeIn);
SafeRelease(&pAttrib);
return hr;
}
//Write a video frame
HRESULT WriteVideoFrame(IMFSinkWriter *pWriter, DWORD streamIndex, const LONGLONG& rtStart, const LONGLONG& rtDuration)
//Write an audio packet
HRESULT WriteAudioPacket(IMFSinkWriter *pWriter, DWORD streamIndex, const LONGLONG& rtStart, const LONGLONG& rtDuration, UINT32 Quantity)
{
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
const DWORD cbBuffer = Quantity * 2;
BYTE *pData = NULL;
//Create a new memory buffer, whose max. size is cbBuffer (QuantityOfSamplesPerVideoFrame * 2 Bytes)
HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
//Lock the buffer and copy the audio packet to the buffer
if (SUCCEEDED(hr))
{
hr = pBuffer->Lock(&pData, NULL, NULL);
}
if (SUCCEEDED(hr))
{
for (DWORD n = 0; n < cbBuffer; n++)
{
pData[n] = (BYTE)(rand() & 0xFF); //generation of random noise
}
}
if (SUCCEEDED(hr))
{
hr = pBuffer->Unlock();
}
// Set the data length of the buffer
if (SUCCEEDED(hr))
{
hr = pBuffer->SetCurrentLength(cbBuffer);
}
//Create a media sample and add the buffer to the sample
if (SUCCEEDED(hr))
{
hr = MFCreateSample(&pSample);
}
if (SUCCEEDED(hr))
{
hr = pSample->AddBuffer(pBuffer);
}
//Set the time stamp and the duration
if (SUCCEEDED(hr))
{
hr = pSample->SetSampleTime(rtStart);
}
if (SUCCEEDED(hr))
{
hr = pSample->SetSampleDuration(rtDuration);
}
//Send the sample to the Sink Writer
if (SUCCEEDED(hr))
{
hr = pWriter->WriteSample(streamIndex, pSample);
}
//Release pointers
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
// MAIN FUNCTION
void main()
{
HRESULT hr = S_OK;
IMFByteStream *spByteStream = NULL;
IMFMediaSink *pMediaSink = NULL;
IMFSinkWriter *spSinkWriterVid = NULL;
IMFSinkWriter *spSinkWriterAud = NULL;
IMFMediaType *spVideo = NULL;
IMFMediaType *spAudio = NULL;
//IMFMediaEventGenerator *spMFMediaEvtGene = NULL;
//IMFMediaEvent *spMFMediaEvent = NULL;
IMFAttributes *spAttrib = NULL;
DWORD sindexVid = 0, sindexAud = 0, j = 0;
LPCWSTR str = L"outputfile.mp4";
hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
if (SUCCEEDED(hr))
{
spByteStream = CreateFileByteStream(str);
if (spByteStream != NULL)
{
spVideo = CreateVideoProfile();
}
if (spVideo != NULL)
{
spAudio = CreateAudioProfile();
}
if (spAudio != NULL)
{
pMediaSink = CreateMediaSink(spByteStream, spVideo, spAudio);
}
if (pMediaSink != NULL)
{
hr = InitializeSinkWriterVideo(&spSinkWriterVid, &sindexVid, pMediaSink);
if (SUCCEEDED(hr))
{
LONGLONG rtStartVid = 0;
UINT64 rtDurationVid = 0;
/********************************************************
* VIDEO PART *
********************************************************/
//Calculate the average time per frame, for video
//MFFrameRateToAverageTimePerFrame(VIDEO_FPS, 1, &rtDurationVid);
//loop to treat all the pictures
for (DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i, ++j)
{
//Picture pixels
for (DWORD k = 0; k < VIDEO_PELS; k++)
{
if (j>255)
j = 0;
videoFrameBuffer[k] = ((j << 16) & 0x00FF0000) | ((j << 8) & 0x0000FF00) | (j & 0x000000FF);
}
hr = WriteVideoFrame(spSinkWriterVid, sindexVid, rtStartVid, rtDurationVid);
if (FAILED(hr))
{
break;
}
//Update the time stamp value
rtStartVid += rtDurationVid;
}
//Finalization of writing with the Video SinkWriter
if (SUCCEEDED(hr))
{
hr = spSinkWriterVid->Finalize();
}
}
}
SafeRelease(&spVideo);
SafeRelease(&spSinkWriterVid);
if (SUCCEEDED(hr))
{
hr = InitializeSinkWriterAudio(&spSinkWriterAud, &sindexAud, pMediaSink);
if (SUCCEEDED(hr))
{
LONGLONG rtStartAud = 0;
UINT64 rtDurationAud;
double QtyAudioSamplesPerVideoFrame = 0;
//Calculate the approximate quantity of samples, according to a video frame duration
//44100 Hz -> 1 s
//????? Hz -> 0.04 s (= 40 ms = one video frame duration)
if (VIDEO_FPS != 0)
QtyAudioSamplesPerVideoFrame = ((double)AUDIO_SAMPLES_PER_SECOND / (double)VIDEO_FPS);
else
QtyAudioSamplesPerVideoFrame = 0;
MFFrameRateToAverageTimePerFrame(VIDEO_FPS, 1, &rtDurationAud); //we treat the same duration as the video
//it means that we will treat N audio packets for the last of one picture (=40 ms)
//loop to treat all the audio packets
if (rtDurationAud != 0)
{
for (DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i)
{
//Audio packets
hr = WriteAudioPacket(spSinkWriterAud, sindexAud, rtStartAud, rtDurationAud, (UINT32)QtyAudioSamplesPerVideoFrame);
if (FAILED(hr))
{
break;
}
//Update the time stamp value
rtStartAud += rtDurationAud;
}
//Finalization of writing with the Audio SinkWriter
if (SUCCEEDED(hr))
{
hr = spSinkWriterAud->Finalize();
}
}
}
}
//Release pointers
SafeRelease(&spByteStream);
SafeRelease(&spAudio);
SafeRelease(&spSinkWriterAud);
SafeRelease(&spAttrib);
//Shutdown the MediaSink (not done by the SinkWriter)
pMediaSink->Shutdown();
SafeRelease(&pMediaSink);
}
//Shutdown MediaFoundation
MFShutdown();
CoUninitialize();
}
//CDialog::OnOK();
}
Reference: How to add audio data to a video file created by a SinkWriter ? on MSDN Forums
On existing IMFSinkWriter you already have for video (e.g. using MSDN tutorial code) you call AddStream and SetInputMediaType initializing additional stream for audio, resulting in audio track in produced MP4 file. You obtain a separate stream index for audio, which you are to use in consequent calls. Similar to WriteSample for video, you use WriteSample for audio with respective stream index and add audio data for encoding/writing in the main loop of your application.
I'm trying to get notifications whenever the master volume changes on Windows Vista/7. This is the code I'm using:
#include <audiopolicy.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <windows.h>
#include <shlwapi.h>
#include <iostream>
#include <Tchar.h>
static const GUID AudioSessionVolumeCtx = { 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
class CAudioSessionVolume : public IAudioSessionEvents
{
public:
static HRESULT CreateInstance( UINT uNotificationMessage, HWND hwndNotification, CAudioSessionVolume **ppAudioSessionVolume )
{
CAudioSessionVolume *pAudioSessionVolume = new (std::nothrow)
CAudioSessionVolume(uNotificationMessage, hwndNotification);
if (pAudioSessionVolume == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT hr = pAudioSessionVolume->Initialize();
if (SUCCEEDED(hr))
{
*ppAudioSessionVolume = pAudioSessionVolume;
}
else
{
pAudioSessionVolume->Release();
}
return hr;
}
// IUnknown methods.
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CAudioSessionVolume, IAudioSessionEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement( &m_cRef );
if (cRef == 0)
{
delete this;
}
return cRef;
}
STDMETHODIMP OnSimpleVolumeChanged( float NewVolume, BOOL NewMute, LPCGUID EventContext )
{
MessageBox(NULL, _T("vol changed"), _T("test"), MB_OK);
return S_OK;
}
// The remaining audio session events do not require any action.
STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnChannelVolumeChanged(DWORD,float[],DWORD,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnStateChanged(AudioSessionState)
{
return S_OK;
}
STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
{
return S_OK;
}
// Other methods
HRESULT EnableNotifications(BOOL bEnable)
{
HRESULT hr = S_OK;
if (bEnable)
{
hr = m_pAudioSession->RegisterAudioSessionNotification(this);
}
else
{
hr = m_pAudioSession->UnregisterAudioSessionNotification(this);
}
return hr;
}
HRESULT SetDisplayName(const WCHAR *wszName)
{
if (m_pAudioSession == NULL)
{
return E_FAIL;
}
else
{
return m_pAudioSession->SetDisplayName(wszName, NULL);
}
}
protected:
CAudioSessionVolume( UINT uNotificationMessage, HWND hwndNotification ) :
m_cRef(1), m_pAudioSession(NULL), m_pSimpleAudioVolume(NULL)
{
}
~CAudioSessionVolume()
{
EnableNotifications(FALSE);
SafeRelease(&m_pAudioSession);
SafeRelease(&m_pSimpleAudioVolume);
}
HRESULT Initialize()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pDeviceEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager *pAudioSessionManager = NULL;
// Get the enumerator for the audio endpoint devices.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator));
if (FAILED(hr))
{
goto done;
}
// Get the default audio endpoint that the SAR will use.
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
&pDevice);
if (FAILED(hr))
{
goto done;
}
// Get the session manager for this device.
hr = pDevice->Activate(__uuidof(IAudioSessionManager),
CLSCTX_INPROC_SERVER, NULL, (void**) &pAudioSessionManager);
if (FAILED(hr))
{
goto done;
}
// Get the audio session.
hr = pAudioSessionManager->GetAudioSessionControl(
&GUID_NULL, // Get the default audio session.
FALSE, // The session is not cross-process.
&m_pAudioSession);
if (FAILED(hr))
{
goto done;
}
hr = pAudioSessionManager->GetSimpleAudioVolume(&GUID_NULL, 0,
&m_pSimpleAudioVolume);
done:
SafeRelease(&pDeviceEnumerator);
SafeRelease(&pDevice);
SafeRelease(&pAudioSessionManager);
return hr;
}
private:
LONG m_cRef;
IAudioSessionControl *m_pAudioSession;
ISimpleAudioVolume *m_pSimpleAudioVolume;
};
int main()
{
CoInitialize(NULL);
CAudioSessionVolume *asv;
CAudioSessionVolume::CreateInstance(0, NULL, &asv);
asv->EnableNotifications(true);
char s;
gets(&s);
}
I was expecting to get a message box saying "vol changed" when the system volume changes, but I never get a message box.
I got most of the code from http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx
What am I doing wrong?
EDIT: I do get notifications when changing the volume for my application (thanks #nobugz). But I want notification when changing the master volume. (Listed as "Device" in Win7's Volume Mixers dialog)
EDIT 2: Larry Osterman has a series of blog posts about the volume in Vista/7. This one in particular is interesting: http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx I'll try the code out tomorrow to see if I can get what I want. Now it's time for bed.
EDIT 3: This is the complete code from Larry's blog posts up to and including the link posted above. It does what I need and then some. I'll try to trim the features I don't need.
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <Tchar.h>
#include <strsafe.h>
class CVolumeNotification : public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
~CVolumeNotification(void) {};
public:
CVolumeNotification(void) : m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(&m_RefCount);
if (ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue)
{
if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))
{
*ReturnValue = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ReturnValue = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
wchar_t outputString[256];
DWORD written;
COORD writeCoord;
StringCbPrintf(outputString, sizeof(outputString), L"Volume Changed: %f", NotificationData->fMasterVolume);
writeCoord.X = 0;
writeCoord.Y = 3;
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written);
return S_OK;
}
};
struct TimerContext
{
IAudioMeterInformation *_Meter;
};
const int TimerPeriodicityMS = 100;
void CALLBACK TimerMeterCallback(PTP_CALLBACK_INSTANCE CallbackInstance, PVOID Context, PTP_TIMER Timer)
{
TimerContext *timerContext = (TimerContext *)Context;
wchar_t outputString[256];
float peakValue;
DWORD written;
COORD writeCoord;
StringCbCopy(outputString, sizeof(outputString), L"Meter: ");
timerContext->_Meter->GetPeakValue(&peakValue);
for (size_t i = 0 ; i < peakValue*100; i += 1)
{
StringCbCat(outputString, sizeof(outputString), L"*");
}
for (size_t i = (size_t)(peakValue*100) ; i < 100; i += 1)
{
StringCbCat(outputString, sizeof(outputString), L".");
}
writeCoord.X = 0;
writeCoord.Y = 1;
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written);
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;;
HANDLE handle;
TP_CALLBACK_ENVIRON callbackEnvironment;
PTP_CLEANUP_GROUP cleanupGroup;
PTP_TIMER timer;
TimerContext context;
InitializeThreadpoolEnvironment(&callbackEnvironment);
cleanupGroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackCleanupGroup(&callbackEnvironment, cleanupGroup, NULL);
timer = CreateThreadpoolTimer(TimerMeterCallback, &context, &callbackEnvironment);
//
// Clear the screen. Code stolen from: http://support.microsoft.com/kb/319257.
//
handle = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD writtenChars = 0;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
COORD home;
home.X = home.Y = 0;
GetConsoleScreenBufferInfo(handle, &consoleInfo);
FillConsoleOutputCharacter(handle, L' ', consoleInfo.dwSize.X * consoleInfo.dwSize.Y, home, &writtenChars);
SetConsoleCursorPosition(handle, home);
//
// Set the console to raw mode.
//
DWORD oldMode;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldMode);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
//
// Instantiate an endpoint volume object.
//
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;
IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
CVolumeNotification *volumeNotification = new CVolumeNotification();
hr = endpointVolume->RegisterControlChangeNotify(volumeNotification);
hr = defaultDevice->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&context._Meter);
defaultDevice->Release();
defaultDevice = NULL;
// Set a 100 millisecond timer.
LARGE_INTEGER timerPeriodicityLI;
FILETIME timerPeriodicity;
timerPeriodicityLI.QuadPart = -1*(TimerPeriodicityMS* 10000 );
timerPeriodicity.dwLowDateTime = timerPeriodicityLI.LowPart;
timerPeriodicity.dwHighDateTime = timerPeriodicityLI.HighPart;
SetThreadpoolTimer(timer, &timerPeriodicity, TimerPeriodicityMS, 10);
wchar_t inputChar = '\0';
while (inputChar != '\r')
{
UINT currentStep, stepCount;
DWORD read, written;
COORD writeCoord;
wchar_t outputString[256];
StringCbCopy(outputString, sizeof(outputString), L"Volume: ");
//
// Calculate the cheesy OSD.
//
endpointVolume->GetVolumeStepInfo(¤tStep, &stepCount);
for (size_t i = 0 ; i < stepCount ; i += 1)
{
if (i <= currentStep)
{
StringCbCat(outputString, sizeof(outputString), L"=");
}
else
{
StringCbCat(outputString, sizeof(outputString), L"-");
}
}
writeCoord.X = 0;
writeCoord.Y = 0;
WriteConsoleOutputCharacter(handle, outputString, (DWORD)wcslen(outputString), writeCoord, &written);
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
if (inputChar == '+')
{
endpointVolume->VolumeStepUp(NULL);
}
else if (inputChar == '-')
{
endpointVolume->VolumeStepDown(NULL);
}
}
//
// Remove our notification.
//
endpointVolume->UnregisterControlChangeNotify(volumeNotification);
endpointVolume->Release();
volumeNotification->Release();
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode);
CloseThreadpoolCleanupGroupMembers(cleanupGroup, FALSE, NULL);
CloseThreadpoolCleanupGroup(cleanupGroup);
cleanupGroup = NULL;
DestroyThreadpoolEnvironment(&callbackEnvironment);
context._Meter->Release();
CoUninitialize();
return 0;
}
EDIT 4: This is a stripped down version of Larry's code.
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <iostream>
#include <Tchar.h>
class CVolumeNotification : public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
~CVolumeNotification(void) {};
public:
CVolumeNotification(void) : m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(&m_RefCount);
if (ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue)
{
if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))
{
*ReturnValue = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ReturnValue = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
std::cout << NotificationData->fMasterVolume << _T(" ");
return S_OK;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;;
//
// Instantiate an endpoint volume object.
//
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;
IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
CVolumeNotification *volumeNotification = new CVolumeNotification();
hr = endpointVolume->RegisterControlChangeNotify(volumeNotification);
defaultDevice->Release();
defaultDevice = NULL;
wchar_t inputChar = '\0';
while (inputChar != '\r')
{
DWORD read;
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
}
//
// Remove our notification.
//
endpointVolume->UnregisterControlChangeNotify(volumeNotification);
endpointVolume->Release();
volumeNotification->Release();
CoUninitialize();
return 0;
}
I still haven't looked at the SDK example. I now know how this stuff works well enough to add what I need to my application. Thanks for everyones help!
It works fine when I try it. Be sure to click on the volume control tray icon, click Mixer and modify the volume for your app.
The Windows SDK "OSD" sample is a much simpler example of monitoring the hardware volume than the one I posted on the blog (you don't need all the metering junk in that post).