QWORD MMDeviceAudioSource::GetTimestamp(QWORD qpcTimestamp) { QWORD newTimestamp; if(bIsMic) { newTimestamp = (bUseQPC) ? qpcTimestamp : App->GetAudioTime(); newTimestamp += GetTimeOffset(); //Log(TEXT("got some mic audio, timestamp: %llu"), newTimestamp); return newTimestamp; } else { //we're doing all these checks because device timestamps are only reliable "sometimes" if(!bFirstFrameReceived) { QWORD curTime = GetQPCTimeMS(); newTimestamp = qpcTimestamp; curVideoTime = lastVideoTime = App->GetVideoTime(); if(bUseVideoTime || newTimestamp < (curTime-App->bufferingTime) || newTimestamp > (curTime+2000)) { if(!bUseVideoTime) Log(TEXT("Bad timestamp detected, syncing audio to video time")); else Log(TEXT("Syncing audio to video time (WARNING: you should not be doing this if you are just having webcam desync, that's a separate issue)")); SetTimeOffset(GetTimeOffset()-int(lastVideoTime-App->GetSceneTimestamp())); bUseVideoTime = true; newTimestamp = lastVideoTime+GetTimeOffset(); } bFirstFrameReceived = true; } else { QWORD newVideoTime = App->GetVideoTime(); if(newVideoTime != lastVideoTime) curVideoTime = lastVideoTime = newVideoTime; else curVideoTime += 10; newTimestamp = (bUseVideoTime) ? curVideoTime : qpcTimestamp; newTimestamp += GetTimeOffset(); } //Log(TEXT("qpc timestamp: %llu, lastUsed: %llu, dif: %llu"), newTimestamp, lastUsedTimestamp, difVal); return newTimestamp; } }
void CDmeTimeFrame::SetTimeScale( float flScale, DmeTime_t scaleCenter, bool bChangeDuration ) { #ifdef _DEBUG DmeTime_t preCenterTime = ToChildMediaTime( scaleCenter, false ); #endif float ratio = m_Scale / flScale; int t = scaleCenter.GetTenthsOfMS() - m_Start; if ( bChangeDuration ) { int newDuration = int( m_Duration * ratio ); if ( scaleCenter.GetTenthsOfMS() != m_Start ) { int newStart = int( ( m_Start - scaleCenter.GetTenthsOfMS() ) * ratio + scaleCenter.GetTenthsOfMS() ); SetStartTime( DmeTime_t( newStart ) ); } int newStart = m_Start; int newOffset = int( ( t + m_Offset ) * ratio + newStart - scaleCenter.GetTenthsOfMS() ); SetTimeOffset( DmeTime_t( newOffset ) ); SetDuration( DmeTime_t( newDuration ) ); } else { int newOffset = int( ( t + m_Offset ) * ratio - t ); SetTimeOffset( DmeTime_t( newOffset ) ); } SetTimeScale( flScale ); #ifdef _DEBUG DmeTime_t postCenterTime = ToChildMediaTime( scaleCenter, false ); Assert( abs( preCenterTime - postCenterTime ) <= DMETIME_MINDELTA ); #endif }
bool MMDeviceAudioSource::Initialize(bool bMic, CTSTR lpID) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDeviceEnumerator = %08lX"), (BOOL)bMic, err); return false; } bIsMic = bMic; if (bIsMic) { BOOL bMicSyncFixHack = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicSyncFixHack")); angerThreshold = bMicSyncFixHack ? 40 : 1000; } if (scmpi(lpID, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(bMic ? eCapture : eRender, bMic ? eCommunications : eConsole, &mmDevice); else err = mmEnumerator->GetDevice(lpID, &mmDevice); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDevice = %08lX"), (BOOL)bMic, err); return false; } err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IAudioClient = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // get name IPropertyStore *store; if(SUCCEEDED(mmDevice->OpenPropertyStore(STGM_READ, &store))) { PROPVARIANT varName; PropVariantInit(&varName); if(SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) { CWSTR wstrName = varName.pwszVal; strDeviceName = wstrName; } store->Release(); } if(bMic) { Log(TEXT("------------------------------------------")); Log(TEXT("Using auxilary audio input: %s"), GetDeviceName()); bUseQPC = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicQPC")) != 0; if (bUseQPC) Log(TEXT("Using Mic QPC timestamps")); } else { Log(TEXT("------------------------------------------")); Log(TEXT("Using desktop audio input: %s"), GetDeviceName()); bUseVideoTime = AppConfig->GetInt(TEXT("Audio"), TEXT("SyncToVideoTime")) != 0; SetTimeOffset(GlobalConfig->GetInt(TEXT("Audio"), TEXT("GlobalAudioTimeAdjust"))); } //----------------------------------------------------------------- // get format WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get mix format from audio client = %08lX"), (BOOL)bMic, err); return false; } bool bFloat; UINT inputChannels; UINT inputSamplesPerSec; UINT inputBitsPerSample; UINT inputBlockSize; DWORD inputChannelMask = 0; WAVEFORMATEXTENSIBLE *wfext = NULL; //the internal audio engine should always use floats (or so I read), but I suppose just to be safe better check if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { wfext = (WAVEFORMATEXTENSIBLE*)pwfx; inputChannelMask = wfext->dwChannelMask; if(wfext->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } } else if(pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } bFloat = true; inputChannels = pwfx->nChannels; inputBitsPerSample = 32; inputBlockSize = pwfx->nBlockAlign; inputSamplesPerSec = pwfx->nSamplesPerSec; sampleWindowSize = (inputSamplesPerSec/100); DWORD flags = bMic ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); //err = AUDCLNT_E_UNSUPPORTED_FORMAT; if (err == AUDCLNT_E_UNSUPPORTED_FORMAT) { //workaround for razer kraken headset (bad drivers) pwfx->nBlockAlign = 2*pwfx->nChannels; pwfx->nAvgBytesPerSec = inputSamplesPerSec*pwfx->nBlockAlign; pwfx->wBitsPerSample = 16; wfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfext->Samples.wValidBitsPerSample = 16; bConvert = true; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); } if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initialize audio client, result = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // acquire services err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture client, result = %08lX"), (BOOL)bMic, err); return false; } err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture clock, result = %08lX"), (BOOL)bMic, err); return false; } CoTaskMemFree(pwfx); //----------------------------------------------------------------- InitAudioData(bFloat, inputChannels, inputSamplesPerSec, inputBitsPerSample, inputBlockSize, inputChannelMask); return true; }
QWORD MMDeviceAudioSource::GetTimestamp(QWORD qpcTimestamp) { QWORD newTimestamp; if(bIsMic) { newTimestamp = (bUseQPC) ? qpcTimestamp : App->GetAudioTime(); newTimestamp += GetTimeOffset(); //Log(TEXT("got some mic audio, timestamp: %llu"), newTimestamp); return newTimestamp; } else { //we're doing all these checks because device timestamps are only reliable "sometimes" if(!bFirstFrameReceived) { QWORD curTime = GetQPCTimeMS(); newTimestamp = qpcTimestamp; curVideoTime = lastVideoTime = App->GetVideoTime(); if(bUseVideoTime || newTimestamp < (curTime-App->bufferingTime) || newTimestamp > (curTime+2000)) { if(!bUseVideoTime) Log(TEXT("Bad timestamp detected, syncing audio to video time")); else Log(TEXT("Syncing audio to video time (WARNING: you should not be doing this if you are just having webcam desync, that's a separate issue)")); SetTimeOffset(GetTimeOffset()-int(lastVideoTime-App->GetSceneTimestamp())); bUseVideoTime = true; newTimestamp = lastVideoTime+GetTimeOffset(); } lastUsedTimestamp = newTimestamp; bFirstFrameReceived = true; } else { QWORD newVideoTime = App->GetVideoTime(); if(newVideoTime != lastVideoTime) curVideoTime = lastVideoTime = newVideoTime; else curVideoTime += 10; newTimestamp = (bUseVideoTime) ? curVideoTime : qpcTimestamp; newTimestamp += GetTimeOffset(); lastUsedTimestamp += 10; } //------------------------------------------------------ // timestamp smoothing QWORD difVal = GetQWDif(newTimestamp, lastUsedTimestamp); //Log(TEXT("qpc timestamp: %llu, lastUsed: %llu, dif: %llu"), newTimestamp, lastUsedTimestamp, difVal); if (difVal > 70) { /*QWORD curTimeMS = App->GetVideoTime()-App->GetSceneTimestamp(); UINT curTimeTotalSec = (UINT)(curTimeMS/1000); UINT curTimeTotalMin = curTimeTotalSec/60; UINT curTimeHr = curTimeTotalMin/60; UINT curTimeMin = curTimeTotalMin-(curTimeHr*60); UINT curTimeSec = curTimeTotalSec-(curTimeTotalMin*60); Log(TEXT("A timestamp adjustment was encountered for device %s, approximate stream time is: %u:%u:%u, prev value: %llu, new value: %llu"), GetDeviceName(), curTimeHr, curTimeMin, curTimeSec, lastUsedTimestamp, newTimestamp);*/ lastUsedTimestamp = newTimestamp; } //Log(TEXT("got some desktop audio, timestamp: %llu"), lastUsedTimestamp); return lastUsedTimestamp; } }