Last active
September 6, 2017 18:41
-
-
Save daramkun/f94fe6e8acdb0d23878bb084d641dfe6 to your computer and use it in GitHub Desktop.
OpenAL Streaming with MediaFoundation
This file contains 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 <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 <al.h> | |
#include <alc.h> | |
#pragma comment ( lib, "OpenAL32.lib" ) | |
#define AL_EXT_MCFORMATS 1 | |
#define AL_FORMAT_QUAD8 0x1204 | |
#define AL_FORMAT_QUAD16 0x1205 | |
#define AL_FORMAT_QUAD32 0x1206 | |
#define AL_FORMAT_REAR8 0x1207 | |
#define AL_FORMAT_REAR16 0x1208 | |
#define AL_FORMAT_REAR32 0x1209 | |
#define AL_FORMAT_51CHN8 0x120A | |
#define AL_FORMAT_51CHN16 0x120B | |
#define AL_FORMAT_51CHN32 0x120C | |
#define AL_FORMAT_61CHN8 0x120D | |
#define AL_FORMAT_61CHN16 0x120E | |
#define AL_FORMAT_61CHN32 0x120F | |
#define AL_FORMAT_71CHN8 0x1210 | |
#define AL_FORMAT_71CHN16 0x1211 | |
#define AL_FORMAT_71CHN32 0x1212 | |
#define BUFFER_COUNT 2 | |
bool readSample ( IMFSourceReader * reader, ALuint sourceId, ALuint bufferId, ALenum bufferFormat, ALint samplerate, 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 ) ) ) | |
{ | |
alSourceStop ( sourceId ); | |
return false; | |
} | |
if ( flags & MF_SOURCE_READERF_ENDOFSTREAM ) | |
{ | |
alSourceStop ( sourceId ); | |
return false; | |
} | |
CComPtr<IMFMediaBuffer> buf; | |
if ( FAILED ( hResult = sample->ConvertToContiguousBuffer ( &buf ) ) ) | |
{ | |
alSourceStop ( sourceId ); | |
return false; | |
} | |
BYTE * lockedBuffer; | |
DWORD lockedCurrentLength; | |
if ( FAILED ( hResult = buf->Lock ( &lockedBuffer, nullptr, &lockedCurrentLength ) ) ) | |
{ | |
alSourceStop ( sourceId ); | |
return false; | |
} | |
alBufferData ( bufferId, bufferFormat, lockedBuffer, lockedCurrentLength, samplerate ); | |
return true; | |
} | |
int main ( void ) | |
{ | |
HRESULT hResult; | |
MFStartup ( MF_VERSION, 0 ); | |
ALCdevice * alcDevice = alcOpenDevice ( nullptr ); | |
ALCint attrList [] = { 0, }; | |
ALCcontext * alcContext = alcCreateContext ( alcDevice, attrList ); | |
alcMakeContextCurrent ( alcContext ); | |
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; | |
ALenum bufferFormat = | |
( ( channels == 2 ) ? ( ( bitsPerSample == 16 ) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8 ) : | |
( ( channels == 1 ) ? ( ( bitsPerSample == 16 ) ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8 ) : | |
( ( channels == 6 ) ? ( ( bitsPerSample == 16 ) ? AL_FORMAT_51CHN16 : AL_FORMAT_51CHN8 ) : | |
( channels == 8 ) ? ( ( bitsPerSample == 16 ) ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN8 ) : 0 | |
) ) ); | |
int byteRate = channels * ( bitsPerSample / 8 ) * samplerate; | |
ALuint sourceId; | |
alGenSources ( 1, &sourceId ); | |
std::vector<ALuint> bufferIds ( BUFFER_COUNT ); | |
alGenBuffers ( BUFFER_COUNT, &bufferIds [ 0 ] ); | |
for ( int i = 0; i < BUFFER_COUNT; ++i ) | |
readSample ( reader, sourceId, bufferIds [ i ], bufferFormat, samplerate, nullptr ); | |
alSourceQueueBuffers ( sourceId, BUFFER_COUNT, &bufferIds [ 0 ] ); | |
alSourcePlay ( sourceId ); | |
ALint totalPlaying = 0; | |
bool failed = false; | |
while ( true ) | |
{ | |
ALint status; | |
alGetSourcei ( sourceId, AL_SOURCE_STATE, &status ); | |
if ( status == AL_PLAYING ) | |
{ | |
//printf ( "Still Playing.\n" ); | |
} | |
if ( GetAsyncKeyState ( VK_SPACE ) & 0x8000 != 0 ) | |
{ | |
alSourceStop ( sourceId ); | |
alSourceUnqueueBuffers ( sourceId, BUFFER_COUNT, &bufferIds [ 0 ] ); | |
PROPVARIANT pos; | |
pos.vt = VT_I8; | |
pos.uhVal.QuadPart = 30 * 10000000ULL; | |
reader->SetCurrentPosition ( GUID_NULL, pos ); | |
totalPlaying = 30 * byteRate; | |
for ( int i = 0; i < BUFFER_COUNT; ++i ) | |
readSample ( reader, sourceId, bufferIds [ i ], bufferFormat, samplerate, nullptr ); | |
alSourceQueueBuffers ( sourceId, 8, &bufferIds [ 0 ] ); | |
alSourcePlay ( sourceId ); | |
} | |
ALint position; | |
alGetSourcei ( sourceId, AL_SAMPLE_OFFSET, &position ); | |
printf ( "Position: %f / %f\n", ( totalPlaying + position ) / ( float ) byteRate, duration / 10000000.0f ); | |
if ( failed ) | |
break; | |
ALint proceed; | |
alGetSourcei ( sourceId, AL_BUFFERS_PROCESSED, &proceed ); | |
//printf ( "Proceed Buffer Count: %d.\n", proceed ); | |
if ( proceed > 0 ) | |
{ | |
std::vector<ALuint> proceedBuffers; | |
for ( int i = 0; i < proceed; ++i ) | |
{ | |
proceedBuffers.push_back ( bufferIds [ 0 ] ); | |
bufferIds.erase ( bufferIds.begin () ); | |
} | |
for ( auto i = proceedBuffers.begin (); i != proceedBuffers.end (); ++i ) | |
{ | |
ALuint temp = *i; | |
bufferIds.push_back ( temp ); | |
alSourceUnqueueBuffers ( sourceId, 1, &temp ); | |
if ( !readSample ( reader, sourceId, temp, bufferFormat, samplerate, nullptr ) ) | |
{ | |
printf ( "All buffer readed.\n" ); | |
failed = true; | |
} | |
ALint size; | |
alGetBufferi ( temp, AL_SIZE, &size ); | |
totalPlaying += size; | |
alSourceQueueBuffers ( sourceId, 1, &temp ); | |
} | |
} | |
} | |
alDeleteBuffers ( BUFFER_COUNT, &bufferIds [ 0 ] ); | |
alDeleteSources ( 1, &sourceId ); | |
alcDestroyContext ( alcContext ); | |
alcCloseDevice ( alcDevice ); | |
mediaType.Release (); | |
reader.Release (); | |
attr.Release (); | |
MFShutdown (); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment