Skip to content

Instantly share code, notes, and snippets.

@daramkun
Created September 7, 2017 07:37
Show Gist options
  • Save daramkun/1fc57dc50f1b98834b38c06a65c4e56e to your computer and use it in GitHub Desktop.
Save daramkun/1fc57dc50f1b98834b38c06a65c4e56e to your computer and use it in GitHub Desktop.
XAudio2 Streaming with MediaFoundation
#include <Windows.h>
#include <atlbase.h>
#include <vector>
#include <functional>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <mfplay.h>
#include <mftransform.h>
#pragma comment ( lib, "mfplat.lib" )
#pragma comment ( lib, "mfuuid.lib" )
#pragma comment ( lib, "mfreadwrite.lib" )
#include <xaudio2.h>
#pragma comment ( lib, "xaudio2.lib" )
#define BUFFER_COUNT 2
using namespace std;
class MyCallback : public IXAudio2VoiceCallback
{
public:
virtual void __stdcall OnVoiceProcessingPassStart ( UINT32 BytesRequired ) { if ( voiceProcessingPassStart != nullptr ) voiceProcessingPassStart ( BytesRequired ); }
virtual void __stdcall OnVoiceProcessingPassEnd () { if ( voiceProcessingPassEnd != nullptr ) voiceProcessingPassEnd (); }
virtual void __stdcall OnStreamEnd () { if ( streamEnd != nullptr ) streamEnd (); }
virtual void __stdcall OnBufferStart ( void * pBufferContext ) { if ( bufferStart != nullptr ) bufferStart ( pBufferContext ); }
virtual void __stdcall OnBufferEnd ( void * pBufferContext ) { if ( bufferEnd != nullptr ) bufferEnd ( pBufferContext ); }
virtual void __stdcall OnLoopEnd ( void * pBufferContext ) { if ( loopEnd != nullptr ) loopEnd ( pBufferContext ); }
virtual void __stdcall OnVoiceError ( void * pBufferContext, HRESULT Error ) { if ( voiceError != nullptr ) voiceError ( pBufferContext, Error ); }
public:
std::function<void ( UINT32 )> voiceProcessingPassStart;
std::function<void ()> voiceProcessingPassEnd;
std::function<void ()> streamEnd;
std::function<void ( void * )> bufferStart;
std::function<void ( void * )> bufferEnd;
std::function<void ( void * )> loopEnd;
std::function<void ( void *, HRESULT )> voiceError;
};
bool readSample ( IMFSourceReader * reader, IXAudio2SourceVoice * voice, LONGLONG * timeStamp )
{
HRESULT hResult;
DWORD flags;
LONGLONG tempTimeStamp;
CComPtr<IMFSample> sample;
if ( FAILED ( hResult = reader->ReadSample ( MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &flags, timeStamp ? timeStamp : &tempTimeStamp, &sample ) ) )
{
return false;
}
if ( flags & MF_SOURCE_READERF_ENDOFSTREAM )
{
return false;
}
CComPtr<IMFMediaBuffer> buf;
if ( FAILED ( hResult = sample->ConvertToContiguousBuffer ( &buf ) ) )
{
return false;
}
BYTE * lockedBuffer;
DWORD lockedCurrentLength;
if ( FAILED ( hResult = buf->Lock ( &lockedBuffer, nullptr, &lockedCurrentLength ) ) )
{
return false;
}
XAUDIO2_BUFFER xaudioBuffer = { 0, };
xaudioBuffer.Flags = 0;
xaudioBuffer.pAudioData = lockedBuffer;
xaudioBuffer.AudioBytes = lockedCurrentLength;
voice->SubmitSourceBuffer ( &xaudioBuffer );
buf->Unlock ();
return true;
}
int main ( void )
{
CoInitialize ( 0 );
CComPtr<IXAudio2> xaudio2;
XAudio2Create ( &xaudio2 );
xaudio2->StartEngine ();
IXAudio2MasteringVoice * masteringVoice;
xaudio2->CreateMasteringVoice ( &masteringVoice );
HRESULT hResult;
MFStartup ( MF_VERSION, 0 );
CComPtr<IMFAttributes> attr;
hResult = MFCreateAttributes ( &attr, 0 );
hResult = attr->SetUINT32 ( MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1 );
CComPtr<IMFSourceReader> reader;
hResult = MFCreateSourceReaderFromURL ( TEXT ( "Sample.mp3" ), attr, &reader );
hResult = reader->SetStreamSelection ( MF_SOURCE_READER_ALL_STREAMS, false );
hResult = reader->SetStreamSelection ( MF_SOURCE_READER_FIRST_AUDIO_STREAM, true );
CComPtr<IMFMediaType> setMediaType;
MFCreateMediaType ( &setMediaType );
setMediaType->SetGUID ( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
setMediaType->SetGUID ( MF_MT_SUBTYPE, MFAudioFormat_PCM );
hResult = reader->SetCurrentMediaType ( MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, setMediaType );
setMediaType.Release ();
CComPtr<IMFMediaType> mediaType;
hResult = reader->GetCurrentMediaType ( MF_SOURCE_READER_FIRST_AUDIO_STREAM, &mediaType );
WAVEFORMATEX * wf;
UINT wfSize = sizeof ( WAVEFORMATEX );
hResult = MFCreateWaveFormatExFromMFMediaType ( mediaType, &wf, &wfSize, 0 );
PROPVARIANT va;
hResult = reader->GetPresentationAttribute ( MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &va );
int channels = wf->nChannels;
int bitsPerSample = wf->wBitsPerSample;
int samplerate = wf->nSamplesPerSec;
LONGLONG duration = va.uhVal.QuadPart;
int byteRate = channels * ( bitsPerSample / 8 ) * samplerate;
MyCallback callback;
IXAudio2SourceVoice * sourceVoice;
xaudio2->CreateSourceVoice ( &sourceVoice, wf, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &callback );
LONGLONG playingPos;
callback.bufferEnd = [ & ] ( void * pBufferContext )
{
readSample ( reader, sourceVoice, &playingPos );
};
readSample ( reader, sourceVoice, nullptr );
sourceVoice->Start ();
bool failed = false;
while ( true )
{
if ( ( GetAsyncKeyState ( VK_SPACE ) & 0x8000 ) != 0 )
{
PROPVARIANT pos;
pos.vt = VT_I8;
pos.uhVal.QuadPart = 30 * 10000000ULL;
reader->SetCurrentPosition ( GUID_NULL, pos );
}
XAUDIO2_VOICE_STATE voiceState;
sourceVoice->GetState ( &voiceState );
printf ( "Position: %f / %f\n", playingPos / 10000000.0f, duration / 10000000.0f );
}
sourceVoice->DestroyVoice ();
mediaType.Release ();
reader.Release ();
attr.Release ();
MFShutdown ();
masteringVoice->DestroyVoice ();
xaudio2->StopEngine ();
xaudio2.Release ();
CoUninitialize ();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment