MFGetService doesnt get IMFSimpleAudioVolume on first try - c++

I am playing a mp3 file with windows media foundation and try to set the volume but when i try to get IMFSimpleAudioVolume interface it takes a seemingly random amount of tries to do so. I have got 1 through 200ish tries.
I tried using IMFAudioStreamVolume but the results are the same
Here is the main.cpp
#include <iostream>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <winrt/base.h>
#pragma comment (lib, "Mfplat.lib")
#pragma comment (lib, "Mfuuid.lib")
#pragma comment (lib, "Mf.lib")
#pragma comment (lib, "Winmm.lib")
template <class T>
using comptr = winrt::com_ptr<T>;
void AddBranchToTopology(comptr<IMFTopology> topology, comptr<IMFMediaSource> mediaSource, comptr<IMFPresentationDescriptor> presentationDesc);
int main()
{
MFStartup(MF_VERSION);
HRESULT hr = S_OK;
comptr<IMFMediaSession> mediaSession;
hr = MFCreateMediaSession(NULL, mediaSession.put());
comptr<IMFSourceResolver> sourceResolver;
MFCreateSourceResolver(sourceResolver.put());
comptr<IMFMediaSource> mediaSource;
MF_OBJECT_TYPE objType = MF_OBJECT_INVALID;
{
comptr<IUnknown> unknown;
hr = sourceResolver->CreateObjectFromURL(L"<file path>",
MF_RESOLUTION_MEDIASOURCE, NULL, &objType, unknown.put());
mediaSource = unknown.as<IMFMediaSource>();
unknown.detach();
}
comptr<IMFPresentationDescriptor> presentationDesc;
hr = mediaSource->CreatePresentationDescriptor(presentationDesc.put());
comptr<IMFTopology> topology;
hr = MFCreateTopology(topology.put());
AddBranchToTopology(topology, mediaSource, presentationDesc);
mediaSession->SetTopology(NULL, topology.get());
comptr<IMFSimpleAudioVolume> simpleVolume;
DWORD tries = 0;
while (!simpleVolume)
{
hr = MFGetService(mediaSession.get(), MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(simpleVolume.put()));
tries++;
}
std::cout << tries << "\n";
PROPVARIANT var;
PropVariantInit(&var);
var.vt = VT_EMPTY;
mediaSession->Start(&GUID_NULL, &var);
simpleVolume->SetMasterVolume(0.5f);
while (true)
{
if (GetAsyncKeyState(VK_ESCAPE) & 1)
{
break;
}
}
mediaSession->Shutdown();
mediaSource->Shutdown();
MFShutdown();
return 0;
}
void AddBranchToTopology(comptr<IMFTopology> topology, comptr<IMFMediaSource> mediaSource, comptr<IMFPresentationDescriptor> presentationDesc)
{
comptr<IMFStreamDescriptor> streamDesc;
comptr<IMFTopologyNode> sourceNode;
comptr <IMFTopologyNode> outputNode;
comptr<IMFActivate> activate;
BOOL selected = FALSE;
HRESULT hr = S_OK;
hr = presentationDesc->GetStreamDescriptorByIndex(0, &selected, streamDesc.put());
hr = MFCreateAudioRendererActivate(activate.put());
hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, sourceNode.put());
hr = sourceNode->SetUnknown(MF_TOPONODE_SOURCE, mediaSource.get());
hr = sourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc.get());
hr = sourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc.get());
hr = topology->AddNode(sourceNode.get());
hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, outputNode.put());
hr = outputNode->SetObject(activate.get());
hr = outputNode->SetUINT32(MF_TOPONODE_STREAMID, 0);
hr = outputNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
hr = topology->AddNode(outputNode.get());
hr = sourceNode->ConnectOutput(0, outputNode.get(), 0);
activate->ShutdownObject();
}

IMFMediaSession::SetTopology is asynchronous, so you'd want to sync before asking for service.
This method is asynchronous. If the method returns S_OK, the Media Session sends an MESessionTopologySet event when the operation completes.
mediaSession->SetTopology(NULL, topology.get());
// INSERT BEGIN //
// https://learn.microsoft.com/en-us/windows/win32/medfound/mesessiontopologyset
auto const MediaEventGenerator = mediaSession.as<IMFMediaEventGenerator>();
for(; ; )
{
winrt::com_ptr<IMFMediaEvent> MediaEvent;
winrt::check_hresult(MediaEventGenerator->GetEvent(0, MediaEvent.put()));
MediaEventType Type;
winrt::check_hresult(MediaEvent->GetType(&Type));
//std::cout << Type << std::endl;
if(Type == MESessionTopologySet)
break;
}
// INSERT END //
comptr<IMFSimpleAudioVolume> simpleVolume;
This is just for illustration, in real code you would want to subscribe to events and handle asynchronously (see e.g. Media Foundation samples).

Related

How to read from a USB Joystick with C++ and DirectX8 in Visual Studio 2019

I need help in reading data from a specific joystick when it is connected to a PC in which the data read is received by 2019 visual studio c++ pc program. I want it to detect a particular joystick when connected to a PC based on a product ID or something similar, I don't want it just to detect a joystick that's connected. Then, I would obviously like to have it be read from it and display the read data on the console using cout or something similar. Please provide me a complete example as I've looked at many APIs and function prototypes of the DirectX library to use and I'm still very lost. Thanks. Below is what I have so far;
#include <iostream>
using namespace std;
#include <Windows.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
LPDIRECTINPUT8 di;
HRESULT hr;
LPDIRECTINPUTDEVICE8 joystick;
OLECHAR* ap;
DIDEVCAPS capabilities;
BOOL CALLBACK
enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
HRESULT hr;
// Obtain an interface to the enumerated joystick.
hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
// If it failed, then we can't use this joystick. (Maybe the user unplugged
// it while we were in the middle of enumerating it.)
if (FAILED(hr)) {
return DIENUM_CONTINUE;
}
// Stop enumeration. Note: we're just taking the first joystick we get. You
// could store all the enumerated joysticks and let the user pick.
return DIENUM_STOP;
}
BOOL CALLBACK
enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context) {
HWND hDlg = (HWND)context;
DIPROPRANGE propRange;
propRange.diph.dwSize = sizeof(DIPROPRANGE);
propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
propRange.diph.dwHow = DIPH_BYID;
propRange.diph.dwObj = instance->dwType;
propRange.lMin = -1000;
propRange.lMax = +1000;
if (FAILED(joystick->SetProperty(DIPROP_RANGE, &propRange.diph))) return DIENUM_STOP;
return DIENUM_CONTINUE;
}
HRESULT
poll(DIJOYSTATE2* js) {
HRESULT hr;
if (joystick == NULL) {
return S_OK;
}
hr = joystick->Poll();
if (FAILED(hr)) {
hr = joystick->Acquire();
while (hr == DIERR_INPUTLOST) hr = joystick->Acquire();
if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) return E_FAIL;
if (hr == DIERR_OTHERAPPHASPRIO) return S_OK;
}
if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), js))) return hr;
return S_OK;
}
int main() {
// Create a DirectInput device
if(FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&di, NULL))) return hr;
if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, NULL, DIEDFL_ATTACHEDONLY))) return hr;
// Make sure we got a joystick
if (joystick == NULL){
printf("Joystick not found.\n");
return E_FAIL;
}
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) return hr;
if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND))) return hr;
capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = joystick->GetCapabilities(&capabilities))) return hr;
if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) return hr;
if (joystick) {
joystick->Unacquire();
}
return 0;
}

How to play audio using Sink Writer from Microsoft Media Foundation

