Created
September 7, 2017 07:37
-
-
Save daramkun/1fc57dc50f1b98834b38c06a65c4e56e to your computer and use it in GitHub Desktop.
XAudio2 Streaming with MediaFoundation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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