/* * Copyright (C) 2010-2012 Hendrik Leppkes * http://www.1f0.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "stdafx.h" #include "LAVAudio.h" #include "PostProcessor.h" #include #include #include "moreuuids.h" #include "DShowUtil.h" #include "AudioSettingsProp.h" #include "registry.h" #include "DeCSS/DeCSSInputPin.h" extern "C" { #include "libavformat/spdif.h" extern void ff_rm_reorder_sipr_data(uint8_t *buf, int sub_packet_h, int framesize); __declspec(dllimport) extern const unsigned char ff_sipr_subpk_size[4]; }; #ifdef DEBUG #include "lavf_log.h" #endif extern HINSTANCE g_hInst; // Constructor CLAVAudio::CLAVAudio(LPUNKNOWN pUnk, HRESULT* phr) : CTransformFilter(NAME("lavc audio decoder"), 0, __uuidof(CLAVAudio)) , m_nCodecId(AV_CODEC_ID_NONE) , m_pAVCodec(NULL) , m_pAVCtx(NULL) , m_pFrame(NULL) , m_bDiscontinuity(FALSE) , m_rtStart(0) , m_dStartOffset(0.0) , m_DecodeFormat(SampleFormat_16) , m_pFFBuffer(NULL) , m_nFFBufferSize(0) , m_bRuntimeConfig(FALSE) , m_bVolumeStats(FALSE) , m_pParser(NULL) , m_bQueueResync(FALSE) , m_avioBitstream(NULL) , m_avBSContext(NULL) , m_bDTSHD(FALSE) , m_bUpdateTimeCache(TRUE) , m_rtStartInputCache(AV_NOPTS_VALUE) , m_rtStopInputCache(AV_NOPTS_VALUE) , m_rtBitstreamCache(AV_NOPTS_VALUE) , m_faJitter(50) , m_JitterLimit(MAX_JITTER_DESYNC) , m_hDllExtraDecoder(NULL) , m_pDTSDecoderContext(NULL) , m_DecodeLayout(0) , m_DecodeLayoutSanified(0) , m_bChannelMappingRequired(FALSE) , m_bFindDTSInPCM(FALSE) , m_bFallback16Int(FALSE) , m_dwOverrideMixer(0) , m_bNeedSyncpoint(FALSE) , m_dRate(1.0) , m_bInputPadded(FALSE) , m_avrContext(NULL) , m_bAVResampleFailed(FALSE) , m_bMixingSettingsChanged(FALSE) , m_fMixingClipThreshold(1.0f) , m_bHasVideo(TRUE) , m_dwRemixLayout(0) { #ifdef DEBUG DbgSetModuleLevel (LOG_CUSTOM1, DWORD_MAX); // FFMPEG messages use custom1 av_log_set_callback(lavf_log_callback); #else av_log_set_callback(NULL); #endif av_register_all(); m_pInput = new CDeCSSTransformInputPin(TEXT("CDeCSSTransformInputPin"), this, phr, L"Input"); if(!m_pInput) { *phr = E_OUTOFMEMORY; } if (FAILED(*phr)) { return; } m_pOutput = new CTransformOutputPin(NAME("CTransformOutputPin"), this, phr, L"Output"); if(!m_pOutput) { *phr = E_OUTOFMEMORY; } if(FAILED(*phr)) { SAFE_DELETE(m_pInput); return; } m_bSampleSupport[SampleFormat_U8] = TRUE; m_bSampleSupport[SampleFormat_16] = TRUE; m_bSampleSupport[SampleFormat_24] = TRUE; m_bSampleSupport[SampleFormat_32] = TRUE; m_bSampleSupport[SampleFormat_FP32] = TRUE; LoadSettings(); InitBitstreaming(); #ifdef DEBUG DbgSetModuleLevel (LOG_ERROR, DWORD_MAX); DbgSetModuleLevel (LOG_TRACE, DWORD_MAX); //DbgSetModuleLevel (LOG_CUSTOM2, DWORD_MAX); // Jitter statistics //DbgSetModuleLevel (LOG_CUSTOM5, DWORD_MAX); // Extensive timing options #if ENABLE_DEBUG_LOGFILE DbgSetLogFileDesktop(LAVC_AUDIO_LOG_FILE); #endif #endif } CLAVAudio::~CLAVAudio() { ffmpeg_shutdown(); av_freep(&m_pFFBuffer); ShutdownBitstreaming(); if (m_hDllExtraDecoder) { FreeLibrary(m_hDllExtraDecoder); m_hDllExtraDecoder = NULL; } #if defined(DEBUG) && ENABLE_DEBUG_LOGFILE DbgCloseLogFile(); #endif } HRESULT CLAVAudio::LoadDefaults() { // Query OS version info OSVERSIONINFO os; ZeroMemory(&os, sizeof(os)); os.dwOSVersionInfoSize = sizeof(os); GetVersionEx(&os); m_settings.DRCEnabled = FALSE; m_settings.DRCLevel = 100; // Default all Codecs to enabled for(int i = 0; i < Codec_NB; ++i) m_settings.bFormats[i] = TRUE; // Disable WMA codecs by default m_settings.bFormats[Codec_WMA2] = FALSE; m_settings.bFormats[Codec_WMAPRO] = FALSE; m_settings.bFormats[Codec_WMALL] = FALSE; // Default bitstreaming to disabled memset(m_settings.bBitstream, 0, sizeof(m_settings.bBitstream)); m_settings.DTSHDFraming = FALSE; m_settings.AutoAVSync = TRUE; m_settings.ExpandMono = FALSE; m_settings.Expand61 = FALSE; m_settings.OutputStandardLayout = TRUE; m_settings.AllowRawSPDIF = FALSE; // Default all Sample Formats to enabled for(int i = 0; i < SampleFormat_NB; ++i) m_settings.bSampleFormats[i] = TRUE; if (os.dwMajorVersion < 6) m_settings.bSampleFormats[SampleFormat_FP32] = FALSE; m_settings.AudioDelayEnabled = FALSE; m_settings.AudioDelay = 0; m_settings.MixingEnabled = FALSE; m_settings.MixingLayout = AV_CH_LAYOUT_STEREO; m_settings.MixingFlags = LAV_MIXING_FLAG_CLIP_PROTECTION; m_settings.MixingMode = MatrixEncoding_None; m_settings.MixingCenterLevel = 7071; m_settings.MixingSurroundLevel = 7071; m_settings.MixingLFELevel = 0; return S_OK; } static const WCHAR* bitstreamingCodecs[Bitstream_NB] = { L"ac3", L"eac3", L"truehd", L"dts", L"dtshd" }; static const WCHAR* sampleFormats[SampleFormat_Bitstream] = { L"s16", L"s24", L"s32", L"u8", L"fp32" }; HRESULT CLAVAudio::LoadSettings() { LoadDefaults(); if (m_bRuntimeConfig) return S_FALSE; HRESULT hr; DWORD dwVal; BOOL bFlag; BYTE *pBuf = NULL; CreateRegistryKey(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY); CRegistry reg = CRegistry(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY, hr); // We don't check if opening succeeded, because the read functions will set their hr accordingly anyway, // and we need to fill the settings with defaults. // ReadString returns an empty string in case of failure, so thats fine! bFlag = reg.ReadDWORD(L"DRCEnabled", hr); if (SUCCEEDED(hr)) m_settings.DRCEnabled = bFlag; dwVal = reg.ReadDWORD(L"DRCLevel", hr); if (SUCCEEDED(hr)) m_settings.DRCLevel = (int)dwVal; // Deprecated format storage pBuf = reg.ReadBinary(L"Formats", dwVal, hr); if (SUCCEEDED(hr)) { memcpy(&m_settings.bFormats, pBuf, min(dwVal, sizeof(m_settings.bFormats))); SAFE_CO_FREE(pBuf); } // Deprecated bitstreaming storage pBuf = reg.ReadBinary(L"Bitstreaming", dwVal, hr); if (SUCCEEDED(hr)) { memcpy(&m_settings.bBitstream, pBuf, min(dwVal, sizeof(m_settings.bBitstream))); SAFE_CO_FREE(pBuf); } bFlag = reg.ReadBOOL(L"DTSHDFraming", hr); if (SUCCEEDED(hr)) m_settings.DTSHDFraming = bFlag; bFlag = reg.ReadBOOL(L"AutoAVSync", hr); if (SUCCEEDED(hr)) m_settings.AutoAVSync = bFlag; bFlag = reg.ReadBOOL(L"ExpandMono", hr); if (SUCCEEDED(hr)) m_settings.ExpandMono = bFlag; bFlag = reg.ReadBOOL(L"Expand61", hr); if (SUCCEEDED(hr)) m_settings.Expand61 = bFlag; bFlag = reg.ReadBOOL(L"OutputStandardLayout", hr); if (SUCCEEDED(hr)) m_settings.OutputStandardLayout = bFlag; bFlag = reg.ReadBOOL(L"Mixing", hr); if (SUCCEEDED(hr)) m_settings.MixingEnabled = bFlag; dwVal = reg.ReadDWORD(L"MixingLayout", hr); if (SUCCEEDED(hr)) m_settings.MixingLayout = dwVal; dwVal = reg.ReadDWORD(L"MixingFlags", hr); if (SUCCEEDED(hr)) m_settings.MixingFlags = dwVal; dwVal = reg.ReadDWORD(L"MixingMode", hr); if (SUCCEEDED(hr)) m_settings.MixingMode = dwVal; dwVal = reg.ReadDWORD(L"MixingCenterLevel", hr); if (SUCCEEDED(hr)) m_settings.MixingCenterLevel = dwVal; dwVal = reg.ReadDWORD(L"MixingSurroundLevel", hr); if (SUCCEEDED(hr)) m_settings.MixingSurroundLevel = dwVal; dwVal = reg.ReadDWORD(L"MixingLFELevel", hr); if (SUCCEEDED(hr)) m_settings.MixingLFELevel = dwVal; // Deprecated sample format storage pBuf = reg.ReadBinary(L"SampleFormats", dwVal, hr); if (SUCCEEDED(hr)) { memcpy(&m_settings.bSampleFormats, pBuf, min(dwVal, sizeof(m_settings.bSampleFormats))); SAFE_CO_FREE(pBuf); } bFlag = reg.ReadBOOL(L"AudioDelayEnabled", hr); if (SUCCEEDED(hr)) m_settings.AudioDelayEnabled = bFlag; dwVal = reg.ReadDWORD(L"AudioDelay", hr); if (SUCCEEDED(hr)) m_settings.AudioDelay = (int)dwVal; CreateRegistryKey(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY_FORMATS); CRegistry regF = CRegistry(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY_FORMATS, hr); for (int i = 0; i < Codec_NB; ++i) { const codec_config_t *info = get_codec_config((LAVAudioCodec)i); ATL::CA2W name(info->name); bFlag = regF.ReadBOOL(name, hr); if (SUCCEEDED(hr)) m_settings.bFormats[i] = bFlag; } for (int i = 0; i < Bitstream_NB; ++i) { std::wstring key = std::wstring(L"Bitstreaming_") + std::wstring(bitstreamingCodecs[i]); bFlag = reg.ReadBOOL(key.c_str(), hr); if (SUCCEEDED(hr)) m_settings.bBitstream[i] = bFlag; } for (int i = 0; i < SampleFormat_Bitstream; ++i) { std::wstring key = std::wstring(L"SampleFormat_") + std::wstring(sampleFormats[i]); bFlag = reg.ReadBOOL(key.c_str(), hr); if (SUCCEEDED(hr)) m_settings.bSampleFormats[i] = bFlag; } return S_OK; } HRESULT CLAVAudio::SaveSettings() { if (m_bRuntimeConfig) return S_FALSE; HRESULT hr; CRegistry reg = CRegistry(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY, hr); if (SUCCEEDED(hr)) { reg.WriteBOOL(L"DRCEnabled", m_settings.DRCEnabled); reg.WriteDWORD(L"DRCLevel", m_settings.DRCLevel); reg.WriteBOOL(L"DTSHDFraming", m_settings.DTSHDFraming); reg.WriteBOOL(L"AutoAVSync", m_settings.AutoAVSync); reg.WriteBOOL(L"ExpandMono", m_settings.ExpandMono); reg.WriteBOOL(L"Expand61", m_settings.Expand61); reg.WriteBOOL(L"OutputStandardLayout", m_settings.OutputStandardLayout); reg.WriteBOOL(L"AudioDelayEnabled", m_settings.AudioDelayEnabled); reg.WriteDWORD(L"AudioDelay", m_settings.AudioDelay); reg.WriteBOOL(L"Mixing", m_settings.MixingEnabled); reg.WriteDWORD(L"MixingLayout", m_settings.MixingLayout); reg.WriteDWORD(L"MixingFlags", m_settings.MixingFlags); reg.WriteDWORD(L"MixingMode", m_settings.MixingMode); reg.WriteDWORD(L"MixingCenterLevel", m_settings.MixingCenterLevel); reg.WriteDWORD(L"MixingSurroundLevel", m_settings.MixingSurroundLevel); reg.WriteDWORD(L"MixingLFELevel", m_settings.MixingLFELevel); reg.DeleteKey(L"Formats"); CRegistry regF = CRegistry(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY_FORMATS, hr); for (int i = 0; i < Codec_NB; ++i) { const codec_config_t *info = get_codec_config((LAVAudioCodec)i); ATL::CA2W name(info->name); regF.WriteBOOL(name, m_settings.bFormats[i]); } reg.DeleteKey(L"Bitstreaming"); for (int i = 0; i < Bitstream_NB; ++i) { std::wstring key = std::wstring(L"Bitstreaming_") + std::wstring(bitstreamingCodecs[i]); reg.WriteBOOL(key.c_str(), m_settings.bBitstream[i]); } reg.DeleteKey(L"SampleFormats"); for (int i = 0; i < SampleFormat_Bitstream; ++i) { std::wstring key = std::wstring(L"SampleFormat_") + std::wstring(sampleFormats[i]); reg.WriteBOOL(key.c_str(), m_settings.bSampleFormats[i]); } } return S_OK; } void CLAVAudio::ffmpeg_shutdown() { m_pAVCodec = NULL; if (m_pAVCtx) { avcodec_close(m_pAVCtx); av_freep(&m_pAVCtx->extradata); av_freep(&m_pAVCtx); } av_freep(&m_pFrame); if (m_pParser) { av_parser_close(m_pParser); m_pParser = NULL; } if (m_avrContext) { avresample_close(m_avrContext); avresample_free(&m_avrContext); } FreeBitstreamContext(); FreeDTSDecoder(); m_nCodecId = AV_CODEC_ID_NONE; } // IUnknown STDMETHODIMP CLAVAudio::NonDelegatingQueryInterface(REFIID riid, void** ppv) { CheckPointer(ppv, E_POINTER); *ppv = NULL; return QI(ISpecifyPropertyPages) QI(ISpecifyPropertyPages2) QI2(ILAVAudioSettings) QI2(ILAVAudioStatus) __super::NonDelegatingQueryInterface(riid, ppv); } // ISpecifyPropertyPages2 STDMETHODIMP CLAVAudio::GetPages(CAUUID *pPages) { CheckPointer(pPages, E_POINTER); BOOL bShowStatusPage = m_pInput && m_pInput->IsConnected(); pPages->cElems = bShowStatusPage ? 4 : 3; pPages->pElems = (GUID *)CoTaskMemAlloc(sizeof(GUID) * pPages->cElems); if (pPages->pElems == NULL) { return E_OUTOFMEMORY; } pPages->pElems[0] = CLSID_LAVAudioSettingsProp; pPages->pElems[1] = CLSID_LAVAudioMixingProp; pPages->pElems[2] = CLSID_LAVAudioFormatsProp; if (bShowStatusPage) pPages->pElems[3] = CLSID_LAVAudioStatusProp; return S_OK; } STDMETHODIMP CLAVAudio::CreatePage(const GUID& guid, IPropertyPage** ppPage) { CheckPointer(ppPage, E_POINTER); HRESULT hr = S_OK; if (*ppPage != NULL) return E_INVALIDARG; if (guid == CLSID_LAVAudioSettingsProp) *ppPage = new CLAVAudioSettingsProp(NULL, &hr); else if (guid == CLSID_LAVAudioMixingProp) *ppPage = new CLAVAudioMixingProp(NULL, &hr); else if (guid == CLSID_LAVAudioFormatsProp) *ppPage = new CLAVAudioFormatsProp(NULL, &hr); else if (guid == CLSID_LAVAudioStatusProp) *ppPage = new CLAVAudioStatusProp(NULL, &hr); if (SUCCEEDED(hr) && *ppPage) { (*ppPage)->AddRef(); return S_OK; } else { SAFE_DELETE(*ppPage); return E_FAIL; } } // ILAVAudioSettings HRESULT CLAVAudio::GetDRC(BOOL *pbDRCEnabled, int *piDRCLevel) { if (pbDRCEnabled) { *pbDRCEnabled = m_settings.DRCEnabled; } if (piDRCLevel) { *piDRCLevel = m_settings.DRCLevel; } return S_OK; } // ILAVAudioSettings HRESULT CLAVAudio::SetRuntimeConfig(BOOL bRuntimeConfig) { m_bRuntimeConfig = bRuntimeConfig; LoadSettings(); return S_OK; } HRESULT CLAVAudio::SetDRC(BOOL bDRCEnabled, int fDRCLevel) { m_settings.DRCEnabled = bDRCEnabled; m_settings.DRCLevel = fDRCLevel; if (m_pAVCtx) { float fDRC = bDRCEnabled ? (float)fDRCLevel / 100.0f : 0.0f; av_opt_set_double(m_pAVCtx, "drc_scale", fDRC, AV_OPT_SEARCH_CHILDREN); } SaveSettings(); return S_OK; } BOOL CLAVAudio::GetFormatConfiguration(LAVAudioCodec aCodec) { if (aCodec < 0 || aCodec >= Codec_NB) return FALSE; return m_settings.bFormats[aCodec]; } HRESULT CLAVAudio::SetFormatConfiguration(LAVAudioCodec aCodec, BOOL bEnabled) { if (aCodec < 0 || aCodec >= Codec_NB) return E_FAIL; m_settings.bFormats[aCodec] = (bEnabled != 0); SaveSettings(); return S_OK; } BOOL CLAVAudio::GetBitstreamConfig(LAVBitstreamCodec bsCodec) { if (bsCodec < 0 || bsCodec >= Bitstream_NB) return FALSE; return m_settings.bBitstream[bsCodec]; } HRESULT CLAVAudio::SetBitstreamConfig(LAVBitstreamCodec bsCodec, BOOL bEnabled) { if (bsCodec >= Bitstream_NB) return E_FAIL; m_settings.bBitstream[bsCodec] = (bEnabled != 0); SaveSettings(); UpdateBitstreamContext(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetDTSHDFraming() { return m_settings.DTSHDFraming; } STDMETHODIMP CLAVAudio::SetDTSHDFraming(BOOL bHDFraming) { m_settings.DTSHDFraming = bHDFraming; SaveSettings(); UpdateBitstreamContext(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetAutoAVSync() { return m_settings.AutoAVSync; } STDMETHODIMP CLAVAudio::SetAutoAVSync(BOOL bAutoSync) { m_settings.AutoAVSync = bAutoSync; SaveSettings(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetOutputStandardLayout() { return m_settings.OutputStandardLayout; } STDMETHODIMP CLAVAudio::SetOutputStandardLayout(BOOL bStdLayout) { m_settings.OutputStandardLayout = bStdLayout; SaveSettings(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetExpandMono() { return m_settings.ExpandMono; } STDMETHODIMP CLAVAudio::SetExpandMono(BOOL bExpandMono) { m_settings.ExpandMono = bExpandMono; SaveSettings(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetExpand61() { return m_settings.Expand61; } STDMETHODIMP CLAVAudio::SetExpand61(BOOL bExpand61) { m_settings.Expand61 = bExpand61; SaveSettings(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetAllowRawSPDIFInput() { return m_settings.AllowRawSPDIF; } STDMETHODIMP CLAVAudio::SetAllowRawSPDIFInput(BOOL bAllow) { m_settings.AllowRawSPDIF = bAllow; SaveSettings(); return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetSampleFormat(LAVAudioSampleFormat format) { if (format < 0 || format >= SampleFormat_NB) return FALSE; return m_settings.bSampleFormats[format]; } STDMETHODIMP CLAVAudio::SetSampleFormat(LAVAudioSampleFormat format, BOOL bEnabled) { if (format < 0 || format >= SampleFormat_NB) return E_FAIL; m_settings.bSampleFormats[format] = (bEnabled != 0); SaveSettings(); return S_OK; } STDMETHODIMP CLAVAudio::GetAudioDelay(BOOL *pbEnabled, int *pDelay) { if (pbEnabled) *pbEnabled = m_settings.AudioDelayEnabled; if (pDelay) *pDelay = m_settings.AudioDelay; return S_OK; } STDMETHODIMP CLAVAudio::SetAudioDelay(BOOL bEnabled, int delay) { m_settings.AudioDelayEnabled = bEnabled; m_settings.AudioDelay = delay; SaveSettings(); return S_OK; } STDMETHODIMP CLAVAudio::SetMixingEnabled(BOOL bEnabled) { m_settings.MixingEnabled = bEnabled; SaveSettings(); m_bMixingSettingsChanged = TRUE; return S_OK; } STDMETHODIMP_(BOOL) CLAVAudio::GetMixingEnabled() { return m_settings.MixingEnabled; } STDMETHODIMP CLAVAudio::SetMixingLayout(DWORD dwLayout) { m_settings.MixingLayout = dwLayout; SaveSettings(); m_bMixingSettingsChanged = TRUE; return S_OK; } STDMETHODIMP_(DWORD) CLAVAudio::GetMixingLayout() { return m_settings.MixingLayout; } STDMETHODIMP CLAVAudio::SetMixingFlags(DWORD dwFlags) { m_settings.MixingFlags = dwFlags; SaveSettings(); m_bMixingSettingsChanged = TRUE; return S_OK; } STDMETHODIMP_(DWORD) CLAVAudio::GetMixingFlags() { return m_settings.MixingFlags; } STDMETHODIMP CLAVAudio::SetMixingMode(LAVAudioMixingMode mixingMode) { m_settings.MixingMode = mixingMode; SaveSettings(); m_bMixingSettingsChanged = TRUE; return S_OK; } STDMETHODIMP_(LAVAudioMixingMode) CLAVAudio::GetMixingMode() { return (LAVAudioMixingMode)m_settings.MixingMode; } STDMETHODIMP CLAVAudio::SetMixingLevels(DWORD dwCenterLevel, DWORD dwSurroundLevel, DWORD dwLFELevel) { m_settings.MixingCenterLevel = dwCenterLevel; m_settings.MixingSurroundLevel = dwSurroundLevel; m_settings.MixingLFELevel = dwLFELevel; SaveSettings(); m_bMixingSettingsChanged = TRUE; return S_OK; } STDMETHODIMP CLAVAudio::GetMixingLevels(DWORD *dwCenterLevel, DWORD *dwSurroundLevel, DWORD *dwLFELevel) { if (dwCenterLevel) *dwCenterLevel = m_settings.MixingCenterLevel; if (dwSurroundLevel) *dwSurroundLevel = m_settings.MixingSurroundLevel; if (dwLFELevel) *dwLFELevel = m_settings.MixingLFELevel; return S_OK; } // ILAVAudioStatus BOOL CLAVAudio::IsSampleFormatSupported(LAVAudioSampleFormat sfCheck) { if(!m_pOutput || m_pOutput->IsConnected() == FALSE) { return FALSE; } return m_bSampleSupport[sfCheck]; } HRESULT CLAVAudio::GetDecodeDetails(const char **pCodec, const char **pDecodeFormat, int *pnChannels, int *pSampleRate, DWORD *pChannelMask) { if(!m_pInput || m_pInput->IsConnected() == FALSE) { return E_UNEXPECTED; } if (m_avBSContext) { if (pCodec) { AVCodec *codec = avcodec_find_decoder(m_nCodecId); *pCodec = codec->name; } if (pnChannels) { *pnChannels = m_avBSContext->streams[0]->codec->channels; } if (pSampleRate) { *pSampleRate = m_avBSContext->streams[0]->codec->sample_rate; } if (pDecodeFormat) { *pDecodeFormat = ""; } if (pChannelMask) { *pChannelMask = 0; } } else { if (pCodec) { if (m_pDTSDecoderContext) { static const char *DTSProfiles[] = { "dts", NULL, "dts-es", "dts 96/24", NULL, "dts-hd hra", "dts-hd ma", "dts express" }; int index = 0, profile = m_pAVCtx->profile; if (profile != FF_PROFILE_UNKNOWN) while(profile >>= 1) index++; if (index > 7) index = 0; *pCodec = DTSProfiles[index] ? DTSProfiles[index] : "dts"; } else if (m_pAVCodec) { *pCodec = m_pAVCodec->name; } } if (pnChannels) { *pnChannels = m_pAVCtx->channels; } if (pSampleRate) { *pSampleRate = m_pAVCtx->sample_rate; } if (pDecodeFormat) { if (IsActive()) *pDecodeFormat = get_sample_format_desc(m_DecodeFormat); else *pDecodeFormat = "Not Running"; } if (pChannelMask) { *pChannelMask = m_DecodeLayout; } } return S_OK; } HRESULT CLAVAudio::GetOutputDetails(const char **pOutputFormat, int *pnChannels, int *pSampleRate, DWORD *pChannelMask) { if(!m_pOutput || m_pOutput->IsConnected() == FALSE) { return E_UNEXPECTED; } if (m_avBSContext) { if (pOutputFormat) { *pOutputFormat = get_sample_format_desc(SampleFormat_Bitstream); } return S_FALSE; } if (pOutputFormat) { *pOutputFormat = get_sample_format_desc(m_OutputQueue.sfFormat); } if (pnChannels) { *pnChannels = m_OutputQueue.wChannels; } if (pSampleRate) { *pSampleRate = m_OutputQueue.dwSamplesPerSec; } if (pChannelMask) { *pChannelMask = m_OutputQueue.dwChannelMask; } return S_OK; } HRESULT CLAVAudio::EnableVolumeStats() { DbgLog((LOG_TRACE, 1, L"Volume Statistics Enabled")); m_bVolumeStats = TRUE; return S_OK; } HRESULT CLAVAudio::DisableVolumeStats() { DbgLog((LOG_TRACE, 1, L"Volume Statistics Disabled")); m_bVolumeStats = FALSE; return S_OK; } HRESULT CLAVAudio::GetChannelVolumeAverage(WORD nChannel, float *pfDb) { CheckPointer(pfDb, E_POINTER); if (!m_pOutput || m_pOutput->IsConnected() == FALSE || !m_bVolumeStats || m_avBSContext) { return E_UNEXPECTED; } if (nChannel >= m_OutputQueue.wChannels || nChannel >= 8) { return E_INVALIDARG; } *pfDb = m_faVolume[nChannel].Average(); return S_OK; } // CTransformFilter HRESULT CLAVAudio::CheckInputType(const CMediaType *mtIn) { for(int i = 0; i < sudPinTypesInCount; i++) { if(*sudPinTypesIn[i].clsMajorType == mtIn->majortype && *sudPinTypesIn[i].clsMinorType == mtIn->subtype && (mtIn->formattype == FORMAT_WaveFormatEx || mtIn->formattype == FORMAT_WaveFormatExFFMPEG || mtIn->formattype == FORMAT_VorbisFormat2)) { return S_OK; } } if (m_settings.AllowRawSPDIF) { if (mtIn->majortype == MEDIATYPE_Audio && mtIn->formattype == FORMAT_WaveFormatEx && (mtIn->subtype == MEDIASUBTYPE_PCM || mtIn->subtype == MEDIASUBTYPE_DOLBY_AC3_SPDIF)) { return S_OK; } } return VFW_E_TYPE_NOT_ACCEPTED; } // Get the output media types HRESULT CLAVAudio::GetMediaType(int iPosition, CMediaType *pMediaType) { DbgLog((LOG_TRACE, 5, L"GetMediaType")); if(m_pInput->IsConnected() == FALSE || !((m_pAVCtx && m_pAVCodec) || m_avBSContext)) { return E_UNEXPECTED; } if(iPosition < 0) { return E_INVALIDARG; } int maxIndex = m_avBSContext ? 0 : 1; if(iPosition > maxIndex) { return VFW_S_NO_MORE_ITEMS; } if (m_avBSContext) { *pMediaType = CreateBitstreamMediaType(m_nCodecId, m_pAVCtx->sample_rate); } else { const int nSamplesPerSec = m_pAVCtx->sample_rate; int nChannels = m_pAVCtx->channels; DWORD dwChannelMask = get_channel_mask(nChannels); AVSampleFormat sample_fmt = (m_pAVCtx->sample_fmt != AV_SAMPLE_FMT_NONE) ? m_pAVCtx->sample_fmt : (m_pAVCodec->sample_fmts ? m_pAVCodec->sample_fmts[0] : AV_SAMPLE_FMT_NONE); if (sample_fmt == AV_SAMPLE_FMT_NONE) { if (m_pAVCtx->bits_per_coded_sample > 16) sample_fmt = AV_SAMPLE_FMT_S32; else sample_fmt = AV_SAMPLE_FMT_S16; } // Prefer bits_per_raw_sample if set, but if not, try to do a better guess with bits per coded sample const int bits = m_pAVCtx->bits_per_raw_sample ? m_pAVCtx->bits_per_raw_sample : m_pAVCtx->bits_per_coded_sample; LAVAudioSampleFormat lav_sample_fmt = m_pDTSDecoderContext ? SampleFormat_24 : get_lav_sample_fmt(sample_fmt, bits); if (m_settings.MixingEnabled) { if (nChannels != av_get_channel_layout_nb_channels(m_settings.MixingLayout) && (nChannels > 2 || !(m_settings.MixingFlags & LAV_MIXING_FLAG_UNTOUCHED_STEREO))) { lav_sample_fmt = SampleFormat_FP32; dwChannelMask = m_settings.MixingLayout; nChannels = av_get_channel_layout_nb_channels(dwChannelMask); } } if (iPosition == 1) lav_sample_fmt = SampleFormat_16; lav_sample_fmt = GetBestAvailableSampleFormat(lav_sample_fmt, TRUE); *pMediaType = CreateMediaType(lav_sample_fmt, nSamplesPerSec, nChannels, dwChannelMask, m_pAVCtx->bits_per_raw_sample); } return S_OK; } HRESULT CLAVAudio::ReconnectOutput(long cbBuffer, CMediaType& mt) { HRESULT hr = S_FALSE; IMemInputPin *pPin = NULL; IMemAllocator *pAllocator = NULL; CHECK_HR(hr = m_pOutput->GetConnected()->QueryInterface(&pPin)); if(FAILED(hr = pPin->GetAllocator(&pAllocator)) || !pAllocator) { goto done; } ALLOCATOR_PROPERTIES props, actual; CHECK_HR(hr = pAllocator->GetProperties(&props)); hr = S_FALSE; if(mt != m_pOutput->CurrentMediaType() || cbBuffer > props.cbBuffer) { DbgLog((LOG_TRACE, 10, L"::ReconnectOutput(): Reconnecting output because media type or buffer size changed...")); if(cbBuffer > props.cbBuffer) { DbgLog((LOG_TRACE, 10, L"::ReconnectOutput(): -> Increasing buffer size")); props.cBuffers = 4; props.cbBuffer = cbBuffer*3/2; if(FAILED(hr = m_pOutput->DeliverBeginFlush()) || FAILED(hr = m_pOutput->DeliverEndFlush()) || FAILED(hr = pAllocator->Decommit()) || FAILED(hr = pAllocator->SetProperties(&props, &actual)) || FAILED(hr = pAllocator->Commit())) { goto done; } if(props.cBuffers > actual.cBuffers || props.cbBuffer > actual.cbBuffer) { NotifyEvent(EC_ERRORABORT, hr, 0); hr = E_FAIL; goto done; } } hr = S_OK; } done: SafeRelease(&pPin); SafeRelease(&pAllocator); return hr; } CMediaType CLAVAudio::CreateMediaType(LAVAudioSampleFormat outputFormat, DWORD nSamplesPerSec, WORD nChannels, DWORD dwChannelMask, WORD wBitsPerSample) const { CMediaType mt; mt.majortype = MEDIATYPE_Audio; mt.subtype = (outputFormat == SampleFormat_FP32) ? MEDIASUBTYPE_IEEE_FLOAT : MEDIASUBTYPE_PCM; mt.formattype = FORMAT_WaveFormatEx; WAVEFORMATEXTENSIBLE wfex; memset(&wfex, 0, sizeof(wfex)); if (wBitsPerSample >> 3 > get_byte_per_sample(outputFormat)) { DbgLog((LOG_TRACE, 20, L"Invalid combination of sample format and bits per sample")); outputFormat = get_lav_sample_fmt(AV_SAMPLE_FMT_S32, wBitsPerSample); } WAVEFORMATEX* wfe = &wfex.Format; wfe->wFormatTag = (WORD)mt.subtype.Data1; wfe->nChannels = nChannels; wfe->nSamplesPerSec = nSamplesPerSec; wfe->wBitsPerSample = get_byte_per_sample(outputFormat) << 3; wfe->nBlockAlign = wfe->nChannels * wfe->wBitsPerSample / 8; wfe->nAvgBytesPerSec = wfe->nSamplesPerSec * wfe->nBlockAlign; if(dwChannelMask == 0 && (wfe->wBitsPerSample > 16 || wfe->nSamplesPerSec > 48000)) { dwChannelMask = nChannels == 2 ? (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) : SPEAKER_FRONT_CENTER; } // Dont use a channel mask for "default" mono/stereo sources if ((outputFormat == SampleFormat_FP32 || wfe->wBitsPerSample <= 16) && wfe->nSamplesPerSec <= 48000 && ((nChannels == 1 && dwChannelMask == SPEAKER_FRONT_CENTER) || (nChannels == 2 && dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)))) { dwChannelMask = 0; } if(dwChannelMask) { wfex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfex.Format.cbSize = sizeof(wfex) - sizeof(wfex.Format); wfex.dwChannelMask = dwChannelMask; if (wBitsPerSample > 0 && (outputFormat == SampleFormat_24 || outputFormat == SampleFormat_32)) { WORD wBpp = wBitsPerSample; if ( (outputFormat == SampleFormat_24 && wBpp <= 16) || (outputFormat == SampleFormat_32 && wBpp < 24)) wBpp = 24; wfex.Samples.wValidBitsPerSample = wBpp; } else { wfex.Samples.wValidBitsPerSample = wfex.Format.wBitsPerSample; } wfex.SubFormat = mt.subtype; } mt.SetSampleSize(wfe->wBitsPerSample * wfe->nChannels / 8); mt.SetFormat((BYTE*)&wfex, sizeof(wfex.Format) + wfex.Format.cbSize); return mt; } // Check if the types are compatible HRESULT CLAVAudio::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut) { // Check major types if (FAILED(CheckInputType(mtIn)) || mtOut->majortype != MEDIATYPE_Audio || (mtOut->subtype != MEDIASUBTYPE_PCM && mtOut->subtype != MEDIASUBTYPE_IEEE_FLOAT) || mtOut->formattype != FORMAT_WaveFormatEx) { return VFW_E_TYPE_NOT_ACCEPTED; } else { // Check for valid pcm settings if (!m_avBSContext && m_pAVCtx) { WAVEFORMATEX *wfex = (WAVEFORMATEX *)mtOut->pbFormat; if (wfex->nSamplesPerSec != m_pAVCtx->sample_rate) { return VFW_E_TYPE_NOT_ACCEPTED; } } } return S_OK; } HRESULT CLAVAudio::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties) { if(m_pInput->IsConnected() == FALSE) { return E_UNEXPECTED; } /*CMediaType& mt = m_pInput->CurrentMediaType(); WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.Format(); UNUSED_ALWAYS(wfe); */ pProperties->cBuffers = 4; // TODO: we should base this on the output media type pProperties->cbBuffer = LAV_AUDIO_BUFFER_SIZE; // 48KHz 6ch 32bps 100ms pProperties->cbAlign = 1; pProperties->cbPrefix = 0; HRESULT hr; ALLOCATOR_PROPERTIES Actual; if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual))) { return hr; } return pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer ? E_FAIL : NOERROR; } HRESULT CLAVAudio::ffmpeg_init(AVCodecID codec, const void *format, const GUID format_type, DWORD formatlen) { CAutoLock lock(&m_csReceive); ffmpeg_shutdown(); DbgLog((LOG_TRACE, 10, L"::ffmpeg_init(): Initializing decoder for codec %S", avcodec_get_name(codec))); if (codec == AV_CODEC_ID_DTS || codec == AV_CODEC_ID_TRUEHD) { m_faJitter.SetNumSamples(200); m_JitterLimit = MAX_JITTER_DESYNC * 10; } else { m_faJitter.SetNumSamples(50); m_JitterLimit = MAX_JITTER_DESYNC; } // Fake codecs that are dependant in input bits per sample, mostly to handle QT PCM tracks if (codec == AV_CODEC_ID_PCM_QTRAW || codec == AV_CODEC_ID_PCM_SxxBE || codec == AV_CODEC_ID_PCM_SxxLE || codec == AV_CODEC_ID_PCM_UxxBE || codec == AV_CODEC_ID_PCM_UxxLE) { if (format_type == FORMAT_WaveFormatEx) { WAVEFORMATEX *wfein = (WAVEFORMATEX *)format; ASSERT(wfein->wBitsPerSample == 8 || wfein->wBitsPerSample == 16); switch(codec) { case AV_CODEC_ID_PCM_QTRAW: codec = wfein->wBitsPerSample == 8 ? AV_CODEC_ID_PCM_U8 : AV_CODEC_ID_PCM_S16BE; break; case AV_CODEC_ID_PCM_SxxBE: codec = wfein->wBitsPerSample == 8 ? AV_CODEC_ID_PCM_S8 : AV_CODEC_ID_PCM_S16BE; break; case AV_CODEC_ID_PCM_SxxLE: codec = wfein->wBitsPerSample == 8 ? AV_CODEC_ID_PCM_S8 : AV_CODEC_ID_PCM_S16LE; break; case AV_CODEC_ID_PCM_UxxBE: codec = wfein->wBitsPerSample == 8 ? AV_CODEC_ID_PCM_U8 : AV_CODEC_ID_PCM_U16BE; break; case AV_CODEC_ID_PCM_UxxLE: codec = wfein->wBitsPerSample == 8 ? AV_CODEC_ID_PCM_U8 : AV_CODEC_ID_PCM_U16LE; break; } } } // Special check for enabled PCM if (codec >= 0x10000 && codec < 0x12000 && codec != AV_CODEC_ID_PCM_BLURAY && codec != AV_CODEC_ID_PCM_DVD && !m_settings.bFormats[Codec_PCM]) return VFW_E_UNSUPPORTED_AUDIO; for(int i = 0; i < Codec_NB; ++i) { const codec_config_t *config = get_codec_config((LAVAudioCodec)i); bool bMatched = false; for (int k = 0; k < config->nCodecs; ++k) { if (config->codecs[k] == codec) { bMatched = true; break; } } if (bMatched && !m_settings.bFormats[i]) { return VFW_E_UNSUPPORTED_AUDIO; } } if (codec == AV_CODEC_ID_PCM_DVD) { if (format_type == FORMAT_WaveFormatEx) { WAVEFORMATEX *wfein = (WAVEFORMATEX *)format; if (wfein->wBitsPerSample == 16) { codec = AV_CODEC_ID_PCM_S16BE; } } } WCHAR fileName[1024]; GetModuleFileName(NULL, fileName, 1024); std::wstring processName = PathFindFileName(fileName); m_bInputPadded = FilterInGraphSafe(m_pInput, CLSID_LAVSplitter) || FilterInGraphSafe(m_pInput, CLSID_LAVSplitterSource); m_bHasVideo = _wcsicmp(processName.c_str(), L"dllhost.exe") == 0 || _wcsicmp(processName.c_str(), L"explorer.exe") == 0 || _wcsicmp(processName.c_str(), L"ReClockHelper.dll") == 0 || _wcsicmp(processName.c_str(), L"dvbviewer.exe") == 0 || HasSourceWithType(m_pInput, MEDIATYPE_Video) || m_pInput->CurrentMediaType().majortype != MEDIATYPE_Audio; DbgLog((LOG_TRACE, 10, L"-> Do we have video? %d", m_bHasVideo)); // If the codec is bitstreaming, and enabled for it, go there now if (IsBitstreaming(codec)) { WAVEFORMATEX *wfe = (format_type == FORMAT_WaveFormatEx) ? (WAVEFORMATEX *)format : NULL; if(SUCCEEDED(CreateBitstreamContext(codec, wfe))) { return S_OK; } } if (codec == AV_CODEC_ID_DTS) { InitDTSDecoder(); } m_pAVCodec = NULL; // Try codec overrides const char *codec_override = find_codec_override(codec); if (codec_override) m_pAVCodec = avcodec_find_decoder_by_name(codec_override); // Fallback to default if (!m_pAVCodec) m_pAVCodec = avcodec_find_decoder(codec); CheckPointer(m_pAVCodec, VFW_E_UNSUPPORTED_AUDIO); m_pAVCtx = avcodec_alloc_context3(m_pAVCodec); CheckPointer(m_pAVCtx, E_POINTER); if (codec != AV_CODEC_ID_AAC && codec != AV_CODEC_ID_FLAC && codec != AV_CODEC_ID_COOK) m_pParser = av_parser_init(codec); if (m_pAVCodec->capabilities & CODEC_CAP_TRUNCATED) m_pAVCtx->flags |= CODEC_FLAG_TRUNCATED; if ( codec == AV_CODEC_ID_AAC || codec == AV_CODEC_ID_AC3 || codec == AV_CODEC_ID_ATRAC3 || codec == AV_CODEC_ID_DTS || codec == AV_CODEC_ID_OPUS || codec == AV_CODEC_ID_NELLYMOSER || codec == AV_CODEC_ID_VORBIS) { m_pAVCtx->request_sample_fmt = AV_SAMPLE_FMT_FLT; } else if (codec == AV_CODEC_ID_ALAC) { m_pAVCtx->request_sample_fmt = AV_SAMPLE_FMT_S32P; } // We can only trust LAV Splitters LATM AAC header... BOOL bTrustExtraData = TRUE; if (codec == AV_CODEC_ID_AAC_LATM) { if (!(FilterInGraphSafe(m_pInput, CLSID_LAVSplitter) || FilterInGraphSafe(m_pInput, CLSID_LAVSplitterSource))) { bTrustExtraData = FALSE; } } DWORD nSamples, nBytesPerSec; WORD nChannels, nBitsPerSample, nBlockAlign; audioFormatTypeHandler((BYTE *)format, &format_type, &nSamples, &nChannels, &nBitsPerSample, &nBlockAlign, &nBytesPerSec); size_t extralen = 0; getExtraData((BYTE *)format, &format_type, formatlen, NULL, &extralen); m_pAVCtx->sample_rate = nSamples; m_pAVCtx->channels = nChannels; m_pAVCtx->bit_rate = nBytesPerSec << 3; m_pAVCtx->bits_per_coded_sample = nBitsPerSample; m_pAVCtx->block_align = nBlockAlign; m_pAVCtx->err_recognition = AV_EF_CAREFUL; memset(&m_raData, 0, sizeof(m_raData)); if (bTrustExtraData && extralen) { if (codec == AV_CODEC_ID_COOK || codec == AV_CODEC_ID_ATRAC3 || codec == AV_CODEC_ID_SIPR) { uint8_t *extra = (uint8_t *)av_mallocz(extralen + FF_INPUT_BUFFER_PADDING_SIZE); getExtraData((BYTE *)format, &format_type, formatlen, extra, NULL); if (extra[0] == '.' && extra[1] == 'r' && extra[2] == 'a' && extra[3] == 0xfd) { HRESULT hr = ParseRealAudioHeader(extra, extralen); av_freep(&extra); if (FAILED(hr)) return hr; if (codec == AV_CODEC_ID_SIPR) { if (m_raData.flavor > 3) { DbgLog((LOG_TRACE, 10, L"-> Invalid SIPR flavor (%d)", m_raData.flavor)); return VFW_E_UNSUPPORTED_AUDIO; } m_pAVCtx->block_align = ff_sipr_subpk_size[m_raData.flavor]; } else if (codec == AV_CODEC_ID_COOK || codec == AV_CODEC_ID_ATRAC3) { m_pAVCtx->block_align = m_raData.sub_packet_size; } // Validate some settings if (m_raData.deint_id == MKBETAG('g', 'e', 'n', 'r')) { if (m_raData.sub_packet_size <= 0 || m_raData.sub_packet_size > m_raData.audio_framesize) return VFW_E_UNSUPPORTED_AUDIO; } } else { // Try without any processing? m_pAVCtx->extradata_size = (int)extralen; m_pAVCtx->extradata = extra; } } else { m_pAVCtx->extradata_size = (int)extralen; m_pAVCtx->extradata = (uint8_t *)av_mallocz(m_pAVCtx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); getExtraData((BYTE *)format, &format_type, formatlen, m_pAVCtx->extradata, NULL); } } m_nCodecId = codec; int ret = avcodec_open2(m_pAVCtx, m_pAVCodec, NULL); if (ret >= 0) { m_pFrame = avcodec_alloc_frame(); } else { return VFW_E_UNSUPPORTED_AUDIO; } // Set Dynamic Range Compression float drc_scale = m_settings.DRCEnabled ? (float)m_settings.DRCLevel / 100.0f : 0.0f; ret = av_opt_set_double(m_pAVCtx, "drc_scale", drc_scale, AV_OPT_SEARCH_CHILDREN); // This could probably be a bit smarter.. if (codec == AV_CODEC_ID_PCM_BLURAY || codec == AV_CODEC_ID_PCM_DVD) { m_pAVCtx->bits_per_raw_sample = m_pAVCtx->bits_per_coded_sample; } // Some sanity checks if (m_pAVCtx->channels > 8) { return VFW_E_UNSUPPORTED_AUDIO; } m_bFindDTSInPCM = (codec == AV_CODEC_ID_PCM_S16LE && m_settings.bFormats[Codec_DTS]); m_bFallback16Int = FALSE; m_dwOverrideMixer = 0; m_bMixingSettingsChanged = TRUE; m_fMixingClipThreshold = 1.0f; return S_OK; } HRESULT CLAVAudio::SetMediaType(PIN_DIRECTION dir, const CMediaType *pmt) { DbgLog((LOG_TRACE, 5, L"SetMediaType -- %S", dir == PINDIR_INPUT ? "in" : "out")); if (dir == PINDIR_INPUT) { AVCodecID codec = AV_CODEC_ID_NONE; const void *format = pmt->Format(); GUID format_type = pmt->formattype; DWORD formatlen = pmt->cbFormat; // Override the format type if (pmt->subtype == MEDIASUBTYPE_FFMPEG_AUDIO && pmt->formattype == FORMAT_WaveFormatExFFMPEG) { WAVEFORMATEXFFMPEG *wfexff = (WAVEFORMATEXFFMPEG *)pmt->Format(); codec = (AVCodecID)wfexff->nCodecId; format = &wfexff->wfex; format_type = FORMAT_WaveFormatEx; formatlen -= sizeof(WAVEFORMATEXFFMPEG) - sizeof(WAVEFORMATEX); } else { codec = FindCodecId(pmt); } if (codec == AV_CODEC_ID_NONE) { if (m_settings.AllowRawSPDIF) { if (pmt->formattype == FORMAT_WaveFormatEx && pmt->subtype == MEDIASUBTYPE_PCM) { WAVEFORMATEX *wfex = (WAVEFORMATEX *)pmt->Format(); switch (wfex->wBitsPerSample) { case 8: codec = AV_CODEC_ID_PCM_U8; break; case 16: codec = AV_CODEC_ID_PCM_S16LE; break; case 24: codec = AV_CODEC_ID_PCM_S24LE; break; case 32: codec = AV_CODEC_ID_PCM_S32LE; break; } } else if (pmt->subtype == MEDIASUBTYPE_DOLBY_AC3_SPDIF) { codec = AV_CODEC_ID_AC3; } } if (codec == AV_CODEC_ID_NONE) return VFW_E_TYPE_NOT_ACCEPTED; } HRESULT hr = ffmpeg_init(codec, format, format_type, formatlen); if (FAILED(hr)) { return hr; } } return __super::SetMediaType(dir, pmt); } HRESULT CLAVAudio::CheckConnect(PIN_DIRECTION dir, IPin *pPin) { DbgLog((LOG_TRACE, 5, L"CheckConnect -- %S", dir == PINDIR_INPUT ? "in" : "out")); if (dir == PINDIR_INPUT) { // TODO: Check if the upstream source filter is LAVFSplitter, and store that somewhere // Validate that this is called before any media type negotiation } else if (dir == PINDIR_OUTPUT) { CMediaType check_mt; const int nChannels = m_pAVCtx ? m_pAVCtx->channels : 2; const int nSamplesPerSec = m_pAVCtx ? m_pAVCtx->sample_rate : 48000; const DWORD dwChannelMask = get_channel_mask(nChannels); check_mt = CreateMediaType(SampleFormat_FP32, nSamplesPerSec, nChannels, dwChannelMask); m_bSampleSupport[SampleFormat_FP32] = pPin->QueryAccept(&check_mt) == S_OK; check_mt = CreateMediaType(SampleFormat_32, nSamplesPerSec, nChannels, dwChannelMask); m_bSampleSupport[SampleFormat_32] = pPin->QueryAccept(&check_mt) == S_OK; check_mt = CreateMediaType(SampleFormat_24, nSamplesPerSec, nChannels, dwChannelMask); m_bSampleSupport[SampleFormat_24] = pPin->QueryAccept(&check_mt) == S_OK; check_mt = CreateMediaType(SampleFormat_16, nSamplesPerSec, nChannels, dwChannelMask); m_bSampleSupport[SampleFormat_16] = pPin->QueryAccept(&check_mt) == S_OK; check_mt = CreateMediaType(SampleFormat_U8, nSamplesPerSec, nChannels, dwChannelMask); m_bSampleSupport[SampleFormat_U8] = pPin->QueryAccept(&check_mt) == S_OK; } return __super::CheckConnect(dir, pPin); } HRESULT CLAVAudio::EndOfStream() { DbgLog((LOG_TRACE, 10, L"CLAVAudio::EndOfStream()")); CAutoLock cAutoLock(&m_csReceive); // Flush the last data out of the parser ProcessBuffer(); ProcessBuffer(TRUE); FlushOutput(TRUE); return __super::EndOfStream(); } HRESULT CLAVAudio::BeginFlush() { DbgLog((LOG_TRACE, 10, L"CLAVAudio::BeginFlush()")); return __super::BeginFlush(); } HRESULT CLAVAudio::EndFlush() { DbgLog((LOG_TRACE, 10, L"CLAVAudio::EndFlush()")); CAutoLock cAutoLock(&m_csReceive); m_buff.SetSize(0); FlushOutput(FALSE); FlushDecoder(); m_bsOutput.SetSize(0); m_bQueueResync = TRUE; return __super::EndFlush(); } HRESULT CLAVAudio::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { DbgLog((LOG_TRACE, 10, L"CLAVAudio::NewSegment() tStart: %I64d, tStop: %I64d, dRate: %.2f", tStart, tStop, dRate)); CAutoLock cAutoLock(&m_csReceive); m_rtStart = 0; m_bQueueResync = TRUE; m_bNeedSyncpoint = (m_raData.deint_id != 0); if (dRate > 0.0) m_dRate = dRate; else m_dRate = 1.0; return __super::NewSegment(tStart, tStop, dRate); } HRESULT CLAVAudio::FlushDecoder() { if(m_pParser) { av_parser_close(m_pParser); m_pParser = av_parser_init(m_nCodecId); m_bUpdateTimeCache = TRUE; } if (m_pAVCtx && m_pAVCtx->codec) { avcodec_flush_buffers (m_pAVCtx); } FlushDTSDecoder(); return S_OK; } HRESULT CLAVAudio::Receive(IMediaSample *pIn) { CAutoLock cAutoLock(&m_csReceive); HRESULT hr; AM_SAMPLE2_PROPERTIES const *pProps = m_pInput->SampleProps(); if(pProps->dwStreamId != AM_STREAM_MEDIA) { return m_pOutput->Deliver(pIn); } AM_MEDIA_TYPE *pmt; if(SUCCEEDED(pIn->GetMediaType(&pmt)) && pmt) { DbgLog((LOG_TRACE, 10, L"::Receive(): Input sample contained media type, dynamic format change...")); CMediaType mt(*pmt); m_pInput->SetMediaType(&mt); DeleteMediaType(pmt); pmt = NULL; m_buff.SetSize(0); m_bQueueResync = TRUE; } if (!m_pAVCtx) { return E_FAIL; } BYTE *pDataIn = NULL; if(FAILED(hr = pIn->GetPointer(&pDataIn))) { return hr; } long len = pIn->GetActualDataLength(); if (len == 0) { return S_OK; } (static_cast(m_pInput))->StripPacket(pDataIn, len); REFERENCE_TIME rtStart = _I64_MIN, rtStop = _I64_MIN; hr = pIn->GetTime(&rtStart, &rtStop); if(pIn->IsDiscontinuity() == S_OK || (m_bNeedSyncpoint && pIn->IsSyncPoint() == S_OK)) { DbgLog((LOG_ERROR, 10, L"::Receive(): Discontinuity, flushing decoder..")); m_bDiscontinuity = TRUE; m_buff.SetSize(0); FlushOutput(FALSE); FlushDecoder(); m_bQueueResync = TRUE; if(FAILED(hr)) { DbgLog((LOG_ERROR, 10, L" -> Discontinuity without timestamp")); } if (m_bNeedSyncpoint && pIn->IsSyncPoint() == S_OK) { DbgLog((LOG_TRACE, 10, L"::Receive(): Got SyncPoint, resuming decoding....")); m_bNeedSyncpoint = FALSE; } } if(m_bQueueResync && SUCCEEDED(hr)) { DbgLog((LOG_TRACE, 10, L"Resync Request; old: %I64d; new: %I64d; buffer: %d", m_rtStart, rtStart, m_buff.GetCount())); FlushOutput(); m_rtStart = rtStart; m_rtStartInputCache = AV_NOPTS_VALUE; m_rtBitstreamCache = AV_NOPTS_VALUE; m_dStartOffset = 0.0; m_bQueueResync = FALSE; } m_rtStartInput = SUCCEEDED(hr) ? rtStart : AV_NOPTS_VALUE; m_rtStopInput = SUCCEEDED(hr) ? rtStop : AV_NOPTS_VALUE; int bufflen = m_buff.GetCount(); // Hack to re-create the BD LPCM header because in the MPC-HC format its stripped off. CMediaType inMt(m_pInput->CurrentMediaType()); if (inMt.subtype == MEDIASUBTYPE_HDMV_LPCM_AUDIO && inMt.formattype == FORMAT_WaveFormatEx) { m_buff.SetSize(bufflen + 4); BYTE *buf = m_buff.Ptr() + bufflen; CreateBDLPCMHeader(buf, (WAVEFORMATEX_HDMV_LPCM *)inMt.pbFormat); bufflen = m_buff.GetCount(); } m_buff.SetSize(bufflen + len); memcpy(m_buff.Ptr() + bufflen, pDataIn, len); len += bufflen; hr = ProcessBuffer(); if (FAILED(hr)) return hr; return S_OK; } // Copy of ff_spdif_bswap_buf16, which sadly is a private symbol static void lav_spdif_bswap_buf16(uint16_t *dst, const uint16_t *src, int w) { int i; for (i = 0; i + 8 <= w; i += 8) { dst[i + 0] = av_bswap16(src[i + 0]); dst[i + 1] = av_bswap16(src[i + 1]); dst[i + 2] = av_bswap16(src[i + 2]); dst[i + 3] = av_bswap16(src[i + 3]); dst[i + 4] = av_bswap16(src[i + 4]); dst[i + 5] = av_bswap16(src[i + 5]); dst[i + 6] = av_bswap16(src[i + 6]); dst[i + 7] = av_bswap16(src[i + 7]); } for (; i < w; i++) dst[i + 0] = av_bswap16(src[i + 0]); } HRESULT CLAVAudio::ProcessBuffer(BOOL bEOF) { HRESULT hr = S_OK, hr2 = S_OK; int buffer_size = m_buff.GetCount(); BYTE *p = m_buff.Ptr(); BYTE *base = p; BYTE *end = p + buffer_size; int consumed = 0; if (!bEOF) { if (m_bFindDTSInPCM) { int i = 0, count = 0; uint32_t state = -1; for (i = 0; i < buffer_size; ++i) { state = (state << 8) | p[i]; if ((state == DCA_MARKER_14B_LE && (i < buffer_size-2) && (p[i+1] & 0xF0) == 0xF0 && p[i+2] == 0x07) || (state == DCA_MARKER_14B_BE && (i < buffer_size-2) && p[i+1] == 0x07 && (p[i+2] & 0xF0) == 0xF0) || state == DCA_MARKER_RAW_LE || state == DCA_MARKER_RAW_BE) { count++; } } if (count >= 4) { DbgLog((LOG_TRACE, 10, L"::ProcessBuffer(): Detected %d DTS sync words in %d bytes of data, switching to DTS-in-WAV decoding", count, buffer_size)); CMediaType mt = m_pInput->CurrentMediaType(); ffmpeg_init(AV_CODEC_ID_DTS, mt.Format(), *mt.FormatType(), mt.FormatLength()); m_bFindDTSInPCM = FALSE; } if (buffer_size > (16384 * (4 + count))) { m_bFindDTSInPCM = FALSE; } if (m_bFindDTSInPCM) { return S_FALSE; } } if (m_pInput->CurrentMediaType().subtype == MEDIASUBTYPE_DOLBY_AC3_SPDIF) { if (buffer_size < BURST_HEADER_SIZE) return S_FALSE; uint16_t word1 = AV_RL16(p); uint16_t word2 = AV_RL16(p+2); if (word1 == SYNCWORD1 && word2 == SYNCWORD2) { uint16_t type = AV_RL16(p+4); int spdif_buffer_size = AV_RL16(p+6) >> 3; p += BURST_HEADER_SIZE; if (spdif_buffer_size+BURST_HEADER_SIZE > buffer_size) { DbgLog((LOG_ERROR, 10, L"::ProcessBuffer(): SPDIF sample is too small (%d required, %d present)", spdif_buffer_size+BURST_HEADER_SIZE, buffer_size)); m_buff.SetSize(0); m_bQueueResync = TRUE; return S_FALSE; } buffer_size = spdif_buffer_size; // SPDIF is apparently big-endian coded lav_spdif_bswap_buf16((uint16_t *)p, (uint16_t *)p, buffer_size >> 1); end = p + buffer_size; } } } else { if (!m_bFindDTSInPCM) { p = NULL; buffer_size = -1; } } // If a bitstreaming context exists, we should bitstream if (m_avBSContext) { hr2 = Bitstream(p, buffer_size, consumed, &hr); if (FAILED(hr2)) { DbgLog((LOG_TRACE, 10, L"Invalid sample when bitstreaming!")); m_buff.SetSize(0); m_bQueueResync = TRUE; return S_FALSE; } else if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"::Bitstream indicates delivery failed")); } else if (hr2 == S_FALSE) { //DbgLog((LOG_TRACE, 10, L"::Bitstream returned S_FALSE")); hr = S_FALSE; } } else { // Decoding // Consume the buffer data if (m_pDTSDecoderContext) hr2 = DecodeDTS(p, buffer_size, consumed, &hr); else hr2 = Decode(p, buffer_size, consumed, &hr); // FAILED - throw away the data if (FAILED(hr2)) { DbgLog((LOG_TRACE, 10, L"Dropped invalid sample in ProcessBuffer")); m_buff.SetSize(0); m_bQueueResync = TRUE; return S_FALSE; } else if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"::Decode indicates delivery failed")); } else if (hr2 == S_FALSE) { //DbgLog((LOG_TRACE, 10, L"::Decode returned S_FALSE")); hr = S_FALSE; } } if (bEOF || consumed <= 0) { return hr; } // This really shouldn't be needed, but apparently it is. consumed = min(consumed, buffer_size); // Remove the consumed data from the buffer p += consumed; memmove(base, p, end - p); end = base + (end - p); p = base; m_buff.SetSize((DWORD)(end - p)); return hr; } static DWORD get_lav_channel_layout(uint64_t layout) { if (layout > UINT32_MAX) { if (layout & AV_CH_WIDE_LEFT) layout = (layout & ~AV_CH_WIDE_LEFT) | AV_CH_FRONT_LEFT_OF_CENTER; if (layout & AV_CH_WIDE_RIGHT) layout = (layout & ~AV_CH_WIDE_RIGHT) | AV_CH_FRONT_RIGHT_OF_CENTER; if (layout & AV_CH_SURROUND_DIRECT_LEFT) layout = (layout & ~AV_CH_SURROUND_DIRECT_LEFT) | AV_CH_SIDE_LEFT; if (layout & AV_CH_SURROUND_DIRECT_RIGHT) layout = (layout & ~AV_CH_SURROUND_DIRECT_RIGHT) | AV_CH_SIDE_RIGHT; } return (DWORD)layout; } HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed, HRESULT *hrDeliver) { int got_frame = 0; BYTE *tmpProcessBuf = NULL; HRESULT hr = S_FALSE; BOOL bFlush = (buffer == NULL); AVPacket avpkt; av_init_packet(&avpkt); BufferDetails out; // Copy data onto our properly padded data buffer (to avoid overreads) const uint8_t *pDataBuffer = NULL; if (!m_bInputPadded) { if (!bFlush) { COPY_TO_BUFFER(buffer, buffsize); } pDataBuffer = m_pFFBuffer; } else { pDataBuffer = buffer; } if (m_raData.deint_id == MKBETAG('g', 'e', 'n', 'r') || m_raData.deint_id == MKBETAG('s', 'i', 'p', 'r')) { int w = m_raData.audio_framesize; int h = m_raData.sub_packet_h; int sps = m_raData.sub_packet_size; int len = w * h; if (buffsize >= len) { tmpProcessBuf = (BYTE *)av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); // "genr" deinterleaving is used for COOK and ATRAC if (m_raData.deint_id == MKBETAG('g', 'e', 'n', 'r')) { const BYTE *srcBuf = pDataBuffer; for(int y = 0; y < h; y++) { for(int x = 0, w2 = w / sps; x < w2; x++) { memcpy(tmpProcessBuf + sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), srcBuf, sps); srcBuf += sps; } } // "sipr" deinterleaving is used for ... SIPR } else if (m_raData.deint_id == MKBETAG('s', 'i', 'p', 'r')) { memcpy(tmpProcessBuf, pDataBuffer, len); ff_rm_reorder_sipr_data(tmpProcessBuf, h, w); } pDataBuffer = tmpProcessBuf; buffsize = len; m_rtStartInput = m_rtStartInputCache; m_rtStartInputCache = AV_NOPTS_VALUE; } else { if (m_rtStartInputCache == AV_NOPTS_VALUE) m_rtStartInputCache = m_rtStartInput; return S_FALSE; } } #ifdef DEBUG else if (m_raData.deint_id) { const char *deint = (const char *)&m_raData.deint_id; DbgLog((LOG_TRACE, 10, L"::Decode(): Unsupported deinterleaving algorithm '%c%c%c%c'", deint[3], deint[2], deint[1], deint[0])); } #endif consumed = 0; while (buffsize > 0 || bFlush) { got_frame = 0; if (bFlush) buffsize = 0; if (m_pParser) { BYTE *pOut = NULL; int pOut_size = 0; int used_bytes = av_parser_parse2(m_pParser, m_pAVCtx, &pOut, &pOut_size, pDataBuffer, buffsize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (used_bytes < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - audio parsing failed (ret: %d)", -used_bytes)); goto fail; } else if(used_bytes == 0 && pOut_size == 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } // Timestamp cache to compensate for one frame delay the parser might introduce, in case the frames were already perfectly sliced apart // If we used more (or equal) bytes then was output again, we encountered a new frame, update timestamps if (used_bytes >= pOut_size && m_bUpdateTimeCache) { m_rtStartInputCache = m_rtStartInput; m_rtStopInputCache = m_rtStopInput; m_rtStartInput = m_rtStopInput = AV_NOPTS_VALUE; m_bUpdateTimeCache = FALSE; } if (!bFlush && used_bytes > 0) { buffsize -= used_bytes; pDataBuffer += used_bytes; consumed += used_bytes; } if (pOut_size > 0) { avpkt.data = pOut; avpkt.size = pOut_size; int ret2 = avcodec_decode_audio4(m_pAVCtx, m_pFrame, &got_frame, &avpkt); if (ret2 < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - decoding failed despite successfull parsing")); m_bQueueResync = TRUE; continue; } // Send current input time to the delivery function out.rtStart = m_rtStartInputCache; m_rtStartInputCache = AV_NOPTS_VALUE; m_bUpdateTimeCache = TRUE; } else { continue; } } else if(bFlush) { hr = S_FALSE; break; } else { avpkt.data = (uint8_t *)pDataBuffer; avpkt.size = buffsize; int used_bytes = avcodec_decode_audio4(m_pAVCtx, m_pFrame, &got_frame, &avpkt); if(used_bytes < 0) { goto fail; } else if(used_bytes == 0 && !got_frame) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } buffsize -= used_bytes; pDataBuffer += used_bytes; consumed += used_bytes; // Send current input time to the delivery function out.rtStart = m_rtStartInput; m_rtStartInput = AV_NOPTS_VALUE; } // Channel re-mapping and sample format conversion if (got_frame && m_pFrame->nb_samples > 0) { out.wChannels = m_pAVCtx->channels; out.dwSamplesPerSec = m_pAVCtx->sample_rate; if (m_pAVCtx->channel_layout) out.dwChannelMask = get_lav_channel_layout(m_pAVCtx->channel_layout); else out.dwChannelMask = get_channel_mask(out.wChannels); out.nSamples = m_pFrame->nb_samples; DWORD dwPCMSize = out.nSamples * out.wChannels * av_get_bytes_per_sample(m_pAVCtx->sample_fmt); DWORD dwPCMSizeAligned = FFALIGN(out.nSamples, 32) * out.wChannels * av_get_bytes_per_sample(m_pAVCtx->sample_fmt); switch (m_pAVCtx->sample_fmt) { case AV_SAMPLE_FMT_U8: out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->Append(m_pFrame->data[0], dwPCMSize); out.sfFormat = SampleFormat_U8; break; case AV_SAMPLE_FMT_S16: out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->Append(m_pFrame->data[0], dwPCMSize); out.sfFormat = SampleFormat_16; break; case AV_SAMPLE_FMT_S32: out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->Append(m_pFrame->data[0], dwPCMSize); out.sfFormat = SampleFormat_32; out.wBitsPerSample = m_pAVCtx->bits_per_raw_sample; break; case AV_SAMPLE_FMT_FLT: out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->Append(m_pFrame->data[0], dwPCMSize); out.sfFormat = SampleFormat_FP32; break; case AV_SAMPLE_FMT_DBL: { out.bBuffer->Allocate(dwPCMSizeAligned / 2); out.bBuffer->SetSize(dwPCMSize / 2); float *pDataOut = (float *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pDataOut = (float)((double *)m_pFrame->data[0]) [ch+i*m_pAVCtx->channels]; pDataOut++; } } } out.sfFormat = SampleFormat_FP32; break; // Planar Formats case AV_SAMPLE_FMT_U8P: { out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->SetSize(dwPCMSize); uint8_t *pOut = (uint8_t *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pOut++ = ((uint8_t *)m_pFrame->extended_data[ch])[i]; } } } out.sfFormat = SampleFormat_U8; break; case AV_SAMPLE_FMT_S16P: { out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->SetSize(dwPCMSize); int16_t *pOut = (int16_t *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pOut++ = ((int16_t *)m_pFrame->extended_data[ch])[i]; } } } out.sfFormat = SampleFormat_16; break; case AV_SAMPLE_FMT_S32P: { out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->SetSize(dwPCMSize); int32_t *pOut = (int32_t *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pOut++ = ((int32_t *)m_pFrame->extended_data[ch])[i]; } } } out.sfFormat = SampleFormat_32; out.wBitsPerSample = m_pAVCtx->bits_per_raw_sample; break; case AV_SAMPLE_FMT_FLTP: { out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->SetSize(dwPCMSize); float *pOut = (float *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pOut++ = ((float *)m_pFrame->extended_data[ch])[i]; } } } out.sfFormat = SampleFormat_FP32; break; case AV_SAMPLE_FMT_DBLP: { out.bBuffer->Allocate(dwPCMSizeAligned); out.bBuffer->SetSize(dwPCMSize / 2); float *pOut = (float *)(out.bBuffer->Ptr()); for (size_t i = 0; i < out.nSamples; ++i) { for(int ch = 0; ch < out.wChannels; ++ch) { *pOut++ = (float)((double *)m_pFrame->extended_data[ch])[i]; } } } out.sfFormat = SampleFormat_FP32; break; default: assert(FALSE); break; } hr = S_OK; m_DecodeFormat = out.sfFormat == SampleFormat_32 && out.wBitsPerSample > 0 && out.wBitsPerSample <= 24 ? (out.wBitsPerSample <= 16 ? SampleFormat_16 : SampleFormat_24) : out.sfFormat; m_DecodeLayout = out.dwChannelMask; if (SUCCEEDED(PostProcess(&out))) { *hrDeliver = QueueOutput(out); if (FAILED(*hrDeliver)) { hr = S_FALSE; break; } } } } av_free(tmpProcessBuf); return hr; fail: av_free(tmpProcessBuf); return E_FAIL; } HRESULT CLAVAudio::GetDeliveryBuffer(IMediaSample** pSample, BYTE** pData) { HRESULT hr; *pData = NULL; if(FAILED(hr = m_pOutput->GetDeliveryBuffer(pSample, NULL, NULL, 0)) || FAILED(hr = (*pSample)->GetPointer(pData))) { return hr; } AM_MEDIA_TYPE* pmt = NULL; if(SUCCEEDED((*pSample)->GetMediaType(&pmt)) && pmt) { CMediaType mt = *pmt; m_pOutput->SetMediaType(&mt); DeleteMediaType(pmt); pmt = NULL; } return S_OK; } HRESULT CLAVAudio::QueueOutput(BufferDetails &buffer) { HRESULT hr = S_OK; if (m_OutputQueue.wChannels != buffer.wChannels || m_OutputQueue.sfFormat != buffer.sfFormat || m_OutputQueue.dwSamplesPerSec != buffer.dwSamplesPerSec || m_OutputQueue.dwChannelMask != buffer.dwChannelMask || m_OutputQueue.wBitsPerSample != buffer.wBitsPerSample) { if (m_OutputQueue.nSamples > 0) FlushOutput(); m_OutputQueue.sfFormat = buffer.sfFormat; m_OutputQueue.wChannels = buffer.wChannels; m_OutputQueue.dwChannelMask = buffer.dwChannelMask; m_OutputQueue.dwSamplesPerSec = buffer.dwSamplesPerSec; m_OutputQueue.wBitsPerSample = buffer.wBitsPerSample; } if (m_OutputQueue.nSamples == 0) m_OutputQueue.rtStart = buffer.rtStart; else if (m_OutputQueue.rtStart == AV_NOPTS_VALUE && buffer.rtStart != AV_NOPTS_VALUE) m_OutputQueue.rtStart = buffer.rtStart - (REFERENCE_TIME)((double)m_OutputQueue.nSamples / m_OutputQueue.dwSamplesPerSec * 10000000.0); // Try to retain the buffer, if possible if (m_OutputQueue.nSamples == 0) { FFSWAP(GrowableArray*, m_OutputQueue.bBuffer, buffer.bBuffer); } else { m_OutputQueue.bBuffer->Append(buffer.bBuffer); } m_OutputQueue.nSamples += buffer.nSamples; buffer.bBuffer->SetSize(0); buffer.nSamples = 0; // Length of the current sample double dDuration = (double)m_OutputQueue.nSamples / m_OutputQueue.dwSamplesPerSec * 10000000.0; double dOffset = fmod(dDuration, 1.0); // Don't exceed the buffer if (dDuration >= PCM_BUFFER_MAX_DURATION || (dDuration >= PCM_BUFFER_MIN_DURATION && dOffset <= FLT_EPSILON)) { hr = FlushOutput(); } return hr; } HRESULT CLAVAudio::FlushOutput(BOOL bDeliver) { CAutoLock cAutoLock(&m_csReceive); HRESULT hr = S_OK; if (bDeliver && m_OutputQueue.nSamples > 0) hr = Deliver(m_OutputQueue); // Clear Queue m_OutputQueue.nSamples = 0; m_OutputQueue.bBuffer->SetSize(0); return hr; } HRESULT CLAVAudio::Deliver(BufferDetails &buffer) { HRESULT hr = S_OK; CMediaType mt = CreateMediaType(buffer.sfFormat, buffer.dwSamplesPerSec, buffer.wChannels, buffer.dwChannelMask, buffer.wBitsPerSample); WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.Format(); long cbBuffer = buffer.nSamples * wfe->nBlockAlign; if(FAILED(hr = ReconnectOutput(cbBuffer, mt))) { return hr; } IMediaSample *pOut; BYTE *pDataOut = NULL; if(FAILED(GetDeliveryBuffer(&pOut, &pDataOut))) { return E_FAIL; } // Length of the current sample double dDuration = (double)buffer.nSamples / buffer.dwSamplesPerSec * DBL_SECOND_MULT / m_dRate; m_dStartOffset += fmod(dDuration, 1.0); // Delivery Timestamps REFERENCE_TIME rtStart = m_rtStart, rtStop = m_rtStart + (REFERENCE_TIME)(dDuration + 0.5); // Compute next start time m_rtStart += (REFERENCE_TIME)dDuration; // If the offset reaches one (100ns), add it to the next frame if (m_dStartOffset > 0.5) { m_rtStart++; m_dStartOffset -= 1.0; } if (buffer.rtStart != AV_NOPTS_VALUE) { REFERENCE_TIME rtJitter = rtStart - buffer.rtStart; m_faJitter.Sample(rtJitter); REFERENCE_TIME rtJitterMin = m_faJitter.AbsMinimum(); if (m_settings.AutoAVSync && abs(rtJitterMin) > m_JitterLimit && m_bHasVideo) { DbgLog((LOG_TRACE, 10, L"::Deliver(): corrected A/V sync by %I64d", rtJitterMin)); m_rtStart -= rtJitterMin; m_faJitter.OffsetValues(-rtJitterMin); } #ifdef DEBUG if (m_faJitter.CurrentSample() == 0) { DbgLog((LOG_CUSTOM2, 20, L"Jitter Stats: min: %I64d - max: %I64d - avg: %I64d", rtJitterMin, m_faJitter.AbsMaximum(), m_faJitter.Average())); } #endif DbgLog((LOG_CUSTOM5, 20, L"PCM Delivery, rtStart(calc): %I64d, rtStart(input): %I64d, sample duration: %I64d, diff: %I64d", rtStart, buffer.rtStart, rtStop-rtStart, rtJitter)); } else { DbgLog((LOG_CUSTOM5, 20, L"PCM Delivery, rtStart(calc): %I64d, rtStart(input): N/A, sample duration: %I64d, diff: N/A", rtStart, rtStop-rtStart)); } if(rtStart < 0) { goto done; } if(hr == S_OK) { hr = m_pOutput->GetConnected()->QueryAccept(&mt); DbgLog((LOG_TRACE, 1, L"Sending new Media Type (QueryAccept: %0#.8x)", hr)); if (hr != S_OK) { if (buffer.sfFormat != SampleFormat_16) { mt = CreateMediaType(SampleFormat_16, buffer.dwSamplesPerSec, buffer.wChannels, buffer.dwChannelMask, 16); hr = m_pOutput->GetConnected()->QueryAccept(&mt); if (hr == S_OK) { DbgLog((LOG_TRACE, 1, L"-> 16-bit fallback type accepted")); ConvertSampleFormat(&buffer, SampleFormat_16); m_bFallback16Int = TRUE; } } // If a 16-bit fallback isn't enough, try to retain current channel layout if (hr != S_OK) { WAVEFORMATEX* wfeCurrent = (WAVEFORMATEX*)m_pOutput->CurrentMediaType().Format(); WORD wChannels = wfeCurrent->nChannels; DWORD dwChannelMask = 0; if (wfeCurrent->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)wfeCurrent; dwChannelMask = wfex->dwChannelMask; } else { dwChannelMask = wChannels == 2 ? (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT) : SPEAKER_FRONT_CENTER; } mt = CreateMediaType(buffer.sfFormat, buffer.dwSamplesPerSec, wChannels, dwChannelMask, buffer.wBitsPerSample); hr = m_pOutput->GetConnected()->QueryAccept(&mt); if (hr == S_OK) { DbgLog((LOG_TRACE, 1, L"-> Override Mixing to layout 0x%x", dwChannelMask)); m_dwOverrideMixer = dwChannelMask; m_bMixingSettingsChanged = TRUE; LAVAudioSampleFormat sf = buffer.sfFormat; // Mix to the new layout PerformMixing(&buffer); // Convert to old sample format, if required if (buffer.sfFormat != sf) { ConvertSampleFormat(&buffer, sf); } } else { // If current channel layout alone wasn't enough, also go 16-bit mt = CreateMediaType(SampleFormat_16, buffer.dwSamplesPerSec, wChannels, dwChannelMask, 16); hr = m_pOutput->GetConnected()->QueryAccept(&mt); if (hr == S_OK) { DbgLog((LOG_TRACE, 1, L"-> Override Mixing to layout 0x%x with 16-bit fallback type", dwChannelMask)); m_dwOverrideMixer = dwChannelMask; m_bMixingSettingsChanged = TRUE; // Mix to new layout PerformMixing(&buffer); // Convert to 16-bit ConvertSampleFormat(&buffer, SampleFormat_16); m_bFallback16Int = TRUE; } } } } m_pOutput->SetMediaType(&mt); pOut->SetMediaType(&mt); } if(m_settings.AudioDelayEnabled) { REFERENCE_TIME rtDelay = (REFERENCE_TIME)((m_settings.AudioDelay * 10000i64) / m_dRate); rtStart += rtDelay; rtStop += rtDelay; } pOut->SetTime(&rtStart, &rtStop); pOut->SetMediaTime(NULL, NULL); pOut->SetPreroll(FALSE); pOut->SetDiscontinuity(m_bDiscontinuity); m_bDiscontinuity = FALSE; pOut->SetSyncPoint(TRUE); pOut->SetActualDataLength(buffer.bBuffer->GetCount()); memcpy(pDataOut, buffer.bBuffer->Ptr(), buffer.bBuffer->GetCount()); hr = m_pOutput->Deliver(pOut); if (FAILED(hr)) { DbgLog((LOG_ERROR, 10, L"::Deliver failed with code: %0#.8x", hr)); } done: SafeRelease(&pOut); return hr; } HRESULT CLAVAudio::StartStreaming() { HRESULT hr = __super::StartStreaming(); if(FAILED(hr)) { return hr; } m_bDiscontinuity = FALSE; return S_OK; } HRESULT CLAVAudio::StopStreaming() { return __super::StopStreaming(); } HRESULT CLAVAudio::BreakConnect(PIN_DIRECTION dir) { if(dir == PINDIR_INPUT) { ffmpeg_shutdown(); } return __super::BreakConnect(dir); }