I'm trying to create an audio visualizer using Microsoft Media Foundation. For this I need to intercept the samples and simultaneously play them. Using a Media Session with Topology and a sample-grabber sink seems impractical and over-complicated, hence I'm trying to use a combination of a Sink Reader and Sink Writer for this (see the right half of the image on Overview of the Media Foundation Architecture). Unfortunately, Audio/Video Playback does not really explain how to do this. The book Developing Microsoft Media Foundation Applications contains a source-to-sink loop on page 92, but that still does not really help me.
Creating the Source Reader works fine and I'm reading nonzero samples. Writing them to the Sink Writer (which uses the Streaming Audio Renderer) does not give me any errors, but I don't hear anything. I tried multiple things like selecting other media types and explicitly selecting the rendering device (although I only have one, as it indicated), but to no avail. Note that playing audio using a Media Session works fine, though!
I based my code on this question: Play audio from file to speaker with Media Foundation.
This is my code at this moment:
#include <iostream>
#include <cassert>
#include <mfidl.h>
#include <mfapi.h>
#include <mfreadwrite.h>
#include <Mferror.h>
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#include <winrt/base.h>
#pragma comment(lib, "windowsapp")
void winHr(const HRESULT result) { winrt::check_hresult(result); }
template<class T>
struct ComPtr : winrt::com_ptr<T>
{
auto operator&() noexcept { return this->put(); }
operator T*() noexcept
{
assert(this->get());
return this->get();
}
};
int main() noexcept
{
winHr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
winHr(MFStartup(MF_VERSION));
{
ComPtr<IMFSourceReader> reader;
winHr(MFCreateSourceReaderFromURL(
LR"(test.wav)",
nullptr, &reader));
constexpr auto inStreamIndex = MF_SOURCE_READER_FIRST_AUDIO_STREAM;
// Select only the audio stream
winHr(reader->SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false));
winHr(reader->SetStreamSelection(inStreamIndex, true));
ComPtr<IMFMediaSink> mediaSink;
winHr(MFCreateAudioRenderer(nullptr, &mediaSink));
ComPtr<IMFSinkWriter> writer;
{
ComPtr<IMFStreamSink> streamSink;
winHr(mediaSink->GetStreamSinkByIndex(0, &streamSink));
ComPtr<IMFMediaTypeHandler> typeHandler;
winHr(streamSink->GetMediaTypeHandler(&typeHandler));
ComPtr<IMFMediaType> inputType;
winHr(reader->GetCurrentMediaType(inStreamIndex, &inputType));
ComPtr<IMFMediaType> closestSupportedType;
const auto result = typeHandler->IsMediaTypeSupported(inputType, &closestSupportedType);
if (result == MF_E_INVALIDMEDIATYPE)
{
if (!closestSupportedType)
{
std::cerr << "Media type not supported" << std::endl;
winHr(mediaSink->Shutdown());
goto end; //:o
}
winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, closestSupportedType));
winHr(typeHandler->SetCurrentMediaType(closestSupportedType));
winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
winHr(writer->SetInputMediaType(0, closestSupportedType, nullptr));
}
else {
winHr(result);
winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, inputType));
winHr(typeHandler->SetCurrentMediaType(inputType));
winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
winHr(writer->SetInputMediaType(0, inputType, nullptr));
}
}
winHr(writer->BeginWriting());
while (true)
{
ComPtr<IMFSample> sample;
DWORD streamFlags;
MFTIME timestamp;
winHr(reader->ReadSample(inStreamIndex, 0, nullptr, &streamFlags, &timestamp, &sample));
if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
winHr(writer->NotifyEndOfSegment(0));
break;
}
if (streamFlags & MF_SOURCE_READERF_STREAMTICK)
winHr(writer->SendStreamTick(0, timestamp));
if (!sample) continue;
winHr(sample->SetSampleTime(timestamp));
winHr(writer->WriteSample(0, sample));
}
winHr(writer->Flush(0));
std::cout << "(Press enter to stop)" << std::endl;
std::cin.get();
winHr(writer->Finalize());
writer.attach(nullptr);
winHr(mediaSink->Shutdown());
}
end:
winHr(MFShutdown());
CoUninitialize();
}
Just to be clear: when I run this it prints (Press enter to stop) and I can hear from the noise (read: distortions from electronic signals) from my headphones that I can deduce that for a short moment an audio port was opened and then closed, but no actual audio is played. How can I get this to work?
Edit 1: I just fixed that if result != MF_E_INVALIDMEDIATYPE I didn't set the media type, but now I often (but not always, for some reason) get MF_E_TOPO_CODEC_NOT_FOUND at the line winHr(writer->SetInputMediaType(0, inputType, nullptr));. Why would this be? (Still no audio is played in any case.)
Edit 2: Apparently it matters when I create the writer, so now I do that only at the last moment, but now I get the "Media type not supported" error. Maybe I need to manually pick some media type but I will look in to this later -- unless someone knows the answer.
I modified your code to work differently in the following manner:
1. Enumerates the Audio Sink output media types, until it finds a supported one.
2. Sets this media type to the Reader in order to force it to use Audio Resampler DSP (this is what IMFMediaTopology does).
Here is the code, it plays back the input wav file properly. Let me know if it works for you.
#include <iostream>
#include <cassert>
#include <mfidl.h>
#include <mfapi.h>
#include <mfreadwrite.h>
#include <Mferror.h>
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#include <winrt/base.h>
#pragma comment(lib, "windowsapp")
void winHr(const HRESULT result) { winrt::check_hresult(result); }
template<class T>
struct ComPtr : winrt::com_ptr<T>
{
auto operator&() noexcept { return this->put(); }
operator T*() noexcept
{
assert(this->get());
return this->get();
}
};
int main() noexcept
{
winHr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
winHr(MFStartup(MF_VERSION));
{
ComPtr<IMFSourceReader> reader;
winHr(MFCreateSourceReaderFromURL(
LR"(test.wav)",
nullptr, &reader));
constexpr auto inStreamIndex = MF_SOURCE_READER_FIRST_AUDIO_STREAM;
// Select only the audio stream
winHr(reader->SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false));
winHr(reader->SetStreamSelection(inStreamIndex, true));
ComPtr<IMFMediaSink> mediaSink;
winHr(MFCreateAudioRenderer(nullptr, &mediaSink));
ComPtr<IMFSinkWriter> writer;
{
ComPtr<IMFStreamSink> streamSink;
winHr(mediaSink->GetStreamSinkByIndex(0, &streamSink));
ComPtr<IMFMediaTypeHandler> typeHandler;
winHr(streamSink->GetMediaTypeHandler(&typeHandler));
DWORD dwCount = 0;
ComPtr<IMFMediaType> inputType;
winHr(typeHandler->GetMediaTypeCount(&dwCount));
for (INT i = 0; i < dwCount; i++)
{
inputType.attach(nullptr);
winHr(typeHandler->GetMediaTypeByIndex(i, &inputType));
if (SUCCEEDED(typeHandler->IsMediaTypeSupported(inputType, NULL)))
break;
}
//ComPtr<IMFMediaType> inputType;
//winHr(reader->GetCurrentMediaType(inStreamIndex, &inputType));
winHr(reader->SetCurrentMediaType(inStreamIndex, NULL, inputType));
//ComPtr<IMFMediaType> closestSupportedType;
//const auto result = typeHandler->IsMediaTypeSupported(inputType, &closestSupportedType);
//if (result == MF_E_INVALIDMEDIATYPE)
//{
// if (!closestSupportedType)
// {
// std::cerr << "Media type not supported" << std::endl;
// winHr(mediaSink->Shutdown());
// goto end; //:o
// }
// winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, closestSupportedType));
// winHr(typeHandler->SetCurrentMediaType(closestSupportedType));
// winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
// winHr(writer->SetInputMediaType(0, closestSupportedType, nullptr));
//}
//else
{
//winHr(result);
//winHr(reader->SetCurrentMediaType(inStreamIndex, nullptr, inputType));
winHr(typeHandler->SetCurrentMediaType(inputType));
winHr(MFCreateSinkWriterFromMediaSink(mediaSink, nullptr, &writer));
winHr(writer->SetInputMediaType(0, inputType, nullptr));
}
}
winHr(writer->BeginWriting());
while (true)
{
ComPtr<IMFSample> sample;
DWORD streamFlags;
MFTIME timestamp;
winHr(reader->ReadSample(inStreamIndex, 0, nullptr, &streamFlags, &timestamp, &sample));
if (streamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
{
winHr(writer->NotifyEndOfSegment(0));
break;
}
if (streamFlags & MF_SOURCE_READERF_STREAMTICK)
winHr(writer->SendStreamTick(0, timestamp));
if (!sample)
continue;
// SetSampleTime is redundant
//winHr(sample->SetSampleTime(timestamp));
winHr(writer->WriteSample(0, sample));
}
// Flush shouldn't be called!
// winHr(writer->Flush(0));
std::cout << "(Press enter to stop)" << std::endl;
std::cin.get();
winHr(writer->Finalize());
writer.attach(nullptr);
winHr(mediaSink->Shutdown());
}
end:
winHr(MFShutdown());
CoUninitialize();
}
This code works just fine for me with wav files (Win7, default sound card) :
//----------------------------------------------------------------------------------------------
// Main.cpp
//----------------------------------------------------------------------------------------------
#pragma once
#define WIN32_LEAN_AND_MEAN
#define STRICT
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
//----------------------------------------------------------------------------------------------
// Microsoft Windows SDK for Windows 7
#include <WinSDKVer.h>
#include <new>
#include <windows.h>
#include <strsafe.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mferror.h>
#include <mfreadwrite.h>
template <class T> inline void SAFE_RELEASE(T*& p){
if(p){
p->Release();
p = NULL;
}
}
#define AUDIO_FILE L"C:\\Project\\Media\\Audio\\test.wav"
HRESULT ProcessAudio(const WCHAR*);
void main(){
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if(SUCCEEDED(hr)){
hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);
if(SUCCEEDED(hr)){
hr = ProcessAudio(AUDIO_FILE);
hr = MFShutdown();
}
CoUninitialize();
}
}
HRESULT ProcessAudio(const WCHAR*){
IMFSourceReader* pSourceReader = NULL;
IMFMediaType* pType = NULL;
DWORD dwMediaTypeIndex = 0;
IMFMediaSink* pAudioSink = NULL;
IMFStreamSink* pStreamSink = NULL;
IMFMediaTypeHandler* pMediaTypeHandler = NULL;
IMFSinkWriter* pSinkWriter = NULL;
HRESULT hr = MFCreateSourceReaderFromURL(AUDIO_FILE, NULL, &pSourceReader);
// Check native media type
if(SUCCEEDED(hr))
hr = pSourceReader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, dwMediaTypeIndex, &pType);
SAFE_RELEASE(pType);
// Get current media type
if(SUCCEEDED(hr))
hr = pSourceReader->GetCurrentMediaType(dwMediaTypeIndex, &pType);
if(SUCCEEDED(hr))
hr = MFCreateAudioRenderer(NULL, &pAudioSink);
if(SUCCEEDED(hr))
hr = pAudioSink->GetStreamSinkByIndex(0, &pStreamSink);
if(SUCCEEDED(hr))
hr = pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler);
if(FAILED(hr = pMediaTypeHandler->IsMediaTypeSupported(pType, NULL))){
SAFE_RELEASE(pType);
// This is a compatible type with my soundcard
// MF_MT_MAJOR_TYPE MFMediaType_Audio
// MF_MT_SUBTYPE MFAudioFormat_PCM
// MF_MT_AUDIO_NUM_CHANNELS 2
// MF_MT_AUDIO_SAMPLES_PER_SECOND 48000
// MF_MT_AUDIO_BLOCK_ALIGNMENT 4
// MF_MT_AUDIO_AVG_BYTES_PER_SECOND 192000
// MF_MT_AUDIO_BITS_PER_SAMPLE 16
// MF_MT_ALL_SAMPLES_INDEPENDENT 1
// MF_MT_AUDIO_PREFER_WAVEFORMATEX 1
hr = MFCreateMediaType(&pType);
if(SUCCEEDED(hr))
hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
if(SUCCEEDED(hr))
hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, 2);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 48000);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 4);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 192000);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if(SUCCEEDED(hr))
hr = pType->SetUINT32(MF_MT_AUDIO_PREFER_WAVEFORMATEX, TRUE);
}
if(SUCCEEDED(hr))
hr = pMediaTypeHandler->SetCurrentMediaType(pType);
if(SUCCEEDED(hr))
hr = MFCreateSinkWriterFromMediaSink(pAudioSink, NULL, &pSinkWriter);
if(SUCCEEDED(hr))
hr = pSinkWriter->BeginWriting();
BOOL bProcess = (hr == S_OK ? TRUE : FALSE);
DWORD streamIndex;
DWORD flags;
LONGLONG llTimeStamp;
IMFSample* pSample = NULL;
while(bProcess){
hr = pSourceReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);
if(SUCCEEDED(hr) && (flags == 0)){
if(pSample){
hr = pSinkWriter->WriteSample(0, pSample);
SAFE_RELEASE(pSample);
}
}
else{
bProcess = FALSE;
}
}
if(pSinkWriter)
pSinkWriter->Finalize();
if(pAudioSink)
pAudioSink->Shutdown();
SAFE_RELEASE(pSample);
SAFE_RELEASE(pSinkWriter);
SAFE_RELEASE(pMediaTypeHandler);
SAFE_RELEASE(pStreamSink);
SAFE_RELEASE(pAudioSink);
SAFE_RELEASE(pType);
SAFE_RELEASE(pSourceReader);
return hr;
}
If you face problem with MF_E_TOPO_CODEC_NOT_FOUND, share your file. Perhaps extra code is needed in this case.
EDIT
Do you have more than one sound card ?
Share your audio file, so we can see what's going on.
My little hack with Sleep(40), is just here to tell you that doing "while(true)" without pausing have no sens (your processor is working too much). Off course 40 should be better decided. 40 is the minimum processing time processor preemption on windows OS. That my choice for this sample code. We can talk about the best way to choose this value. I'm waiting for your suggestion.

