// Format negotiation HRESULT CTimeStretchFilter::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; #ifdef INTEGER_SAMPLES // only accept 16bit int if (pwfx->Format.wBitsPerSample != 16 || pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_PCM) return VFW_E_TYPE_NOT_ACCEPTED; #else // only accept 32bit float if (pwfx->Format.wBitsPerSample != 32 || pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) return VFW_E_TYPE_NOT_ACCEPTED; #endif if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } bool bApplyChanges = (nApplyChangesDepth != 0); if (nApplyChangesDepth != INFINITE && nApplyChangesDepth > 0) nApplyChangesDepth--; HRESULT hr = m_pNextSink->NegotiateFormat(pwfx, nApplyChangesDepth, pChOrder); if (FAILED(hr)) return hr; hr = VFW_E_CANNOT_CONNECT; if (!pwfx) return SetFormat(NULL); if (bApplyChanges) { LogWaveFormat(pwfx, "TS - applying "); AM_MEDIA_TYPE tmp; HRESULT result = CreateAudioMediaType((WAVEFORMATEX*)pwfx, &tmp, true); if (SUCCEEDED(result)) { if (m_pMediaType) DeleteMediaType(m_pMediaType); m_pMediaType = CreateMediaType(&tmp); } SetInputFormat(pwfx); SetOutputFormat(pwfx); SetFormat(pwfx); } else LogWaveFormat(pwfx, "TS - "); m_chOrder = *pChOrder; return S_OK; }
HRESULT CTimeStretchFilter::CheckSample(IMediaSample* pSample) { if (!pSample) return S_OK; AM_MEDIA_TYPE *pmt = NULL; bool bFormatChanged = false; HRESULT hr = S_OK; if (SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt) bFormatChanged = !FormatsEqual((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, m_pInputFormat); if (bFormatChanged) { uint unprocessedSamplesBefore = numUnprocessedSamples(); uint zeros = flushEx(); uint unprocessedSamplesAfter = numUnprocessedSamples(); UINT32 outFramesAfter = numSamples(); UINT32 totalSamples = zeros + unprocessedSamplesBefore; UINT32 totalProcessedSamples = totalSamples - unprocessedSamplesAfter; //double bias = (double)totalProcessedSamples / (double)outFramesAfter; REFERENCE_TIME estimatedSampleDuration = totalProcessedSamples * UNITS / m_pOutputFormat->Format.nSamplesPerSec; double bias = m_pClock->GetBias(); double adjustment = m_pClock->Adjustment(); double AVMult = m_pClock->SuggestedAudioMultiplier(estimatedSampleDuration, bias, adjustment); setTempoInternal(AVMult, 1.0); CreateOutput(totalProcessedSamples, outFramesAfter, bias, adjustment, AVMult, true); // Empty SoundTouch's buffers clear(); // Apply format change ChannelOrder chOrder; hr = NegotiateFormat((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, 1, &chOrder); pSample->SetDiscontinuity(false); if (FAILED(hr)) { DeleteMediaType(pmt); Log("CTimeStretchFilter::CheckFormat failed to change format: 0x%08x", hr); return hr; } else { m_chOrder = chOrder; return S_FALSE; // format changed } } return S_OK; }
HRESULT CTimeStretchFilter::CheckSample(IMediaSample* pSample, REFERENCE_TIME* rtDrained) { if (!pSample) return S_OK; AM_MEDIA_TYPE *pmt = NULL; bool bFormatChanged = false; HRESULT hr = S_OK; if (SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt) bFormatChanged = !FormatsEqual((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, m_pInputFormat); if (bFormatChanged) { REFERENCE_TIME rtStart = 0; REFERENCE_TIME rtStop = 0; HRESULT hr = pSample->GetTime(&rtStart, &rtStop); if (SUCCEEDED(hr)) { Log("CTimeStretchFilter - CheckSample - resyncing in middle of stream on format change - rtStart: %6.3f m_rtInSampleTime: %6.3f", rtStart / 10000000.0, m_rtInSampleTime / 10000000.0); *rtDrained = DrainBuffers(pSample, rtStart); } else Log("CTimeStretchFilter::CheckFormat failed to get timestamps from sample: 0x%08x", hr); // Apply format change ChannelOrder chOrder; hr = NegotiateFormat((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, 1, &chOrder); pSample->SetDiscontinuity(false); if (FAILED(hr)) { DeleteMediaType(pmt); Log("CTimeStretchFilter::CheckFormat failed to change format: 0x%08x", hr); return hr; } else { m_chOrder = chOrder; return S_FALSE; // format changed } } return S_OK; }
HRESULT CWASAPIRenderFilter::CheckSample(IMediaSample* pSample, UINT32 framesToFlush) { if (!pSample) return S_OK; AM_MEDIA_TYPE *pmt = NULL; bool bFormatChanged = false; HRESULT hr = S_OK; if (SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt) bFormatChanged = !FormatsEqual((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, m_pInputFormat); if (bFormatChanged) { // Release outstanding buffer hr = m_pRenderClient->ReleaseBuffer(framesToFlush, 0); if (FAILED(hr)) Log("CWASAPIRenderFilter::CheckFormat - ReleaseBuffer: 0x%08x", hr); // Apply format change ChannelOrder chOrder; hr = NegotiateFormat((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, 1, &chOrder); pSample->SetDiscontinuity(false); if (FAILED(hr)) { DeleteMediaType(pmt); Log("CWASAPIRenderFilter::CheckFormat failed to change format: 0x%08x", hr); return hr; } else { m_chOrder = chOrder; return S_FALSE; } } else if (pSample->IsDiscontinuity() == S_OK) { hr = m_pRenderClient->ReleaseBuffer(framesToFlush, 0); if (FAILED(hr)) Log("CWASAPIRenderFilter::CheckFormat - discontinuity - ReleaseBuffer: 0x%08x", hr); pSample->SetDiscontinuity(false); m_nSampleNum = 0; return S_FALSE; } return S_OK; }
HRESULT CChannelMixer::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } if (!m_pNextSink) return VFW_E_TYPE_NOT_ACCEPTED; bool bApplyChanges = (nApplyChangesDepth != 0); if (nApplyChangesDepth != INFINITE && nApplyChangesDepth > 0) nApplyChangesDepth--; if (pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) return VFW_E_TYPE_NOT_ACCEPTED; HRESULT hr = S_OK; bool expandToStereo = pwfx->Format.nChannels == 1 && m_pSettings->m_bExpandMonoToStereo; if (!m_pSettings->m_bForceChannelMixing && !expandToStereo) { // try the format directly hr = m_pNextSink->NegotiateFormat(pwfx, nApplyChangesDepth, pChOrder); if (SUCCEEDED(hr)) { if (bApplyChanges) { SetInputFormat(pwfx); SetOutputFormat(pwfx); m_bPassThrough = false; hr = SetupConversion(*pChOrder); } m_chOrder = *pChOrder; return hr; } } WAVEFORMATEXTENSIBLE* pOutWfx; CopyWaveFormatEx(&pOutWfx, pwfx); if (!expandToStereo || m_pSettings->m_bForceChannelMixing) { pOutWfx->dwChannelMask = m_pSettings->m_lSpeakerConfig; pOutWfx->Format.nChannels = m_pSettings->m_lSpeakerCount; } else // Expand mono to stereo { pOutWfx->dwChannelMask = KSAUDIO_SPEAKER_STEREO; pOutWfx->Format.nChannels = 2; } pOutWfx->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; pOutWfx->Format.nBlockAlign = pOutWfx->Format.wBitsPerSample / 8 * pOutWfx->Format.nChannels; pOutWfx->Format.nAvgBytesPerSec = pOutWfx->Format.nBlockAlign * pOutWfx->Format.nSamplesPerSec; hr = m_pNextSink->NegotiateFormat(pOutWfx, nApplyChangesDepth, pChOrder); m_chOrder = *pChOrder; if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pOutWfx); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "MIX - applying "); m_bPassThrough = false; SetInputFormat(pwfx); SetOutputFormat(pOutWfx, true); hr = SetupConversion(*pChOrder); } else { LogWaveFormat(pwfx, "MIX - "); SAFE_DELETE_WAVEFORMATEX(pOutWfx); } return hr; }
// Processing HRESULT CChannelMixer::PutSample(IMediaSample *pSample) { if (!pSample) return S_OK; AM_MEDIA_TYPE *pmt = NULL; bool bFormatChanged = false; HRESULT hr = S_OK; if (SUCCEEDED(pSample->GetMediaType(&pmt)) && pmt) bFormatChanged = !FormatsEqual((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, m_pInputFormat); if (pSample->IsDiscontinuity() == S_OK) m_bDiscontinuity = true; CAutoLock lock (&m_csOutputSample); if (m_bFlushing) return S_OK; if (bFormatChanged) { // Process any remaining input if (!m_bPassThrough) hr = ProcessData(NULL, 0, NULL); // Apply format change locally, // next filter will evaluate the format change when it receives the sample Log("CChannelMixer::PutSample: Processing format change"); ChannelOrder chOrder; hr = NegotiateFormat((WAVEFORMATEXTENSIBLE*)pmt->pbFormat, 1, &chOrder); if (FAILED(hr)) { DeleteMediaType(pmt); Log("CChannelMixer: PutSample failed to change format: 0x%08x", hr); return hr; } m_chOrder = chOrder; } if (pmt) DeleteMediaType(pmt); if (m_bPassThrough) { if (m_pNextSink) return m_pNextSink->PutSample(pSample); return S_OK; // perhaps we should return S_FALSE to indicate sample was dropped } long nOffset = 0; long cbSampleData = pSample->GetActualDataLength(); BYTE *pData = NULL; REFERENCE_TIME rtStop = 0; REFERENCE_TIME rtStart = 0; pSample->GetTime(&rtStart, &rtStop); // Detect discontinuity in stream timeline if ((abs(m_rtNextIncomingSampleTime - rtStart) > MAX_SAMPLE_TIME_ERROR) && m_nSampleNum != 0) { Log("CChannelMixer - stream discontinuity: %6.3f", (rtStart - m_rtNextIncomingSampleTime) / 10000000.0); m_rtInSampleTime = rtStart; if (m_nSampleNum > 0) { Log("CChannelMixer - using buffered sample data"); FlushStream(); } else Log("CChannelMixer - discarding buffered sample data"); } if (m_nSampleNum == 0) m_rtInSampleTime = rtStart; UINT nFrames = cbSampleData / m_pInputFormat->Format.nBlockAlign; REFERENCE_TIME duration = nFrames * UNITS / m_pInputFormat->Format.nSamplesPerSec; m_rtNextIncomingSampleTime = rtStart + duration; m_nSampleNum++; hr = pSample->GetPointer(&pData); ASSERT(pData); if (FAILED(hr)) { Log("CChannelMixer::PutSample - failed to get sample's data pointer: 0x%08x", hr); return hr; } while (nOffset < cbSampleData && SUCCEEDED(hr)) { long cbProcessed = 0; hr = ProcessData(pData + nOffset, cbSampleData - nOffset, &cbProcessed); nOffset += cbProcessed; } return hr; }
HRESULT CSampleRateConverter::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } if (!m_pNextSink) return VFW_E_TYPE_NOT_ACCEPTED; bool bApplyChanges = (nApplyChangesDepth != 0); if (nApplyChangesDepth != INFINITE && nApplyChangesDepth > 0) nApplyChangesDepth--; // Try passthrough HRESULT hr = m_pNextSink->NegotiateFormat(pwfx, nApplyChangesDepth, pChOrder); if (SUCCEEDED(hr)) { if (bApplyChanges) { m_bPassThrough = true; SetInputFormat(pwfx); SetOutputFormat(pwfx); } m_chOrder = *pChOrder; return hr; } if (pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) return VFW_E_TYPE_NOT_ACCEPTED; WAVEFORMATEXTENSIBLE* pOutWfx; CopyWaveFormatEx(&pOutWfx, pwfx); pOutWfx->Format.nSamplesPerSec = 0; hr = VFW_E_TYPE_NOT_ACCEPTED; const unsigned int sampleRateCount = sizeof(gAllowedSampleRates) / sizeof(int); unsigned int startPoint = 0; // TODO test duplicate sample rates first // Search for the input sample rate in sample rate array bool foundSampleRate = false; for (unsigned int i = 0; i < sampleRateCount && !foundSampleRate; i++) { if (gAllowedSampleRates[i] == pwfx->Format.nSamplesPerSec) { startPoint = ++i; // select closest sample rate in ascending order foundSampleRate = true; } } if (!foundSampleRate) Log("CSampleRateConverter::NegotiateFormat - sample rate (%d) not found in the source array", pwfx->Format.nSamplesPerSec); unsigned int sampleRatesTested = 0; for (int i = startPoint; FAILED(hr) && pOutWfx->Format.nSamplesPerSec == 0 && sampleRatesTested < sampleRateCount; i++) { if (pOutWfx->Format.nSamplesPerSec == pwfx->Format.nSamplesPerSec) { sampleRatesTested++; continue; // skip if same as source } pOutWfx->Format.nSamplesPerSec = gAllowedSampleRates[i]; pOutWfx->Format.nAvgBytesPerSec = gAllowedSampleRates[i] * pOutWfx->Format.nBlockAlign; hr = m_pNextSink->NegotiateFormat(pOutWfx, nApplyChangesDepth, pChOrder); sampleRatesTested++; if (FAILED(hr)) pOutWfx->Format.nSamplesPerSec = 0; // Search from the lower end if (i == sampleRateCount - 1) i = 0; } if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pOutWfx); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "SRC - applying "); m_bPassThrough = false; SetInputFormat(pwfx); SetOutputFormat(pOutWfx, true); hr = SetupConversion(); // TODO: do something meaningfull if SetupConversion fails //if (FAILED(hr)) } else { LogWaveFormat(pwfx, "SRC - "); SAFE_DELETE_WAVEFORMATEX(pOutWfx); } m_chOrder = *pChOrder; return S_OK; }
// Format negotiation HRESULT CWASAPIRenderFilter::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } bool bApplyChanges = nApplyChangesDepth != 0; bool bitDepthForced = (m_pSettings->m_nForceBitDepth != 0 && m_pSettings->m_nForceBitDepth != pwfx->Format.wBitsPerSample); bool sampleRateForced = (m_pSettings->m_nForceSamplingRate != 0 && m_pSettings->m_nForceSamplingRate != pwfx->Format.nSamplesPerSec); if ((bitDepthForced || sampleRateForced) && pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL || pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT && bitDepthForced) return VFW_E_TYPE_NOT_ACCEPTED; if (((bitDepthForced && m_pSettings->m_nForceBitDepth != pwfx->Format.wBitsPerSample) || (sampleRateForced && m_pSettings->m_nForceSamplingRate != pwfx->Format.nSamplesPerSec))) return VFW_E_TYPE_NOT_ACCEPTED; CAutoLock lock(&m_csResources); HRESULT hr = CreateAudioClient(); if (FAILED(hr)) { Log("CWASAPIRenderFilter::NegotiateFormat Error, audio client not initialized: (0x%08x)", hr); return VFW_E_CANNOT_CONNECT; } WAVEFORMATEXTENSIBLE* pwfxAccepted = NULL; hr = IsFormatSupported(pwfx, &pwfxAccepted); if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "REN - applying "); // Stop and discard audio client StopAudioClient(); SAFE_RELEASE(m_pRenderClient); SAFE_RELEASE(m_pAudioClock); SAFE_RELEASE(m_pAudioClient); // We must use incoming format so the WAVEFORMATEXTENSIBLE to WAVEFORMATEXT difference // that some audio drivers require is not causing an infonite loop of format changes SetInputFormat(pwfx); // Reinitialize audio client hr = CreateAudioClient(true); } else LogWaveFormat(pwfx, "Input format "); m_chOrder = *pChOrder = DS_ORDER; SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; }