Path: blob/master/libs/faudio/src/FAudio_platform_win32_wmadec.c
4389 views
/* FAudio WMADEC implementation over Win32 MF */12#ifdef HAVE_WMADEC34#include "FAudio_internal.h"56#include <stddef.h>78#define COBJMACROS9#include <windows.h>10#include <mfidl.h>11#include <mfapi.h>12#include <mferror.h>1314#include <initguid.h>1516DEFINE_GUID(CLSID_CWMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a);1718DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, FAUDIO_FORMAT_XMAUDIO2);1920struct FAudioWMADEC21{22IMFTransform *decoder;23IMFSample *output_sample;2425char *output_buf;26size_t output_pos;27size_t output_size;28size_t input_pos;29size_t input_size;30};3132static HRESULT FAudio_WMAMF_ProcessInput(33FAudioVoice *voice,34FAudioBuffer *buffer35) {36struct FAudioWMADEC *impl = voice->src.wmadec;37IMFMediaBuffer *media_buffer;38IMFSample *sample;39DWORD copy_size;40BYTE *copy_buf;41HRESULT hr;4243copy_size = (DWORD)min(buffer->AudioBytes - impl->input_pos, impl->input_size);44if (!copy_size) return S_FALSE;45LOG_INFO(voice->audio, "pushing %lx bytes at %Ix", copy_size, impl->input_pos);4647hr = MFCreateSample(&sample);48FAudio_assert(!FAILED(hr) && "Failed to create sample!");49hr = MFCreateMemoryBuffer(copy_size, &media_buffer);50FAudio_assert(!FAILED(hr) && "Failed to create buffer!");51hr = IMFMediaBuffer_SetCurrentLength(media_buffer, copy_size);52FAudio_assert(!FAILED(hr) && "Failed to set buffer length!");53hr = IMFMediaBuffer_Lock(54media_buffer,55©_buf,56NULL,57©_size58);59FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");60FAudio_memcpy(copy_buf, buffer->pAudioData + impl->input_pos, copy_size);61hr = IMFMediaBuffer_Unlock(media_buffer);62FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");6364hr = IMFSample_AddBuffer(sample, media_buffer);65FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");66IMFMediaBuffer_Release(media_buffer);6768hr = IMFTransform_ProcessInput(impl->decoder, 0, sample, 0);69IMFSample_Release(sample);70if (hr == MF_E_NOTACCEPTING) return S_OK;71if (FAILED(hr))72{73LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);74return hr;75}7677impl->input_pos += copy_size;78return S_OK;79};8081static HRESULT FAudio_WMAMF_ProcessOutput(82FAudioVoice *voice,83FAudioBuffer *buffer84) {85struct FAudioWMADEC *impl = voice->src.wmadec;86MFT_OUTPUT_DATA_BUFFER output;87IMFMediaBuffer *media_buffer;88DWORD status, copy_size;89BYTE *copy_buf;90HRESULT hr;9192while (1)93{94FAudio_memset(&output, 0, sizeof(output));95output.pSample = impl->output_sample;96hr = IMFTransform_ProcessOutput(impl->decoder, 0, 1, &output, &status);97if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) return S_FALSE;98if (FAILED(hr))99{100LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);101return hr;102}103104if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;105106hr = IMFSample_ConvertToContiguousBuffer(107output.pSample,108&media_buffer109);110FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");111hr = IMFMediaBuffer_Lock(112media_buffer,113©_buf,114NULL,115©_size116);117FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");118if (impl->output_pos + copy_size > impl->output_size)119{120impl->output_size = max(121impl->output_pos + copy_size,122impl->output_size * 3 / 2123);124impl->output_buf = voice->audio->pRealloc(125impl->output_buf,126impl->output_size127);128FAudio_assert(impl->output_buf && "Failed to resize output buffer!");129}130FAudio_memcpy(impl->output_buf + impl->output_pos, copy_buf, copy_size);131impl->output_pos += copy_size;132LOG_INFO(voice->audio, "pulled %lx bytes at %Ix", copy_size, impl->output_pos);133hr = IMFMediaBuffer_Unlock(media_buffer);134FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");135136IMFMediaBuffer_Release(media_buffer);137if (!impl->output_sample) IMFSample_Release(output.pSample);138}139140return S_OK;141};142143static void FAudio_INTERNAL_DecodeWMAMF(144FAudioVoice *voice,145FAudioBuffer *buffer,146float *decodeCache,147uint32_t samples148) {149const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;150size_t samples_pos, samples_size, copy_size = 0;151struct FAudioWMADEC *impl = voice->src.wmadec;152HRESULT hr;153154LOG_FUNC_ENTER(voice->audio)155156if (!impl->output_pos)157{158if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)159{160const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;161const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;162163impl->input_size = wfx->Format.nBlockAlign;164impl->output_size = max(165impl->output_size,166output_sizes[wma->PacketCount - 1]167);168}169else170{171const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;172173impl->input_size = xwf->dwBytesPerBlock;174impl->output_size = max(175impl->output_size,176(size_t) xwf->dwSamplesEncoded *177voice->src.format->nChannels *178(voice->src.format->wBitsPerSample / 8)179);180}181182impl->output_buf = voice->audio->pRealloc(183impl->output_buf,184impl->output_size185);186FAudio_assert(impl->output_buf && "Failed to allocate output buffer!");187188LOG_INFO(voice->audio, "sending BOS to %p", impl->decoder);189hr = IMFTransform_ProcessMessage(190impl->decoder,191MFT_MESSAGE_NOTIFY_START_OF_STREAM,1920193);194FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");195FAudio_WMAMF_ProcessInput(voice, buffer);196}197198samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);199samples_size = samples * voice->src.format->nChannels * sizeof(float);200201while (impl->output_pos < samples_pos + samples_size)202{203hr = FAudio_WMAMF_ProcessOutput(voice, buffer);204if (FAILED(hr)) goto error;205if (hr == S_OK) continue;206207hr = FAudio_WMAMF_ProcessInput(voice, buffer);208if (FAILED(hr)) goto error;209if (hr == S_OK) continue;210211if (!impl->input_size) break;212213LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);214hr = IMFTransform_ProcessMessage(215impl->decoder,216MFT_MESSAGE_NOTIFY_END_OF_STREAM,2170218);219FAudio_assert(!FAILED(hr) && "Failed to send EOS!");220impl->input_size = 0;221}222223if (impl->output_pos > samples_pos)224{225copy_size = FAudio_min(impl->output_pos - samples_pos, samples_size);226FAudio_memcpy(decodeCache, impl->output_buf + samples_pos, copy_size);227}228FAudio_zero((char *)decodeCache + copy_size, samples_size - copy_size);229LOG_INFO(230voice->audio,231"decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",232impl->output_pos,233impl->output_size,234copy_size,235samples_size236);237238LOG_FUNC_EXIT(voice->audio)239return;240241error:242FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));243LOG_FUNC_EXIT(voice->audio)244}245246uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type)247{248static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};249uint8_t fake_codec_data_wma3[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0};250const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;251struct FAudioWMADEC *impl;252MFT_OUTPUT_STREAM_INFO info = {0};253IMFMediaBuffer *media_buffer;254IMFMediaType *media_type;255IMFTransform *decoder;256HRESULT hr;257UINT32 i, value;258GUID guid;259260LOG_FUNC_ENTER(voice->audio)261262if (!(impl = voice->audio->pMalloc(sizeof(*impl)))) return -1;263FAudio_memset(impl, 0, sizeof(*impl));264265hr = CoCreateInstance(266&CLSID_CWMADecMediaObject,2670,268CLSCTX_INPROC_SERVER,269&IID_IMFTransform,270(void **)&decoder271);272if (FAILED(hr))273{274voice->audio->pFree(impl->output_buf);275return -2;276}277278hr = MFCreateMediaType(&media_type);279FAudio_assert(!FAILED(hr) && "Failed create media type!");280hr = IMFMediaType_SetGUID(281media_type,282&MF_MT_MAJOR_TYPE,283&MFMediaType_Audio284);285FAudio_assert(!FAILED(hr) && "Failed set media major type!");286287switch (type)288{289case FAUDIO_FORMAT_WMAUDIO2:290hr = IMFMediaType_SetBlob(291media_type,292&MF_MT_USER_DATA,293(void *)fake_codec_data,294sizeof(fake_codec_data)295);296FAudio_assert(!FAILED(hr) && "Failed set codec private data!");297hr = IMFMediaType_SetGUID(298media_type,299&MF_MT_SUBTYPE,300&MFAudioFormat_WMAudioV8301);302FAudio_assert(!FAILED(hr) && "Failed set media sub type!");303hr = IMFMediaType_SetUINT32(304media_type,305&MF_MT_AUDIO_BLOCK_ALIGNMENT,306wfx->Format.nBlockAlign307);308FAudio_assert(!FAILED(hr) && "Failed set input block align!");309break;310case FAUDIO_FORMAT_WMAUDIO3:311*(uint16_t *)fake_codec_data_wma3 = voice->src.format->wBitsPerSample;312for (i = 0; i < voice->src.format->nChannels; i++)313{314fake_codec_data_wma3[2] <<= 1;315fake_codec_data_wma3[2] |= 1;316}317hr = IMFMediaType_SetBlob(318media_type,319&MF_MT_USER_DATA,320(void *)fake_codec_data_wma3,321sizeof(fake_codec_data_wma3)322);323FAudio_assert(!FAILED(hr) && "Failed set codec private data!");324hr = IMFMediaType_SetGUID(325media_type,326&MF_MT_SUBTYPE,327&MFAudioFormat_WMAudioV9328);329FAudio_assert(!FAILED(hr) && "Failed set media sub type!");330hr = IMFMediaType_SetUINT32(331media_type,332&MF_MT_AUDIO_BLOCK_ALIGNMENT,333wfx->Format.nBlockAlign334);335FAudio_assert(!FAILED(hr) && "Failed set input block align!");336break;337case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:338hr = IMFMediaType_SetBlob(339media_type,340&MF_MT_USER_DATA,341(void *)&wfx->Samples,342wfx->Format.cbSize343);344FAudio_assert(!FAILED(hr) && "Failed set codec private data!");345hr = IMFMediaType_SetGUID(346media_type,347&MF_MT_SUBTYPE,348&MFAudioFormat_WMAudio_Lossless349);350FAudio_assert(!FAILED(hr) && "Failed set media sub type!");351hr = IMFMediaType_SetUINT32(352media_type,353&MF_MT_AUDIO_BLOCK_ALIGNMENT,354wfx->Format.nBlockAlign355);356FAudio_assert(!FAILED(hr) && "Failed set input block align!");357break;358case FAUDIO_FORMAT_XMAUDIO2:359{360const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;361hr = IMFMediaType_SetBlob(362media_type,363&MF_MT_USER_DATA,364(void *)&wfx->Samples,365wfx->Format.cbSize366);367FAudio_assert(!FAILED(hr) && "Failed set codec private data!");368hr = IMFMediaType_SetGUID(369media_type,370&MF_MT_SUBTYPE,371&MFAudioFormat_XMAudio2372);373FAudio_assert(!FAILED(hr) && "Failed set media sub type!");374hr = IMFMediaType_SetUINT32(375media_type,376&MF_MT_AUDIO_BLOCK_ALIGNMENT,377xwf->dwBytesPerBlock378);379FAudio_assert(!FAILED(hr) && "Failed set input block align!");380break;381}382default:383FAudio_assert(0 && "Unsupported type!");384break;385}386387hr = IMFMediaType_SetUINT32(388media_type,389&MF_MT_AUDIO_BITS_PER_SAMPLE,390wfx->Format.wBitsPerSample391);392FAudio_assert(!FAILED(hr) && "Failed set input bits per sample!");393hr = IMFMediaType_SetUINT32(394media_type,395&MF_MT_AUDIO_AVG_BYTES_PER_SECOND,396wfx->Format.nAvgBytesPerSec397);398FAudio_assert(!FAILED(hr) && "Failed set input bytes per sample!");399hr = IMFMediaType_SetUINT32(400media_type,401&MF_MT_AUDIO_NUM_CHANNELS,402wfx->Format.nChannels403);404FAudio_assert(!FAILED(hr) && "Failed set input channel count!");405hr = IMFMediaType_SetUINT32(406media_type,407&MF_MT_AUDIO_SAMPLES_PER_SECOND,408wfx->Format.nSamplesPerSec409);410FAudio_assert(!FAILED(hr) && "Failed set input sample rate!");411412hr = IMFTransform_SetInputType(413decoder,4140,415media_type,4160417);418FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");419IMFMediaType_Release(media_type);420421i = 0;422while (SUCCEEDED(hr))423{424hr = IMFTransform_GetOutputAvailableType(425decoder,4260,427i++,428&media_type429);430FAudio_assert(!FAILED(hr) && "Failed get output media type!");431432hr = IMFMediaType_GetGUID(433media_type,434&MF_MT_MAJOR_TYPE,435&guid436);437FAudio_assert(!FAILED(hr) && "Failed get media major type!");438if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;439440hr = IMFMediaType_GetGUID(441media_type,442&MF_MT_SUBTYPE,443&guid444);445FAudio_assert(!FAILED(hr) && "Failed get media major type!");446if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;447448hr = IMFMediaType_GetUINT32(449media_type,450&MF_MT_AUDIO_BITS_PER_SAMPLE,451&value452);453if (FAILED(hr))454{455value = 32;456hr = IMFMediaType_SetUINT32(457media_type,458&MF_MT_AUDIO_BITS_PER_SAMPLE,459value460);461}462FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");463if (value != 32) goto next;464465hr = IMFMediaType_GetUINT32(466media_type,467&MF_MT_AUDIO_NUM_CHANNELS,468&value469);470if (FAILED(hr))471{472value = wfx->Format.nChannels;473hr = IMFMediaType_SetUINT32(474media_type,475&MF_MT_AUDIO_NUM_CHANNELS,476value477);478}479FAudio_assert(!FAILED(hr) && "Failed get channel count!");480if (value != wfx->Format.nChannels) goto next;481482hr = IMFMediaType_GetUINT32(483media_type,484&MF_MT_AUDIO_SAMPLES_PER_SECOND,485&value486);487if (FAILED(hr))488{489value = wfx->Format.nSamplesPerSec;490hr = IMFMediaType_SetUINT32(491media_type,492&MF_MT_AUDIO_SAMPLES_PER_SECOND,493value494);495}496FAudio_assert(!FAILED(hr) && "Failed get sample rate!");497if (value != wfx->Format.nSamplesPerSec) goto next;498499hr = IMFMediaType_GetUINT32(500media_type,501&MF_MT_AUDIO_BLOCK_ALIGNMENT,502&value503);504if (FAILED(hr))505{506value = wfx->Format.nChannels * sizeof(float);507hr = IMFMediaType_SetUINT32(508media_type,509&MF_MT_AUDIO_BLOCK_ALIGNMENT,510value511);512}513FAudio_assert(!FAILED(hr) && "Failed get block align!");514if (value == wfx->Format.nChannels * sizeof(float)) break;515516next:517IMFMediaType_Release(media_type);518}519FAudio_assert(!FAILED(hr) && "Failed to find output media type!");520hr = IMFTransform_SetOutputType(decoder, 0, media_type, 0);521FAudio_assert(!FAILED(hr) && "Failed set decoder output type!");522IMFMediaType_Release(media_type);523524hr = IMFTransform_GetOutputStreamInfo(decoder, 0, &info);525FAudio_assert(!FAILED(hr) && "Failed to get output stream info!");526527impl->decoder = decoder;528if (!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))529{530hr = MFCreateSample(&impl->output_sample);531FAudio_assert(!FAILED(hr) && "Failed to create sample!");532hr = MFCreateMemoryBuffer(info.cbSize, &media_buffer);533FAudio_assert(!FAILED(hr) && "Failed to create buffer!");534hr = IMFSample_AddBuffer(impl->output_sample, media_buffer);535FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");536IMFMediaBuffer_Release(media_buffer);537}538539hr = IMFTransform_ProcessMessage(540decoder,541MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,5420543);544FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");545546voice->src.wmadec = impl;547voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;548549LOG_FUNC_EXIT(voice->audio);550return 0;551}552553void FAudio_WMADEC_free(FAudioSourceVoice *voice)554{555struct FAudioWMADEC *impl = voice->src.wmadec;556HRESULT hr;557558LOG_FUNC_ENTER(voice->audio)559FAudio_PlatformLockMutex(voice->audio->sourceLock);560LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)561562if (impl->input_size)563{564LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);565hr = IMFTransform_ProcessMessage(566impl->decoder,567MFT_MESSAGE_NOTIFY_END_OF_STREAM,5680569);570FAudio_assert(!FAILED(hr) && "Failed to send EOS!");571impl->input_size = 0;572}573if (impl->output_pos)574{575LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);576hr = IMFTransform_ProcessMessage(577impl->decoder,578MFT_MESSAGE_COMMAND_DRAIN,5790580);581FAudio_assert(!FAILED(hr) && "Failed to send DRAIN!");582impl->output_pos = 0;583}584585if (impl->output_sample) IMFSample_Release(impl->output_sample);586IMFTransform_Release(impl->decoder);587voice->audio->pFree(impl->output_buf);588voice->audio->pFree(voice->src.wmadec);589voice->src.wmadec = NULL;590voice->src.decode = NULL;591592FAudio_PlatformUnlockMutex(voice->audio->sourceLock);593LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)594LOG_FUNC_EXIT(voice->audio)595}596597void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)598{599struct FAudioWMADEC *impl = voice->src.wmadec;600HRESULT hr;601602LOG_FUNC_ENTER(voice->audio)603604if (impl->input_size)605{606LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);607hr = IMFTransform_ProcessMessage(608impl->decoder,609MFT_MESSAGE_NOTIFY_END_OF_STREAM,6100611);612FAudio_assert(!FAILED(hr) && "Failed to send EOS!");613impl->input_size = 0;614}615impl->output_pos = 0;616impl->input_pos = 0;617618LOG_FUNC_EXIT(voice->audio)619}620621#endif /* HAVE_WMADEC */622623624