C++ Capture Full screen in real time

I'm developing a software that should capture what is happening on the screen to make some processing. One of the requirements is that the software has to run at least 30FPS.
I've tried several options and I'm going to shopw you two, But none of them fully works to my needs:
(1) Using Direct X - The problem with this approach is that I cannot run it at 30 FPS (I get between 15 and 20):
DirectXScreenCapturer.h:
#include "IScreenCapturer.h"
#include <Wincodec.h> // we use WIC for saving images
#include <d3d9.h> // DirectX 9 header
#pragma comment(lib, "d3d9.lib") // link to DirectX 9 library
class DirectXScreenCapturer : public IScreenCapturer
{
public:
DirectXScreenCapturer();
~DirectXScreenCapturer();
virtual bool CaptureScreen(cv::Mat&) override;
private:
IDirect3D9* _d3d;
IDirect3DDevice9* _device;
IDirect3DSurface9* _surface;
cv::Mat _screen;
};
DirectXScreenCapturer.cpp:
#include "DirectXScreenCapturer.h"
DirectXScreenCapturer::DirectXScreenCapturer() : _d3d(NULL), _device(NULL), _surface(NULL)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
LPBYTE *shots = nullptr;
// init D3D and get screen size
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = mode.Height;
parameters.BackBufferWidth = mode.Width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;
// create device & capture surface
hr = _d3d->CreateDevice(D3DADAPTER_DEFAULT, _D3DDEVTYPE::D3DDEVTYPE_HAL, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_PSGP_THREADING | D3DCREATE_PUREDEVICE, &parameters, &_device);
hr = _device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &_surface, nullptr);
// compute the required buffer size
hr = _surface->LockRect(&rc, NULL, 0);
hr = _surface->UnlockRect();
// allocate screenshots buffers
_screen = cv::Mat(mode.Height, mode.Width, CV_8UC4, rc.pBits);
}
DirectXScreenCapturer::~DirectXScreenCapturer()
{
}
bool DirectXScreenCapturer::CaptureScreen(cv::Mat& result)
{
_device->GetFrontBufferData(0, _surface);
result = _screen;
return true;
}
(2) I've tried DXGI with Direct X. This solution works really well in Real Time, but fails to capture the screen when another application is running in full screen, so the software doesn't work if I'm watching movies, playign games, etc:
DXGIScreenCapturer.h
#include "IScreenCapturer.h"
#include <DXGI1_2.h>
#pragma comment(lib, "Dxgi.lib")
#include <D3D11.h>
#pragma comment(lib, "D3D11.lib")
class DXGIScreenCapturer : public IScreenCapturer
{
public:
DXGIScreenCapturer();
~DXGIScreenCapturer();
bool Init();
virtual bool CaptureScreen(cv::Mat&) override;
private:
ID3D11Device* _lDevice;
ID3D11DeviceContext* _lImmediateContext;
IDXGIOutputDuplication* _lDeskDupl;
ID3D11Texture2D* _lAcquiredDesktopImage;
DXGI_OUTPUT_DESC _lOutputDesc;
DXGI_OUTDUPL_DESC _lOutputDuplDesc;
D3D11_MAPPED_SUBRESOURCE _resource;
ID3D11Texture2D* currTexture;
cv::Mat _result;
};
DXGIScreenCapturer.cpp
#include "DXGIScreenCapturer.h"
DXGIScreenCapturer::DXGIScreenCapturer()
{
Init();
}
DXGIScreenCapturer::~DXGIScreenCapturer()
{
}
bool DXGIScreenCapturer::Init() {
// Feature levels supported
D3D_FEATURE_LEVEL gFeatureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1
};
UINT gNumFeatureLevels = ARRAYSIZE(gFeatureLevels);
D3D_FEATURE_LEVEL lFeatureLevel;
HRESULT hr(E_FAIL);
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &_lDevice, &lFeatureLevel, &_lImmediateContext);
if (FAILED(hr))
return false;
if (!_lDevice)
return false;
// Get DXGI device
IDXGIDevice* lDxgiDevice;
hr = _lDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&lDxgiDevice));
if (FAILED(hr))
return false;
// Get DXGI adapter
IDXGIAdapter* lDxgiAdapter;
hr = lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&lDxgiAdapter));
lDxgiDevice->Release();
lDxgiDevice = nullptr;
if (FAILED(hr))
return false;
UINT Output = 0;
// Get output
IDXGIOutput* lDxgiOutput;
hr = lDxgiAdapter->EnumOutputs(Output, &lDxgiOutput);
if (FAILED(hr))
return false;
lDxgiAdapter->Release();
lDxgiAdapter = nullptr;
hr = lDxgiOutput->GetDesc(&_lOutputDesc);
if (FAILED(hr))
return false;
// QI for Output 1
IDXGIOutput1* lDxgiOutput1;
hr = lDxgiOutput->QueryInterface(__uuidof(lDxgiOutput1), reinterpret_cast<void**>(&lDxgiOutput1));
lDxgiOutput->Release();
lDxgiOutput = nullptr;
if (FAILED(hr))
return false;
// Create desktop duplication
hr = lDxgiOutput1->DuplicateOutput(_lDevice, &_lDeskDupl);
if (FAILED(hr))
return false;
lDxgiOutput1->Release();
lDxgiOutput1 = nullptr;
// Create GUI drawing texture
_lDeskDupl->GetDesc(&_lOutputDuplDesc);
// Create CPU access texture
D3D11_TEXTURE2D_DESC desc;
desc.Width = _lOutputDuplDesc.ModeDesc.Width;
desc.Height = _lOutputDuplDesc.ModeDesc.Height;
desc.Format = _lOutputDuplDesc.ModeDesc.Format;
desc.ArraySize = 1;
desc.BindFlags = 0;
desc.MiscFlags = 0;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MipLevels = 1;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG::D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE::D3D11_USAGE_STAGING;
hr = _lDevice->CreateTexture2D(&desc, NULL, &currTexture);
if (!currTexture)
{
hr = _lDeskDupl->ReleaseFrame();
return false;
}
while (!CaptureScreen(_result));
_result = cv::Mat(desc.Height, desc.Width, CV_8UC4, _resource.pData);
return true;
}
bool DXGIScreenCapturer::CaptureScreen(cv::Mat& output)
{
HRESULT hr(E_FAIL);
IDXGIResource* lDesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO lFrameInfo;
hr = _lDeskDupl->AcquireNextFrame(999, &lFrameInfo, &lDesktopResource);
if (FAILED(hr))
return false;
if (lFrameInfo.LastPresentTime.HighPart == 0) // not interested in just mouse updates, which can happen much faster than 60fps if you really shake the mouse
{
hr = _lDeskDupl->ReleaseFrame();
return false;
}
// QI for ID3D11Texture2D
hr = lDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&_lAcquiredDesktopImage));
lDesktopResource->Release();
lDesktopResource = nullptr;
if (FAILED(hr))
{
hr = _lDeskDupl->ReleaseFrame();
return false;
}
_lImmediateContext->CopyResource(currTexture, _lAcquiredDesktopImage);
UINT subresource = D3D11CalcSubresource(0, 0, 0);
_lImmediateContext->Map(currTexture, subresource, D3D11_MAP_READ, 0, &_resource);
_lImmediateContext->Unmap(currTexture, 0);
hr = _lDeskDupl->ReleaseFrame();
output = _result;
return true;
}
Please note that IScreenCapturer is just and interface to quickly swap implementations and also note that the return result is a cv::Mat object (OpenCV to do the rest of processing).
Any help on this issue? I'm still trying to figure out a solution that can capture the screen at least 30 FPS and that can capture the whole screen even when an app is running at full screen.

Directshow cant start capture twice

I am trying to follow through the DirectShow examples on the windows dev center to make my own application that can capture screen and audio to video: Capturing Video to an AVI File
The first time capture starts all is ok, but at the second nothing happens, the file with video not appearing. Is it possible that I forgot to uninitialize sometfing?
UPDATE
The problem seems not to be in missing releasing. The second time stream writes the file 1.avi is creating but it empty and when the pMediaControl->Stop(); is done it automatically deletes
UPDATE2
At the second time I found that:
hr = pBuild->RenderStream(
&PIN_CATEGORY_CAPTURE, // Pin category.
&MEDIATYPE_Video, // Media type.
pCap, // Capture filter.
NULL, // Intermediate filter (optional).
pMux); // Mux or file sink filter.
returns E_INVALIDARG. So I added (using this):
if (a == 1) {
CComPtr<IPin> sourcePin;
CComPtr<IPin> dumpPin;
sourcePin = GetPin(pMux, PINDIR_OUTPUT);
dumpPin = GetPin(pCap, PINDIR_INPUT);
hr = ppGraph->Connect(sourcePin, dumpPin);
}
And I found thaht on the second time the dumpPin value is NULL .The hr = ppGraph->AddFilter(pCap, L"Capture Filter"); runs ok. Where can I dig next to find error?
(code is updated)
My code:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <dshow.h>
#include <atlbase.h>
#include <dshow.h>
#include <vector>
#include <string>
#pragma comment(lib, "strmiids")
IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir)
{
BOOL bFound = FALSE;
IEnumPins *pEnum;
IPin *pPin;
pFilter->EnumPins(&pEnum);
while (pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
if (bFound = (PinDir == PinDirThis))
break;
pPin->Release();
}
pEnum->Release();
return (bFound ? pPin : 0);
}
HRESULT EnumerateDevices(REFGUID category, IEnumMoniker **ppEnum)
{
// Create the System Device Enumerator.
ICreateDevEnum *pDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the category.
hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
if (hr == S_FALSE)
{
hr = VFW_E_NOT_FOUND; // The category is empty. Treat as an error.
}
pDevEnum->Release();
}
return hr;
}
HRESULT InitCaptureGraphBuilder(
IGraphBuilder **ppGraph, // Receives the pointer.
ICaptureGraphBuilder2 **ppBuild // Receives the pointer.
)
{
if (!ppGraph || !ppBuild)
{
return E_POINTER;
}
IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuild = NULL;
// Create the Capture Graph Builder.
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuild);
if (SUCCEEDED(hr))
{
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Initialize the Capture Graph Builder.
pBuild->SetFiltergraph(pGraph);
// Return both interface pointers to the caller.
*ppBuild = pBuild;
*ppGraph = pGraph; // The caller must release both interfaces.
return S_OK;
}
else
{
pBuild->Release();
}
}
return hr; // Failed
}
struct Capture {
IPropertyBag *pPropBag;
IGraphBuilder *ppGraph;
IBaseFilter *pCap;
ICaptureGraphBuilder2 *pBuild;
};
void DisplayDeviceInformation(IEnumMoniker *pEnum,int a)
{
IMoniker *pMoniker = NULL;
std::vector<Capture> captures;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue;
}
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"DevicePath", &var, 0);
if (SUCCEEDED(hr))
{
// The device path is not intended for display.
printf("Device path: %S\n", var.bstrVal);
VariantClear(&var);
}
IGraphBuilder *ppGraph;
ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
hr = InitCaptureGraphBuilder(&ppGraph, &pBuild);
IBaseFilter *pCap; // Video capture filter.
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
std::wstring name = std::wstring(L"C:\\a\\") + std::to_wstring(a) + std::wstring(L".avi");
const wchar_t *cname = name.c_str();
hr = ppGraph->AddFilter(pCap, L"Capture Filter");
if (SUCCEEDED(hr)) {
IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(
&MEDIASUBTYPE_Avi, // Specifies AVI for the target file.
cname, // File name.
&pMux, // Receives a pointer to the mux.
NULL); // (Optional) Receives a pointer to the file sink.
if (a == 1) {
CComPtr<IPin> sourcePin;
CComPtr<IPin> dumpPin;
sourcePin = GetPin(pMux, PINDIR_OUTPUT);
dumpPin = GetPin(pCap, PINDIR_INPUT);
hr = ppGraph->Connect(sourcePin, dumpPin);
}
hr = pBuild->RenderStream(
&PIN_CATEGORY_CAPTURE, // Pin category.
&MEDIATYPE_Video, // Media type.
pCap, // Capture filter.
NULL, // Intermediate filter (optional).
pMux); // Mux or file sink filter.
// Release the mux filter.
pMux->Release();
IConfigAviMux *pConfigMux = NULL;
hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);
if (SUCCEEDED(hr))
{
pConfigMux->SetMasterStream(0);
pConfigMux->Release();
}
IConfigInterleaving *pInterleave = NULL;
hr = pMux->QueryInterface(IID_IConfigInterleaving, (void**)&pInterleave);
if (SUCCEEDED(hr))
{
pInterleave->put_Mode(INTERLEAVE_CAPTURE);
pInterleave->Release();
}
pMux->Release();
}
}
Capture capt;
capt.ppGraph = ppGraph;
capt.pPropBag = pPropBag;
capt.pCap = pCap;
capt.pBuild = pBuild;
captures.push_back(capt);
}
for (auto cap : captures)
{
IMediaControl* pMediaControl;
cap.ppGraph->QueryInterface(&pMediaControl);
pMediaControl->Run();
}
Sleep(5000);
for (auto cap : captures)
{
IMediaControl* pMediaControl;
cap.ppGraph->QueryInterface(&pMediaControl);
pMediaControl->Stop();
pMediaControl->Release();
cap.pCap->Release();
cap.ppGraph->Release();
cap.pBuild->Release();
cap.pPropBag->Release();
}
pMoniker->Release();
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
for (int a = 0; a <= 1; a++) {
if (SUCCEEDED(hr))
{
IEnumMoniker *pEnum;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
if (SUCCEEDED(hr))
{
DisplayDeviceInformation(pEnum,a);
pEnum->Release();
}
}
}
if (SUCCEEDED(hr))
{
CoUninitialize();
}
int i;
std::cin >> i;
return 0;
}
You might need to Release ppGraph, pBuild, pMediaControl and pCap at the end of DisplayDeviceInformation function and pMux at the end of the cycle. It will be better to use some sort of smart pointers instead.
I didn't figure out how too resolve this, so I just used spawn of external process.

Loopback is saved in buffer, how to write to disk as wav?

In the code below, you can copy paste it to an empty proejct, add main.cpp and it is saving it to buffer, however I don't know how to write that buffer to file. You can see in SetFormat I set it up, and then in CopyData I am writing to the buffer.
Complete C++ beginner here, I was able to mash this up. How can I write the buffer to file?
#include <mmdeviceapi.h>
#include <audioclient.h>
#include "debug.h"
#include <comdef.h>
#define UNICODE
//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
// function strof(jsint) { var prim = jsint >>> 0; return prim.toString(16) }
#define EXIT_ON_ERROR(hres, title) \
if (FAILED(hres)) { \
_com_error hres_str(hres); \
debug_log("exit due to error on title", title, "hres:", hres, "hres_str:", hres_str.ErrorMessage()); \
goto Exit; \
}
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
PWAVEFORMATEX m_pwfx;
byte* recordBuffer;;
HRESULT SetFormat(WAVEFORMATEX *pwfx, UINT32 bufferSize) {
HRESULT hr;
m_pwfx = pwfx;
recordBuffer = new byte[bufferSize * pwfx->nBlockAlign];
debug_log("Record buffer set to:", (bufferSize * pwfx->nBlockAlign), "bytes");
return 0;
}
HRESULT CopyData(BYTE *pData, UINT32 numFramesAvailable, BOOL *bDone) {
/* Get the lock */
// memcpy((void *)MyAudioSink::buff, (void *)pData, numFramesAvailable);
// printf("%f\n", buff[0]);
memcpy((void*)recordBuffer, pData, numFramesAvailable * m_pwfx->nBlockAlign);
for (size_t i = 0; i < numFramesAvailable; i++) {
debug_log((short)pData[i]);
}
return 0;
}
HRESULT RecordAudioStream() {
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoInitialize(NULL);
EXIT_ON_ERROR(hr, "CoInitialize");
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
EXIT_ON_ERROR(hr, "CoCreateInstance");
hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pDevice);
EXIT_ON_ERROR(hr, "GetDefaultAudioEndpoint");
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr, "Activate");
hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr, "GetMixFormat");
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, pwfx, NULL);
EXIT_ON_ERROR(hr, "Initialize");
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr, "GetBufferSize");
hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
EXIT_ON_ERROR(hr, "GetService");
// Notify the audio sink which format to use.
hr = SetFormat(pwfx, bufferFrameCount);
EXIT_ON_ERROR(hr, "SetFormat");
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr, "Start");
// Each loop fills about half of the shared buffer.
while (bDone == FALSE) {
debug_log("at top of loop");
// Sleep for half the buffer duration.
Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr, "GetNextPacketSize");
while (packetLength != 0 && bDone == FALSE) {
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
EXIT_ON_ERROR(hr, "GetBuffer");
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
hr = CopyData(pData, numFramesAvailable, &bDone);
EXIT_ON_ERROR(hr, "CopyData");
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr, "ReleaseBuffer");
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr, "GetNextPacketSize 2");
}
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr, "Stop");
Exit:
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pDevice);
SAFE_RELEASE(pAudioClient);
SAFE_RELEASE(pCaptureClient);
CoUninitialize();
return hr;
}
int main() {
RecordAudioStream();
}
debug.h - in case you want to copy paste the above
#include <utility>
#include <fstream>
#include <string>
template <typename HeadType> bool
debug_log_rec(std::ostream& out, HeadType&& head) {
out << head;
out << std::endl;
return true;
}
template <typename HeadType, typename... TailTypes> bool
debug_log_rec(std::ostream& out, HeadType&& head, TailTypes&&... tails) {
out << head;
out << " ";
debug_log_rec(out, std::forward<TailTypes>(tails)...);
return true;
}
template <typename... ArgTypes> bool
debug_log(ArgTypes&&... args) {
std::fstream fs;
fs.open("C:\\Users\\Mercurius\\Desktop\\log.txt", std::fstream::app);
debug_log_rec(fs, std::forward<ArgTypes>(args)...);
fs.close();
return true;
}